Initialisation

Added the packages and files for the backend server
This commit is contained in:
jackbeeby
2024-12-15 17:48:45 +11:00
parent 25066e1ee8
commit b412dfe2ca
2732 changed files with 330572 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
import type { CacheHint } from '@apollo/cache-control-types';
export interface ApolloServerPluginCacheControlOptions {
defaultMaxAge?: number;
calculateHttpHeaders?: boolean | 'if-cacheable';
__testing__cacheHints?: Map<string, CacheHint>;
}
export declare function ApolloServerPluginCacheControl(options?: ApolloServerPluginCacheControlOptions): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/cacheControl/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAcvE,OAAO,KAAK,EACV,SAAS,EAGV,MAAM,6BAA6B,CAAC;AAYrC,MAAM,WAAW,qCAAqC;IASpD,aAAa,CAAC,EAAE,MAAM,CAAC;IAUvB,oBAAoB,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAEhD,qBAAqB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAChD;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,qCAA2D,GACnE,kBAAkB,CAyRpB"}

View File

@@ -0,0 +1,220 @@
import { getNamedType, isCompositeType, isInterfaceType, isObjectType, responsePathAsArray, } from 'graphql';
import { newCachePolicy } from '../../cachePolicy.js';
import { internalPlugin } from '../../internalPlugin.js';
import LRUCache from 'lru-cache';
export function ApolloServerPluginCacheControl(options = Object.create(null)) {
let typeAnnotationCache;
let fieldAnnotationCache;
return internalPlugin({
__internal_plugin_id__: 'CacheControl',
__is_disabled_plugin__: false,
async serverWillStart({ schema }) {
typeAnnotationCache = new LRUCache({
max: Object.values(schema.getTypeMap()).filter(isCompositeType)
.length,
});
fieldAnnotationCache = new LRUCache({
max: Object.values(schema.getTypeMap())
.filter(isObjectType)
.flatMap((t) => Object.values(t.getFields())).length +
Object.values(schema.getTypeMap())
.filter(isInterfaceType)
.flatMap((t) => Object.values(t.getFields())).length,
});
return undefined;
},
async requestDidStart(requestContext) {
function memoizedCacheAnnotationFromType(t) {
const existing = typeAnnotationCache.get(t);
if (existing) {
return existing;
}
const annotation = cacheAnnotationFromType(t);
typeAnnotationCache.set(t, annotation);
return annotation;
}
function memoizedCacheAnnotationFromField(field) {
const existing = fieldAnnotationCache.get(field);
if (existing) {
return existing;
}
const annotation = cacheAnnotationFromField(field);
fieldAnnotationCache.set(field, annotation);
return annotation;
}
const defaultMaxAge = options.defaultMaxAge ?? 0;
const calculateHttpHeaders = options.calculateHttpHeaders ?? true;
const { __testing__cacheHints } = options;
return {
async executionDidStart() {
if (isRestricted(requestContext.overallCachePolicy)) {
const fakeFieldPolicy = newCachePolicy();
return {
willResolveField({ info }) {
info.cacheControl = {
setCacheHint: (dynamicHint) => {
fakeFieldPolicy.replace(dynamicHint);
},
cacheHint: fakeFieldPolicy,
cacheHintFromType: memoizedCacheAnnotationFromType,
};
},
};
}
return {
willResolveField({ info }) {
const fieldPolicy = newCachePolicy();
let inheritMaxAge = false;
const targetType = getNamedType(info.returnType);
if (isCompositeType(targetType)) {
const typeAnnotation = memoizedCacheAnnotationFromType(targetType);
fieldPolicy.replace(typeAnnotation);
inheritMaxAge = !!typeAnnotation.inheritMaxAge;
}
const fieldAnnotation = memoizedCacheAnnotationFromField(info.parentType.getFields()[info.fieldName]);
if (fieldAnnotation.inheritMaxAge &&
fieldPolicy.maxAge === undefined) {
inheritMaxAge = true;
if (fieldAnnotation.scope) {
fieldPolicy.replace({ scope: fieldAnnotation.scope });
}
}
else {
fieldPolicy.replace(fieldAnnotation);
}
info.cacheControl = {
setCacheHint: (dynamicHint) => {
fieldPolicy.replace(dynamicHint);
},
cacheHint: fieldPolicy,
cacheHintFromType: memoizedCacheAnnotationFromType,
};
return () => {
if (fieldPolicy.maxAge === undefined &&
((isCompositeType(targetType) && !inheritMaxAge) ||
!info.path.prev)) {
fieldPolicy.restrict({ maxAge: defaultMaxAge });
}
if (__testing__cacheHints && isRestricted(fieldPolicy)) {
const path = responsePathAsArray(info.path).join('.');
if (__testing__cacheHints.has(path)) {
throw Error("shouldn't happen: addHint should only be called once per path");
}
__testing__cacheHints.set(path, {
maxAge: fieldPolicy.maxAge,
scope: fieldPolicy.scope,
});
}
requestContext.overallCachePolicy.restrict(fieldPolicy);
};
},
};
},
async willSendResponse(requestContext) {
if (!calculateHttpHeaders) {
return;
}
const { response, overallCachePolicy } = requestContext;
const existingCacheControlHeader = parseExistingCacheControlHeader(response.http.headers.get('cache-control'));
if (existingCacheControlHeader.kind === 'unparsable') {
return;
}
const cachePolicy = newCachePolicy();
cachePolicy.replace(overallCachePolicy);
if (existingCacheControlHeader.kind === 'parsable-and-cacheable') {
cachePolicy.restrict(existingCacheControlHeader.hint);
}
const policyIfCacheable = cachePolicy.policyIfCacheable();
if (policyIfCacheable &&
existingCacheControlHeader.kind !== 'uncacheable' &&
response.body.kind === 'single' &&
!response.body.singleResult.errors) {
response.http.headers.set('cache-control', `max-age=${policyIfCacheable.maxAge}, ${policyIfCacheable.scope.toLowerCase()}`);
}
else if (calculateHttpHeaders !== 'if-cacheable') {
response.http.headers.set('cache-control', CACHE_CONTROL_HEADER_UNCACHEABLE);
}
},
};
},
});
}
const CACHE_CONTROL_HEADER_CACHEABLE_REGEXP = /^max-age=(\d+), (public|private)$/;
const CACHE_CONTROL_HEADER_UNCACHEABLE = 'no-store';
function parseExistingCacheControlHeader(header) {
if (!header) {
return { kind: 'no-header' };
}
if (header === CACHE_CONTROL_HEADER_UNCACHEABLE) {
return { kind: 'uncacheable' };
}
const match = CACHE_CONTROL_HEADER_CACHEABLE_REGEXP.exec(header);
if (!match) {
return { kind: 'unparsable' };
}
return {
kind: 'parsable-and-cacheable',
hint: {
maxAge: +match[1],
scope: match[2] === 'public' ? 'PUBLIC' : 'PRIVATE',
},
};
}
function cacheAnnotationFromDirectives(directives) {
if (!directives)
return undefined;
const cacheControlDirective = directives.find((directive) => directive.name.value === 'cacheControl');
if (!cacheControlDirective)
return undefined;
if (!cacheControlDirective.arguments)
return undefined;
const maxAgeArgument = cacheControlDirective.arguments.find((argument) => argument.name.value === 'maxAge');
const scopeArgument = cacheControlDirective.arguments.find((argument) => argument.name.value === 'scope');
const inheritMaxAgeArgument = cacheControlDirective.arguments.find((argument) => argument.name.value === 'inheritMaxAge');
const scopeString = scopeArgument?.value?.kind === 'EnumValue'
? scopeArgument.value.value
: undefined;
const scope = scopeString === 'PUBLIC' || scopeString === 'PRIVATE'
? scopeString
: undefined;
if (inheritMaxAgeArgument?.value?.kind === 'BooleanValue' &&
inheritMaxAgeArgument.value.value) {
return { inheritMaxAge: true, scope };
}
return {
maxAge: maxAgeArgument?.value?.kind === 'IntValue'
? parseInt(maxAgeArgument.value.value)
: undefined,
scope,
};
}
function cacheAnnotationFromType(t) {
if (t.astNode) {
const hint = cacheAnnotationFromDirectives(t.astNode.directives);
if (hint) {
return hint;
}
}
if (t.extensionASTNodes) {
for (const node of t.extensionASTNodes) {
const hint = cacheAnnotationFromDirectives(node.directives);
if (hint) {
return hint;
}
}
}
return {};
}
function cacheAnnotationFromField(field) {
if (field.astNode) {
const hint = cacheAnnotationFromDirectives(field.astNode.directives);
if (hint) {
return hint;
}
}
return {};
}
function isRestricted(hint) {
return hint.maxAge !== undefined || hint.scope !== undefined;
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
export declare function ApolloServerPluginDisableSuggestions(): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/disableSuggestions/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,wBAAgB,oCAAoC,IAAI,kBAAkB,CAmBzE"}

View File

@@ -0,0 +1,19 @@
import { internalPlugin } from '../../internalPlugin.js';
export function ApolloServerPluginDisableSuggestions() {
return internalPlugin({
__internal_plugin_id__: 'DisableSuggestions',
__is_disabled_plugin__: false,
async requestDidStart() {
return {
async validationDidStart() {
return async (validationErrors) => {
validationErrors?.forEach((error) => {
error.message = error.message.replace(/ ?Did you mean(.+?)\?$/, '');
});
};
},
};
},
});
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/disableSuggestions/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,UAAU,oCAAoC;IAClD,OAAO,cAAc,CAAC;QACpB,sBAAsB,EAAE,oBAAoB;QAC5C,sBAAsB,EAAE,KAAK;QAC7B,KAAK,CAAC,eAAe;YACnB,OAAO;gBACL,KAAK,CAAC,kBAAkB;oBACtB,OAAO,KAAK,EAAE,gBAAgB,EAAE,EAAE;wBAChC,gBAAgB,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;4BAClC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CACnC,wBAAwB,EACxB,EAAE,CACH,CAAC;wBACJ,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC;gBACJ,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,7 @@
import type { BaseContext, ApolloServerPlugin } from '../../index.js';
export declare function ApolloServerPluginCacheControlDisabled(): ApolloServerPlugin<BaseContext>;
export declare function ApolloServerPluginInlineTraceDisabled(): ApolloServerPlugin<BaseContext>;
export declare function ApolloServerPluginLandingPageDisabled(): ApolloServerPlugin<BaseContext>;
export declare function ApolloServerPluginSchemaReportingDisabled(): ApolloServerPlugin<BaseContext>;
export declare function ApolloServerPluginUsageReportingDisabled(): ApolloServerPlugin<BaseContext>;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/disabled/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AActE,wBAAgB,sCAAsC,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAExF;AAED,wBAAgB,qCAAqC,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAEvF;AAED,wBAAgB,qCAAqC,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAEvF;AAED,wBAAgB,yCAAyC,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAE3F;AAED,wBAAgB,wCAAwC,IAAI,kBAAkB,CAAC,WAAW,CAAC,CAE1F"}

View File

@@ -0,0 +1,23 @@
function disabledPlugin(id) {
const plugin = {
__internal_plugin_id__: id,
__is_disabled_plugin__: true,
};
return plugin;
}
export function ApolloServerPluginCacheControlDisabled() {
return disabledPlugin('CacheControl');
}
export function ApolloServerPluginInlineTraceDisabled() {
return disabledPlugin('InlineTrace');
}
export function ApolloServerPluginLandingPageDisabled() {
return disabledPlugin('LandingPageDisabled');
}
export function ApolloServerPluginSchemaReportingDisabled() {
return disabledPlugin('SchemaReporting');
}
export function ApolloServerPluginUsageReportingDisabled() {
return disabledPlugin('UsageReporting');
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/disabled/index.ts"],"names":[],"mappings":"AAcA,SAAS,cAAc,CAAC,EAAoB;IAC1C,MAAM,MAAM,GAA4C;QACtD,sBAAsB,EAAE,EAAE;QAC1B,sBAAsB,EAAE,IAAI;KAC7B,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,sCAAsC;IACpD,OAAO,cAAc,CAAC,cAAc,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,OAAO,cAAc,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,OAAO,cAAc,CAAC,qBAAqB,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,yCAAyC;IACvD,OAAO,cAAc,CAAC,iBAAiB,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,wCAAwC;IACtD,OAAO,cAAc,CAAC,gBAAgB,CAAC,CAAC;AAC1C,CAAC"}

View File

@@ -0,0 +1,9 @@
/// <reference types="node" />
import type http from 'http';
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
export interface ApolloServerPluginDrainHttpServerOptions {
httpServer: http.Server;
stopGracePeriodMillis?: number;
}
export declare function ApolloServerPluginDrainHttpServer(options: ApolloServerPluginDrainHttpServerOptions): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/drainHttpServer/index.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAMvE,MAAM,WAAW,wCAAwC;IAIvD,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;IAKxB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAQD,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD,kBAAkB,CA2BpB"}

View File

@@ -0,0 +1,24 @@
import { AbortController } from 'node-abort-controller';
import { Stopper } from './stoppable.js';
export function ApolloServerPluginDrainHttpServer(options) {
const stopper = new Stopper(options.httpServer);
return {
async serverWillStart() {
return {
async drainServer() {
const hardDestroyAbortController = new AbortController();
const stopGracePeriodMillis = options.stopGracePeriodMillis ?? 10000;
let timeout;
if (stopGracePeriodMillis < Infinity) {
timeout = setTimeout(() => hardDestroyAbortController.abort(), stopGracePeriodMillis);
}
await stopper.stop(hardDestroyAbortController.signal);
if (timeout) {
clearTimeout(timeout);
}
},
};
},
};
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/drainHttpServer/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAuBzC,MAAM,UAAU,iCAAiC,CAC/C,OAAiD;IAEjD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,OAAO;QACL,KAAK,CAAC,eAAe;YACnB,OAAO;gBACL,KAAK,CAAC,WAAW;oBAKf,MAAM,0BAA0B,GAAG,IAAI,eAAe,EAAE,CAAC;oBACzD,MAAM,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,IAAI,KAAM,CAAC;oBACtE,IAAI,OAAmC,CAAC;oBACxC,IAAI,qBAAqB,GAAG,QAAQ,EAAE,CAAC;wBACrC,OAAO,GAAG,UAAU,CAClB,GAAG,EAAE,CAAC,0BAA0B,CAAC,KAAK,EAAE,EACxC,qBAAqB,CACtB,CAAC;oBACJ,CAAC;oBACD,MAAM,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;oBACtD,IAAI,OAAO,EAAE,CAAC;wBACZ,YAAY,CAAC,OAAO,CAAC,CAAC;oBACxB,CAAC;gBACH,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,13 @@
/// <reference types="node" />
/// <reference types="node" />
import type http from 'http';
import https from 'https';
import type { AbortSignal } from 'node-abort-controller';
export declare class Stopper {
private server;
private requestCountPerSocket;
private stopped;
constructor(server: http.Server | https.Server);
stop(hardDestroyAbortSignal?: AbortSignal): Promise<boolean>;
}
//# sourceMappingURL=stoppable.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stoppable.d.ts","sourceRoot":"","sources":["../../../../src/plugin/drainHttpServer/stoppable.ts"],"names":[],"mappings":";;AA4BA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,qBAAa,OAAO;IAIN,OAAO,CAAC,MAAM;IAH1B,OAAO,CAAC,qBAAqB,CAA6B;IAC1D,OAAO,CAAC,OAAO,CAAS;gBAEJ,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;IA+BhD,IAAI,CAAC,sBAAsB,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;CAyCnE"}

View File

@@ -0,0 +1,46 @@
import https from 'https';
export class Stopper {
constructor(server) {
this.server = server;
this.requestCountPerSocket = new Map();
this.stopped = false;
server.on(server instanceof https.Server ? 'secureConnection' : 'connection', (socket) => {
this.requestCountPerSocket.set(socket, 0);
socket.once('close', () => this.requestCountPerSocket.delete(socket));
});
server.on('request', (req, res) => {
this.requestCountPerSocket.set(req.socket, (this.requestCountPerSocket.get(req.socket) ?? 0) + 1);
res.once('finish', () => {
const pending = (this.requestCountPerSocket.get(req.socket) ?? 0) - 1;
this.requestCountPerSocket.set(req.socket, pending);
if (this.stopped && pending === 0) {
req.socket.end();
}
});
});
}
async stop(hardDestroyAbortSignal) {
let gracefully = true;
await new Promise((resolve) => setImmediate(resolve));
this.stopped = true;
const onAbort = () => {
gracefully = false;
this.requestCountPerSocket.forEach((_, socket) => socket.end());
setImmediate(() => {
this.requestCountPerSocket.forEach((_, socket) => socket.destroy());
});
};
hardDestroyAbortSignal?.addEventListener('abort', onAbort);
const closePromise = new Promise((resolve) => this.server.close(() => {
hardDestroyAbortSignal?.removeEventListener('abort', onAbort);
resolve();
}));
this.requestCountPerSocket.forEach((requests, socket) => {
if (requests === 0)
socket.end();
});
await closePromise;
return gracefully;
}
}
//# sourceMappingURL=stoppable.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stoppable.js","sourceRoot":"","sources":["../../../../src/plugin/drainHttpServer/stoppable.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,8 @@
import type { SendErrorsOptions } from '../usageReporting/index.js';
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
export interface ApolloServerPluginInlineTraceOptions {
includeErrors?: SendErrorsOptions;
__onlyIfSchemaIsSubgraph?: boolean;
}
export declare function ApolloServerPluginInlineTrace(options?: ApolloServerPluginInlineTraceOptions): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/inlineTrace/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,MAAM,WAAW,oCAAoC;IAoBnD,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAWlC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAOD,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,oCAA0D,GAClE,kBAAkB,CA2GpB"}

View File

@@ -0,0 +1,67 @@
import { Trace } from '@apollo/usage-reporting-protobuf';
import { TraceTreeBuilder } from '../traceTreeBuilder.js';
import { internalPlugin } from '../../internalPlugin.js';
import { schemaIsSubgraph } from '../schemaIsSubgraph.js';
export function ApolloServerPluginInlineTrace(options = Object.create(null)) {
let enabled = options.__onlyIfSchemaIsSubgraph ? null : true;
return internalPlugin({
__internal_plugin_id__: 'InlineTrace',
__is_disabled_plugin__: false,
async serverWillStart({ schema, logger }) {
if (enabled === null) {
enabled = schemaIsSubgraph(schema);
if (enabled) {
logger.info('Enabling inline tracing for this subgraph. To disable, use ' +
'ApolloServerPluginInlineTraceDisabled.');
}
}
},
async requestDidStart({ request: { http }, metrics }) {
if (!enabled) {
return;
}
const treeBuilder = new TraceTreeBuilder({
maskedBy: 'ApolloServerPluginInlineTrace',
sendErrors: options.includeErrors,
});
if (http?.headers.get('apollo-federation-include-trace') !== 'ftv1') {
return;
}
if (metrics.captureTraces === false) {
return;
}
metrics.captureTraces = true;
treeBuilder.startTiming();
return {
async executionDidStart() {
return {
willResolveField({ info }) {
return treeBuilder.willResolveField(info);
},
};
},
async didEncounterErrors({ errors }) {
treeBuilder.didEncounterErrors(errors);
},
async willSendResponse({ response }) {
treeBuilder.stopTiming();
if (response.body.kind === 'incremental') {
return;
}
if (metrics.queryPlanTrace) {
treeBuilder.trace.queryPlan = metrics.queryPlanTrace;
}
const encodedUint8Array = Trace.encode(treeBuilder.trace).finish();
const encodedBuffer = Buffer.from(encodedUint8Array, encodedUint8Array.byteOffset, encodedUint8Array.byteLength);
const extensions = response.body.singleResult.extensions ||
(response.body.singleResult.extensions = Object.create(null));
if (typeof extensions.ftv1 !== 'undefined') {
throw new Error('The `ftv1` extension was already present.');
}
extensions.ftv1 = encodedBuffer.toString('base64');
},
};
},
});
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/inlineTrace/index.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,4 @@
import type { ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions, ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions } from './types';
export declare const getEmbeddedExplorerHTML: (explorerCdnVersion: string, config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions, apolloServerVersion: string, nonce: string) => string;
export declare const getEmbeddedSandboxHTML: (sandboxCdnVersion: string, config: ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions, apolloServerVersion: string, nonce: string) => string;
//# sourceMappingURL=getEmbeddedHTML.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getEmbeddedHTML.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/getEmbeddedHTML.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wDAAwD,EACxD,6DAA6D,EAC9D,MAAM,SAAS,CAAC;AAmBjB,eAAO,MAAM,uBAAuB,uBACd,MAAM,UAClB,6DAA6D,uBAChD,MAAM,SACpB,MAAM,WAsGd,CAAC;AAEF,eAAO,MAAM,sBAAsB,sBACd,MAAM,UACjB,wDAAwD,uBAC3C,MAAM,SACpB,MAAM,WAqEd,CAAC"}

View File

@@ -0,0 +1,138 @@
function getConfigStringForHtml(config) {
return JSON.stringify(config)
.replace('<', '\\u003c')
.replace('>', '\\u003e')
.replace('&', '\\u0026')
.replace("'", '\\u0027');
}
export const getEmbeddedExplorerHTML = (explorerCdnVersion, config, apolloServerVersion, nonce) => {
const productionLandingPageEmbedConfigOrDefault = {
displayOptions: {},
persistExplorerState: false,
runTelemetry: true,
...(typeof config.embed === 'boolean' ? {} : config.embed),
};
const embeddedExplorerParams = {
graphRef: config.graphRef,
target: '#embeddableExplorer',
initialState: {
...('document' in config || 'headers' in config || 'variables' in config
? {
document: config.document,
headers: config.headers,
variables: config.variables,
}
: {}),
...('collectionId' in config
? {
collectionId: config.collectionId,
operationId: config.operationId,
}
: {}),
displayOptions: {
...productionLandingPageEmbedConfigOrDefault.displayOptions,
},
},
persistExplorerState: productionLandingPageEmbedConfigOrDefault.persistExplorerState,
includeCookies: config.includeCookies,
runtime: apolloServerVersion,
runTelemetry: productionLandingPageEmbedConfigOrDefault.runTelemetry,
allowDynamicStyles: false,
};
return `
<div class="fallback">
<h1>Welcome to Apollo Server</h1>
<p>Apollo Explorer cannot be loaded; it appears that you might be offline.</p>
</div>
<style nonce=${nonce}>
iframe {
background-color: white;
height: 100%;
width: 100%;
border: none;
}
#embeddableExplorer {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
}
</style>
<div id="embeddableExplorer"></div>
<script nonce="${nonce}" src="https://embeddable-explorer.cdn.apollographql.com/${encodeURIComponent(explorerCdnVersion)}/embeddable-explorer.umd.production.min.js?runtime=${encodeURIComponent(apolloServerVersion)}"></script>
<script nonce="${nonce}">
var endpointUrl = window.location.href;
var embeddedExplorerConfig = ${getConfigStringForHtml(embeddedExplorerParams)};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`;
};
export const getEmbeddedSandboxHTML = (sandboxCdnVersion, config, apolloServerVersion, nonce) => {
const localDevelopmentEmbedConfigOrDefault = {
runTelemetry: true,
endpointIsEditable: false,
initialState: {},
...(typeof config.embed === 'boolean' ? {} : (config.embed ?? {})),
};
const embeddedSandboxConfig = {
target: '#embeddableSandbox',
initialState: {
...('document' in config || 'headers' in config || 'variables' in config
? {
document: config.document,
variables: config.variables,
headers: config.headers,
}
: {}),
...('collectionId' in config
? {
collectionId: config.collectionId,
operationId: config.operationId,
}
: {}),
includeCookies: config.includeCookies,
...localDevelopmentEmbedConfigOrDefault.initialState,
},
hideCookieToggle: false,
endpointIsEditable: localDevelopmentEmbedConfigOrDefault.endpointIsEditable,
runtime: apolloServerVersion,
runTelemetry: localDevelopmentEmbedConfigOrDefault.runTelemetry,
allowDynamicStyles: false,
};
return `
<div class="fallback">
<h1>Welcome to Apollo Server</h1>
<p>Apollo Sandbox cannot be loaded; it appears that you might be offline.</p>
</div>
<style nonce=${nonce}>
iframe {
background-color: white;
height: 100%;
width: 100%;
border: none;
}
#embeddableSandbox {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
}
</style>
<div id="embeddableSandbox"></div>
<script nonce="${nonce}" src="https://embeddable-sandbox.cdn.apollographql.com/${encodeURIComponent(sandboxCdnVersion)}/embeddable-sandbox.umd.production.min.js?runtime=${encodeURIComponent(apolloServerVersion)}"></script>
<script nonce="${nonce}">
var initialEndpoint = window.location.href;
var embeddedSandboxConfig = ${getConfigStringForHtml(embeddedSandboxConfig)};
new window.EmbeddedSandbox(
{
...embeddedSandboxConfig,
initialEndpoint,
}
);
</script>
`;
};
//# sourceMappingURL=getEmbeddedHTML.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getEmbeddedHTML.js","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/getEmbeddedHTML.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,9 @@
import type { ApolloServerPlugin } from '../../../externalTypes/index.js';
import type { ApolloServerPluginLandingPageLocalDefaultOptions, ApolloServerPluginLandingPageProductionDefaultOptions } from './types.js';
export type { ApolloServerPluginLandingPageLocalDefaultOptions, ApolloServerPluginLandingPageProductionDefaultOptions, };
export declare function ApolloServerPluginLandingPageLocalDefault(options?: ApolloServerPluginLandingPageLocalDefaultOptions): ApolloServerPlugin;
export declare function ApolloServerPluginLandingPageProductionDefault(options?: ApolloServerPluginLandingPageProductionDefaultOptions): ApolloServerPlugin;
export declare const DEFAULT_EMBEDDED_EXPLORER_VERSION = "v3";
export declare const DEFAULT_EMBEDDED_SANDBOX_VERSION = "v2";
export declare const DEFAULT_APOLLO_SERVER_LANDING_PAGE_VERSION = "_latest";
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAEnB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EACV,gDAAgD,EAChD,qDAAqD,EAEtD,MAAM,YAAY,CAAC;AASpB,YAAY,EACV,gDAAgD,EAChD,qDAAqD,GACtD,CAAC;AAEF,wBAAgB,yCAAyC,CACvD,OAAO,GAAE,gDAAqD,GAC7D,kBAAkB,CAWpB;AAED,wBAAgB,8CAA8C,CAC5D,OAAO,GAAE,qDAA0D,GAClE,kBAAkB,CAOpB;AAiCD,eAAO,MAAM,iCAAiC,OAAO,CAAC;AACtD,eAAO,MAAM,gCAAgC,OAAO,CAAC;AACrD,eAAO,MAAM,0CAA0C,YAAY,CAAC"}

View File

@@ -0,0 +1,145 @@
import { getEmbeddedExplorerHTML, getEmbeddedSandboxHTML, } from './getEmbeddedHTML.js';
import { packageVersion } from '../../../generated/packageVersion.js';
import { createHash } from '@apollo/utils.createhash';
import { v4 as uuidv4 } from 'uuid';
export function ApolloServerPluginLandingPageLocalDefault(options = {}) {
const { version, __internal_apolloStudioEnv__, ...rest } = {
embed: true,
...options,
};
return ApolloServerPluginLandingPageDefault(version, {
isProd: false,
apolloStudioEnv: __internal_apolloStudioEnv__,
...rest,
});
}
export function ApolloServerPluginLandingPageProductionDefault(options = {}) {
const { version, __internal_apolloStudioEnv__, ...rest } = options;
return ApolloServerPluginLandingPageDefault(version, {
isProd: true,
apolloStudioEnv: __internal_apolloStudioEnv__,
...rest,
});
}
function encodeConfig(config) {
return JSON.stringify(encodeURIComponent(JSON.stringify(config)));
}
const getNonEmbeddedLandingPageHTML = (cdnVersion, config, apolloServerVersion, nonce) => {
const encodedConfig = encodeConfig(config);
return `
<div class="fallback">
<h1>Welcome to Apollo Server</h1>
<p>The full landing page cannot be loaded; it appears that you might be offline.</p>
</div>
<script nonce="${nonce}">window.landingPage = ${encodedConfig};</script>
<script nonce="${nonce}" src="https://apollo-server-landing-page.cdn.apollographql.com/${encodeURIComponent(cdnVersion)}/static/js/main.js?runtime=${apolloServerVersion}"></script>`;
};
export const DEFAULT_EMBEDDED_EXPLORER_VERSION = 'v3';
export const DEFAULT_EMBEDDED_SANDBOX_VERSION = 'v2';
export const DEFAULT_APOLLO_SERVER_LANDING_PAGE_VERSION = '_latest';
function ApolloServerPluginLandingPageDefault(maybeVersion, config) {
const explorerVersion = maybeVersion ?? DEFAULT_EMBEDDED_EXPLORER_VERSION;
const sandboxVersion = maybeVersion ?? DEFAULT_EMBEDDED_SANDBOX_VERSION;
const apolloServerLandingPageVersion = maybeVersion ?? DEFAULT_APOLLO_SERVER_LANDING_PAGE_VERSION;
const apolloServerVersion = `@apollo/server@${packageVersion}`;
const scriptSafeList = [
'https://apollo-server-landing-page.cdn.apollographql.com',
'https://embeddable-sandbox.cdn.apollographql.com',
'https://embeddable-explorer.cdn.apollographql.com',
].join(' ');
const styleSafeList = [
'https://apollo-server-landing-page.cdn.apollographql.com',
'https://embeddable-sandbox.cdn.apollographql.com',
'https://embeddable-explorer.cdn.apollographql.com',
'https://fonts.googleapis.com',
].join(' ');
const iframeSafeList = [
'https://explorer.embed.apollographql.com',
'https://sandbox.embed.apollographql.com',
'https://embed.apollo.local:3000',
].join(' ');
return {
__internal_installed_implicitly__: false,
async serverWillStart(server) {
if (config.precomputedNonce) {
server.logger.warn("The `precomputedNonce` landing page configuration option is deprecated. Removing this option is strictly an improvement to Apollo Server's landing page Content Security Policy (CSP) implementation for preventing XSS attacks.");
}
return {
async renderLandingPage() {
const encodedASLandingPageVersion = encodeURIComponent(apolloServerLandingPageVersion);
async function html() {
const nonce = config.precomputedNonce ??
createHash('sha256').update(uuidv4()).digest('hex');
const scriptCsp = `script-src 'self' 'nonce-${nonce}' ${scriptSafeList}`;
const styleCsp = `style-src 'nonce-${nonce}' ${styleSafeList}`;
const imageCsp = `img-src https://apollo-server-landing-page.cdn.apollographql.com`;
const manifestCsp = `manifest-src https://apollo-server-landing-page.cdn.apollographql.com`;
const frameCsp = `frame-src ${iframeSafeList}`;
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="${scriptCsp}; ${styleCsp}; ${imageCsp}; ${manifestCsp}; ${frameCsp}" />
<link
rel="icon"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodedASLandingPageVersion}/assets/favicon.png"
/>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap"
rel="stylesheet"
/>
<meta name="theme-color" content="#000000" />
<meta name="description" content="Apollo server landing page" />
<link
rel="apple-touch-icon"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodedASLandingPageVersion}/assets/favicon.png"
/>
<link
rel="manifest"
href="https://apollo-server-landing-page.cdn.apollographql.com/${encodedASLandingPageVersion}/manifest.json"
/>
<title>Apollo Server</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="react-root">
<style nonce=${nonce}>
body {
margin: 0;
overflow-x: hidden;
overflow-y: hidden;
}
.fallback {
opacity: 0;
animation: fadeIn 1s 1s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
padding: 1em;
}
@keyframes fadeIn {
0% {opacity:0;}
100% {opacity:1; }
}
</style>
${config.embed
? 'graphRef' in config && config.graphRef
? getEmbeddedExplorerHTML(explorerVersion, config, apolloServerVersion, nonce)
: !('graphRef' in config)
? getEmbeddedSandboxHTML(sandboxVersion, config, apolloServerVersion, nonce)
: getNonEmbeddedLandingPageHTML(apolloServerLandingPageVersion, config, apolloServerVersion, nonce)
: getNonEmbeddedLandingPageHTML(apolloServerLandingPageVersion, config, apolloServerVersion, nonce)}
</div>
</body>
</html>
`;
}
return { html };
},
};
},
};
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/index.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,57 @@
type InitialDocumentVariablesHeaders = {
document?: string;
variables?: Record<string, any>;
headers?: Record<string, string>;
collectionId?: never;
operationId?: never;
};
type InitialStateForEmbeds = {
collectionId: string;
operationId: string;
document?: never;
variables?: never;
headers?: never;
} | InitialDocumentVariablesHeaders;
export type ApolloServerPluginLandingPageDefaultBaseOptions = {
version?: string;
footer?: boolean;
includeCookies?: boolean;
precomputedNonce?: string;
__internal_apolloStudioEnv__?: 'staging' | 'prod';
};
export type ApolloServerPluginNonEmbeddedLandingPageLocalDefaultOptions = ApolloServerPluginLandingPageDefaultBaseOptions & InitialDocumentVariablesHeaders & {
embed: false;
};
export type ApolloServerPluginNonEmbeddedLandingPageProductionDefaultOptions = ApolloServerPluginLandingPageDefaultBaseOptions & InitialDocumentVariablesHeaders & {
graphRef?: string;
embed?: false;
};
export type ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions = ApolloServerPluginLandingPageDefaultBaseOptions & {
embed?: true | EmbeddableSandboxOptions;
} & (InitialDocumentVariablesHeaders | InitialStateForEmbeds);
export type ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions = ApolloServerPluginLandingPageDefaultBaseOptions & {
graphRef: string;
embed: true | EmbeddableExplorerOptions;
} & InitialStateForEmbeds;
type EmbeddableSandboxOptions = {
runTelemetry?: boolean;
initialState?: {
pollForSchemaUpdates?: boolean;
sharedHeaders?: Record<string, string>;
};
endpointIsEditable?: boolean;
};
type EmbeddableExplorerOptions = {
runTelemetry?: boolean;
displayOptions?: {
showHeadersAndEnvVars: boolean;
docsPanelState: 'open' | 'closed';
theme: 'light' | 'dark';
};
persistExplorerState?: boolean;
};
export type ApolloServerPluginLandingPageLocalDefaultOptions = ApolloServerPluginEmbeddedLandingPageLocalDefaultOptions | ApolloServerPluginNonEmbeddedLandingPageLocalDefaultOptions;
export type ApolloServerPluginLandingPageProductionDefaultOptions = ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions | ApolloServerPluginNonEmbeddedLandingPageProductionDefaultOptions;
export type LandingPageConfig = ApolloServerPluginLandingPageLocalDefaultOptions | ApolloServerPluginLandingPageProductionDefaultOptions;
export {};
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/types.ts"],"names":[],"mappings":"AAAA,KAAK,+BAA+B,GAAG;IAKrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAKlB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAMhC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,CAAC;AAEF,KAAK,qBAAqB,GACtB;IAQE,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,SAAS,CAAC,EAAE,KAAK,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB,GACD,+BAA+B,CAAC;AAEpC,MAAM,MAAM,+CAA+C,GAAG;IAS5D,OAAO,CAAC,EAAE,MAAM,CAAC;IAKjB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,cAAc,CAAC,EAAE,OAAO,CAAC;IAWzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,4BAA4B,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,2DAA2D,GACrE,+CAA+C,GAC7C,+BAA+B,GAAG;IAKhC,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEN,MAAM,MAAM,gEAAgE,GAC1E,+CAA+C,GAC7C,+BAA+B,GAAG;IAOhC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAKlB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,wDAAwD,GAClE,+CAA+C,GAAG;IAKhD,KAAK,CAAC,EAAE,IAAI,GAAG,wBAAwB,CAAC;CACzC,GAAG,CAAC,+BAA+B,GAAG,qBAAqB,CAAC,CAAC;AAEhE,MAAM,MAAM,6DAA6D,GACvE,+CAA+C,GAAG;IAKhD,QAAQ,EAAE,MAAM,CAAC;IAIjB,KAAK,EAAE,IAAI,GAAG,yBAAyB,CAAC;CACzC,GAAG,qBAAqB,CAAC;AAE5B,KAAK,wBAAwB,GAAG;IAI9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE;QAMb,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAY/B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACxC,CAAC;IAOF,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAI/B,YAAY,CAAC,EAAE,OAAO,CAAC;IAIvB,cAAc,CAAC,EAAE;QAQf,qBAAqB,EAAE,OAAO,CAAC;QAO/B,cAAc,EAAE,MAAM,GAAG,QAAQ,CAAC;QAMlC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;KACzB,CAAC;IAWF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,gDAAgD,GACxD,wDAAwD,GACxD,2DAA2D,CAAC;AAEhE,MAAM,MAAM,qDAAqD,GAC7D,6DAA6D,GAC7D,gEAAgE,CAAC;AAErE,MAAM,MAAM,iBAAiB,GACzB,gDAAgD,GAChD,qDAAqD,CAAC"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/plugin/landingPage/default/types.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,3 @@
import { type GraphQLSchema } from 'graphql';
export declare function schemaIsSubgraph(schema: GraphQLSchema): boolean;
//# sourceMappingURL=schemaIsSubgraph.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"schemaIsSubgraph.d.ts","sourceRoot":"","sources":["../../../src/plugin/schemaIsSubgraph.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,SAAS,CAAC;AAiBjB,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAkB/D"}

View File

@@ -0,0 +1,20 @@
import { isObjectType, isScalarType, isNonNullType, } from 'graphql';
export function schemaIsSubgraph(schema) {
const serviceType = schema.getType('_Service');
if (!isObjectType(serviceType)) {
return false;
}
const sdlField = serviceType.getFields().sdl;
if (!sdlField) {
return false;
}
let sdlFieldType = sdlField.type;
if (isNonNullType(sdlFieldType)) {
sdlFieldType = sdlFieldType.ofType;
}
if (!isScalarType(sdlFieldType)) {
return false;
}
return sdlFieldType.name == 'String';
}
//# sourceMappingURL=schemaIsSubgraph.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"schemaIsSubgraph.js","sourceRoot":"","sources":["../../../src/plugin/schemaIsSubgraph.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,SAAS,CAAC;AAiBjB,MAAM,UAAU,gBAAgB,CAAC,MAAqB;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC;IAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;IACjC,IAAI,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;IACrC,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,IAAI,QAAQ,CAAC;AACvC,CAAC"}

View File

@@ -0,0 +1,10 @@
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
import type { Fetcher } from '@apollo/utils.fetcher';
export interface ApolloServerPluginSchemaReportingOptions {
initialDelayMaxMs?: number;
overrideReportedSchema?: string;
endpointUrl?: string;
fetcher?: Fetcher;
}
export declare function ApolloServerPluginSchemaReporting({ initialDelayMaxMs, overrideReportedSchema, endpointUrl, fetcher, }?: ApolloServerPluginSchemaReportingOptions): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/schemaReporting/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAIrD,MAAM,WAAW,wCAAwC;IAavD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAuB3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAKhC,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iCAAiC,CAC/C,EACE,iBAAiB,EACjB,sBAAsB,EACtB,WAAW,EACX,OAAO,GACR,GAAE,wCAA8D,GAChE,kBAAkB,CAkIpB"}

View File

@@ -0,0 +1,98 @@
import os from 'os';
import { internalPlugin } from '../../internalPlugin.js';
import { v4 as uuidv4 } from 'uuid';
import { printSchema, validateSchema, buildSchema } from 'graphql';
import { SchemaReporter } from './schemaReporter.js';
import { schemaIsSubgraph } from '../schemaIsSubgraph.js';
import { packageVersion } from '../../generated/packageVersion.js';
import { computeCoreSchemaHash } from '../../utils/computeCoreSchemaHash.js';
export function ApolloServerPluginSchemaReporting({ initialDelayMaxMs, overrideReportedSchema, endpointUrl, fetcher, } = Object.create(null)) {
const bootId = uuidv4();
return internalPlugin({
__internal_plugin_id__: 'SchemaReporting',
__is_disabled_plugin__: false,
async serverWillStart({ apollo, schema, logger }) {
const { key, graphRef } = apollo;
if (!key) {
throw Error('To use ApolloServerPluginSchemaReporting, you must provide an Apollo API ' +
'key, via the APOLLO_KEY environment variable or via `new ApolloServer({apollo: {key})`');
}
if (!graphRef) {
throw Error('To use ApolloServerPluginSchemaReporting, you must provide your graph ref (eg, ' +
"'my-graph-id@my-graph-variant'). Try setting the APOLLO_GRAPH_REF environment " +
'variable or passing `new ApolloServer({apollo: {graphRef}})`.');
}
if (overrideReportedSchema) {
try {
const validationErrors = validateSchema(buildSchema(overrideReportedSchema, { noLocation: true }));
if (validationErrors.length) {
throw new Error(validationErrors.map((error) => error.message).join('\n'));
}
}
catch (err) {
throw new Error('The schema provided to overrideReportedSchema failed to parse or ' +
`validate: ${err.message}`);
}
}
if (schemaIsSubgraph(schema)) {
throw Error([
'Schema reporting is not yet compatible with Apollo Federation subgraphs.',
"If you're interested in using schema reporting with subgraphs,",
'please contact Apollo support. To set up managed federation, see',
'https://go.apollo.dev/s/managed-federation',
].join(' '));
}
if (endpointUrl !== undefined) {
logger.info(`Apollo schema reporting: schema reporting URL override: ${endpointUrl}`);
}
const baseSchemaReport = {
bootId,
graphRef,
platform: process.env.APOLLO_SERVER_PLATFORM || 'local',
runtimeVersion: `node ${process.version}`,
userVersion: process.env.APOLLO_SERVER_USER_VERSION,
serverId: process.env.APOLLO_SERVER_ID || process.env.HOSTNAME || os.hostname(),
libraryVersion: `@apollo/server@${packageVersion}`,
};
let currentSchemaReporter;
return {
schemaDidLoadOrUpdate({ apiSchema, coreSupergraphSdl }) {
if (overrideReportedSchema !== undefined) {
if (currentSchemaReporter) {
return;
}
else {
logger.info('Apollo schema reporting: schema to report has been overridden');
}
}
const coreSchema = overrideReportedSchema ??
coreSupergraphSdl ??
printSchema(apiSchema);
const coreSchemaHash = computeCoreSchemaHash(coreSchema);
const schemaReport = {
...baseSchemaReport,
coreSchemaHash,
};
currentSchemaReporter?.stop();
currentSchemaReporter = new SchemaReporter({
schemaReport,
coreSchema,
apiKey: key,
endpointUrl,
logger,
initialReportingDelayInMs: Math.floor(Math.random() * (initialDelayMaxMs ?? 10000)),
fallbackReportingDelayInMs: 20000,
fetcher,
});
currentSchemaReporter.start();
logger.info('Apollo schema reporting: reporting a new schema to Studio! See your graph at ' +
`https://studio.apollographql.com/graph/${encodeURI(graphRef)}/ with server info ${JSON.stringify(schemaReport)}`);
},
async serverWillStop() {
currentSchemaReporter?.stop();
},
};
},
});
}
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/schemaReporting/index.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,33 @@
import type { Logger } from '@apollo/utils.logger';
import type { SchemaReport, ReportSchemaResponse } from './generated/operations';
import type { Fetcher } from '@apollo/utils.fetcher';
export declare const schemaReportGql = "#graphql\n mutation SchemaReport($report: SchemaReport!, $coreSchema: String) {\n reportSchema(report: $report, coreSchema: $coreSchema) {\n __typename\n ... on ReportSchemaError {\n message\n code\n }\n ... on ReportSchemaResponse {\n inSeconds\n withCoreSchema\n }\n }\n }\n";
export declare class SchemaReporter {
private readonly schemaReport;
private readonly coreSchema;
private readonly endpointUrl;
private readonly logger;
private readonly initialReportingDelayInMs;
private readonly fallbackReportingDelayInMs;
private readonly fetcher;
private isStopped;
private pollTimer?;
private readonly headers;
constructor(options: {
schemaReport: SchemaReport;
coreSchema: string;
apiKey: string;
endpointUrl: string | undefined;
logger: Logger;
initialReportingDelayInMs: number;
fallbackReportingDelayInMs: number;
fetcher?: Fetcher;
});
stopped(): boolean;
start(): void;
stop(): void;
private sendOneReportAndScheduleNext;
reportSchema(withCoreSchema: boolean): Promise<ReportSchemaResponse | null>;
private apolloQuery;
}
//# sourceMappingURL=schemaReporter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"schemaReporter.d.ts","sourceRoot":"","sources":["../../../../src/plugin/schemaReporting/schemaReporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EACV,YAAY,EAGZ,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKrD,eAAO,MAAM,eAAe,sVAc3B,CAAC;AAGF,qBAAa,cAAc;IAEzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAS;IACnD,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAElC,OAAO,CAAC,SAAS,CAAU;IAC3B,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,OAAO,EAAE;QACnB,YAAY,EAAE,YAAY,CAAC;QAC3B,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,MAAM,EAAE,MAAM,CAAC;QACf,yBAAyB,EAAE,MAAM,CAAC;QAClC,0BAA0B,EAAE,MAAM,CAAC;QACnC,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB;IAqBM,OAAO,IAAI,OAAO;IAIlB,KAAK;IAOL,IAAI;YAQG,4BAA4B;IAiC7B,YAAY,CACvB,cAAc,EAAE,OAAO,GACtB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;YAwCzB,WAAW;CAsC1B"}

View File

@@ -0,0 +1,134 @@
import fetch from 'node-fetch';
import { packageVersion } from '../../generated/packageVersion.js';
export const schemaReportGql = `#graphql
mutation SchemaReport($report: SchemaReport!, $coreSchema: String) {
reportSchema(report: $report, coreSchema: $coreSchema) {
__typename
... on ReportSchemaError {
message
code
}
... on ReportSchemaResponse {
inSeconds
withCoreSchema
}
}
}
`;
export class SchemaReporter {
constructor(options) {
this.headers = {
'Content-Type': 'application/json',
'x-api-key': options.apiKey,
'apollographql-client-name': 'ApolloServerPluginSchemaReporting',
'apollographql-client-version': packageVersion,
};
this.endpointUrl =
options.endpointUrl ||
'https://schema-reporting.api.apollographql.com/api/graphql';
this.schemaReport = options.schemaReport;
this.coreSchema = options.coreSchema;
this.isStopped = false;
this.logger = options.logger;
this.initialReportingDelayInMs = options.initialReportingDelayInMs;
this.fallbackReportingDelayInMs = options.fallbackReportingDelayInMs;
this.fetcher = options.fetcher ?? fetch;
}
stopped() {
return this.isStopped;
}
start() {
this.pollTimer = setTimeout(() => this.sendOneReportAndScheduleNext(false), this.initialReportingDelayInMs);
}
stop() {
this.isStopped = true;
if (this.pollTimer) {
clearTimeout(this.pollTimer);
this.pollTimer = undefined;
}
}
async sendOneReportAndScheduleNext(sendNextWithCoreSchema) {
this.pollTimer = undefined;
if (this.stopped())
return;
try {
const result = await this.reportSchema(sendNextWithCoreSchema);
if (!result) {
return;
}
if (!this.stopped()) {
this.pollTimer = setTimeout(() => this.sendOneReportAndScheduleNext(result.withCoreSchema), result.inSeconds * 1000);
}
return;
}
catch (error) {
this.logger.error(`Error reporting server info to Apollo during schema reporting: ${error}`);
if (!this.stopped()) {
this.pollTimer = setTimeout(() => this.sendOneReportAndScheduleNext(false), this.fallbackReportingDelayInMs);
}
}
}
async reportSchema(withCoreSchema) {
const { data, errors } = await this.apolloQuery({
report: this.schemaReport,
coreSchema: withCoreSchema ? this.coreSchema : null,
});
if (errors) {
throw new Error(errors.map((x) => x.message).join('\n'));
}
function msgForUnexpectedResponse(data) {
return [
'Unexpected response shape from Apollo when',
'reporting schema. If this continues, please reach',
'out to support@apollographql.com.',
'Received response:',
JSON.stringify(data),
].join(' ');
}
if (!data || !data.reportSchema) {
throw new Error(msgForUnexpectedResponse(data));
}
if (data.reportSchema.__typename === 'ReportSchemaResponse') {
return data.reportSchema;
}
else if (data.reportSchema.__typename === 'ReportSchemaError') {
this.logger.error([
'Received input validation error from Apollo:',
data.reportSchema.message,
'Stopping reporting. Please fix the input errors.',
].join(' '));
this.stop();
return null;
}
throw new Error(msgForUnexpectedResponse(data));
}
async apolloQuery(variables) {
const request = {
query: schemaReportGql,
variables,
};
const httpResponse = await this.fetcher(this.endpointUrl, {
method: 'POST',
headers: this.headers,
body: JSON.stringify(request),
});
if (!httpResponse.ok) {
throw new Error([
`An unexpected HTTP status code (${httpResponse.status}) was`,
'encountered during schema reporting.',
].join(' '));
}
try {
return await httpResponse.json();
}
catch (error) {
throw new Error([
"Couldn't report schema to Apollo.",
'Parsing response as JSON failed.',
'If this continues please reach out to support@apollographql.com',
error,
].join(' '));
}
}
}
//# sourceMappingURL=schemaReporter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"schemaReporter.js","sourceRoot":"","sources":["../../../../src/plugin/schemaReporting/schemaReporter.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
import type { Logger } from '@apollo/utils.logger';
import retry from 'async-retry';
import type { ApolloServerPlugin } from '../../externalTypes/index.js';
export interface ApolloServerPluginSubscriptionCallbackOptions {
maxConsecutiveHeartbeatFailures?: number;
logger?: Logger;
retry?: retry.Options;
}
export declare function ApolloServerPluginSubscriptionCallback(options?: ApolloServerPluginSubscriptionCallbackOptions): ApolloServerPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/subscriptionCallback/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,MAAM,aAAa,CAAC;AAIhC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,MAAM,WAAW,6CAA6C;IAC5D,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;CACvB;AAED,wBAAgB,sCAAsC,CACpD,OAAO,GAAE,6CAAmE,GAC3E,kBAAkB,CAmLpB"}

View File

@@ -0,0 +1,422 @@
import retry from 'async-retry';
import { subscribe } from 'graphql';
import fetch from 'node-fetch';
import { ensureError, ensureGraphQLError } from '../../errorNormalize.js';
import { HeaderMap } from '../../utils/HeaderMap.js';
export function ApolloServerPluginSubscriptionCallback(options = Object.create(null)) {
const subscriptionManager = new SubscriptionManager(options);
const logger = options.logger
? prefixedLogger(options.logger, 'SubscriptionCallback')
: undefined;
return {
async requestDidStart({ request }) {
const subscriptionExtension = request?.extensions?.subscription;
if (!subscriptionExtension)
return;
let { callbackUrl, subscriptionId: id, verifier, heartbeatIntervalMs, } = subscriptionExtension;
callbackUrl = callbackUrl || subscriptionExtension.callback_url;
id = id || subscriptionExtension.subscription_id;
heartbeatIntervalMs =
heartbeatIntervalMs ??
subscriptionExtension.heartbeat_interval_ms ??
5000;
return {
async responseForOperation() {
logger?.debug('Received new subscription request', id);
return {
http: {
status: 200,
headers: new HeaderMap([['content-type', 'application/json']]),
},
body: {
kind: 'single',
singleResult: {
data: null,
},
},
};
},
async willSendResponse({ request, schema, document, contextValue, operationName, response, }) {
try {
await subscriptionManager.checkRequest({
callbackUrl,
id,
verifier,
});
}
catch (e) {
const graphqlError = ensureGraphQLError(e);
logger?.error(`\`check\` request failed: ${graphqlError.message}`, id);
if (response.body.kind === 'single') {
response.body.singleResult.errors = [graphqlError];
response.http.status = 500;
}
return;
}
subscriptionManager.initHeartbeat({
callbackUrl,
id,
verifier,
heartbeatIntervalMs,
});
logger?.debug(`Starting graphql-js subscription`, id);
let subscription;
try {
subscription = await subscribe({
schema,
document: document,
variableValues: request.variables,
contextValue: contextValue,
operationName: operationName,
});
}
catch (e) {
const graphqlError = ensureGraphQLError(e);
logger?.error(`Programming error: graphql-js subscribe() threw unexpectedly! Please report this bug to Apollo. The error was: ${e}`, id);
subscriptionManager.completeRequest({
errors: [graphqlError],
callbackUrl,
id,
verifier,
});
return;
}
if ('errors' in subscription) {
logger?.error(`graphql-js subscription unsuccessful: [\n\t${subscription.errors
?.map((e) => e.message)
.join(',\n\t')}\n]`, id);
try {
subscriptionManager.completeRequest({
errors: subscription.errors,
callbackUrl,
id,
verifier,
});
}
catch (e) {
logger?.error(`\`complete\` request failed: ${e}`, id);
}
}
else if (isAsyncIterable(subscription)) {
logger?.debug('graphql-js subscription successful', id);
subscriptionManager.startConsumingSubscription({
subscription,
callbackUrl,
id,
verifier,
});
}
logger?.debug(`Responding to original subscription request`, id);
},
};
},
async serverWillStart() {
return {
async drainServer() {
logger?.debug('Server is shutting down. Cleaning up outstanding subscriptions and heartbeat intervals');
await subscriptionManager.cleanup();
logger?.debug('Successfully cleaned up outstanding subscriptions and heartbeat intervals.');
},
};
},
};
}
function isAsyncIterable(value) {
return value && typeof value[Symbol.asyncIterator] === 'function';
}
class SubscriptionManager {
constructor(options) {
this.requestsInFlight = new Set();
this.subscriptionInfoByCallbackUrl = new Map();
this.maxConsecutiveHeartbeatFailures =
options.maxConsecutiveHeartbeatFailures ?? 5;
this.retryConfig = {
retries: 5,
minTimeout: 100,
maxTimeout: 1000,
...options.retry,
};
this.logger = options.logger
? prefixedLogger(options.logger, 'SubscriptionManager')
: undefined;
}
async retryFetch({ url, action, id, verifier, payload, errors, headers, }) {
let response;
try {
const maybeWithErrors = errors?.length ? ` with errors` : '';
this.logger?.debug(`Sending \`${action}\` request to router` + maybeWithErrors, id);
return retry(async (bail) => {
response = fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
...headers,
},
body: JSON.stringify({
kind: 'subscription',
action,
id,
verifier,
...(payload && { payload }),
...(errors?.length && { errors }),
}),
});
this.requestsInFlight.add(response);
const result = await response;
if (!result.ok) {
if (result.status >= 500) {
throw new Error(`\`${action}\` request failed with unexpected status code: ${result.status}`);
}
else {
if (result.status === 404) {
this.logger?.debug(`\`${action}\` request received 404, terminating subscription`, id);
}
else {
const errMsg = `\`${action}\` request failed with unexpected status code: ${result.status}, terminating subscription`;
this.logger?.debug(errMsg, id);
bail(new Error(errMsg));
}
this.terminateSubscription(id, url);
return result;
}
}
this.logger?.debug(`\`${action}\` request successful`, id);
return result;
}, {
...this.retryConfig,
onRetry: (e, attempt) => {
this.requestsInFlight.delete(response);
this.logger?.warn(`Retrying \`${action}\` request (attempt ${attempt}) due to error: ${e.message}`, id);
this.retryConfig?.onRetry?.(e, attempt);
},
});
}
finally {
this.requestsInFlight.delete(response);
}
}
async checkRequest({ callbackUrl, id, verifier, }) {
return this.retryFetch({
url: callbackUrl,
action: 'check',
id,
verifier,
headers: { 'subscription-protocol': 'callback/1.0' },
});
}
initHeartbeat({ callbackUrl, id, verifier, heartbeatIntervalMs, }) {
if (!this.subscriptionInfoByCallbackUrl.has(callbackUrl)) {
this.subscriptionInfoByCallbackUrl.set(callbackUrl, {});
}
if (heartbeatIntervalMs === 0) {
this.logger?.debug(`Heartbeat disabled for ${callbackUrl}`, id);
return;
}
this.logger?.debug(`Starting new heartbeat interval for ${callbackUrl}`, id);
let consecutiveHeartbeatFailureCount = 0;
const heartbeatInterval = setInterval(async () => {
let heartbeatRequest;
let resolveHeartbeatPromise;
const heartbeatPromise = new Promise((r) => {
resolveHeartbeatPromise = r;
});
const existingSubscriptionInfo = this.subscriptionInfoByCallbackUrl.get(callbackUrl);
if (!existingSubscriptionInfo?.heartbeat) {
clearInterval(heartbeatInterval);
this.logger?.error(`Programming error: Heartbeat interval unexpectedly missing for ${callbackUrl}. This is probably a bug in Apollo Server.`);
return;
}
const existingHeartbeat = existingSubscriptionInfo.heartbeat;
const { queue } = existingHeartbeat;
queue.push(heartbeatPromise);
if (queue.length > 1) {
const requestBeforeMe = queue[existingHeartbeat?.queue.length - 2];
await requestBeforeMe;
}
try {
this.logger?.debug(`Sending \`check\` request to ${callbackUrl} for ID: ${id}`);
heartbeatRequest = fetch(callbackUrl, {
method: 'POST',
body: JSON.stringify({
kind: 'subscription',
action: 'check',
id,
verifier,
}),
headers: {
'content-type': 'application/json',
'subscription-protocol': 'callback/1.0',
},
});
this.requestsInFlight.add(heartbeatRequest);
const result = await heartbeatRequest;
this.logger?.debug(`Heartbeat received response for ID: ${id}`);
if (result.ok) {
this.logger?.debug(`Heartbeat request successful, ID: ${id}`);
}
else if (result.status === 400) {
this.logger?.debug(`Heartbeat request received invalid ID: ${id}`);
this.terminateSubscription(id, callbackUrl);
}
else if (result.status === 404) {
this.logger?.debug(`Heartbeat request received invalid ID: ${id}`);
this.terminateSubscription(id, callbackUrl);
}
else {
throw new Error(`Unexpected status code: ${result.status}`);
}
consecutiveHeartbeatFailureCount = 0;
}
catch (e) {
const err = ensureError(e);
this.logger?.error(`Heartbeat request failed (${++consecutiveHeartbeatFailureCount} consecutive): ${err.message}`, existingHeartbeat.id);
if (consecutiveHeartbeatFailureCount >=
this.maxConsecutiveHeartbeatFailures) {
this.logger?.error(`Heartbeat request failed ${consecutiveHeartbeatFailureCount} times, terminating subscriptions and heartbeat interval: ${err.message}`, existingHeartbeat.id);
this.terminateSubscription(id, callbackUrl);
}
return;
}
finally {
if (heartbeatRequest) {
this.requestsInFlight.delete(heartbeatRequest);
}
existingHeartbeat?.queue.shift();
resolveHeartbeatPromise();
}
}, heartbeatIntervalMs);
const subscriptionInfo = this.subscriptionInfoByCallbackUrl.get(callbackUrl);
subscriptionInfo.heartbeat = {
interval: heartbeatInterval,
id,
verifier,
queue: [],
};
}
terminateSubscription(id, callbackUrl) {
this.logger?.debug(`Terminating subscriptions for ID: ${id}`);
const subscriptionInfo = this.subscriptionInfoByCallbackUrl.get(callbackUrl);
if (!subscriptionInfo) {
this.logger?.error(`No subscriptions found for ${callbackUrl}, skipping termination`);
return;
}
const { subscription, heartbeat } = subscriptionInfo;
if (subscription) {
subscription.cancelled = true;
subscription.asyncIter?.return();
}
if (heartbeat) {
this.logger?.debug(`Terminating heartbeat interval for ${callbackUrl}`);
clearInterval(heartbeat.interval);
}
this.subscriptionInfoByCallbackUrl.delete(callbackUrl);
}
startConsumingSubscription({ subscription, callbackUrl, id, verifier, }) {
const self = this;
const subscriptionObject = {
asyncIter: subscription,
cancelled: false,
async startConsumingSubscription() {
self.logger?.debug(`Listening to graphql-js subscription`, id);
try {
for await (const payload of subscription) {
if (this.cancelled) {
self.logger?.debug(`Subscription already cancelled, ignoring current and future payloads`, id);
return;
}
try {
await self.retryFetch({
url: callbackUrl,
action: 'next',
id,
verifier,
payload,
});
}
catch (e) {
const originalError = ensureError(e);
self.logger?.error(`\`next\` request failed, terminating subscription: ${originalError.message}`, id);
self.terminateSubscription(id, callbackUrl);
}
}
self.logger?.debug(`Subscription completed without errors`, id);
await this.completeSubscription();
}
catch (e) {
const error = ensureGraphQLError(e);
self.logger?.error(`Generator threw an error, terminating subscription: ${error.message}`, id);
this.completeSubscription([error]);
}
},
async completeSubscription(errors) {
if (this.cancelled)
return;
this.cancelled = true;
try {
await self.completeRequest({
callbackUrl,
id,
verifier,
...(errors && { errors }),
});
}
catch (e) {
const error = ensureError(e);
self.logger?.error(`\`complete\` request failed: ${error.message}`, id);
}
finally {
self.terminateSubscription(id, callbackUrl);
}
},
};
subscriptionObject.startConsumingSubscription();
const subscriptionInfo = this.subscriptionInfoByCallbackUrl.get(callbackUrl);
if (!subscriptionInfo) {
this.logger?.error(`No existing heartbeat found for ${callbackUrl}, skipping subscription`);
}
else {
subscriptionInfo.subscription = subscriptionObject;
}
}
async completeRequest({ errors, callbackUrl, id, verifier, }) {
return this.retryFetch({
url: callbackUrl,
action: 'complete',
id,
verifier,
errors,
});
}
collectAllSubscriptions() {
return Array.from(this.subscriptionInfoByCallbackUrl.values()).reduce((subscriptions, { subscription }) => {
if (subscription) {
subscriptions.push(subscription);
}
return subscriptions;
}, []);
}
async cleanup() {
await Promise.allSettled(Array.from(this.subscriptionInfoByCallbackUrl.values()).map(async ({ heartbeat }) => {
clearInterval(heartbeat?.interval);
await heartbeat?.queue[heartbeat.queue.length - 1];
}));
await Promise.allSettled(this.collectAllSubscriptions()
.filter((s) => !s.cancelled)
.map((s) => s.completeSubscription()));
await Promise.allSettled(this.requestsInFlight.values());
}
}
function prefixedLogger(logger, prefix) {
function log(level) {
return function (message, id) {
logger[level](`${prefix}${id ? `[${id}]` : ''}: ${message}`);
};
}
return {
debug: log('debug'),
error: log('error'),
info: log('info'),
warn: log('warn'),
};
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
import { GraphQLError, type GraphQLResolveInfo } from 'graphql';
import { Trace, google } from '@apollo/usage-reporting-protobuf';
import type { SendErrorsOptions } from './usageReporting';
export declare class TraceTreeBuilder {
private rootNode;
trace: Trace;
startHrTime?: [number, number];
private stopped;
private nodes;
private readonly transformError;
constructor(options: {
maskedBy: string;
sendErrors?: SendErrorsOptions;
});
startTiming(): void;
stopTiming(): void;
willResolveField(info: GraphQLResolveInfo): () => void;
didEncounterErrors(errors: readonly GraphQLError[]): void;
private addProtobufError;
private newNode;
private ensureParentNode;
private transformAndNormalizeError;
}
export declare function dateToProtoTimestamp(date: Date): google.protobuf.Timestamp;
//# sourceMappingURL=traceTreeBuilder.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traceTreeBuilder.d.ts","sourceRoot":"","sources":["../../../src/plugin/traceTreeBuilder.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,KAAK,kBAAkB,EAExB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAO1D,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAoB;IAC7B,KAAK,QAUT;IACI,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAEV;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAEtB;gBAEU,OAAO,EAAE;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,iBAAiB,CAAC;KAChC;IAgBM,WAAW;IAWX,UAAU;IAeV,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,IAAI;IAiEtD,kBAAkB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE;IA6BzD,OAAO,CAAC,gBAAgB;IA+BxB,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,0BAA0B;CAkDnC;AAkED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAO1E"}

View File

@@ -0,0 +1,194 @@
import { GraphQLError, } from 'graphql';
import { Trace, google } from '@apollo/usage-reporting-protobuf';
import { UnreachableCaseError } from '../utils/UnreachableCaseError.js';
function internalError(message) {
return new Error(`[internal apollo-server error] ${message}`);
}
export class TraceTreeBuilder {
constructor(options) {
this.rootNode = new Trace.Node();
this.trace = new Trace({
root: this.rootNode,
fieldExecutionWeight: 1,
});
this.stopped = false;
this.nodes = new Map([
[responsePathAsString(), this.rootNode],
]);
const { sendErrors, maskedBy } = options;
if (!sendErrors || 'masked' in sendErrors) {
this.transformError = () => new GraphQLError('<masked>', {
extensions: { maskedBy },
});
}
else if ('transform' in sendErrors) {
this.transformError = sendErrors.transform;
}
else if ('unmodified' in sendErrors) {
this.transformError = null;
}
else {
throw new UnreachableCaseError(sendErrors);
}
}
startTiming() {
if (this.startHrTime) {
throw internalError('startTiming called twice!');
}
if (this.stopped) {
throw internalError('startTiming called after stopTiming!');
}
this.trace.startTime = dateToProtoTimestamp(new Date());
this.startHrTime = process.hrtime();
}
stopTiming() {
if (!this.startHrTime) {
throw internalError('stopTiming called before startTiming!');
}
if (this.stopped) {
throw internalError('stopTiming called twice!');
}
this.trace.durationNs = durationHrTimeToNanos(process.hrtime(this.startHrTime));
this.trace.endTime = dateToProtoTimestamp(new Date());
this.stopped = true;
}
willResolveField(info) {
if (!this.startHrTime) {
throw internalError('willResolveField called before startTiming!');
}
if (this.stopped) {
return () => { };
}
const path = info.path;
const node = this.newNode(path);
node.type = info.returnType.toString();
node.parentType = info.parentType.toString();
node.startTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
if (typeof path.key === 'string' && path.key !== info.fieldName) {
node.originalFieldName = info.fieldName;
}
return () => {
node.endTime = durationHrTimeToNanos(process.hrtime(this.startHrTime));
};
}
didEncounterErrors(errors) {
errors.forEach((err) => {
if (err.extensions?.serviceName) {
return;
}
const errorForReporting = this.transformAndNormalizeError(err);
if (errorForReporting === null) {
return;
}
this.addProtobufError(errorForReporting.path, errorToProtobufError(errorForReporting));
});
}
addProtobufError(path, error) {
if (!this.startHrTime) {
throw internalError('addProtobufError called before startTiming!');
}
if (this.stopped) {
throw internalError('addProtobufError called after stopTiming!');
}
let node = this.rootNode;
if (Array.isArray(path)) {
const specificNode = this.nodes.get(path.join('.'));
if (specificNode) {
node = specificNode;
}
else {
const responsePath = responsePathFromArray(path, this.rootNode);
if (!responsePath) {
throw internalError('addProtobufError called with invalid path!');
}
node = this.newNode(responsePath);
}
}
node.error.push(error);
}
newNode(path) {
const node = new Trace.Node();
const id = path.key;
if (typeof id === 'number') {
node.index = id;
}
else {
node.responseName = id;
}
this.nodes.set(responsePathAsString(path), node);
const parentNode = this.ensureParentNode(path);
parentNode.child.push(node);
return node;
}
ensureParentNode(path) {
const parentPath = responsePathAsString(path.prev);
const parentNode = this.nodes.get(parentPath);
if (parentNode) {
return parentNode;
}
return this.newNode(path.prev);
}
transformAndNormalizeError(err) {
if (this.transformError) {
const clonedError = Object.assign(Object.create(Object.getPrototypeOf(err)), err);
const rewrittenError = this.transformError(clonedError);
if (rewrittenError === null) {
return null;
}
if (!(rewrittenError instanceof GraphQLError)) {
return err;
}
return new GraphQLError(rewrittenError.message, {
nodes: err.nodes,
source: err.source,
positions: err.positions,
path: err.path,
originalError: err.originalError,
extensions: rewrittenError.extensions || err.extensions,
});
}
return err;
}
}
function durationHrTimeToNanos(hrtime) {
return hrtime[0] * 1e9 + hrtime[1];
}
function responsePathAsString(p) {
if (p === undefined) {
return '';
}
let res = String(p.key);
while ((p = p.prev) !== undefined) {
res = `${p.key}.${res}`;
}
return res;
}
function responsePathFromArray(path, node) {
let responsePath;
let nodePtr = node;
for (const key of path) {
nodePtr = nodePtr?.child?.find((child) => child.responseName === key);
responsePath = {
key,
prev: responsePath,
typename: nodePtr?.type ?? undefined,
};
}
return responsePath;
}
function errorToProtobufError(error) {
return new Trace.Error({
message: error.message,
location: (error.locations || []).map(({ line, column }) => new Trace.Location({ line, column })),
json: JSON.stringify(error),
});
}
export function dateToProtoTimestamp(date) {
const totalMillis = +date;
const millis = totalMillis % 1000;
return new google.protobuf.Timestamp({
seconds: (totalMillis - millis) / 1000,
nanos: millis * 1e6,
});
}
//# sourceMappingURL=traceTreeBuilder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { Trace } from '@apollo/usage-reporting-protobuf';
export declare function defaultSendOperationsAsTrace(): (trace: Trace, statsReportKey: string) => boolean;
//# sourceMappingURL=defaultSendOperationsAsTrace.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"defaultSendOperationsAsTrace.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/defaultSendOperationsAsTrace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAI9D,wBAAgB,4BAA4B,YA2B3B,KAAK,kBAAkB,MAAM,KAAG,OAAO,CAyBvD"}

View File

@@ -0,0 +1,41 @@
import LRUCache from 'lru-cache';
import { iterateOverTrace } from './iterateOverTrace.js';
import { DurationHistogram } from './durationHistogram.js';
export function defaultSendOperationsAsTrace() {
const cache = new LRUCache({
maxSize: Math.pow(2, 20),
sizeCalculation: (_val, key) => {
return (key && Buffer.byteLength(key)) || 0;
},
});
return (trace, statsReportKey) => {
const endTimeSeconds = trace.endTime?.seconds;
if (endTimeSeconds == null) {
throw Error('programming error: endTime not set on trace');
}
const hasErrors = traceHasErrors(trace);
const cacheKey = JSON.stringify([
statsReportKey,
DurationHistogram.durationToBucket(trace.durationNs),
Math.floor(endTimeSeconds / 60),
hasErrors ? Math.floor(endTimeSeconds / 5) : '',
]);
if (cache.get(cacheKey)) {
return false;
}
cache.set(cacheKey, true);
return true;
};
}
function traceHasErrors(trace) {
let hasErrors = false;
function traceNodeStats(node) {
if ((node.error?.length ?? 0) > 0) {
hasErrors = true;
}
return hasErrors;
}
iterateOverTrace(trace, traceNodeStats, false);
return hasErrors;
}
//# sourceMappingURL=defaultSendOperationsAsTrace.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"defaultSendOperationsAsTrace.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/defaultSendOperationsAsTrace.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,MAAM,UAAU,4BAA4B;IAU1C,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAe;QAWvC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;QACxB,eAAe,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,CAAC,KAAY,EAAE,cAAsB,EAAW,EAAE;QACvD,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;QAC9C,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;YAC3B,MAAM,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;YAC9B,cAAc;YACd,iBAAiB,CAAC,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC;YAEpD,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,EAAE,CAAC;YAG/B,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;SAChD,CAAC,CAAC;QAGH,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;AACJ,CAAC;AAID,SAAS,cAAc,CAAC,KAAY;IAClC,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,SAAS,cAAc,CAAC,IAAiB;QACvC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAClC,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,CAAC;AACnB,CAAC"}

View File

@@ -0,0 +1,16 @@
export interface DurationHistogramOptions {
initSize?: number;
buckets?: number[];
}
export declare class DurationHistogram {
private readonly buckets;
static readonly BUCKET_COUNT = 384;
static readonly EXPONENT_LOG: number;
toArray(): number[];
static durationToBucket(durationNs: number): number;
incrementDuration(durationNs: number, value?: number): DurationHistogram;
incrementBucket(bucket: number, value?: number): void;
combine(otherHistogram: DurationHistogram): void;
constructor(options?: DurationHistogramOptions);
}
//# sourceMappingURL=durationHistogram.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"durationHistogram.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/durationHistogram.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AACD,qBAAa,iBAAiB;IAO5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IACnC,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAO;IACnC,MAAM,CAAC,QAAQ,CAAC,YAAY,SAAiB;IAE7C,OAAO,IAAI,MAAM,EAAE;IAoBnB,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAYnD,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,iBAAiB;IAKnE,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAI;IAgBzC,OAAO,CAAC,cAAc,EAAE,iBAAiB;gBAM7B,OAAO,CAAC,EAAE,wBAAwB;CAY/C"}

View File

@@ -0,0 +1,63 @@
export class DurationHistogram {
toArray() {
let bufferedZeroes = 0;
const outputArray = [];
for (const value of this.buckets) {
if (value === 0) {
bufferedZeroes++;
}
else {
if (bufferedZeroes === 1) {
outputArray.push(0);
}
else if (bufferedZeroes !== 0) {
outputArray.push(-bufferedZeroes);
}
outputArray.push(Math.floor(value));
bufferedZeroes = 0;
}
}
return outputArray;
}
static durationToBucket(durationNs) {
const log = Math.log(durationNs / 1000.0);
const unboundedBucket = Math.ceil(log / DurationHistogram.EXPONENT_LOG);
return unboundedBucket <= 0 || Number.isNaN(unboundedBucket)
? 0
: unboundedBucket >= DurationHistogram.BUCKET_COUNT
? DurationHistogram.BUCKET_COUNT - 1
: unboundedBucket;
}
incrementDuration(durationNs, value = 1) {
this.incrementBucket(DurationHistogram.durationToBucket(durationNs), value);
return this;
}
incrementBucket(bucket, value = 1) {
if (bucket >= DurationHistogram.BUCKET_COUNT) {
throw Error('Bucket is out of bounds of the buckets array');
}
if (bucket >= this.buckets.length) {
const oldLength = this.buckets.length;
this.buckets.length = bucket + 1;
this.buckets.fill(0, oldLength);
}
this.buckets[bucket] += value;
}
combine(otherHistogram) {
for (let i = 0; i < otherHistogram.buckets.length; i++) {
this.incrementBucket(i, otherHistogram.buckets[i]);
}
}
constructor(options) {
const initSize = options?.initSize || 74;
const buckets = options?.buckets;
const arrayInitSize = Math.max(buckets?.length || 0, initSize);
this.buckets = Array(arrayInitSize).fill(0);
if (buckets) {
buckets.forEach((val, index) => (this.buckets[index] = val));
}
}
}
DurationHistogram.BUCKET_COUNT = 384;
DurationHistogram.EXPONENT_LOG = Math.log(1.1);
//# sourceMappingURL=durationHistogram.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"durationHistogram.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/durationHistogram.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,3 @@
export { ApolloServerPluginUsageReporting } from './plugin.js';
export type { ApolloServerPluginUsageReportingOptions, SendValuesBaseOptions, VariableValueOptions, SendErrorsOptions, ClientInfo, GenerateClientInfo, } from './options.js';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC;AAC/D,YAAY,EACV,uCAAuC,EACvC,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,kBAAkB,GACnB,MAAM,cAAc,CAAC"}

View File

@@ -0,0 +1,2 @@
export { ApolloServerPluginUsageReporting } from './plugin.js';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAC"}

View File

@@ -0,0 +1,7 @@
import type { Trace } from '@apollo/usage-reporting-protobuf';
export declare function iterateOverTrace(trace: Trace, f: (node: Trace.INode, path: ResponseNamePath) => boolean, includePath: boolean): void;
export interface ResponseNamePath {
toArray(): string[];
child(responseName: string): ResponseNamePath;
}
//# sourceMappingURL=iterateOverTrace.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"iterateOverTrace.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/iterateOverTrace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAoB9D,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,gBAAgB,KAAK,OAAO,EACzD,WAAW,EAAE,OAAO,QAYrB;AA8DD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,CAAC;CAC/C"}

View File

@@ -0,0 +1,76 @@
export function iterateOverTrace(trace, f, includePath) {
const rootPath = includePath
? new RootCollectingPathsResponseNamePath()
: notCollectingPathsResponseNamePath;
if (trace.root) {
if (iterateOverTraceNode(trace.root, rootPath, f))
return;
}
if (trace.queryPlan) {
if (iterateOverQueryPlan(trace.queryPlan, rootPath, f))
return;
}
}
function iterateOverQueryPlan(node, rootPath, f) {
if (!node)
return false;
if (node.fetch?.trace?.root && node.fetch.serviceName) {
return iterateOverTraceNode(node.fetch.trace.root, rootPath.child(`service:${node.fetch.serviceName}`), f);
}
if (node.flatten?.node) {
return iterateOverQueryPlan(node.flatten.node, rootPath, f);
}
if (node.parallel?.nodes) {
return node.parallel.nodes.some((node) => iterateOverQueryPlan(node, rootPath, f));
}
if (node.sequence?.nodes) {
return node.sequence.nodes.some((node) => iterateOverQueryPlan(node, rootPath, f));
}
return false;
}
function iterateOverTraceNode(node, path, f) {
if (f(node, path)) {
return true;
}
return (node.child?.some((child) => {
const childPath = child.responseName
? path.child(child.responseName)
: path;
return iterateOverTraceNode(child, childPath, f);
}) ?? false);
}
const notCollectingPathsResponseNamePath = {
toArray() {
throw Error('not collecting paths!');
},
child() {
return this;
},
};
class RootCollectingPathsResponseNamePath {
toArray() {
return [];
}
child(responseName) {
return new ChildCollectingPathsResponseNamePath(responseName, this);
}
}
class ChildCollectingPathsResponseNamePath {
constructor(responseName, prev) {
this.responseName = responseName;
this.prev = prev;
}
toArray() {
const out = [];
let curr = this;
while (curr instanceof ChildCollectingPathsResponseNamePath) {
out.push(curr.responseName);
curr = curr.prev;
}
return out.reverse();
}
child(responseName) {
return new ChildCollectingPathsResponseNamePath(responseName, this);
}
}
//# sourceMappingURL=iterateOverTrace.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"iterateOverTrace.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/iterateOverTrace.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,12 @@
import type { Logger } from '@apollo/utils.logger';
import type { ReferencedFieldsByType } from '@apollo/utils.usagereporting';
import LRUCache from 'lru-cache';
export interface OperationDerivedData {
signature: string;
referencedFieldsByType: ReferencedFieldsByType;
}
export declare function createOperationDerivedDataCache({ logger, }: {
logger: Logger;
}): LRUCache<string, OperationDerivedData>;
export declare function operationDerivedDataCacheKey(queryHash: string, operationName: string): string;
//# sourceMappingURL=operationDerivedDataCache.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"operationDerivedDataCache.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/operationDerivedDataCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,QAAQ,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,MAAM,GACP,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAwCzC;AAED,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,UAGtB"}

View File

@@ -0,0 +1,29 @@
import LRUCache from 'lru-cache';
export function createOperationDerivedDataCache({ logger, }) {
let lastWarn;
let lastDisposals = 0;
return new LRUCache({
sizeCalculation(obj) {
return Buffer.byteLength(JSON.stringify(obj), 'utf8');
},
maxSize: Math.pow(2, 20) * 10,
dispose() {
lastDisposals++;
if (!lastWarn || new Date().getTime() - lastWarn.getTime() > 60000) {
lastWarn = new Date();
logger.warn([
'This server is processing a high number of unique operations. ',
`A total of ${lastDisposals} records have been `,
'ejected from the ApolloServerPluginUsageReporting signature cache in the past ',
'interval. If you see this warning frequently, please open an ',
'issue on the Apollo Server repository.',
].join(''));
lastDisposals = 0;
}
},
});
}
export function operationDerivedDataCacheKey(queryHash, operationName) {
return `${queryHash}${operationName && ':' + operationName}`;
}
//# sourceMappingURL=operationDerivedDataCache.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"operationDerivedDataCache.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/operationDerivedDataCache.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAOjC,MAAM,UAAU,+BAA+B,CAAC,EAC9C,MAAM,GAGP;IACC,IAAI,QAAc,CAAC;IACnB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,OAAO,IAAI,QAAQ,CAA+B;QAEhD,eAAe,CAAC,GAAG;YACjB,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QACxD,CAAC;QASD,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE;QAC7B,OAAO;YAEL,aAAa,EAAE,CAAC;YAGhB,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;gBAEnE,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CACT;oBACE,iEAAiE;oBACjE,cAAc,aAAa,qBAAqB;oBAChD,gFAAgF;oBAChF,gEAAgE;oBAChE,wCAAwC;iBACzC,CAAC,IAAI,CAAC,EAAE,CAAC,CACX,CAAC;gBAGF,aAAa,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,SAAiB,EACjB,aAAqB;IAErB,OAAO,GAAG,SAAS,GAAG,aAAa,IAAI,GAAG,GAAG,aAAa,EAAE,CAAC;AAC/D,CAAC"}

View File

@@ -0,0 +1,60 @@
import type { GraphQLError, DocumentNode } from 'graphql';
import type { GraphQLRequestContextDidResolveOperation, GraphQLRequestContext, GraphQLRequestContextWillSendResponse, BaseContext } from '../../externalTypes/index.js';
import type { Logger } from '@apollo/utils.logger';
import type { Trace } from '@apollo/usage-reporting-protobuf';
import type { Fetcher } from '@apollo/utils.fetcher';
export interface ApolloServerPluginUsageReportingOptions<TContext extends BaseContext> {
sendTraces?: boolean;
sendVariableValues?: VariableValueOptions;
sendHeaders?: SendValuesBaseOptions;
sendErrors?: SendErrorsOptions;
fieldLevelInstrumentation?: number | ((request: GraphQLRequestContextDidResolveOperation<TContext>) => Promise<number | boolean>);
includeRequest?: (request: GraphQLRequestContextDidResolveOperation<TContext> | GraphQLRequestContextWillSendResponse<TContext>) => Promise<boolean>;
generateClientInfo?: GenerateClientInfo<TContext>;
overrideReportedSchema?: string;
sendUnexecutableOperationDocuments?: boolean;
experimental_sendOperationAsTrace?: (trace: Trace, statsReportKey: string) => boolean;
sendReportsImmediately?: boolean;
fetcher?: Fetcher;
reportIntervalMs?: number;
maxUncompressedReportSize?: number;
maxAttempts?: number;
minimumRetryDelayMs?: number;
requestTimeoutMs?: number;
logger?: Logger;
reportErrorFunction?: (err: Error) => void;
endpointUrl?: string;
debugPrintReports?: boolean;
calculateSignature?: (ast: DocumentNode, operationName: string) => string;
__onlyIfSchemaIsNotSubgraph?: boolean;
}
export type SendValuesBaseOptions = {
onlyNames: Array<string>;
} | {
exceptNames: Array<string>;
} | {
all: true;
} | {
none: true;
};
type VariableValueTransformOptions = {
variables: Record<string, any>;
operationString?: string;
};
export type VariableValueOptions = {
transform: (options: VariableValueTransformOptions) => Record<string, any>;
} | SendValuesBaseOptions;
export type SendErrorsOptions = {
unmodified: true;
} | {
masked: true;
} | {
transform: (err: GraphQLError) => GraphQLError | null;
};
export interface ClientInfo {
clientName?: string;
clientVersion?: string;
}
export type GenerateClientInfo<TContext extends BaseContext> = (requestContext: GraphQLRequestContext<TContext>) => ClientInfo;
export {};
//# sourceMappingURL=options.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/options.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=options.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/options.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,7 @@
import { Trace } from '@apollo/usage-reporting-protobuf';
import type { ApolloServerPlugin, BaseContext } from '../../externalTypes/index.js';
import type { ApolloServerPluginUsageReportingOptions, SendValuesBaseOptions } from './options.js';
import type { HeaderMap } from '../../utils/HeaderMap.js';
export declare function ApolloServerPluginUsageReporting<TContext extends BaseContext>(options?: ApolloServerPluginUsageReportingOptions<TContext>): ApolloServerPlugin<TContext>;
export declare function makeHTTPRequestHeaders(http: Trace.IHTTP, headers: HeaderMap, sendHeaders?: SendValuesBaseOptions): void;
//# sourceMappingURL=plugin.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAc/E,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EAMZ,MAAM,8BAA8B,CAAC;AAStC,OAAO,KAAK,EACV,uCAAuC,EACvC,qBAAqB,EACtB,MAAM,cAAc,CAAC;AAKtB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAW1D,wBAAgB,gCAAgC,CAAC,QAAQ,SAAS,WAAW,EAC3E,OAAO,GAAE,uCAAuC,CAAC,QAAQ,CAExD,GACA,kBAAkB,CAAC,QAAQ,CAAC,CA2vB9B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,KAAK,CAAC,KAAK,EACjB,OAAO,EAAE,SAAS,EAClB,WAAW,CAAC,EAAE,qBAAqB,GAClC,IAAI,CAsCN"}

View File

@@ -0,0 +1,499 @@
import { Report, ReportHeader, Trace } from '@apollo/usage-reporting-protobuf';
import { usageReportingSignature, calculateReferencedFieldsByType, } from '@apollo/utils.usagereporting';
import retry from 'async-retry';
import { printSchema } from 'graphql';
import { AbortController } from 'node-abort-controller';
import fetch from 'node-fetch';
import os from 'os';
import { gzip } from 'zlib';
import { internalPlugin } from '../../internalPlugin.js';
import { dateToProtoTimestamp, TraceTreeBuilder } from '../traceTreeBuilder.js';
import { defaultSendOperationsAsTrace } from './defaultSendOperationsAsTrace.js';
import { createOperationDerivedDataCache, operationDerivedDataCacheKey, } from './operationDerivedDataCache.js';
import { OurReport } from './stats.js';
import { makeTraceDetails } from './traceDetails.js';
import { packageVersion } from '../../generated/packageVersion.js';
import { computeCoreSchemaHash } from '../../utils/computeCoreSchemaHash.js';
import { schemaIsSubgraph } from '../schemaIsSubgraph.js';
const reportHeaderDefaults = {
hostname: os.hostname(),
agentVersion: `@apollo/server@${packageVersion}`,
runtimeVersion: `node ${process.version}`,
uname: `${os.platform()}, ${os.type()}, ${os.release()}, ${os.arch()})`,
};
export function ApolloServerPluginUsageReporting(options = Object.create(null)) {
const fieldLevelInstrumentationOption = options.fieldLevelInstrumentation;
const fieldLevelInstrumentation = typeof fieldLevelInstrumentationOption === 'number'
? async () => Math.random() < fieldLevelInstrumentationOption
? 1 / fieldLevelInstrumentationOption
: 0
: fieldLevelInstrumentationOption
? fieldLevelInstrumentationOption
: async () => true;
let requestDidStartHandler = null;
return internalPlugin({
__internal_plugin_id__: 'UsageReporting',
__is_disabled_plugin__: false,
async requestDidStart(requestContext) {
if (requestDidStartHandler) {
return requestDidStartHandler(requestContext);
}
return {};
},
async serverWillStart({ logger: serverLogger, apollo, startedInBackground, schema, }) {
const logger = options.logger ?? serverLogger;
const { key, graphRef } = apollo;
if (!(key && graphRef)) {
throw new Error("You've enabled usage reporting via ApolloServerPluginUsageReporting, " +
'but you also need to provide your Apollo API key and graph ref, via ' +
'the APOLLO_KEY/APOLLO_GRAPH_REF environment ' +
'variables or via `new ApolloServer({apollo: {key, graphRef})`.');
}
if (schemaIsSubgraph(schema)) {
if (options.__onlyIfSchemaIsNotSubgraph) {
logger.warn('You have specified an Apollo API key and graph ref but this server appears ' +
'to be a subgraph. Typically usage reports are sent to Apollo by your Router ' +
'or Gateway, not directly from your subgraph; usage reporting is disabled. To ' +
'enable usage reporting anyway, explicitly install `ApolloServerPluginUsageReporting`. ' +
'To disable this warning, install `ApolloServerPluginUsageReportingDisabled`.');
return {};
}
else {
logger.warn('You have installed `ApolloServerPluginUsageReporting` but this server appears to ' +
'be a subgraph. Typically usage reports are sent to Apollo by your Router ' +
'or Gateway, not directly from your subgraph. If this was unintentional, remove ' +
"`ApolloServerPluginUsageReporting` from your server's `plugins` array.");
}
}
logger.info('Apollo usage reporting starting! See your graph at ' +
`https://studio.apollographql.com/graph/${encodeURI(graphRef)}/`);
const sendReportsImmediately = options.sendReportsImmediately ?? startedInBackground;
let operationDerivedDataCache = null;
const reportByExecutableSchemaId = new Map();
const getReportWhichMustBeUsedImmediately = (executableSchemaId) => {
const existing = reportByExecutableSchemaId.get(executableSchemaId);
if (existing) {
return existing;
}
const report = new OurReport(new ReportHeader({
...reportHeaderDefaults,
executableSchemaId,
graphRef,
}));
reportByExecutableSchemaId.set(executableSchemaId, report);
return report;
};
const getAndDeleteReport = (executableSchemaId) => {
const report = reportByExecutableSchemaId.get(executableSchemaId);
if (report) {
reportByExecutableSchemaId.delete(executableSchemaId);
return report;
}
return null;
};
const overriddenExecutableSchemaId = options.overrideReportedSchema
? computeCoreSchemaHash(options.overrideReportedSchema)
: undefined;
let lastSeenExecutableSchemaToId;
let reportTimer;
if (!sendReportsImmediately) {
reportTimer = setInterval(() => sendAllReportsAndReportErrors(), options.reportIntervalMs || 10 * 1000);
}
let sendTraces = options.sendTraces ?? true;
const sendOperationAsTrace = options.experimental_sendOperationAsTrace ??
defaultSendOperationsAsTrace();
let stopped = false;
function executableSchemaIdForSchema(schema) {
if (lastSeenExecutableSchemaToId?.executableSchema === schema) {
return lastSeenExecutableSchemaToId.executableSchemaId;
}
const id = computeCoreSchemaHash(printSchema(schema));
lastSeenExecutableSchemaToId = {
executableSchema: schema,
executableSchemaId: id,
};
return id;
}
async function sendAllReportsAndReportErrors() {
await Promise.all([...reportByExecutableSchemaId.keys()].map((executableSchemaId) => sendReportAndReportErrors(executableSchemaId)));
}
async function sendReportAndReportErrors(executableSchemaId) {
return sendReport(executableSchemaId).catch((err) => {
if (options.reportErrorFunction) {
options.reportErrorFunction(err);
}
else {
logger.error(err.message);
}
});
}
const sendReport = async (executableSchemaId) => {
let report = getAndDeleteReport(executableSchemaId);
if (!report ||
(Object.keys(report.tracesPerQuery).length === 0 &&
report.operationCount === 0)) {
return;
}
report.endTime = dateToProtoTimestamp(new Date());
report.ensureCountsAreIntegers();
const protobufError = Report.verify(report);
if (protobufError) {
throw new Error(`Error verifying report: ${protobufError}`);
}
let message = Report.encode(report).finish();
report = null;
if (options.debugPrintReports) {
const decodedReport = Report.decode(message);
logger.info(`Apollo usage report: ${JSON.stringify(decodedReport.toJSON())}`);
}
const compressed = await new Promise((resolve, reject) => {
gzip(message, (error, result) => {
error ? reject(error) : resolve(result);
});
});
message = null;
const fetcher = options.fetcher ?? fetch;
const response = await retry(async () => {
const controller = new AbortController();
const abortTimeout = setTimeout(() => {
controller.abort();
}, options.requestTimeoutMs ?? 30000);
let curResponse;
try {
curResponse = await fetcher((options.endpointUrl ||
'https://usage-reporting.api.apollographql.com') +
'/api/ingress/traces', {
method: 'POST',
headers: {
'user-agent': 'ApolloServerPluginUsageReporting',
'x-api-key': key,
'content-encoding': 'gzip',
accept: 'application/json',
},
body: compressed,
signal: controller.signal,
});
}
finally {
clearTimeout(abortTimeout);
}
if (curResponse.status >= 500 && curResponse.status < 600) {
throw new Error(`HTTP status ${curResponse.status}, ${(await curResponse.text()) || '(no body)'}`);
}
else {
return curResponse;
}
}, {
retries: (options.maxAttempts || 5) - 1,
minTimeout: options.minimumRetryDelayMs || 100,
factor: 2,
}).catch((err) => {
throw new Error(`Error sending report to Apollo servers: ${err.message}`);
});
if (response.status < 200 || response.status >= 300) {
throw new Error(`Error sending report to Apollo servers: HTTP status ${response.status}, ${(await response.text()) || '(no body)'}`);
}
if (sendTraces &&
response.status === 200 &&
response.headers
.get('content-type')
?.match(/^\s*application\/json\s*(?:;|$)/i)) {
const body = await response.text();
let parsedBody;
try {
parsedBody = JSON.parse(body);
}
catch (e) {
throw new Error(`Error parsing response from Apollo servers: ${e}`);
}
if (parsedBody.tracesIgnored === true) {
logger.debug("This graph's organization does not have access to traces; sending all " +
'subsequent operations as stats.');
sendTraces = false;
}
}
if (options.debugPrintReports) {
logger.info(`Apollo usage report: status ${response.status}`);
}
};
requestDidStartHandler = ({ metrics, schema, request: { http, variables }, }) => {
const treeBuilder = new TraceTreeBuilder({
maskedBy: 'ApolloServerPluginUsageReporting',
sendErrors: options.sendErrors,
});
treeBuilder.startTiming();
metrics.startHrTime = treeBuilder.startHrTime;
let graphqlValidationFailure = false;
let graphqlUnknownOperationName = false;
let includeOperationInUsageReporting = null;
if (http) {
treeBuilder.trace.http = new Trace.HTTP({
method: Trace.HTTP.Method[http.method] || Trace.HTTP.Method.UNKNOWN,
});
if (options.sendHeaders) {
makeHTTPRequestHeaders(treeBuilder.trace.http, http.headers, options.sendHeaders);
}
}
async function maybeCallIncludeRequestHook(requestContext) {
if (includeOperationInUsageReporting !== null)
return;
if (typeof options.includeRequest !== 'function') {
includeOperationInUsageReporting = true;
return;
}
includeOperationInUsageReporting =
await options.includeRequest(requestContext);
if (typeof includeOperationInUsageReporting !== 'boolean') {
logger.warn("The 'includeRequest' async predicate function must return a boolean value.");
includeOperationInUsageReporting = true;
}
}
let didResolveSource = false;
return {
async didResolveSource(requestContext) {
didResolveSource = true;
if (metrics.persistedQueryHit) {
treeBuilder.trace.persistedQueryHit = true;
}
if (metrics.persistedQueryRegister) {
treeBuilder.trace.persistedQueryRegister = true;
}
if (variables) {
treeBuilder.trace.details = makeTraceDetails(variables, options.sendVariableValues, requestContext.source);
}
const clientInfo = (options.generateClientInfo || defaultGenerateClientInfo)(requestContext);
if (clientInfo) {
const { clientName, clientVersion } = clientInfo;
treeBuilder.trace.clientVersion = clientVersion || '';
treeBuilder.trace.clientName = clientName || '';
}
},
async validationDidStart() {
return async (validationErrors) => {
graphqlValidationFailure = validationErrors
? validationErrors.length !== 0
: false;
};
},
async didResolveOperation(requestContext) {
graphqlUnknownOperationName =
requestContext.operation === undefined;
await maybeCallIncludeRequestHook(requestContext);
if (includeOperationInUsageReporting &&
!graphqlUnknownOperationName) {
if (metrics.captureTraces === undefined) {
const rawWeight = await fieldLevelInstrumentation(requestContext);
treeBuilder.trace.fieldExecutionWeight =
typeof rawWeight === 'number' ? rawWeight : rawWeight ? 1 : 0;
metrics.captureTraces =
!!treeBuilder.trace.fieldExecutionWeight;
}
}
},
async executionDidStart() {
if (!metrics.captureTraces)
return;
return {
willResolveField({ info }) {
return treeBuilder.willResolveField(info);
},
};
},
async didEncounterSubsequentErrors(_requestContext, errors) {
treeBuilder.didEncounterErrors(errors);
},
async willSendSubsequentPayload(requestContext, payload) {
if (!payload.hasNext) {
await operationFinished(requestContext);
}
},
async willSendResponse(requestContext) {
if (!didResolveSource)
return;
if (requestContext.errors) {
treeBuilder.didEncounterErrors(requestContext.errors);
}
if (requestContext.response.body.kind === 'single') {
await operationFinished(requestContext);
}
},
};
async function operationFinished(requestContext) {
const resolvedOperation = !!requestContext.operation;
await maybeCallIncludeRequestHook(requestContext);
treeBuilder.stopTiming();
const executableSchemaId = overriddenExecutableSchemaId ?? executableSchemaIdForSchema(schema);
if (includeOperationInUsageReporting === false) {
if (resolvedOperation) {
getReportWhichMustBeUsedImmediately(executableSchemaId)
.operationCount++;
}
return;
}
treeBuilder.trace.fullQueryCacheHit = !!metrics.responseCacheHit;
treeBuilder.trace.forbiddenOperation = !!metrics.forbiddenOperation;
treeBuilder.trace.registeredOperation = !!metrics.registeredOperation;
const policyIfCacheable = requestContext.overallCachePolicy.policyIfCacheable();
if (policyIfCacheable) {
treeBuilder.trace.cachePolicy = new Trace.CachePolicy({
scope: policyIfCacheable.scope === 'PRIVATE'
? Trace.CachePolicy.Scope.PRIVATE
: policyIfCacheable.scope === 'PUBLIC'
? Trace.CachePolicy.Scope.PUBLIC
: Trace.CachePolicy.Scope.UNKNOWN,
maxAgeNs: policyIfCacheable.maxAge * 1e9,
});
}
if (metrics.queryPlanTrace) {
treeBuilder.trace.queryPlan = metrics.queryPlanTrace;
}
addTrace().catch(logger.error);
async function addTrace() {
if (stopped) {
return;
}
await new Promise((res) => setImmediate(res));
const executableSchemaId = overriddenExecutableSchemaId ??
executableSchemaIdForSchema(schema);
const { trace } = treeBuilder;
let statsReportKey = undefined;
let referencedFieldsByType;
if (!requestContext.document) {
statsReportKey = `## GraphQLParseFailure\n`;
}
else if (graphqlValidationFailure) {
statsReportKey = `## GraphQLValidationFailure\n`;
}
else if (graphqlUnknownOperationName) {
statsReportKey = `## GraphQLUnknownOperationName\n`;
}
const isExecutable = statsReportKey === undefined;
if (statsReportKey) {
if (options.sendUnexecutableOperationDocuments) {
trace.unexecutedOperationBody = requestContext.source;
trace.unexecutedOperationName =
requestContext.request.operationName || '';
}
referencedFieldsByType = Object.create(null);
}
else {
const operationDerivedData = getOperationDerivedData();
statsReportKey = `# ${requestContext.operationName || '-'}\n${operationDerivedData.signature}`;
referencedFieldsByType =
operationDerivedData.referencedFieldsByType;
}
const protobufError = Trace.verify(trace);
if (protobufError) {
throw new Error(`Error encoding trace: ${protobufError}`);
}
if (resolvedOperation) {
getReportWhichMustBeUsedImmediately(executableSchemaId)
.operationCount++;
}
getReportWhichMustBeUsedImmediately(executableSchemaId).addTrace({
statsReportKey,
trace,
asTrace: sendTraces &&
(!isExecutable || !!metrics.captureTraces) &&
!metrics.nonFtv1ErrorPaths?.length &&
sendOperationAsTrace(trace, statsReportKey),
referencedFieldsByType,
nonFtv1ErrorPaths: metrics.nonFtv1ErrorPaths ?? [],
});
if (sendReportsImmediately ||
getReportWhichMustBeUsedImmediately(executableSchemaId)
.sizeEstimator.bytes >=
(options.maxUncompressedReportSize || 4 * 1024 * 1024)) {
await sendReportAndReportErrors(executableSchemaId);
}
}
function getOperationDerivedData() {
if (!requestContext.document) {
throw new Error('No document?');
}
const cacheKey = operationDerivedDataCacheKey(requestContext.queryHash, requestContext.operationName || '');
if (!operationDerivedDataCache ||
operationDerivedDataCache.forSchema !== schema) {
operationDerivedDataCache = {
forSchema: schema,
cache: createOperationDerivedDataCache({ logger }),
};
}
const cachedOperationDerivedData = operationDerivedDataCache.cache.get(cacheKey);
if (cachedOperationDerivedData) {
return cachedOperationDerivedData;
}
const generatedSignature = (options.calculateSignature || usageReportingSignature)(requestContext.document, requestContext.operationName || '');
const generatedOperationDerivedData = {
signature: generatedSignature,
referencedFieldsByType: calculateReferencedFieldsByType({
document: requestContext.document,
schema,
resolvedOperationName: requestContext.operationName ?? null,
}),
};
operationDerivedDataCache.cache.set(cacheKey, generatedOperationDerivedData);
return generatedOperationDerivedData;
}
}
};
return {
async serverWillStop() {
if (reportTimer) {
clearInterval(reportTimer);
reportTimer = undefined;
}
stopped = true;
await sendAllReportsAndReportErrors();
},
};
},
});
}
export function makeHTTPRequestHeaders(http, headers, sendHeaders) {
if (!sendHeaders ||
('none' in sendHeaders && sendHeaders.none) ||
('all' in sendHeaders && !sendHeaders.all)) {
return;
}
for (const [key, value] of headers) {
if (('exceptNames' in sendHeaders &&
sendHeaders.exceptNames.some((exceptHeader) => {
return exceptHeader.toLowerCase() === key;
})) ||
('onlyNames' in sendHeaders &&
!sendHeaders.onlyNames.some((header) => {
return header.toLowerCase() === key;
}))) {
continue;
}
switch (key) {
case 'authorization':
case 'cookie':
case 'set-cookie':
break;
default:
http.requestHeaders[key] = new Trace.HTTP.Values({
value: [value],
});
}
}
}
function defaultGenerateClientInfo({ request, }) {
const clientNameHeaderKey = 'apollographql-client-name';
const clientVersionHeaderKey = 'apollographql-client-version';
if (request.http?.headers?.get(clientNameHeaderKey) ||
request.http?.headers?.get(clientVersionHeaderKey)) {
return {
clientName: request.http?.headers?.get(clientNameHeaderKey),
clientVersion: request.http?.headers?.get(clientVersionHeaderKey),
};
}
else if (request.extensions?.clientInfo) {
return request.extensions.clientInfo;
}
else {
return {};
}
}
//# sourceMappingURL=plugin.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,96 @@
import type { NonFtv1ErrorPath } from '@apollo/server-gateway-interface';
import { type google, type IContextualizedStats, type IFieldStat, type IPathErrorStats, type IQueryLatencyStats, type IReport, type IStatsContext, type ITracesAndStats, type ITypeStat, type ReportHeader, Trace } from '@apollo/usage-reporting-protobuf';
import type { ReferencedFieldsByType } from '@apollo/utils.usagereporting';
import { DurationHistogram } from './durationHistogram.js';
export declare class SizeEstimator {
bytes: number;
}
export declare class OurReport implements Required<IReport> {
readonly header: ReportHeader;
tracesPreAggregated: boolean;
constructor(header: ReportHeader);
readonly tracesPerQuery: Record<string, OurTracesAndStats>;
endTime: google.protobuf.ITimestamp | null;
operationCount: number;
readonly sizeEstimator: SizeEstimator;
ensureCountsAreIntegers(): void;
addTrace({ statsReportKey, trace, asTrace, referencedFieldsByType, maxTraceBytes, nonFtv1ErrorPaths, }: {
statsReportKey: string;
trace: Trace;
asTrace: boolean;
referencedFieldsByType: ReferencedFieldsByType;
maxTraceBytes?: number;
nonFtv1ErrorPaths: NonFtv1ErrorPath[];
}): void;
private getTracesAndStats;
}
declare class OurTracesAndStats implements Required<ITracesAndStats> {
readonly referencedFieldsByType: ReferencedFieldsByType;
constructor(referencedFieldsByType: ReferencedFieldsByType);
readonly trace: Uint8Array[];
readonly statsWithContext: StatsByContext;
readonly internalTracesContributingToStats: Uint8Array[];
ensureCountsAreIntegers(): void;
}
declare class StatsByContext {
readonly map: {
[k: string]: OurContextualizedStats;
};
toArray(): IContextualizedStats[];
ensureCountsAreIntegers(): void;
addTrace(trace: Trace, sizeEstimator: SizeEstimator, nonFtv1ErrorPaths: NonFtv1ErrorPath[]): void;
private getContextualizedStats;
}
export declare class OurContextualizedStats implements Required<IContextualizedStats> {
readonly context: IStatsContext;
queryLatencyStats: OurQueryLatencyStats;
perTypeStat: {
[k: string]: OurTypeStat;
};
constructor(context: IStatsContext);
ensureCountsAreIntegers(): void;
addTrace(trace: Trace, sizeEstimator: SizeEstimator, nonFtv1ErrorPaths?: NonFtv1ErrorPath[]): void;
getTypeStat(parentType: string, sizeEstimator: SizeEstimator): OurTypeStat;
}
declare class OurQueryLatencyStats implements Required<IQueryLatencyStats> {
latencyCount: DurationHistogram;
requestCount: number;
requestsWithoutFieldInstrumentation: number;
cacheHits: number;
persistedQueryHits: number;
persistedQueryMisses: number;
cacheLatencyCount: DurationHistogram;
rootErrorStats: OurPathErrorStats;
requestsWithErrorsCount: number;
publicCacheTtlCount: DurationHistogram;
privateCacheTtlCount: DurationHistogram;
registeredOperationCount: number;
forbiddenOperationCount: number;
}
declare class OurPathErrorStats implements Required<IPathErrorStats> {
children: {
[k: string]: OurPathErrorStats;
};
errorsCount: number;
requestsWithErrorsCount: number;
getChild(subPath: string, sizeEstimator: SizeEstimator): OurPathErrorStats;
}
declare class OurTypeStat implements Required<ITypeStat> {
perFieldStat: {
[k: string]: OurFieldStat;
};
getFieldStat(fieldName: string, returnType: string, sizeEstimator: SizeEstimator): OurFieldStat;
ensureCountsAreIntegers(): void;
}
declare class OurFieldStat implements Required<IFieldStat> {
readonly returnType: string;
errorsCount: number;
observedExecutionCount: number;
estimatedExecutionCount: number;
requestsWithErrorsCount: number;
latencyCount: DurationHistogram;
constructor(returnType: string);
ensureCountsAreIntegers(): void;
}
export {};
//# sourceMappingURL=stats.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/stats.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,289 @@
import { Trace, } from '@apollo/usage-reporting-protobuf';
import { DurationHistogram } from './durationHistogram.js';
import { iterateOverTrace } from './iterateOverTrace.js';
export class SizeEstimator {
constructor() {
this.bytes = 0;
}
}
export class OurReport {
constructor(header) {
this.header = header;
this.tracesPreAggregated = false;
this.tracesPerQuery = Object.create(null);
this.endTime = null;
this.operationCount = 0;
this.sizeEstimator = new SizeEstimator();
}
ensureCountsAreIntegers() {
for (const tracesAndStats of Object.values(this.tracesPerQuery)) {
tracesAndStats.ensureCountsAreIntegers();
}
}
addTrace({ statsReportKey, trace, asTrace, referencedFieldsByType, maxTraceBytes = 10 * 1024 * 1024, nonFtv1ErrorPaths, }) {
const tracesAndStats = this.getTracesAndStats({
statsReportKey,
referencedFieldsByType,
});
if (asTrace) {
const encodedTrace = Trace.encode(trace).finish();
if (!isNaN(maxTraceBytes) && encodedTrace.length > maxTraceBytes) {
tracesAndStats.statsWithContext.addTrace(trace, this.sizeEstimator, nonFtv1ErrorPaths);
}
else {
tracesAndStats.trace.push(encodedTrace);
this.sizeEstimator.bytes += 2 + encodedTrace.length;
}
}
else {
tracesAndStats.statsWithContext.addTrace(trace, this.sizeEstimator, nonFtv1ErrorPaths);
}
}
getTracesAndStats({ statsReportKey, referencedFieldsByType, }) {
const existing = this.tracesPerQuery[statsReportKey];
if (existing) {
return existing;
}
this.sizeEstimator.bytes += estimatedBytesForString(statsReportKey);
for (const [typeName, referencedFieldsForType] of Object.entries(referencedFieldsByType)) {
this.sizeEstimator.bytes += 2 + 2;
if (referencedFieldsForType.isInterface) {
this.sizeEstimator.bytes += 2;
}
this.sizeEstimator.bytes += estimatedBytesForString(typeName);
for (const fieldName of referencedFieldsForType.fieldNames) {
this.sizeEstimator.bytes += estimatedBytesForString(fieldName);
}
}
return (this.tracesPerQuery[statsReportKey] = new OurTracesAndStats(referencedFieldsByType));
}
}
class OurTracesAndStats {
constructor(referencedFieldsByType) {
this.referencedFieldsByType = referencedFieldsByType;
this.trace = [];
this.statsWithContext = new StatsByContext();
this.internalTracesContributingToStats = [];
}
ensureCountsAreIntegers() {
this.statsWithContext.ensureCountsAreIntegers();
}
}
class StatsByContext {
constructor() {
this.map = Object.create(null);
}
toArray() {
return Object.values(this.map);
}
ensureCountsAreIntegers() {
for (const contextualizedStats of Object.values(this.map)) {
contextualizedStats.ensureCountsAreIntegers();
}
}
addTrace(trace, sizeEstimator, nonFtv1ErrorPaths) {
this.getContextualizedStats(trace, sizeEstimator).addTrace(trace, sizeEstimator, nonFtv1ErrorPaths);
}
getContextualizedStats(trace, sizeEstimator) {
const statsContext = {
clientName: trace.clientName,
clientVersion: trace.clientVersion,
};
const statsContextKey = JSON.stringify(statsContext);
const existing = this.map[statsContextKey];
if (existing) {
return existing;
}
sizeEstimator.bytes +=
20 +
estimatedBytesForString(trace.clientName) +
estimatedBytesForString(trace.clientVersion);
const contextualizedStats = new OurContextualizedStats(statsContext);
this.map[statsContextKey] = contextualizedStats;
return contextualizedStats;
}
}
export class OurContextualizedStats {
constructor(context) {
this.context = context;
this.queryLatencyStats = new OurQueryLatencyStats();
this.perTypeStat = Object.create(null);
}
ensureCountsAreIntegers() {
for (const typeStat of Object.values(this.perTypeStat)) {
typeStat.ensureCountsAreIntegers();
}
}
addTrace(trace, sizeEstimator, nonFtv1ErrorPaths = []) {
const { fieldExecutionWeight } = trace;
if (!fieldExecutionWeight) {
this.queryLatencyStats.requestsWithoutFieldInstrumentation++;
}
this.queryLatencyStats.requestCount++;
if (trace.fullQueryCacheHit) {
this.queryLatencyStats.cacheLatencyCount.incrementDuration(trace.durationNs);
this.queryLatencyStats.cacheHits++;
}
else {
this.queryLatencyStats.latencyCount.incrementDuration(trace.durationNs);
}
if (!trace.fullQueryCacheHit && trace.cachePolicy?.maxAgeNs != null) {
switch (trace.cachePolicy.scope) {
case Trace.CachePolicy.Scope.PRIVATE:
this.queryLatencyStats.privateCacheTtlCount.incrementDuration(trace.cachePolicy.maxAgeNs);
break;
case Trace.CachePolicy.Scope.PUBLIC:
this.queryLatencyStats.publicCacheTtlCount.incrementDuration(trace.cachePolicy.maxAgeNs);
break;
}
}
if (trace.persistedQueryHit) {
this.queryLatencyStats.persistedQueryHits++;
}
if (trace.persistedQueryRegister) {
this.queryLatencyStats.persistedQueryMisses++;
}
if (trace.forbiddenOperation) {
this.queryLatencyStats.forbiddenOperationCount++;
}
if (trace.registeredOperation) {
this.queryLatencyStats.registeredOperationCount++;
}
let hasError = false;
const errorPathStats = new Set();
const traceNodeStats = (node, path) => {
if (node.error?.length) {
hasError = true;
let currPathErrorStats = this.queryLatencyStats.rootErrorStats;
path.toArray().forEach((subPath) => {
currPathErrorStats = currPathErrorStats.getChild(subPath, sizeEstimator);
});
errorPathStats.add(currPathErrorStats);
currPathErrorStats.errorsCount += node.error.length;
}
if (fieldExecutionWeight) {
const fieldName = node.originalFieldName || node.responseName;
if (node.parentType &&
fieldName &&
node.type &&
node.endTime != null &&
node.startTime != null &&
node.endTime >= node.startTime) {
const typeStat = this.getTypeStat(node.parentType, sizeEstimator);
const fieldStat = typeStat.getFieldStat(fieldName, node.type, sizeEstimator);
fieldStat.errorsCount += node.error?.length ?? 0;
fieldStat.observedExecutionCount++;
fieldStat.estimatedExecutionCount += fieldExecutionWeight;
fieldStat.requestsWithErrorsCount +=
(node.error?.length ?? 0) > 0 ? 1 : 0;
fieldStat.latencyCount.incrementDuration(node.endTime - node.startTime, fieldExecutionWeight);
}
}
return false;
};
iterateOverTrace(trace, traceNodeStats, true);
for (const { subgraph, path } of nonFtv1ErrorPaths) {
hasError = true;
if (path) {
let currPathErrorStats = this.queryLatencyStats.rootErrorStats.getChild(`service:${subgraph}`, sizeEstimator);
path.forEach((subPath) => {
if (typeof subPath === 'string') {
currPathErrorStats = currPathErrorStats.getChild(subPath, sizeEstimator);
}
});
errorPathStats.add(currPathErrorStats);
currPathErrorStats.errorsCount += 1;
}
}
for (const errorPath of errorPathStats) {
errorPath.requestsWithErrorsCount += 1;
}
if (hasError) {
this.queryLatencyStats.requestsWithErrorsCount++;
}
}
getTypeStat(parentType, sizeEstimator) {
const existing = this.perTypeStat[parentType];
if (existing) {
return existing;
}
sizeEstimator.bytes += estimatedBytesForString(parentType);
const typeStat = new OurTypeStat();
this.perTypeStat[parentType] = typeStat;
return typeStat;
}
}
class OurQueryLatencyStats {
constructor() {
this.latencyCount = new DurationHistogram();
this.requestCount = 0;
this.requestsWithoutFieldInstrumentation = 0;
this.cacheHits = 0;
this.persistedQueryHits = 0;
this.persistedQueryMisses = 0;
this.cacheLatencyCount = new DurationHistogram();
this.rootErrorStats = new OurPathErrorStats();
this.requestsWithErrorsCount = 0;
this.publicCacheTtlCount = new DurationHistogram();
this.privateCacheTtlCount = new DurationHistogram();
this.registeredOperationCount = 0;
this.forbiddenOperationCount = 0;
}
}
class OurPathErrorStats {
constructor() {
this.children = Object.create(null);
this.errorsCount = 0;
this.requestsWithErrorsCount = 0;
}
getChild(subPath, sizeEstimator) {
const existing = this.children[subPath];
if (existing) {
return existing;
}
const child = new OurPathErrorStats();
this.children[subPath] = child;
sizeEstimator.bytes += estimatedBytesForString(subPath) + 4;
return child;
}
}
class OurTypeStat {
constructor() {
this.perFieldStat = Object.create(null);
}
getFieldStat(fieldName, returnType, sizeEstimator) {
const existing = this.perFieldStat[fieldName];
if (existing) {
return existing;
}
sizeEstimator.bytes +=
estimatedBytesForString(fieldName) +
estimatedBytesForString(returnType) +
10;
const fieldStat = new OurFieldStat(returnType);
this.perFieldStat[fieldName] = fieldStat;
return fieldStat;
}
ensureCountsAreIntegers() {
for (const fieldStat of Object.values(this.perFieldStat)) {
fieldStat.ensureCountsAreIntegers();
}
}
}
class OurFieldStat {
constructor(returnType) {
this.returnType = returnType;
this.errorsCount = 0;
this.observedExecutionCount = 0;
this.estimatedExecutionCount = 0;
this.requestsWithErrorsCount = 0;
this.latencyCount = new DurationHistogram();
}
ensureCountsAreIntegers() {
this.estimatedExecutionCount = Math.floor(this.estimatedExecutionCount);
}
}
function estimatedBytesForString(s) {
return 2 + Buffer.byteLength(s);
}
//# sourceMappingURL=stats.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { Trace } from '@apollo/usage-reporting-protobuf';
import type { VariableValueOptions } from './options.js';
export declare function makeTraceDetails(variables: Record<string, any>, sendVariableValues?: VariableValueOptions, operationString?: string): Trace.Details;
//# sourceMappingURL=traceDetails.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traceDetails.d.ts","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/traceDetails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AASzD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,kBAAkB,CAAC,EAAE,oBAAoB,EACzC,eAAe,CAAC,EAAE,MAAM,GACvB,KAAK,CAAC,OAAO,CA0Df"}

View File

@@ -0,0 +1,60 @@
import { Trace } from '@apollo/usage-reporting-protobuf';
export function makeTraceDetails(variables, sendVariableValues, operationString) {
const details = new Trace.Details();
const variablesToRecord = (() => {
if (sendVariableValues && 'transform' in sendVariableValues) {
const originalKeys = Object.keys(variables);
try {
const modifiedVariables = sendVariableValues.transform({
variables: variables,
operationString: operationString,
});
return cleanModifiedVariables(originalKeys, modifiedVariables);
}
catch (e) {
return handleVariableValueTransformError(originalKeys);
}
}
else {
return variables;
}
})();
Object.keys(variablesToRecord).forEach((name) => {
if (!sendVariableValues ||
('none' in sendVariableValues && sendVariableValues.none) ||
('all' in sendVariableValues && !sendVariableValues.all) ||
('exceptNames' in sendVariableValues &&
sendVariableValues.exceptNames.includes(name)) ||
('onlyNames' in sendVariableValues &&
!sendVariableValues.onlyNames.includes(name))) {
details.variablesJson[name] = '';
}
else {
try {
details.variablesJson[name] =
typeof variablesToRecord[name] === 'undefined'
? ''
: JSON.stringify(variablesToRecord[name]);
}
catch (e) {
details.variablesJson[name] = JSON.stringify('[Unable to convert value to JSON]');
}
}
});
return details;
}
function handleVariableValueTransformError(variableNames) {
const modifiedVariables = Object.create(null);
variableNames.forEach((name) => {
modifiedVariables[name] = '[PREDICATE_FUNCTION_ERROR]';
});
return modifiedVariables;
}
function cleanModifiedVariables(originalKeys, modifiedVariables) {
const cleanedVariables = Object.create(null);
originalKeys.forEach((name) => {
cleanedVariables[name] = modifiedVariables[name];
});
return cleanedVariables;
}
//# sourceMappingURL=traceDetails.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traceDetails.js","sourceRoot":"","sources":["../../../../src/plugin/usageReporting/traceDetails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAUzD,MAAM,UAAU,gBAAgB,CAC9B,SAA8B,EAC9B,kBAAyC,EACzC,eAAwB;IAExB,MAAM,OAAO,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;IACpC,MAAM,iBAAiB,GAAG,CAAC,GAAG,EAAE;QAC9B,IAAI,kBAAkB,IAAI,WAAW,IAAI,kBAAkB,EAAE,CAAC;YAC5D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC;gBAEH,MAAM,iBAAiB,GAAG,kBAAkB,CAAC,SAAS,CAAC;oBACrD,SAAS,EAAE,SAAS;oBACpB,eAAe,EAAE,eAAe;iBACjC,CAAC,CAAC;gBACH,OAAO,sBAAsB,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;YACjE,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBAGX,OAAO,iCAAiC,CAAC,YAAY,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IAOL,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC9C,IACE,CAAC,kBAAkB;YACnB,CAAC,MAAM,IAAI,kBAAkB,IAAI,kBAAkB,CAAC,IAAI,CAAC;YACzD,CAAC,KAAK,IAAI,kBAAkB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC;YACxD,CAAC,aAAa,IAAI,kBAAkB;gBAIlC,kBAAkB,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAChD,CAAC,WAAW,IAAI,kBAAkB;gBAChC,CAAC,kBAAkB,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAC/C,CAAC;YAID,OAAO,CAAC,aAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC;gBACH,OAAO,CAAC,aAAc,CAAC,IAAI,CAAC;oBAC1B,OAAO,iBAAiB,CAAC,IAAI,CAAC,KAAK,WAAW;wBAC5C,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;YAChD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,aAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAC3C,mCAAmC,CACpC,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,iCAAiC,CACxC,aAAuB;IAEvB,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9C,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC7B,iBAAiB,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC;IACzD,CAAC,CAAC,CAAC;IACH,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAID,SAAS,sBAAsB,CAC7B,YAA2B,EAC3B,iBAAsC;IAEtC,MAAM,gBAAgB,GAAwB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAClE,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,gBAAgB,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IACH,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}