@graphql-yoga/plugin-apollo-usage-report 0.9.0-alpha-20250516145220-f0c0edf8042caaad772e2ada1ef90d4b16ff82f8 → 0.9.0-alpha-20250527093154-d0be5f4626600bfbe255c0ab57590d3963013823

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/cjs/index.js CHANGED
@@ -5,12 +5,13 @@ exports.hashSHA256 = hashSHA256;
5
5
  const graphql_1 = require("graphql");
6
6
  const graphql_yoga_1 = require("graphql-yoga");
7
7
  const utils_usagereporting_1 = require("@apollo/utils.usagereporting");
8
+ const utils_1 = require("@graphql-tools/utils");
8
9
  const plugin_apollo_inline_trace_1 = require("@graphql-yoga/plugin-apollo-inline-trace");
9
10
  const reporter_js_1 = require("./reporter.js");
10
11
  function useApolloUsageReport(options = {}) {
11
12
  const [instrumentation, ctxForReq] = (0, plugin_apollo_inline_trace_1.useApolloInstrumentation)(options);
12
13
  let schemaIdSet$;
13
- let schema;
14
+ let currentSchema;
14
15
  let yoga;
15
16
  let reporter;
16
17
  const logger = Object.fromEntries(['error', 'warn', 'info', 'debug'].map(level => [
@@ -41,9 +42,9 @@ function useApolloUsageReport(options = {}) {
41
42
  },
42
43
  onSchemaChange({ schema }) {
43
44
  if (schema) {
44
- schemaIdSet$ = hashSHA256((0, graphql_1.printSchema)(schema), yoga.fetchAPI)
45
+ schemaIdSet$ = hashSHA256((0, utils_1.printSchemaWithDirectives)(schema), yoga.fetchAPI)
45
46
  .then(id => {
46
- schema = { id, schema };
47
+ currentSchema = { id, schema };
47
48
  schemaIdSet$ = undefined;
48
49
  })
49
50
  .catch(error => {
@@ -56,7 +57,7 @@ function useApolloUsageReport(options = {}) {
56
57
  },
57
58
  onParse() {
58
59
  return function onParseEnd({ result, context }) {
59
- if (!schema) {
60
+ if (!currentSchema) {
60
61
  throw new Error("should not happen: schema doesn't exists");
61
62
  }
62
63
  const ctx = ctxForReq.get(context.request)?.traces.get(context);
@@ -64,18 +65,21 @@ function useApolloUsageReport(options = {}) {
64
65
  logger.debug('operation tracing context not found, this operation will not be traced.');
65
66
  return;
66
67
  }
67
- const operationName = context.params.operationName ??
68
- (isDocumentNode(result) ? (0, graphql_1.getOperationAST)(result)?.name?.value : undefined);
69
- const signature = operationName
70
- ? (0, utils_usagereporting_1.usageReportingSignature)(result, operationName)
71
- : (context.params.query ?? '');
72
- ctx.referencedFieldsByType = (0, utils_usagereporting_1.calculateReferencedFieldsByType)({
73
- document: result,
74
- schema: schema.schema,
75
- resolvedOperationName: operationName ?? null,
76
- });
77
- ctx.operationKey = `# ${operationName || '-'}\n${signature}`;
78
- ctx.schemaId = schema.id;
68
+ ctx.schemaId = currentSchema.id;
69
+ // It is possible that the result is not a document when the parsing fails
70
+ const document = isDocumentNode(result) ? result : null;
71
+ if (document) {
72
+ const opName = (0, graphql_1.getOperationAST)(document, context.params.operationName)?.name?.value;
73
+ ctx.referencedFieldsByType = (0, utils_usagereporting_1.calculateReferencedFieldsByType)({
74
+ document,
75
+ schema: currentSchema.schema,
76
+ resolvedOperationName: opName ?? null,
77
+ });
78
+ ctx.operationKey = `# ${opName || '-'}\n${opName && (0, utils_usagereporting_1.usageReportingSignature)(document, opName)}`;
79
+ }
80
+ else {
81
+ ctx.operationKey = `# ${context.params.operationName || '-'} \n${context.params.query ?? ''}`;
82
+ }
79
83
  };
80
84
  },
81
85
  onResultProcess({ request, result, serverContext }) {
@@ -102,7 +106,7 @@ function useApolloUsageReport(options = {}) {
102
106
  if (clientVersion) {
103
107
  trace.trace.clientVersion = clientVersion;
104
108
  }
105
- serverContext.waitUntil(reporter.addTrace(schema.id, {
109
+ serverContext.waitUntil(reporter.addTrace(currentSchema.id, {
106
110
  statsReportKey: trace.operationKey,
107
111
  trace: trace.trace,
108
112
  referencedFieldsByType: trace.referencedFieldsByType,
package/cjs/reporter.js CHANGED
@@ -19,10 +19,10 @@ class Reporter {
19
19
  this.#yoga = yoga;
20
20
  this.#options = {
21
21
  ...options,
22
- maxBatchDelay: options.maxBatchDelay ?? 20 * 60 * 1000, // 20min
22
+ maxBatchDelay: options.maxBatchDelay ?? 20_000, // 20s
23
23
  maxBatchUncompressedSize: options.maxBatchUncompressedSize ?? 4 * 1024 * 1024, // 4mb
24
24
  maxTraceSize: options.maxTraceSize ?? 10 * 1024 * 1024, // 10mb
25
- exportTimeout: options.exportTimeout ?? 30 * 1000, // 60s
25
+ exportTimeout: options.exportTimeout ?? 30_000, // 30s
26
26
  };
27
27
  this.#reportHeaders = {
28
28
  graphRef: getGraphRef(options),
@@ -62,27 +62,20 @@ class Reporter {
62
62
  }
63
63
  if (this.#nextSendAfterDelay != null) {
64
64
  clearTimeout(this.#nextSendAfterDelay);
65
+ this.#nextSendAfterDelay = undefined;
65
66
  }
66
67
  delete this.#reportsBySchema[schemaId];
67
68
  report.endTime = dateToProtoTimestamp(new Date());
68
69
  report.ensureCountsAreIntegers();
69
70
  const validationError = usage_reporting_protobuf_1.Report.verify(report);
70
- if (!validationError) {
71
+ if (validationError) {
71
72
  throw new TypeError(`Invalid report: ${validationError}`);
72
73
  }
73
- const encodedReport = usage_reporting_protobuf_1.Report.encode(report).finish();
74
- const compressedReport = new ReadableStream({
75
- start(controller) {
76
- controller.enqueue(encodedReport);
77
- controller.close();
78
- },
79
- }).pipeThrough(new CompressionStream('gzip'));
80
74
  const { apiKey = getEnvVar('APOLLO_KEY'), endpoint = DEFAULT_REPORTING_ENDPOINT } = this.#options;
75
+ const encodedReport = usage_reporting_protobuf_1.Report.encode(report).finish();
81
76
  for (let tries = 0; tries < 5; tries++) {
82
77
  try {
83
- const abortCtl = new AbortController();
84
78
  this.#logger?.debug(`Sending report (try ${tries}/5)`);
85
- const timeout = setTimeout(() => abortCtl.abort(), this.#options.exportTimeout);
86
79
  const response = await fetch(endpoint, {
87
80
  method: 'POST',
88
81
  headers: {
@@ -92,10 +85,14 @@ class Reporter {
92
85
  'x-api-key': apiKey,
93
86
  accept: 'application/json',
94
87
  },
95
- body: compressedReport,
96
- signal: abortCtl.signal,
88
+ body: new ReadableStream({
89
+ start(controller) {
90
+ controller.enqueue(encodedReport);
91
+ controller.close();
92
+ },
93
+ }).pipeThrough(new CompressionStream('gzip')),
94
+ signal: AbortSignal.timeout(this.#options.exportTimeout),
97
95
  });
98
- clearTimeout(timeout);
99
96
  const result = await response.text();
100
97
  if (response.ok) {
101
98
  this.#logger?.debug('Report sent:', result);
package/esm/index.js CHANGED
@@ -1,12 +1,13 @@
1
- import { getOperationAST, Kind, printSchema } from 'graphql';
1
+ import { getOperationAST, Kind } from 'graphql';
2
2
  import { isAsyncIterable, } from 'graphql-yoga';
3
3
  import { calculateReferencedFieldsByType, usageReportingSignature, } from '@apollo/utils.usagereporting';
4
+ import { printSchemaWithDirectives } from '@graphql-tools/utils';
4
5
  import { useApolloInstrumentation, } from '@graphql-yoga/plugin-apollo-inline-trace';
5
6
  import { getEnvVar, Reporter } from './reporter.js';
6
7
  export function useApolloUsageReport(options = {}) {
7
8
  const [instrumentation, ctxForReq] = useApolloInstrumentation(options);
8
9
  let schemaIdSet$;
9
- let schema;
10
+ let currentSchema;
10
11
  let yoga;
11
12
  let reporter;
12
13
  const logger = Object.fromEntries(['error', 'warn', 'info', 'debug'].map(level => [
@@ -37,9 +38,9 @@ export function useApolloUsageReport(options = {}) {
37
38
  },
38
39
  onSchemaChange({ schema }) {
39
40
  if (schema) {
40
- schemaIdSet$ = hashSHA256(printSchema(schema), yoga.fetchAPI)
41
+ schemaIdSet$ = hashSHA256(printSchemaWithDirectives(schema), yoga.fetchAPI)
41
42
  .then(id => {
42
- schema = { id, schema };
43
+ currentSchema = { id, schema };
43
44
  schemaIdSet$ = undefined;
44
45
  })
45
46
  .catch(error => {
@@ -52,7 +53,7 @@ export function useApolloUsageReport(options = {}) {
52
53
  },
53
54
  onParse() {
54
55
  return function onParseEnd({ result, context }) {
55
- if (!schema) {
56
+ if (!currentSchema) {
56
57
  throw new Error("should not happen: schema doesn't exists");
57
58
  }
58
59
  const ctx = ctxForReq.get(context.request)?.traces.get(context);
@@ -60,18 +61,21 @@ export function useApolloUsageReport(options = {}) {
60
61
  logger.debug('operation tracing context not found, this operation will not be traced.');
61
62
  return;
62
63
  }
63
- const operationName = context.params.operationName ??
64
- (isDocumentNode(result) ? getOperationAST(result)?.name?.value : undefined);
65
- const signature = operationName
66
- ? usageReportingSignature(result, operationName)
67
- : (context.params.query ?? '');
68
- ctx.referencedFieldsByType = calculateReferencedFieldsByType({
69
- document: result,
70
- schema: schema.schema,
71
- resolvedOperationName: operationName ?? null,
72
- });
73
- ctx.operationKey = `# ${operationName || '-'}\n${signature}`;
74
- ctx.schemaId = schema.id;
64
+ ctx.schemaId = currentSchema.id;
65
+ // It is possible that the result is not a document when the parsing fails
66
+ const document = isDocumentNode(result) ? result : null;
67
+ if (document) {
68
+ const opName = getOperationAST(document, context.params.operationName)?.name?.value;
69
+ ctx.referencedFieldsByType = calculateReferencedFieldsByType({
70
+ document,
71
+ schema: currentSchema.schema,
72
+ resolvedOperationName: opName ?? null,
73
+ });
74
+ ctx.operationKey = `# ${opName || '-'}\n${opName && usageReportingSignature(document, opName)}`;
75
+ }
76
+ else {
77
+ ctx.operationKey = `# ${context.params.operationName || '-'} \n${context.params.query ?? ''}`;
78
+ }
75
79
  };
76
80
  },
77
81
  onResultProcess({ request, result, serverContext }) {
@@ -98,7 +102,7 @@ export function useApolloUsageReport(options = {}) {
98
102
  if (clientVersion) {
99
103
  trace.trace.clientVersion = clientVersion;
100
104
  }
101
- serverContext.waitUntil(reporter.addTrace(schema.id, {
105
+ serverContext.waitUntil(reporter.addTrace(currentSchema.id, {
102
106
  statsReportKey: trace.operationKey,
103
107
  trace: trace.trace,
104
108
  referencedFieldsByType: trace.referencedFieldsByType,
package/esm/reporter.js CHANGED
@@ -14,10 +14,10 @@ export class Reporter {
14
14
  this.#yoga = yoga;
15
15
  this.#options = {
16
16
  ...options,
17
- maxBatchDelay: options.maxBatchDelay ?? 20 * 60 * 1000, // 20min
17
+ maxBatchDelay: options.maxBatchDelay ?? 20_000, // 20s
18
18
  maxBatchUncompressedSize: options.maxBatchUncompressedSize ?? 4 * 1024 * 1024, // 4mb
19
19
  maxTraceSize: options.maxTraceSize ?? 10 * 1024 * 1024, // 10mb
20
- exportTimeout: options.exportTimeout ?? 30 * 1000, // 60s
20
+ exportTimeout: options.exportTimeout ?? 30_000, // 30s
21
21
  };
22
22
  this.#reportHeaders = {
23
23
  graphRef: getGraphRef(options),
@@ -57,27 +57,20 @@ export class Reporter {
57
57
  }
58
58
  if (this.#nextSendAfterDelay != null) {
59
59
  clearTimeout(this.#nextSendAfterDelay);
60
+ this.#nextSendAfterDelay = undefined;
60
61
  }
61
62
  delete this.#reportsBySchema[schemaId];
62
63
  report.endTime = dateToProtoTimestamp(new Date());
63
64
  report.ensureCountsAreIntegers();
64
65
  const validationError = Report.verify(report);
65
- if (!validationError) {
66
+ if (validationError) {
66
67
  throw new TypeError(`Invalid report: ${validationError}`);
67
68
  }
68
- const encodedReport = Report.encode(report).finish();
69
- const compressedReport = new ReadableStream({
70
- start(controller) {
71
- controller.enqueue(encodedReport);
72
- controller.close();
73
- },
74
- }).pipeThrough(new CompressionStream('gzip'));
75
69
  const { apiKey = getEnvVar('APOLLO_KEY'), endpoint = DEFAULT_REPORTING_ENDPOINT } = this.#options;
70
+ const encodedReport = Report.encode(report).finish();
76
71
  for (let tries = 0; tries < 5; tries++) {
77
72
  try {
78
- const abortCtl = new AbortController();
79
73
  this.#logger?.debug(`Sending report (try ${tries}/5)`);
80
- const timeout = setTimeout(() => abortCtl.abort(), this.#options.exportTimeout);
81
74
  const response = await fetch(endpoint, {
82
75
  method: 'POST',
83
76
  headers: {
@@ -87,10 +80,14 @@ export class Reporter {
87
80
  'x-api-key': apiKey,
88
81
  accept: 'application/json',
89
82
  },
90
- body: compressedReport,
91
- signal: abortCtl.signal,
83
+ body: new ReadableStream({
84
+ start(controller) {
85
+ controller.enqueue(encodedReport);
86
+ controller.close();
87
+ },
88
+ }).pipeThrough(new CompressionStream('gzip')),
89
+ signal: AbortSignal.timeout(this.#options.exportTimeout),
92
90
  });
93
- clearTimeout(timeout);
94
91
  const result = await response.text();
95
92
  if (response.ok) {
96
93
  this.#logger?.debug('Report sent:', result);
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@graphql-yoga/plugin-apollo-usage-report",
3
- "version": "0.9.0-alpha-20250516145220-f0c0edf8042caaad772e2ada1ef90d4b16ff82f8",
3
+ "version": "0.9.0-alpha-20250527093154-d0be5f4626600bfbe255c0ab57590d3963013823",
4
4
  "description": "Apollo's GraphOS usage report plugin for GraphQL Yoga.",
5
5
  "peerDependencies": {
6
6
  "graphql": "^15.2.0 || ^16.0.0",
7
- "graphql-yoga": "^5.13.4"
7
+ "graphql-yoga": "^5.13.5"
8
8
  },
9
9
  "dependencies": {
10
10
  "@apollo/server-gateway-interface": "^1.1.1",
11
11
  "@apollo/usage-reporting-protobuf": "^4.1.1",
12
12
  "@apollo/utils.usagereporting": "^2.1.0",
13
+ "@graphql-tools/utils": "^10.8.6",
13
14
  "@whatwg-node/promise-helpers": "^1.2.4",
14
15
  "tslib": "^2.8.1",
15
- "@graphql-yoga/plugin-apollo-inline-trace": "^3.13.4"
16
+ "@graphql-yoga/plugin-apollo-inline-trace": "^3.13.5"
16
17
  },
17
18
  "repository": {
18
19
  "type": "git",