@effect-gql/federation 0.1.0 → 1.0.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/index.d.cts ADDED
@@ -0,0 +1,577 @@
1
+ import * as effect from 'effect';
2
+ import { Effect, Pipeable } from 'effect';
3
+ import * as S from 'effect/Schema';
4
+ import * as _effect_gql_core from '@effect-gql/core';
5
+ import { GraphQLSchemaBuilder, DirectiveApplication, GraphQLSchema, GraphQLScalarType } from '@effect-gql/core';
6
+ import * as graphql from 'graphql';
7
+
8
+ /**
9
+ * Configuration for a @key directive
10
+ */
11
+ interface KeyDirective {
12
+ /** FieldSet selection string (e.g., "id" or "sku package") */
13
+ readonly fields: string;
14
+ /** Whether this key can be used to resolve the entity. Default: true */
15
+ readonly resolvable?: boolean;
16
+ }
17
+ /**
18
+ * All Federation 2.x directive types
19
+ */
20
+ type FederationDirective = {
21
+ readonly _tag: "key";
22
+ readonly fields: string;
23
+ readonly resolvable?: boolean;
24
+ } | {
25
+ readonly _tag: "external";
26
+ } | {
27
+ readonly _tag: "requires";
28
+ readonly fields: string;
29
+ } | {
30
+ readonly _tag: "provides";
31
+ readonly fields: string;
32
+ } | {
33
+ readonly _tag: "shareable";
34
+ } | {
35
+ readonly _tag: "inaccessible";
36
+ } | {
37
+ readonly _tag: "override";
38
+ readonly from: string;
39
+ readonly label?: string;
40
+ } | {
41
+ readonly _tag: "interfaceObject";
42
+ } | {
43
+ readonly _tag: "tag";
44
+ readonly name: string;
45
+ };
46
+ /**
47
+ * Entity representation sent to _entities query
48
+ * Contains __typename plus the key fields
49
+ */
50
+ interface EntityRepresentation {
51
+ readonly __typename: string;
52
+ readonly [key: string]: unknown;
53
+ }
54
+ /**
55
+ * Configuration for registering an entity type
56
+ */
57
+ interface EntityRegistration<A, R = never> {
58
+ /** Type name */
59
+ readonly name: string;
60
+ /** Effect Schema for the entity type */
61
+ readonly schema: S.Schema<A, any, any>;
62
+ /** Key directive configurations (at least one required) */
63
+ readonly keys: readonly KeyDirective[];
64
+ /** Additional directives to apply to the type */
65
+ readonly directives?: readonly FederationDirective[];
66
+ /**
67
+ * Reference resolver - given key fields, return the full entity.
68
+ * The representation contains __typename plus all fields from any matching @key.
69
+ */
70
+ readonly resolveReference: (representation: Partial<A> & {
71
+ __typename: string;
72
+ }) => Effect.Effect<A | null, any, R>;
73
+ }
74
+ /**
75
+ * Configuration for the FederatedSchemaBuilder
76
+ */
77
+ interface FederatedSchemaConfig {
78
+ /** Federation specification version (default: "2.3") */
79
+ readonly version?: string;
80
+ }
81
+ /**
82
+ * Result of building a federated schema
83
+ */
84
+ interface FederatedSchemaResult {
85
+ /** The GraphQL schema with Federation queries */
86
+ readonly schema: graphql.GraphQLSchema;
87
+ /** The Federation-compliant SDL with directive annotations */
88
+ readonly sdl: string;
89
+ }
90
+ /**
91
+ * Convert a FederationDirective to a DirectiveApplication
92
+ */
93
+ declare function toDirectiveApplication(directive: FederationDirective): _effect_gql_core.DirectiveApplication;
94
+
95
+ /**
96
+ * Federation-aware schema builder that extends the core GraphQLSchemaBuilder
97
+ * with Apollo Federation 2.x support.
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const schema = FederatedSchemaBuilder.empty
102
+ * .pipe(
103
+ * entity({
104
+ * name: "User",
105
+ * schema: UserSchema,
106
+ * keys: [key({ fields: "id" })],
107
+ * resolveReference: (ref) => UserService.findById(ref.id),
108
+ * }),
109
+ * query("me", {
110
+ * type: UserSchema,
111
+ * resolve: () => UserService.getCurrentUser(),
112
+ * }),
113
+ * )
114
+ * .buildFederatedSchema()
115
+ * ```
116
+ */
117
+ declare class FederatedSchemaBuilder<R = never> implements Pipeable.Pipeable {
118
+ private readonly state;
119
+ private constructor();
120
+ /**
121
+ * Pipeable interface implementation
122
+ */
123
+ pipe<A>(this: A): A;
124
+ pipe<A, B>(this: A, ab: (a: A) => B): B;
125
+ pipe<A, B, C>(this: A, ab: (a: A) => B, bc: (b: B) => C): C;
126
+ pipe<A, B, C, D>(this: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;
127
+ pipe<A, B, C, D, E>(this: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E): E;
128
+ pipe<A, B, C, D, E, F>(this: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F): F;
129
+ /**
130
+ * Create an empty federated schema builder
131
+ */
132
+ static empty: FederatedSchemaBuilder<never>;
133
+ /**
134
+ * Create a builder with custom configuration
135
+ */
136
+ static create(config?: FederatedSchemaConfig): FederatedSchemaBuilder<never>;
137
+ /**
138
+ * Create a new builder with updated state
139
+ */
140
+ private with;
141
+ /**
142
+ * Get the underlying core builder for advanced usage
143
+ */
144
+ get coreBuilder(): GraphQLSchemaBuilder<R>;
145
+ /**
146
+ * Register an entity type with @key directive(s) and reference resolver.
147
+ *
148
+ * Entities are the core building block of Apollo Federation. They represent
149
+ * types that can be resolved across subgraph boundaries using their key fields.
150
+ *
151
+ * @example
152
+ * ```typescript
153
+ * builder.entity({
154
+ * name: "User",
155
+ * schema: UserSchema,
156
+ * keys: [key({ fields: "id" })],
157
+ * resolveReference: (ref) => UserService.findById(ref.id),
158
+ * })
159
+ * ```
160
+ */
161
+ entity<A, R2>(config: EntityRegistration<A, R2>): FederatedSchemaBuilder<R | R2>;
162
+ /**
163
+ * Add a query field
164
+ */
165
+ query<A, E, R2, Args = void>(name: string, config: {
166
+ type: S.Schema<A, any, any>;
167
+ args?: S.Schema<Args, any, any>;
168
+ description?: string;
169
+ directives?: readonly DirectiveApplication[];
170
+ resolve: (args: Args) => Effect.Effect<A, E, R2>;
171
+ }): FederatedSchemaBuilder<R | R2>;
172
+ /**
173
+ * Add a mutation field
174
+ */
175
+ mutation<A, E, R2, Args = void>(name: string, config: {
176
+ type: S.Schema<A, any, any>;
177
+ args?: S.Schema<Args, any, any>;
178
+ description?: string;
179
+ directives?: readonly DirectiveApplication[];
180
+ resolve: (args: Args) => Effect.Effect<A, E, R2>;
181
+ }): FederatedSchemaBuilder<R | R2>;
182
+ /**
183
+ * Add a subscription field
184
+ */
185
+ subscription<A, E, R2, Args = void>(name: string, config: {
186
+ type: S.Schema<A, any, any>;
187
+ args?: S.Schema<Args, any, any>;
188
+ description?: string;
189
+ directives?: readonly DirectiveApplication[];
190
+ subscribe: (args: Args) => Effect.Effect<effect.Stream.Stream<A, E, R2>, E, R2>;
191
+ resolve?: (value: A, args: Args) => Effect.Effect<A, E, R2>;
192
+ }): FederatedSchemaBuilder<R | R2>;
193
+ /**
194
+ * Register an object type (non-entity)
195
+ */
196
+ objectType<A, R2 = never>(config: {
197
+ name?: string;
198
+ schema: S.Schema<A, any, any>;
199
+ implements?: readonly string[];
200
+ directives?: readonly DirectiveApplication[];
201
+ }): FederatedSchemaBuilder<R | R2>;
202
+ /**
203
+ * Register an interface type
204
+ */
205
+ interfaceType(config: {
206
+ name?: string;
207
+ schema: S.Schema<any, any, any>;
208
+ resolveType?: (value: any) => string;
209
+ directives?: readonly DirectiveApplication[];
210
+ }): FederatedSchemaBuilder<R>;
211
+ /**
212
+ * Register an enum type
213
+ */
214
+ enumType(config: {
215
+ name: string;
216
+ values: readonly string[];
217
+ description?: string;
218
+ directives?: readonly DirectiveApplication[];
219
+ }): FederatedSchemaBuilder<R>;
220
+ /**
221
+ * Register a union type
222
+ */
223
+ unionType(config: {
224
+ name: string;
225
+ types: readonly string[];
226
+ resolveType?: (value: any) => string;
227
+ directives?: readonly DirectiveApplication[];
228
+ }): FederatedSchemaBuilder<R>;
229
+ /**
230
+ * Register an input type
231
+ */
232
+ inputType(config: {
233
+ name?: string;
234
+ schema: S.Schema<any, any, any>;
235
+ description?: string;
236
+ directives?: readonly DirectiveApplication[];
237
+ }): FederatedSchemaBuilder<R>;
238
+ /**
239
+ * Add a computed/relational field to an object type
240
+ */
241
+ field<Parent, A, E, R2, Args = void>(typeName: string, fieldName: string, config: {
242
+ type: S.Schema<A, any, any>;
243
+ args?: S.Schema<Args, any, any>;
244
+ description?: string;
245
+ directives?: readonly DirectiveApplication[];
246
+ resolve: (parent: Parent, args: Args) => Effect.Effect<A, E, R2>;
247
+ }): FederatedSchemaBuilder<R | R2>;
248
+ /**
249
+ * Build the federated GraphQL schema with _entities and _service queries.
250
+ *
251
+ * Returns both the executable schema and the Federation-compliant SDL.
252
+ */
253
+ buildFederatedSchema(): FederatedSchemaResult;
254
+ /**
255
+ * Check if the core builder has any query fields registered
256
+ */
257
+ private hasQueryFields;
258
+ /**
259
+ * Build a standard (non-federated) schema.
260
+ * Useful for testing or running without a gateway.
261
+ */
262
+ buildSchema(): GraphQLSchema;
263
+ /**
264
+ * Generate Federation-compliant SDL with directive annotations.
265
+ */
266
+ private generateFederatedSDL;
267
+ /**
268
+ * Annotate SDL types with their federation directives from extensions.
269
+ */
270
+ private annotateSDLWithDirectives;
271
+ }
272
+
273
+ /**
274
+ * Create a @key directive for entity identification
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * entity({
279
+ * name: "User",
280
+ * schema: UserSchema,
281
+ * keys: [key({ fields: "id" })],
282
+ * resolveReference: (ref) => UserService.findById(ref.id),
283
+ * })
284
+ * ```
285
+ */
286
+ declare const key: (config: KeyDirective) => FederationDirective;
287
+ /**
288
+ * Create a @shareable directive
289
+ * Marks a type or field as resolvable by multiple subgraphs
290
+ *
291
+ * @example
292
+ * ```typescript
293
+ * entity({
294
+ * name: "Product",
295
+ * schema: ProductSchema,
296
+ * keys: [key({ fields: "id" })],
297
+ * directives: [shareable()],
298
+ * })
299
+ * ```
300
+ */
301
+ declare const shareable: () => FederationDirective;
302
+ /**
303
+ * Create an @inaccessible directive
304
+ * Omits the type/field from the public API while keeping it available for federation
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * objectType({
309
+ * name: "InternalMetadata",
310
+ * schema: MetadataSchema,
311
+ * directives: [inaccessible()],
312
+ * })
313
+ * ```
314
+ */
315
+ declare const inaccessible: () => FederationDirective;
316
+ /**
317
+ * Create an @interfaceObject directive
318
+ * Indicates this object represents an interface from another subgraph
319
+ *
320
+ * @example
321
+ * ```typescript
322
+ * objectType({
323
+ * name: "Media",
324
+ * schema: MediaSchema,
325
+ * directives: [interfaceObject()],
326
+ * })
327
+ * ```
328
+ */
329
+ declare const interfaceObject: () => FederationDirective;
330
+ /**
331
+ * Create a @tag directive for metadata annotation
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * entity({
336
+ * name: "Product",
337
+ * schema: ProductSchema,
338
+ * keys: [key({ fields: "id" })],
339
+ * directives: [tag("public"), tag("catalog")],
340
+ * })
341
+ * ```
342
+ */
343
+ declare const tag: (name: string) => FederationDirective;
344
+ /**
345
+ * Create an @external directive
346
+ * Marks a field as defined in another subgraph
347
+ *
348
+ * @example
349
+ * ```typescript
350
+ * field("User", "externalId", {
351
+ * type: S.String,
352
+ * directives: [external()],
353
+ * resolve: (parent) => parent.externalId,
354
+ * })
355
+ * ```
356
+ */
357
+ declare const external: () => FederationDirective;
358
+ /**
359
+ * Create a @requires directive
360
+ * Specifies fields that must be fetched from other subgraphs before this field can be resolved
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * field("Product", "shippingEstimate", {
365
+ * type: S.Int,
366
+ * directives: [requires({ fields: "weight dimensions { height width }" })],
367
+ * resolve: (product) => calculateShipping(product.weight, product.dimensions),
368
+ * })
369
+ * ```
370
+ */
371
+ declare const requires: (config: {
372
+ fields: string;
373
+ }) => FederationDirective;
374
+ /**
375
+ * Create a @provides directive
376
+ * Router optimization hint - indicates this field provides additional fields on the returned type
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * field("Review", "author", {
381
+ * type: UserSchema,
382
+ * directives: [provides({ fields: "name email" })],
383
+ * resolve: (review) => UserService.findById(review.authorId),
384
+ * })
385
+ * ```
386
+ */
387
+ declare const provides: (config: {
388
+ fields: string;
389
+ }) => FederationDirective;
390
+ /**
391
+ * Create an @override directive
392
+ * Transfers resolution responsibility from another subgraph
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * field("Product", "price", {
397
+ * type: S.Number,
398
+ * directives: [override({ from: "legacy-pricing" })],
399
+ * resolve: (product) => PricingService.getPrice(product.id),
400
+ * })
401
+ * ```
402
+ */
403
+ declare const override: (config: {
404
+ from: string;
405
+ label?: string;
406
+ }) => FederationDirective;
407
+
408
+ /**
409
+ * Register an entity type with @key directive(s) and reference resolver.
410
+ *
411
+ * @example
412
+ * ```typescript
413
+ * FederatedSchemaBuilder.empty.pipe(
414
+ * entity({
415
+ * name: "User",
416
+ * schema: UserSchema,
417
+ * keys: [key({ fields: "id" })],
418
+ * resolveReference: (ref) => UserService.findById(ref.id),
419
+ * }),
420
+ * )
421
+ * ```
422
+ */
423
+ declare const entity: <A, R>(config: EntityRegistration<A, R>) => <R2>(builder: FederatedSchemaBuilder<R2>) => FederatedSchemaBuilder<R | R2>;
424
+ /**
425
+ * Add a query field
426
+ */
427
+ declare const query: <A, E, R, Args = void>(name: string, config: {
428
+ type: S.Schema<A, any, any>;
429
+ args?: S.Schema<Args, any, any>;
430
+ description?: string;
431
+ directives?: readonly DirectiveApplication[];
432
+ resolve: (args: Args) => Effect.Effect<A, E, R>;
433
+ }) => <R2>(builder: FederatedSchemaBuilder<R2>) => FederatedSchemaBuilder<R | R2>;
434
+ /**
435
+ * Add a mutation field
436
+ */
437
+ declare const mutation: <A, E, R, Args = void>(name: string, config: {
438
+ type: S.Schema<A, any, any>;
439
+ args?: S.Schema<Args, any, any>;
440
+ description?: string;
441
+ directives?: readonly DirectiveApplication[];
442
+ resolve: (args: Args) => Effect.Effect<A, E, R>;
443
+ }) => <R2>(builder: FederatedSchemaBuilder<R2>) => FederatedSchemaBuilder<R | R2>;
444
+ /**
445
+ * Add a subscription field
446
+ */
447
+ declare const subscription: <A, E, R, Args = void>(name: string, config: {
448
+ type: S.Schema<A, any, any>;
449
+ args?: S.Schema<Args, any, any>;
450
+ description?: string;
451
+ directives?: readonly DirectiveApplication[];
452
+ subscribe: (args: Args) => Effect.Effect<effect.Stream.Stream<A, E, R>, E, R>;
453
+ resolve?: (value: A, args: Args) => Effect.Effect<A, E, R>;
454
+ }) => <R2>(builder: FederatedSchemaBuilder<R2>) => FederatedSchemaBuilder<R | R2>;
455
+ /**
456
+ * Register an object type (non-entity)
457
+ */
458
+ declare const objectType: <A>(config: {
459
+ name?: string;
460
+ schema: S.Schema<A, any, any>;
461
+ implements?: readonly string[];
462
+ directives?: readonly DirectiveApplication[];
463
+ }) => <R>(builder: FederatedSchemaBuilder<R>) => FederatedSchemaBuilder<R>;
464
+ /**
465
+ * Register an interface type
466
+ */
467
+ declare const interfaceType: (config: {
468
+ name?: string;
469
+ schema: S.Schema<any, any, any>;
470
+ resolveType?: (value: any) => string;
471
+ directives?: readonly DirectiveApplication[];
472
+ }) => <R>(builder: FederatedSchemaBuilder<R>) => FederatedSchemaBuilder<R>;
473
+ /**
474
+ * Register an enum type
475
+ */
476
+ declare const enumType: (config: {
477
+ name: string;
478
+ values: readonly string[];
479
+ description?: string;
480
+ directives?: readonly DirectiveApplication[];
481
+ }) => <R>(builder: FederatedSchemaBuilder<R>) => FederatedSchemaBuilder<R>;
482
+ /**
483
+ * Register a union type
484
+ */
485
+ declare const unionType: (config: {
486
+ name: string;
487
+ types: readonly string[];
488
+ resolveType?: (value: any) => string;
489
+ directives?: readonly DirectiveApplication[];
490
+ }) => <R>(builder: FederatedSchemaBuilder<R>) => FederatedSchemaBuilder<R>;
491
+ /**
492
+ * Register an input type
493
+ */
494
+ declare const inputType: (config: {
495
+ name?: string;
496
+ schema: S.Schema<any, any, any>;
497
+ description?: string;
498
+ directives?: readonly DirectiveApplication[];
499
+ }) => <R>(builder: FederatedSchemaBuilder<R>) => FederatedSchemaBuilder<R>;
500
+ /**
501
+ * Add a computed/relational field to an object type
502
+ */
503
+ declare const field: <Parent, A, E, R, Args = void>(typeName: string, fieldName: string, config: {
504
+ type: S.Schema<A, any, any>;
505
+ args?: S.Schema<Args, any, any>;
506
+ description?: string;
507
+ directives?: readonly DirectiveApplication[];
508
+ resolve: (parent: Parent, args: Args) => Effect.Effect<A, E, R>;
509
+ }) => <R2>(builder: FederatedSchemaBuilder<R2>) => FederatedSchemaBuilder<R | R2>;
510
+ /**
511
+ * Create a field configuration with @external directive
512
+ */
513
+ declare const externalField: <A>(config: {
514
+ type: S.Schema<A, any, any>;
515
+ description?: string;
516
+ }) => {
517
+ type: S.Schema<A, any, any>;
518
+ description?: string;
519
+ directives: readonly DirectiveApplication[];
520
+ resolve: (parent: any) => Effect.Effect<A, never, never>;
521
+ };
522
+ /**
523
+ * Create a field configuration with @requires directive
524
+ */
525
+ declare const requiresField: <A, E, R, Parent = any>(config: {
526
+ type: S.Schema<A, any, any>;
527
+ fields: string;
528
+ description?: string;
529
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
530
+ }) => {
531
+ type: S.Schema<A, any, any>;
532
+ description?: string;
533
+ directives: readonly DirectiveApplication[];
534
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
535
+ };
536
+ /**
537
+ * Create a field configuration with @provides directive
538
+ */
539
+ declare const providesField: <A, E, R, Parent = any>(config: {
540
+ type: S.Schema<A, any, any>;
541
+ fields: string;
542
+ description?: string;
543
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
544
+ }) => {
545
+ type: S.Schema<A, any, any>;
546
+ description?: string;
547
+ directives: readonly DirectiveApplication[];
548
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
549
+ };
550
+ /**
551
+ * Create a field configuration with @override directive
552
+ */
553
+ declare const overrideField: <A, E, R, Parent = any>(config: {
554
+ type: S.Schema<A, any, any>;
555
+ from: string;
556
+ label?: string;
557
+ description?: string;
558
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
559
+ }) => {
560
+ type: S.Schema<A, any, any>;
561
+ description?: string;
562
+ directives: readonly DirectiveApplication[];
563
+ resolve: (parent: Parent) => Effect.Effect<A, E, R>;
564
+ };
565
+
566
+ /**
567
+ * The _Any scalar is used for entity representations in the _entities query.
568
+ * It accepts any JSON value representing an entity with __typename and key fields.
569
+ */
570
+ declare const AnyScalar: GraphQLScalarType<unknown, unknown>;
571
+ /**
572
+ * The _FieldSet scalar represents a selection of fields.
573
+ * It's used in directive arguments like @key(fields: "id") and @requires(fields: "weight").
574
+ */
575
+ declare const FieldSetScalar: GraphQLScalarType<unknown, unknown>;
576
+
577
+ export { AnyScalar, type EntityRegistration, type EntityRepresentation, FederatedSchemaBuilder, type FederatedSchemaConfig, type FederatedSchemaResult, type FederationDirective, FieldSetScalar, type KeyDirective, entity, enumType, external, externalField, field, inaccessible, inputType, interfaceObject, interfaceType, key, mutation, objectType, override, overrideField, provides, providesField, query, requires, requiresField, shareable, subscription, tag, toDirectiveApplication, unionType };