fareql 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/price.js ADDED
@@ -0,0 +1,485 @@
1
+ import { getNamedType, getVariableValues, isAbstractType, isCompositeType, isInterfaceType, isListType, isNonNullType, isObjectType, Kind, valueFromASTUntyped, } from 'graphql';
2
+ /**
3
+ * SDL for the directives FareQL reads. Add it to your type definitions.
4
+ *
5
+ * `@listSize` follows the IBM GraphQL Cost Directives draft, so schemas that already
6
+ * use it for complexity analysis price connections correctly without changes.
7
+ * Code-first schemas can set the same values in `extensions`
8
+ * (`{ price: '0.001' }` or `{ directives: { price: { amount: '0.001' } } }`).
9
+ */
10
+ export const fareDirectiveTypeDefs = /* GraphQL */ `
11
+ """
12
+ Price of resolving this field, or of any field returning this type, in token units (e.g. "0.001").
13
+ """
14
+ directive @price(amount: String!) on FIELD_DEFINITION | OBJECT | INTERFACE
15
+
16
+ """
17
+ How many items a list or connection field returns, for pricing.
18
+ """
19
+ directive @listSize(
20
+ assumedSize: Int
21
+ slicingArguments: [String!]
22
+ sizedFields: [String!]
23
+ requireOneSlicingArgument: Boolean = true
24
+ ) on FIELD_DEFINITION
25
+ `;
26
+ export class PricingError extends Error {
27
+ code;
28
+ name = 'PricingError';
29
+ constructor(message, code) {
30
+ super(message);
31
+ this.code = code;
32
+ }
33
+ }
34
+ export class PriceLimitError extends PricingError {
35
+ amount;
36
+ maxPrice;
37
+ name = 'PriceLimitError';
38
+ constructor(amount, maxPrice) {
39
+ super(`Operation price ${amount} exceeds the maximum of ${maxPrice}`, 'PRICE_LIMIT_EXCEEDED');
40
+ this.amount = amount;
41
+ this.maxPrice = maxPrice;
42
+ }
43
+ }
44
+ const DEFAULT_LIST_ARGS = ['first', 'last', 'limit', 'take', 'top', 'pageSize', 'perPage'];
45
+ const MAX_BREAKDOWN_ITEMS = 100;
46
+ const MAX_PATH_LENGTH = 200;
47
+ /** Parses a decimal token amount ("0.001") into base units at `decimals`. */
48
+ export function parseUnits(value, decimals) {
49
+ const m = /^(\d+)(?:\.(\d+))?$/.exec(value.trim());
50
+ if (!m)
51
+ throw new PricingError(`Invalid amount "${value}"`, 'INVALID_PRICE');
52
+ const whole = m[1] ?? '0';
53
+ const frac = m[2] ?? '';
54
+ if (frac.length > decimals)
55
+ throw new PricingError(`Amount "${value}" has more than ${decimals} decimals`, 'INVALID_PRICE');
56
+ return BigInt(whole) * 10n ** BigInt(decimals) + BigInt(frac.padEnd(decimals, '0') || '0');
57
+ }
58
+ /** Formats base units as a decimal token amount without trailing zeros. */
59
+ export function formatUnits(units, decimals) {
60
+ const base = 10n ** BigInt(decimals);
61
+ const whole = units / base;
62
+ const frac = (units % base).toString().padStart(decimals, '0').replace(/0+$/, '');
63
+ return frac ? `${whole}.${frac}` : whole.toString();
64
+ }
65
+ function findDirective(host, name) {
66
+ if (!host)
67
+ return undefined;
68
+ for (const node of [host.astNode, ...(host.extensionASTNodes ?? [])]) {
69
+ const d = node?.directives?.find((x) => x.name.value === name);
70
+ if (!d)
71
+ continue;
72
+ const args = {};
73
+ for (const a of d.arguments ?? [])
74
+ args[a.name.value] = valueFromASTUntyped(a.value);
75
+ return args;
76
+ }
77
+ // Code-first schemas (graphql-tools, Pothos, Nexus…): extensions.directives.<name> or extensions.<name>.
78
+ const ext = host.extensions;
79
+ if (!ext)
80
+ return undefined;
81
+ const fromDirectives = ext['directives']?.[name];
82
+ const value = fromDirectives ?? ext[name];
83
+ const first = Array.isArray(value) ? value[0] : value;
84
+ if (name === 'price' && typeof first === 'string')
85
+ return { amount: first };
86
+ return first && typeof first === 'object' ? first : undefined;
87
+ }
88
+ const priceCache = new WeakMap();
89
+ function directivePrice(host) {
90
+ if (!host)
91
+ return undefined;
92
+ const hit = priceCache.get(host);
93
+ if (hit !== undefined)
94
+ return hit ?? undefined;
95
+ const d = findDirective(host, 'price');
96
+ if (d && typeof d['amount'] !== 'string')
97
+ throw new PricingError('@price(amount:) must be a string such as "0.001"', 'INVALID_PRICE');
98
+ const amount = d ? d['amount'] : undefined;
99
+ priceCache.set(host, amount ?? null);
100
+ return amount;
101
+ }
102
+ /** The higher of two prices; undefined only when both are. */
103
+ function higher(a, b, decimals) {
104
+ if (a === undefined)
105
+ return b;
106
+ if (b === undefined)
107
+ return a;
108
+ return parseUnits(b, decimals) > parseUnits(a, decimals) ? b : a;
109
+ }
110
+ /** Price of a field: its own @price, else the highest @price of the same field on an interface it implements. */
111
+ function fieldDirectivePrice(parent, field, decimals) {
112
+ const own = directivePrice(field);
113
+ if (own !== undefined)
114
+ return own;
115
+ let best;
116
+ for (const i of parent.getInterfaces())
117
+ best = higher(best, directivePrice(i.getFields()[field.name]), decimals);
118
+ return best;
119
+ }
120
+ /** Type-level price of an object type: its own @price, else the highest of its interfaces. */
121
+ function typePrice(type, decimals) {
122
+ const own = directivePrice(type);
123
+ if (own !== undefined)
124
+ return own;
125
+ let best;
126
+ for (const i of type.getInterfaces())
127
+ best = higher(best, directivePrice(i), decimals);
128
+ return best;
129
+ }
130
+ const schemaPriced = new WeakMap();
131
+ /** Whether any type or field in the schema carries a price. */
132
+ function schemaHasPrices(schema) {
133
+ const hit = schemaPriced.get(schema);
134
+ if (hit !== undefined)
135
+ return hit;
136
+ let found = false;
137
+ for (const type of Object.values(schema.getTypeMap())) {
138
+ if (type.name.startsWith('__') || !(isObjectType(type) || isInterfaceType(type)))
139
+ continue;
140
+ if (directivePrice(type) !== undefined || Object.values(type.getFields()).some((f) => directivePrice(f) !== undefined)) {
141
+ found = true;
142
+ break;
143
+ }
144
+ }
145
+ schemaPriced.set(schema, found);
146
+ return found;
147
+ }
148
+ function directiveListSize(host) {
149
+ const d = findDirective(host, 'listSize');
150
+ if (!d)
151
+ return undefined;
152
+ const strings = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string') : []);
153
+ return {
154
+ assumedSize: typeof d['assumedSize'] === 'number' ? d['assumedSize'] : undefined,
155
+ slicingArguments: strings(d['slicingArguments']),
156
+ sizedFields: strings(d['sizedFields']),
157
+ requireOne: d['requireOneSlicingArgument'] !== false,
158
+ };
159
+ }
160
+ // ── graphql 16 / 17 compatibility ─────────────────────────────────────────────
161
+ /** Default value of an argument definition, in graphql 16 (`defaultValue`) and 17 (`default`). */
162
+ function argDefault(def) {
163
+ const v17 = def.default;
164
+ if (v17)
165
+ return v17.literal ? valueFromASTUntyped(v17.literal) : v17.value;
166
+ return def.defaultValue;
167
+ }
168
+ function coerceVariables(schema, op, inputs) {
169
+ const r = getVariableValues(schema, op.variableDefinitions ?? [], inputs);
170
+ if (r.errors)
171
+ throw new PricingError(r.errors.map((e) => e.message).join('; '), 'INVALID_VARIABLES');
172
+ return r.coerced ?? r.variableValues?.coerced ?? {};
173
+ }
174
+ const pathArray = (p) => {
175
+ const out = [];
176
+ for (let n = p; n; n = n.prev)
177
+ out.push(n.key);
178
+ return out.reverse();
179
+ };
180
+ const ZERO = { units: 0n, items: [], truncated: false };
181
+ function addItems(into, from) {
182
+ for (const item of from) {
183
+ if (into.length >= MAX_BREAKDOWN_ITEMS)
184
+ return true;
185
+ into.push(item);
186
+ }
187
+ return false;
188
+ }
189
+ function tick(ctx) {
190
+ if (++ctx.visits > ctx.maxVisits) {
191
+ throw new PricingError(`Operation is too complex to price (more than ${ctx.maxVisits} selections)`, 'TOO_COMPLEX');
192
+ }
193
+ }
194
+ function conditional(node, name, vars) {
195
+ const d = node.directives?.find((x) => x.name.value === name);
196
+ const arg = d?.arguments?.find((a) => a.name.value === 'if');
197
+ return arg ? valueFromASTUntyped(arg.value, vars) : undefined;
198
+ }
199
+ function isIncluded(node, vars) {
200
+ return conditional(node, 'skip', vars) !== true && conditional(node, 'include', vars) !== false;
201
+ }
202
+ /** Whether a fragment with type condition `condition` applies to runtime type `type`. */
203
+ function applies(ctx, condition, type) {
204
+ if (!condition || condition === type)
205
+ return true;
206
+ return isAbstractType(condition) && ctx.schema.isSubType(condition, type);
207
+ }
208
+ function runtimeTypes(ctx, type) {
209
+ if (isObjectType(type))
210
+ return [type];
211
+ if (isAbstractType(type))
212
+ return ctx.schema.getPossibleTypes(type);
213
+ return [];
214
+ }
215
+ /**
216
+ * graphql-js field collection: groups selections by response key for one runtime type,
217
+ * follows fragments whose condition matches, and skips fragments already spread in this
218
+ * selection set. Priced fields are therefore charged as often as the executor resolves them.
219
+ */
220
+ function collectFields(ctx, type, sets, out = new Map(), visited = new Set()) {
221
+ for (const set of sets) {
222
+ for (const sel of set.selections) {
223
+ tick(ctx);
224
+ if (!isIncluded(sel, ctx.variables))
225
+ continue;
226
+ if (sel.kind === Kind.FIELD) {
227
+ const key = sel.alias?.value ?? sel.name.value;
228
+ const list = out.get(key);
229
+ if (list)
230
+ list.push(sel);
231
+ else
232
+ out.set(key, [sel]);
233
+ }
234
+ else if (sel.kind === Kind.INLINE_FRAGMENT) {
235
+ const cond = sel.typeCondition ? ctx.schema.getType(sel.typeCondition.name.value) : undefined;
236
+ if (applies(ctx, cond, type))
237
+ collectFields(ctx, type, [sel.selectionSet], out, visited);
238
+ }
239
+ else if (sel.kind === Kind.FRAGMENT_SPREAD) {
240
+ const name = sel.name.value;
241
+ if (visited.has(name))
242
+ continue;
243
+ visited.add(name);
244
+ const frag = ctx.fragments[name];
245
+ if (!frag)
246
+ throw new PricingError(`Unknown fragment "${name}"`, 'UNKNOWN_FRAGMENT');
247
+ if (applies(ctx, ctx.schema.getType(frag.typeCondition.name.value), type)) {
248
+ collectFields(ctx, type, [frag.selectionSet], out, visited);
249
+ }
250
+ }
251
+ }
252
+ }
253
+ return out;
254
+ }
255
+ function argValues(field, node, vars) {
256
+ const out = {};
257
+ for (const def of field.args) {
258
+ const d = argDefault(def);
259
+ if (d !== undefined)
260
+ out[def.name] = d;
261
+ }
262
+ for (const a of node.arguments ?? []) {
263
+ const v = valueFromASTUntyped(a.value, vars);
264
+ if (v !== undefined)
265
+ out[a.name.value] = v;
266
+ }
267
+ return out;
268
+ }
269
+ /** Number of list levels in a type: `[Hit]` → 1, `[[Cell!]!]!` → 2. */
270
+ function listLevels(type) {
271
+ let levels = 0;
272
+ let t = type;
273
+ while (true) {
274
+ if (isNonNullType(t))
275
+ t = t.ofType;
276
+ else if (isListType(t)) {
277
+ levels++;
278
+ t = t.ofType;
279
+ }
280
+ else
281
+ return levels;
282
+ }
283
+ }
284
+ /**
285
+ * Size given by `names` in `args` (top level, or one level inside an input object).
286
+ * The largest one wins. `null`, NaN and non-finite numbers mean "unbounded": maxListSize.
287
+ * Negative sizes are refused, since resolvers that slice with them return almost everything.
288
+ */
289
+ function sliceSize(ctx, args, names) {
290
+ let size;
291
+ const consider = (name, v) => {
292
+ let n;
293
+ if (v === null)
294
+ n = ctx.maxListSize;
295
+ else if (typeof v === 'number') {
296
+ if (v < 0)
297
+ throw new PricingError(`List size "${name}" must not be negative`, 'INVALID_LIST_SIZE');
298
+ n = Number.isFinite(v) ? BigInt(Math.floor(v)) : ctx.maxListSize;
299
+ if (n > ctx.maxListSize)
300
+ n = ctx.maxListSize;
301
+ }
302
+ else
303
+ return;
304
+ if (size === undefined || n > size)
305
+ size = n;
306
+ };
307
+ for (const name of names) {
308
+ if (name in args)
309
+ consider(name, args[name]);
310
+ for (const v of Object.values(args)) {
311
+ if (v && typeof v === 'object' && !Array.isArray(v) && name in v)
312
+ consider(name, v[name]);
313
+ }
314
+ }
315
+ return size;
316
+ }
317
+ function sizing(ctx, field, args, path, inherited) {
318
+ const levels = listLevels(field.type);
319
+ const inner = levels > 1 ? ctx.defaultListSize ** BigInt(levels - 1) : 1n;
320
+ const ls = directiveListSize(field);
321
+ if (ls) {
322
+ const names = ls.slicingArguments.length > 0 ? ls.slicingArguments : ctx.listArgs;
323
+ if (ls.requireOne && ls.slicingArguments.length > 0) {
324
+ const given = ls.slicingArguments.filter((n) => args[n] !== undefined && args[n] !== null);
325
+ if (given.length !== 1) {
326
+ throw new PricingError(`Field "${pathArray(path).join('.')}" needs exactly one of ${ls.slicingArguments.map((n) => `"${n}"`).join(', ')}`, 'SLICING_ARGUMENT_REQUIRED');
327
+ }
328
+ }
329
+ const assumed = ls.assumedSize !== undefined ? BigInt(Math.min(Math.max(ls.assumedSize, 0), Number(ctx.maxListSize))) : undefined;
330
+ const size = sliceSize(ctx, args, names) ?? assumed ?? inherited ?? ctx.defaultListSize;
331
+ if (ls.sizedFields.length > 0) {
332
+ const self = levels > 0 ? (inherited ?? ctx.defaultListSize) * inner : 1n;
333
+ return { multiplier: self, children: { named: new Map(ls.sizedFields.map((f) => [f, size])) } };
334
+ }
335
+ return { multiplier: levels > 0 ? size * inner : 1n };
336
+ }
337
+ if (levels > 0)
338
+ return { multiplier: (sliceSize(ctx, args, ctx.listArgs) ?? inherited ?? ctx.defaultListSize) * inner };
339
+ if (ctx.connections && isCompositeType(getNamedType(field.type))) {
340
+ const size = sliceSize(ctx, args, ctx.listArgs);
341
+ if (size !== undefined) {
342
+ const named = ctx.connections.sizedFields;
343
+ return { multiplier: inherited ?? 1n, children: named ? { named: new Map(named.map((f) => [f, size])) } : { all: size } };
344
+ }
345
+ }
346
+ return { multiplier: inherited ?? 1n };
347
+ }
348
+ /** Cost of selection sets on one concrete runtime type, after graphql-js field collection. */
349
+ function setCostOn(ctx, type, sets, path, scale, sized, depth) {
350
+ if (depth > ctx.maxDepth)
351
+ throw new PricingError(`Operation is nested deeper than ${ctx.maxDepth} levels`, 'TOO_COMPLEX');
352
+ let units = 0n;
353
+ const items = [];
354
+ let truncated = false;
355
+ for (const [key, nodes] of collectFields(ctx, type, sets)) {
356
+ const c = fieldCost(ctx, type, key, nodes, path, scale, sized, depth);
357
+ units += c.units;
358
+ truncated = addItems(items, c.items) || c.truncated || truncated;
359
+ }
360
+ return { units, items, truncated };
361
+ }
362
+ function fieldCost(ctx, parent, key, nodes, path, scale, sized, depth) {
363
+ const node = nodes[0];
364
+ const name = node.name.value;
365
+ if (name.startsWith('__'))
366
+ return ZERO; // __typename and introspection are free
367
+ tick(ctx);
368
+ const field = parent.getFields()[name];
369
+ if (!field)
370
+ throw new PricingError(`Unknown field "${name}" on type "${parent.name}"`, 'UNKNOWN_FIELD');
371
+ const fieldPath = { prev: path, key };
372
+ const args = argValues(field, node, ctx.variables);
373
+ const override = ctx.fieldPrice?.({ parentType: parent, field, args, path: pathArray(fieldPath) });
374
+ const fieldOwn = override ?? fieldDirectivePrice(parent, field, ctx.decimals);
375
+ const isList = listLevels(field.type) > 0;
376
+ const inherited = sized?.named?.get(name) ?? (isList ? sized?.all : undefined);
377
+ const { multiplier, children } = sizing(ctx, field, args, fieldPath, inherited);
378
+ const count = scale * multiplier;
379
+ const named = getNamedType(field.type);
380
+ const unitOf = (p) => (p !== undefined ? parseUnits(p, ctx.decimals) : ctx.defaultFieldUnits);
381
+ const priced = (unit, rest) => {
382
+ if (unit === 0n)
383
+ return { units: rest.units, items: rest.items, truncated: rest.truncated };
384
+ const items = [{ path: fieldPath, unit, count }];
385
+ const truncated = addItems(items, rest.items) || rest.truncated;
386
+ return { units: unit * count + rest.units, items, truncated };
387
+ };
388
+ const sets = nodes.flatMap((n) => (n.selectionSet ? [n.selectionSet] : []));
389
+ if (sets.length === 0)
390
+ return priced(unitOf(fieldOwn), ZERO);
391
+ // Abstract return types: price every possible runtime type and charge the most expensive.
392
+ // Without a field price, the runtime type's @price applies (or its interfaces', or the return type's).
393
+ const abstractPrice = isAbstractType(named) ? directivePrice(named) : undefined;
394
+ let best;
395
+ for (const t of runtimeTypes(ctx, named)) {
396
+ const own = fieldOwn ?? typePrice(t, ctx.decimals) ?? abstractPrice;
397
+ const c = priced(unitOf(own), setCostOn(ctx, t, sets, fieldPath, count, children, depth + 1));
398
+ if (!best || c.units > best.units)
399
+ best = c;
400
+ }
401
+ return best ?? priced(unitOf(fieldOwn), ZERO);
402
+ }
403
+ /** Selects the operation the request will execute, following GraphQL's rules. */
404
+ export function selectOperation(document, operationName) {
405
+ const ops = document.definitions.filter((d) => d.kind === Kind.OPERATION_DEFINITION);
406
+ if (operationName) {
407
+ const matches = ops.filter((o) => o.name?.value === operationName);
408
+ if (matches.length !== 1) {
409
+ throw new PricingError(matches.length === 0 ? `Unknown operation "${operationName}"` : `Several operations are named "${operationName}"`, 'UNKNOWN_OPERATION');
410
+ }
411
+ return matches[0];
412
+ }
413
+ if (ops.length !== 1) {
414
+ throw new PricingError('Must provide operationName when the document has several operations', 'UNKNOWN_OPERATION');
415
+ }
416
+ return ops[0];
417
+ }
418
+ const shortPath = (p) => (p.length > MAX_PATH_LENGTH ? `…${p.slice(-(MAX_PATH_LENGTH - 1))}` : p);
419
+ /**
420
+ * Computes the price of the operation a GraphQL request will run. Pure and deterministic:
421
+ * the same schema, document and variables always give the same price.
422
+ */
423
+ export function priceOperation(input, options = {}) {
424
+ const decimals = options.decimals ?? 6;
425
+ const defaultFieldUnits = parseUnits(options.defaultFieldPrice ?? '0', decimals);
426
+ if (!options.fieldPrice && defaultFieldUnits === 0n && !schemaHasPrices(input.schema)) {
427
+ // Fail closed: a schema whose directives were lost (introspection copy, code-first without
428
+ // extensions) would otherwise make every operation free.
429
+ throw new PricingError('The schema has no @price anywhere; add fareDirectiveTypeDefs and @price, or pass fieldPrice', 'NO_PRICES');
430
+ }
431
+ const op = selectOperation(input.document, input.operationName);
432
+ const fragments = {};
433
+ for (const d of input.document.definitions)
434
+ if (d.kind === Kind.FRAGMENT_DEFINITION)
435
+ fragments[d.name.value] = d;
436
+ const root = op.operation === 'query'
437
+ ? input.schema.getQueryType()
438
+ : op.operation === 'mutation'
439
+ ? input.schema.getMutationType()
440
+ : input.schema.getSubscriptionType();
441
+ if (!root)
442
+ throw new PricingError(`Schema has no ${op.operation} type`, 'UNKNOWN_OPERATION');
443
+ const ctx = {
444
+ schema: input.schema,
445
+ fragments,
446
+ variables: coerceVariables(input.schema, op, input.variables ?? {}),
447
+ decimals,
448
+ defaultFieldUnits,
449
+ listArgs: options.listSizeArguments ?? DEFAULT_LIST_ARGS,
450
+ defaultListSize: BigInt(options.defaultListSize ?? 10),
451
+ maxListSize: BigInt(options.maxListSize ?? 100),
452
+ connections: options.connections === false ? undefined : (options.connections ?? {}),
453
+ fieldPrice: options.fieldPrice,
454
+ visits: 0,
455
+ maxVisits: options.maxFieldVisits ?? 10_000,
456
+ maxDepth: options.maxDepth ?? 100,
457
+ };
458
+ let cost;
459
+ try {
460
+ cost = setCostOn(ctx, root, [op.selectionSet], undefined, 1n, undefined, 0);
461
+ }
462
+ catch (error) {
463
+ if (error instanceof RangeError)
464
+ throw new PricingError('Operation is too complex to price', 'TOO_COMPLEX');
465
+ throw error;
466
+ }
467
+ const amount = formatUnits(cost.units, decimals);
468
+ if (options.maxPrice !== undefined && cost.units > parseUnits(options.maxPrice, decimals)) {
469
+ throw new PriceLimitError(amount, options.maxPrice);
470
+ }
471
+ const price = {
472
+ amount,
473
+ units: cost.units,
474
+ decimals,
475
+ breakdown: cost.items.map((i) => ({
476
+ path: shortPath(pathArray(i.path).join('.')),
477
+ unitAmount: formatUnits(i.unit, decimals),
478
+ count: i.count.toString(),
479
+ amount: formatUnits(i.unit * i.count, decimals),
480
+ })),
481
+ };
482
+ if (cost.truncated)
483
+ price.breakdownTruncated = true;
484
+ return price;
485
+ }
package/dist/yoga.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { type GraphQLSchema } from 'graphql';
2
+ import type { Plugin } from 'graphql-yoga';
3
+ import { type DocumentLimits, type FareOptions } from './core.js';
4
+ export type UseFareqlOptions = FareOptions & Pick<DocumentLimits, 'maxQueryLength' | 'maxTokens'> & {
5
+ /** Schema to price against. Default: the schema the operation executes on. */
6
+ schema?: GraphQLSchema;
7
+ /**
8
+ * The HTTP request a payment belongs to. Default: `context.request`, which Yoga sets for HTTP.
9
+ * Operations that reach execution without one (graphql-ws, `yoga.getEnveloped()`) are refused
10
+ * when priced, unless this returns a Request (e.g. built from `connectionParams`).
11
+ */
12
+ getRequest?: (context: Record<string, unknown>) => Request | undefined;
13
+ /** Events one payment buys on a priced subscription; the stream then ends. Default: 100. */
14
+ maxSubscriptionEvents?: number;
15
+ };
16
+ /**
17
+ * GraphQL Yoga plugin. It charges right before execution, after Yoga and every other plugin have
18
+ * parsed, validated and restored the document (APQ, persisted operations, depth limits…), so
19
+ * what is priced is exactly what runs, and nobody pays for an operation a later check rejects.
20
+ * A backstop before the response is sent charges results that skipped execution (response
21
+ * caches, other plugins' shortcuts), so a cached priced result is never served unpaid.
22
+ *
23
+ * Priced operations answer with the MPP Challenge status (402) and `WWW-Authenticate` header
24
+ * until the client retries with a valid Credential; the paid response carries `Payment-Receipt`.
25
+ */
26
+ export declare function useFareql(options: UseFareqlOptions): Plugin;