@executor-js/plugin-graphql 1.4.33 → 1.5.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.
Files changed (54) hide show
  1. package/dist/AddGraphqlSource-P3D3UXRJ.js +179 -0
  2. package/dist/AddGraphqlSource-P3D3UXRJ.js.map +1 -0
  3. package/dist/EditGraphqlSource-FVF67CTP.js +38 -0
  4. package/dist/EditGraphqlSource-FVF67CTP.js.map +1 -0
  5. package/dist/GraphqlAccountsPanel-GBNFHLFH.js +9 -0
  6. package/dist/GraphqlAccountsPanel-GBNFHLFH.js.map +1 -0
  7. package/dist/api/group.d.ts +108 -119
  8. package/dist/api/handlers.d.ts +9 -7
  9. package/dist/api/index.d.ts +117 -104
  10. package/dist/chunk-2Y3J3CVO.js +191 -0
  11. package/dist/chunk-2Y3J3CVO.js.map +1 -0
  12. package/dist/chunk-ADQTI2OK.js +1309 -0
  13. package/dist/chunk-ADQTI2OK.js.map +1 -0
  14. package/dist/{chunk-UIAHATHP.js → chunk-N5GJE7R6.js} +42 -58
  15. package/dist/chunk-N5GJE7R6.js.map +1 -0
  16. package/dist/chunk-VCYDSSIK.js +79 -0
  17. package/dist/chunk-VCYDSSIK.js.map +1 -0
  18. package/dist/client.js +7 -7
  19. package/dist/client.js.map +1 -1
  20. package/dist/core.js +20 -28
  21. package/dist/index.js +2 -2
  22. package/dist/react/AddGraphqlSource.d.ts +1 -1
  23. package/dist/react/EditGraphqlSource.d.ts +2 -2
  24. package/dist/react/GraphqlAccountsPanel.d.ts +6 -0
  25. package/dist/react/GraphqlSignInButton.d.ts +6 -2
  26. package/dist/react/GraphqlSourceFields.d.ts +2 -2
  27. package/dist/react/atoms.d.ts +95 -205
  28. package/dist/react/auth-method-config.d.ts +9 -0
  29. package/dist/react/client.d.ts +108 -97
  30. package/dist/react/defaults.d.ts +16 -2
  31. package/dist/react/index.d.ts +2 -2
  32. package/dist/react/source-plugin.d.ts +2 -2
  33. package/dist/sdk/errors.d.ts +8 -6
  34. package/dist/sdk/index.d.ts +5 -5
  35. package/dist/sdk/introspect.d.ts +1 -1
  36. package/dist/sdk/invoke.d.ts +1 -4
  37. package/dist/sdk/plugin.d.ts +94 -104
  38. package/dist/sdk/store.d.ts +11 -28
  39. package/dist/sdk/types.d.ts +110 -72
  40. package/package.json +5 -4
  41. package/dist/AddGraphqlSource-CJCUUYNM.js +0 -250
  42. package/dist/AddGraphqlSource-CJCUUYNM.js.map +0 -1
  43. package/dist/EditGraphqlSource-YLTPUYW4.js +0 -267
  44. package/dist/EditGraphqlSource-YLTPUYW4.js.map +0 -1
  45. package/dist/GraphqlSourceSummary-YWJX6ONU.js +0 -70
  46. package/dist/GraphqlSourceSummary-YWJX6ONU.js.map +0 -1
  47. package/dist/chunk-2252RTCE.js +0 -1730
  48. package/dist/chunk-2252RTCE.js.map +0 -1
  49. package/dist/chunk-D3FN3ZDA.js +0 -125
  50. package/dist/chunk-D3FN3ZDA.js.map +0 -1
  51. package/dist/chunk-M4SJY6CB.js +0 -45
  52. package/dist/chunk-M4SJY6CB.js.map +0 -1
  53. package/dist/chunk-UIAHATHP.js.map +0 -1
  54. package/dist/react/GraphqlSourceSummary.d.ts +0 -5
@@ -0,0 +1,1309 @@
1
+ import {
2
+ graphqlPresets
3
+ } from "./chunk-D6E4WAYW.js";
4
+ import {
5
+ AuthTemplate,
6
+ ExtractedField,
7
+ ExtractionResult,
8
+ GraphqlArgument,
9
+ GraphqlAuthRequiredError,
10
+ GraphqlExtractionError,
11
+ GraphqlIntegrationConfig,
12
+ GraphqlIntrospectionError,
13
+ GraphqlInvocationError,
14
+ InvocationResult,
15
+ OperationBinding,
16
+ decodeGraphqlIntegrationConfig,
17
+ decodeGraphqlIntegrationConfigOption
18
+ } from "./chunk-N5GJE7R6.js";
19
+
20
+ // src/sdk/introspect.ts
21
+ import { Effect, Option, Schema } from "effect";
22
+ import { HttpClient, HttpClientRequest } from "effect/unstable/http";
23
+ var INTROSPECTION_QUERY = `
24
+ query IntrospectionQuery {
25
+ __schema {
26
+ queryType { name }
27
+ mutationType { name }
28
+ types {
29
+ kind
30
+ name
31
+ description
32
+ fields(includeDeprecated: false) {
33
+ name
34
+ description
35
+ args {
36
+ name
37
+ description
38
+ type {
39
+ ...TypeRef
40
+ }
41
+ defaultValue
42
+ }
43
+ type {
44
+ ...TypeRef
45
+ }
46
+ }
47
+ inputFields {
48
+ name
49
+ description
50
+ type {
51
+ ...TypeRef
52
+ }
53
+ defaultValue
54
+ }
55
+ enumValues(includeDeprecated: false) {
56
+ name
57
+ description
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ fragment TypeRef on __Type {
64
+ kind
65
+ name
66
+ ofType {
67
+ kind
68
+ name
69
+ ofType {
70
+ kind
71
+ name
72
+ ofType {
73
+ kind
74
+ name
75
+ ofType {
76
+ kind
77
+ name
78
+ ofType {
79
+ kind
80
+ name
81
+ ofType {
82
+ kind
83
+ name
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
91
+ `;
92
+ var IntrospectionTypeRefLeaf = Schema.Struct({
93
+ kind: Schema.String,
94
+ name: Schema.NullOr(Schema.String),
95
+ ofType: Schema.optional(Schema.Null)
96
+ });
97
+ var IntrospectionTypeRef5 = Schema.Struct({
98
+ kind: Schema.String,
99
+ name: Schema.NullOr(Schema.String),
100
+ ofType: Schema.optional(Schema.NullOr(IntrospectionTypeRefLeaf))
101
+ });
102
+ var IntrospectionTypeRef4 = Schema.Struct({
103
+ kind: Schema.String,
104
+ name: Schema.NullOr(Schema.String),
105
+ ofType: Schema.optional(Schema.NullOr(IntrospectionTypeRef5))
106
+ });
107
+ var IntrospectionTypeRef3 = Schema.Struct({
108
+ kind: Schema.String,
109
+ name: Schema.NullOr(Schema.String),
110
+ ofType: Schema.optional(Schema.NullOr(IntrospectionTypeRef4))
111
+ });
112
+ var IntrospectionTypeRef2 = Schema.Struct({
113
+ kind: Schema.String,
114
+ name: Schema.NullOr(Schema.String),
115
+ ofType: Schema.optional(Schema.NullOr(IntrospectionTypeRef3))
116
+ });
117
+ var IntrospectionTypeRefSchema = Schema.Struct({
118
+ kind: Schema.String,
119
+ name: Schema.NullOr(Schema.String),
120
+ ofType: Schema.optional(Schema.NullOr(IntrospectionTypeRef2))
121
+ });
122
+ var IntrospectionInputValueSchema = Schema.Struct({
123
+ name: Schema.String,
124
+ description: Schema.NullOr(Schema.String),
125
+ type: IntrospectionTypeRefSchema,
126
+ defaultValue: Schema.NullOr(Schema.String)
127
+ });
128
+ var IntrospectionFieldSchema = Schema.Struct({
129
+ name: Schema.String,
130
+ description: Schema.NullOr(Schema.String),
131
+ args: Schema.Array(IntrospectionInputValueSchema),
132
+ type: IntrospectionTypeRefSchema
133
+ });
134
+ var IntrospectionTypeSchema = Schema.Struct({
135
+ kind: Schema.String,
136
+ name: Schema.String,
137
+ description: Schema.NullOr(Schema.String),
138
+ fields: Schema.NullOr(Schema.Array(IntrospectionFieldSchema)),
139
+ inputFields: Schema.NullOr(Schema.Array(IntrospectionInputValueSchema)),
140
+ enumValues: Schema.NullOr(
141
+ Schema.Array(
142
+ Schema.Struct({
143
+ name: Schema.String,
144
+ description: Schema.NullOr(Schema.String)
145
+ })
146
+ )
147
+ )
148
+ });
149
+ var IntrospectionResultSchema = Schema.Struct({
150
+ __schema: Schema.Struct({
151
+ queryType: Schema.NullOr(Schema.Struct({ name: Schema.String })),
152
+ mutationType: Schema.NullOr(Schema.Struct({ name: Schema.String })),
153
+ types: Schema.Array(IntrospectionTypeSchema)
154
+ })
155
+ });
156
+ var IntrospectionResponseSchema = Schema.Struct({
157
+ data: Schema.optional(IntrospectionResultSchema),
158
+ errors: Schema.optional(Schema.Array(Schema.Unknown))
159
+ });
160
+ var UpstreamErrorResponseSchema = Schema.Struct({
161
+ message: Schema.optional(Schema.String),
162
+ errors: Schema.optional(
163
+ Schema.Array(
164
+ Schema.Struct({
165
+ message: Schema.optional(Schema.String)
166
+ })
167
+ )
168
+ )
169
+ });
170
+ var IntrospectionJsonSchema = Schema.Union([
171
+ Schema.Struct({ data: IntrospectionResultSchema }),
172
+ IntrospectionResultSchema
173
+ ]);
174
+ var JsonTextSchema = Schema.fromJsonString(Schema.Unknown);
175
+ var decodeUpstreamErrorResponse = Schema.decodeUnknownOption(UpstreamErrorResponseSchema);
176
+ var firstUpstreamErrorMessage = (value) => {
177
+ const decoded = decodeUpstreamErrorResponse(value);
178
+ return Option.match(decoded, {
179
+ onNone: () => null,
180
+ onSome: (response) => {
181
+ if (response.message) return response.message;
182
+ for (const entry of response.errors ?? []) {
183
+ const message = entry.message;
184
+ if (message) return message;
185
+ }
186
+ return null;
187
+ }
188
+ });
189
+ };
190
+ var redactUpstreamBody = (body) => body.replaceAll(
191
+ /("(?:access_token|refresh_token|id_token|client_secret|token|authorization)"\s*:\s*")[^"]*(")/gi,
192
+ "$1[redacted]$2"
193
+ ).replaceAll(
194
+ /((?:access_token|refresh_token|id_token|client_secret|token|authorization)=)[^&\s]*/gi,
195
+ "$1[redacted]"
196
+ ).replaceAll(
197
+ /((?:authorization|access-token|refresh-token|id-token|client-secret|token)\s*:\s*)(?:bearer\s+)?[^\s,;]+/gi,
198
+ "$1[redacted]"
199
+ );
200
+ var upstreamTextMessage = (body) => {
201
+ const text = redactUpstreamBody(body.replaceAll(/\s+/g, " ").trim());
202
+ if (text.length === 0) return null;
203
+ return text.length > 500 ? `${text.slice(0, 500)}...` : text;
204
+ };
205
+ var introspect = Effect.fn("GraphQL.introspect")(function* (endpoint, headers, queryParams) {
206
+ const client = yield* HttpClient.HttpClient;
207
+ const requestEndpoint = queryParams && Object.keys(queryParams).length > 0 ? (() => {
208
+ const url = new URL(endpoint);
209
+ for (const [name, value] of Object.entries(queryParams)) {
210
+ url.searchParams.set(name, value);
211
+ }
212
+ return url.toString();
213
+ })() : endpoint;
214
+ let request = HttpClientRequest.post(requestEndpoint).pipe(
215
+ HttpClientRequest.setHeader("Content-Type", "application/json"),
216
+ HttpClientRequest.setHeader("Accept", "application/json"),
217
+ HttpClientRequest.setHeader("User-Agent", "executor-graphql"),
218
+ HttpClientRequest.bodyJsonUnsafe({
219
+ query: INTROSPECTION_QUERY
220
+ })
221
+ );
222
+ if (headers) {
223
+ for (const [k, v] of Object.entries(headers)) {
224
+ request = HttpClientRequest.setHeader(request, k, v);
225
+ }
226
+ }
227
+ const response = yield* client.execute(request).pipe(
228
+ Effect.tapCause((cause) => Effect.logError("graphql introspection request failed", cause)),
229
+ Effect.mapError(
230
+ () => new GraphqlIntrospectionError({
231
+ message: "Failed to reach GraphQL endpoint"
232
+ })
233
+ )
234
+ );
235
+ if (response.status !== 200) {
236
+ const responseText = yield* response.text.pipe(Effect.catch(() => Effect.succeed("")));
237
+ const raw2 = responseText ? yield* Schema.decodeUnknownEffect(JsonTextSchema)(responseText).pipe(
238
+ Effect.catch(() => Effect.succeed(null))
239
+ ) : null;
240
+ const upstreamMessage = upstreamTextMessage(
241
+ (raw2 === null ? null : firstUpstreamErrorMessage(raw2)) ?? responseText
242
+ );
243
+ return yield* new GraphqlIntrospectionError({
244
+ message: upstreamMessage ? `Introspection failed with status ${response.status}: ${upstreamMessage}` : `Introspection failed with status ${response.status}`
245
+ });
246
+ }
247
+ const raw = yield* response.json.pipe(
248
+ Effect.tapCause((cause) => Effect.logError("graphql introspection JSON parse failed", cause)),
249
+ Effect.mapError(
250
+ () => new GraphqlIntrospectionError({
251
+ message: `Failed to parse introspection response as JSON`
252
+ })
253
+ )
254
+ );
255
+ const json = yield* Schema.decodeUnknownEffect(IntrospectionResponseSchema)(raw).pipe(
256
+ Effect.mapError(
257
+ () => new GraphqlIntrospectionError({
258
+ message: "Introspection response has an invalid shape"
259
+ })
260
+ )
261
+ );
262
+ if (json.errors && Array.isArray(json.errors) && json.errors.length > 0) {
263
+ const upstreamMessage = firstUpstreamErrorMessage(json);
264
+ return yield* new GraphqlIntrospectionError({
265
+ message: upstreamMessage ? `Introspection returned ${json.errors.length} error(s): ${upstreamMessage}` : `Introspection returned ${json.errors.length} error(s)`
266
+ });
267
+ }
268
+ if (!json.data?.__schema) {
269
+ return yield* new GraphqlIntrospectionError({
270
+ message: "Introspection response missing __schema"
271
+ });
272
+ }
273
+ return json.data;
274
+ });
275
+ var parseIntrospectionJson = (text) => Schema.decodeUnknownEffect(Schema.fromJsonString(IntrospectionJsonSchema))(text).pipe(
276
+ Effect.map((parsed) => "data" in parsed ? parsed.data : parsed),
277
+ Effect.mapError(
278
+ () => new GraphqlIntrospectionError({
279
+ message: "Failed to parse introspection JSON"
280
+ })
281
+ )
282
+ );
283
+
284
+ // src/sdk/extract.ts
285
+ import { Effect as Effect2, Match, Option as Option2 } from "effect";
286
+ var unwrapTypeName = (ref) => {
287
+ if (ref.name) return ref.name;
288
+ if (ref.ofType) return unwrapTypeName(ref.ofType);
289
+ return "Unknown";
290
+ };
291
+ var isNonNull = (ref) => ref.kind === "NON_NULL";
292
+ var buildDefinitions = (types) => {
293
+ const defs = {};
294
+ for (const [name, type] of types) {
295
+ if (name.startsWith("__")) continue;
296
+ if (type.kind === "INPUT_OBJECT" && type.inputFields) {
297
+ const properties = {};
298
+ const required = [];
299
+ for (const field of type.inputFields) {
300
+ const schema = typeRefToJsonSchema(field.type, types);
301
+ if (field.description) {
302
+ schema.description = field.description;
303
+ }
304
+ properties[field.name] = schema;
305
+ if (isNonNull(field.type)) {
306
+ required.push(field.name);
307
+ }
308
+ }
309
+ const def = { type: "object", properties };
310
+ if (required.length > 0) def.required = required;
311
+ if (type.description) def.description = type.description;
312
+ defs[name] = def;
313
+ }
314
+ if (type.kind === "ENUM" && type.enumValues) {
315
+ defs[name] = {
316
+ type: "string",
317
+ enum: type.enumValues.map((v) => v.name),
318
+ ...type.description ? { description: type.description } : {}
319
+ };
320
+ }
321
+ }
322
+ return defs;
323
+ };
324
+ var typeRefToJsonSchema = (ref, types) => Match.value(ref.kind).pipe(
325
+ Match.when(
326
+ "NON_NULL",
327
+ () => ref.ofType ? typeRefToJsonSchema(ref.ofType, types) : {}
328
+ ),
329
+ Match.when(
330
+ "LIST",
331
+ () => ({
332
+ type: "array",
333
+ items: ref.ofType ? typeRefToJsonSchema(ref.ofType, types) : {}
334
+ })
335
+ ),
336
+ Match.when("SCALAR", () => scalarToJsonSchema(ref.name ?? "String")),
337
+ Match.when(
338
+ "ENUM",
339
+ () => ref.name ? { $ref: `#/$defs/${ref.name}` } : { type: "string" }
340
+ ),
341
+ Match.when(
342
+ "INPUT_OBJECT",
343
+ () => ref.name ? { $ref: `#/$defs/${ref.name}` } : { type: "object" }
344
+ ),
345
+ Match.whenOr(
346
+ "OBJECT",
347
+ "INTERFACE",
348
+ "UNION",
349
+ () => ({ type: "object" })
350
+ ),
351
+ Match.option,
352
+ Option2.getOrElse(() => ({}))
353
+ );
354
+ var scalarToJsonSchema = (name) => Match.value(name).pipe(
355
+ Match.whenOr("String", "ID", () => ({ type: "string" })),
356
+ Match.when("Int", () => ({ type: "integer" })),
357
+ Match.when("Float", () => ({ type: "number" })),
358
+ Match.when("Boolean", () => ({ type: "boolean" })),
359
+ Match.option,
360
+ Option2.getOrElse(
361
+ () => ({ type: "string", description: `Custom scalar: ${name}` })
362
+ )
363
+ );
364
+ var buildInputSchema = (args, types) => {
365
+ if (args.length === 0) return void 0;
366
+ const properties = {};
367
+ const required = [];
368
+ for (const arg of args) {
369
+ const schema = typeRefToJsonSchema(arg.type, types);
370
+ if (arg.description) {
371
+ schema.description = arg.description;
372
+ }
373
+ properties[arg.name] = schema;
374
+ if (isNonNull(arg.type)) {
375
+ required.push(arg.name);
376
+ }
377
+ }
378
+ const inputSchema = {
379
+ type: "object",
380
+ properties
381
+ };
382
+ if (required.length > 0) inputSchema.required = required;
383
+ return inputSchema;
384
+ };
385
+ var formatTypeRef = (ref) => Match.value(ref.kind).pipe(
386
+ Match.when("NON_NULL", () => ref.ofType ? `${formatTypeRef(ref.ofType)}!` : "Unknown!"),
387
+ Match.when("LIST", () => ref.ofType ? `[${formatTypeRef(ref.ofType)}]` : "[Unknown]"),
388
+ Match.option,
389
+ Option2.getOrElse(() => ref.name ?? "Unknown")
390
+ );
391
+ var extractFields = (_schema, kind, typeName, types) => {
392
+ if (!typeName) return [];
393
+ const type = types.get(typeName);
394
+ if (!type?.fields) return [];
395
+ return type.fields.filter((f) => !f.name.startsWith("__")).map((field) => {
396
+ const args = field.args.map(
397
+ (arg) => GraphqlArgument.make({
398
+ name: arg.name,
399
+ typeName: formatTypeRef(arg.type),
400
+ required: isNonNull(arg.type),
401
+ description: arg.description ? Option2.some(arg.description) : Option2.none()
402
+ })
403
+ );
404
+ const inputSchema = buildInputSchema(field.args, types);
405
+ return ExtractedField.make({
406
+ fieldName: field.name,
407
+ kind,
408
+ description: field.description ? Option2.some(field.description) : Option2.none(),
409
+ arguments: args,
410
+ inputSchema: inputSchema ? Option2.some(inputSchema) : Option2.none(),
411
+ returnTypeName: unwrapTypeName(field.type)
412
+ });
413
+ });
414
+ };
415
+ var extract = (introspection) => Effect2.try({
416
+ try: () => {
417
+ const schema = introspection.__schema;
418
+ const typeMap = /* @__PURE__ */ new Map();
419
+ for (const t of schema.types) {
420
+ typeMap.set(t.name, t);
421
+ }
422
+ const definitions = buildDefinitions(typeMap);
423
+ const queryFields = extractFields(schema, "query", schema.queryType?.name, typeMap);
424
+ const mutationFields = extractFields(schema, "mutation", schema.mutationType?.name, typeMap);
425
+ return {
426
+ result: ExtractionResult.make({
427
+ schemaName: Option2.none(),
428
+ fields: [...queryFields, ...mutationFields]
429
+ }),
430
+ definitions
431
+ };
432
+ },
433
+ catch: () => new GraphqlExtractionError({
434
+ message: "Failed to extract GraphQL schema"
435
+ })
436
+ });
437
+
438
+ // src/sdk/invoke.ts
439
+ import { Effect as Effect3, Option as Option3 } from "effect";
440
+ import { HttpClient as HttpClient2, HttpClientRequest as HttpClientRequest2 } from "effect/unstable/http";
441
+ var endpointWithQueryParams = (endpoint, queryParams) => {
442
+ if (Object.keys(queryParams).length === 0) return endpoint;
443
+ const url = new URL(endpoint);
444
+ for (const [name, value] of Object.entries(queryParams)) {
445
+ url.searchParams.set(name, value);
446
+ }
447
+ return url.toString();
448
+ };
449
+ var endpointForTelemetry = (endpoint) => {
450
+ if (!URL.canParse(endpoint)) return endpoint;
451
+ const url = new URL(endpoint);
452
+ url.search = "";
453
+ url.hash = "";
454
+ return url.toString();
455
+ };
456
+ var isJsonContentType = (ct) => {
457
+ if (!ct) return false;
458
+ const normalized = ct.split(";")[0]?.trim().toLowerCase() ?? "";
459
+ return normalized === "application/json" || normalized.includes("+json") || normalized.includes("json");
460
+ };
461
+ var invoke = Effect3.fn("GraphQL.invoke")(function* (operation, args, endpoint, resolvedHeaders, resolvedQueryParams = {}) {
462
+ const client = yield* HttpClient2.HttpClient;
463
+ const requestEndpoint = endpointWithQueryParams(endpoint, resolvedQueryParams);
464
+ const telemetryEndpoint = endpointForTelemetry(endpoint);
465
+ yield* Effect3.annotateCurrentSpan({
466
+ "http.method": "POST",
467
+ "http.url": telemetryEndpoint,
468
+ "plugin.graphql.endpoint": telemetryEndpoint,
469
+ "plugin.graphql.operation_kind": operation.kind,
470
+ "plugin.graphql.field_name": operation.fieldName,
471
+ "plugin.graphql.headers.resolved_count": Object.keys(resolvedHeaders).length,
472
+ "plugin.graphql.query_params.resolved_count": Object.keys(resolvedQueryParams).length
473
+ });
474
+ const variables = {};
475
+ for (const varName of operation.variableNames) {
476
+ if (args[varName] !== void 0) {
477
+ variables[varName] = args[varName];
478
+ }
479
+ }
480
+ if (typeof args.variables === "object" && args.variables !== null) {
481
+ Object.assign(variables, args.variables);
482
+ }
483
+ let request = HttpClientRequest2.post(requestEndpoint).pipe(
484
+ HttpClientRequest2.setHeader("Content-Type", "application/json"),
485
+ HttpClientRequest2.bodyJsonUnsafe({
486
+ query: operation.operationString,
487
+ variables: Object.keys(variables).length > 0 ? variables : void 0
488
+ })
489
+ );
490
+ for (const [name, value] of Object.entries(resolvedHeaders)) {
491
+ request = HttpClientRequest2.setHeader(request, name, value);
492
+ }
493
+ const response = yield* client.execute(request).pipe(
494
+ Effect3.mapError(
495
+ (err) => new GraphqlInvocationError({
496
+ message: "GraphQL request failed",
497
+ statusCode: Option3.none(),
498
+ cause: err
499
+ })
500
+ )
501
+ );
502
+ const status = response.status;
503
+ const contentType = response.headers["content-type"] ?? null;
504
+ const body = isJsonContentType(contentType) ? yield* response.json.pipe(Effect3.catch(() => response.text)) : yield* response.text;
505
+ const gqlBody = body;
506
+ const hasErrors = Array.isArray(gqlBody?.errors) && gqlBody.errors.length > 0;
507
+ yield* Effect3.annotateCurrentSpan({
508
+ "http.status_code": status,
509
+ "plugin.graphql.has_errors": hasErrors,
510
+ "plugin.graphql.error_count": hasErrors ? gqlBody.errors.length : 0
511
+ });
512
+ return InvocationResult.make({
513
+ status,
514
+ data: gqlBody?.data ?? null,
515
+ errors: hasErrors ? gqlBody.errors : null
516
+ });
517
+ });
518
+ var invokeWithLayer = (operation, args, endpoint, resolvedHeaders, resolvedQueryParams, httpClientLayer) => invoke(operation, args, endpoint, resolvedHeaders, resolvedQueryParams).pipe(
519
+ Effect3.provide(httpClientLayer),
520
+ Effect3.withSpan("plugin.graphql.invoke", {
521
+ attributes: {
522
+ "plugin.graphql.endpoint": endpointForTelemetry(endpoint),
523
+ "plugin.graphql.operation_kind": operation.kind,
524
+ "plugin.graphql.field_name": operation.fieldName
525
+ }
526
+ })
527
+ );
528
+
529
+ // src/sdk/store.ts
530
+ import { Effect as Effect4, Option as Option4, Predicate, Schema as Schema2 } from "effect";
531
+ var CATALOG_OWNER = "org";
532
+ var OPERATION_COLLECTION = "operation";
533
+ var OperationBindingFromJsonString = Schema2.fromJsonString(OperationBinding);
534
+ var decodeOperationBindingFromJsonString = Schema2.decodeUnknownSync(
535
+ OperationBindingFromJsonString
536
+ );
537
+ var decodeOperationBinding = Schema2.decodeUnknownSync(OperationBinding);
538
+ var encodeBinding = Schema2.encodeSync(OperationBinding);
539
+ var decodeBinding = (value) => {
540
+ if (typeof value === "string") return decodeOperationBindingFromJsonString(value);
541
+ return decodeOperationBinding(value);
542
+ };
543
+ var toJsonRecord = (value) => value;
544
+ var OperationStorage = Schema2.Struct({
545
+ toolName: Schema2.String,
546
+ integration: Schema2.String,
547
+ binding: Schema2.Unknown
548
+ });
549
+ var decodeOperationStorage = Schema2.decodeUnknownOption(OperationStorage);
550
+ var operationKey = (integration, toolName) => `${integration}.${toolName}`;
551
+ var operationData = (operation) => ({
552
+ toolName: operation.toolName,
553
+ integration: operation.integration,
554
+ binding: toJsonRecord(encodeBinding(operation.binding))
555
+ });
556
+ var rowToOperation = (row) => {
557
+ const decoded = decodeOperationStorage(row.data);
558
+ if (Option4.isNone(decoded)) return null;
559
+ const operation = decoded.value;
560
+ return {
561
+ toolName: operation.toolName,
562
+ integration: operation.integration,
563
+ binding: decodeBinding(operation.binding)
564
+ };
565
+ };
566
+ var makeDefaultGraphqlStore = ({ pluginStorage }) => {
567
+ const listOperationRows = (integration) => pluginStorage.list({
568
+ collection: OPERATION_COLLECTION,
569
+ keyPrefix: `${integration}.`
570
+ }).pipe(
571
+ Effect4.map(
572
+ (rows) => rows.filter((row) => rowToOperation(row)?.integration === integration)
573
+ )
574
+ );
575
+ const removeOperations = (integration) => Effect4.gen(function* () {
576
+ const rows = yield* listOperationRows(integration);
577
+ for (const row of rows) {
578
+ yield* pluginStorage.remove({
579
+ owner: CATALOG_OWNER,
580
+ collection: OPERATION_COLLECTION,
581
+ key: row.key
582
+ });
583
+ }
584
+ });
585
+ return {
586
+ replaceOperations: (integration, operations) => Effect4.gen(function* () {
587
+ yield* removeOperations(integration);
588
+ for (const operation of operations) {
589
+ yield* pluginStorage.put({
590
+ owner: CATALOG_OWNER,
591
+ collection: OPERATION_COLLECTION,
592
+ key: operationKey(integration, operation.toolName),
593
+ data: operationData(operation)
594
+ });
595
+ }
596
+ }),
597
+ getOperation: (integration, toolName) => pluginStorage.get({ collection: OPERATION_COLLECTION, key: operationKey(integration, toolName) }).pipe(Effect4.map((row) => row ? rowToOperation(row) : null)),
598
+ listOperations: (integration) => listOperationRows(integration).pipe(
599
+ Effect4.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull))
600
+ ),
601
+ removeOperations
602
+ };
603
+ };
604
+
605
+ // src/sdk/plugin.ts
606
+ import { Effect as Effect5, Match as Match2, Option as Option5, Schema as Schema3 } from "effect";
607
+ import { FetchHttpClient } from "effect/unstable/http";
608
+ import {
609
+ authToolFailure,
610
+ definePlugin,
611
+ IntegrationAlreadyExistsError,
612
+ IntegrationDetectionResult,
613
+ IntegrationSlug,
614
+ ToolName,
615
+ ToolResult
616
+ } from "@executor-js/sdk/core";
617
+ var GraphqlErrorBody = Schema3.Struct({ message: Schema3.String });
618
+ var GraphqlErrorsBody = Schema3.Array(Schema3.Unknown);
619
+ var decodeGraphqlErrorBody = Schema3.decodeUnknownOption(GraphqlErrorBody);
620
+ var decodeGraphqlErrorsBody = Schema3.decodeUnknownOption(GraphqlErrorsBody);
621
+ var decodeGraphqlErrors = (errors) => Option5.getOrUndefined(decodeGraphqlErrorsBody(errors));
622
+ var extractGraphqlErrorMessage = (errors) => errors.map((error) => Option5.getOrUndefined(decodeGraphqlErrorBody(error))?.message).find((message) => message !== void 0 && message.length > 0);
623
+ var GRAPHQL_PLUGIN_ID = "graphql";
624
+ var AuthTemplateSchema = AuthTemplate;
625
+ var GraphqlAddIntegrationInputSchema = Schema3.Struct({
626
+ endpoint: Schema3.String,
627
+ slug: Schema3.optional(Schema3.String),
628
+ name: Schema3.optional(Schema3.String),
629
+ introspectionJson: Schema3.optional(Schema3.String),
630
+ headers: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
631
+ queryParams: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
632
+ authenticationTemplate: Schema3.optional(Schema3.Array(AuthTemplateSchema))
633
+ });
634
+ var GraphqlConfigureInputSchema = Schema3.Struct({
635
+ name: Schema3.optional(Schema3.String),
636
+ endpoint: Schema3.optional(Schema3.String),
637
+ headers: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
638
+ queryParams: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
639
+ authenticationTemplate: Schema3.optional(Schema3.Array(AuthTemplateSchema))
640
+ });
641
+ var GraphqlConfigureAuthInputSchema = Schema3.Struct({
642
+ authenticationTemplate: Schema3.Array(AuthTemplateSchema),
643
+ mode: Schema3.optional(Schema3.Literals(["merge", "replace"]))
644
+ });
645
+ var StaticAddIntegrationOutputSchema = Schema3.Struct({
646
+ slug: Schema3.String,
647
+ name: Schema3.String
648
+ });
649
+ var StaticGetIntegrationInputSchema = Schema3.Struct({
650
+ slug: Schema3.String
651
+ });
652
+ var StaticGetIntegrationOutputSchema = Schema3.Struct({
653
+ integration: Schema3.NullOr(Schema3.Unknown)
654
+ });
655
+ var StaticAddIntegrationInputStandardSchema = Schema3.toStandardSchemaV1(
656
+ Schema3.toStandardJSONSchemaV1(GraphqlAddIntegrationInputSchema)
657
+ );
658
+ var StaticAddIntegrationOutputStandardSchema = Schema3.toStandardSchemaV1(
659
+ Schema3.toStandardJSONSchemaV1(StaticAddIntegrationOutputSchema)
660
+ );
661
+ var StaticGetIntegrationInputStandardSchema = Schema3.toStandardSchemaV1(
662
+ Schema3.toStandardJSONSchemaV1(StaticGetIntegrationInputSchema)
663
+ );
664
+ var StaticGetIntegrationOutputStandardSchema = Schema3.toStandardSchemaV1(
665
+ Schema3.toStandardJSONSchemaV1(StaticGetIntegrationOutputSchema)
666
+ );
667
+ var graphqlToolFailure = (code, message, details) => ToolResult.fail({
668
+ code,
669
+ message,
670
+ ...details === void 0 ? {} : { details }
671
+ });
672
+ var graphqlAuthToolFailure = (failure) => authToolFailure({
673
+ code: failure.code,
674
+ message: failure.message,
675
+ source: { id: failure.integration, scope: failure.owner },
676
+ credential: {
677
+ kind: failure.credentialKind,
678
+ ...failure.credentialLabel ? { label: failure.credentialLabel } : {},
679
+ connectionId: failure.connection
680
+ },
681
+ ...failure.status !== void 0 ? { status: failure.status } : {},
682
+ ...failure.details !== void 0 ? {
683
+ upstream: {
684
+ ...failure.status !== void 0 ? { status: failure.status } : {},
685
+ details: failure.details
686
+ }
687
+ } : {}
688
+ });
689
+ var urlMatchesToken = (url, token) => {
690
+ const re = new RegExp(`(?:^|[^a-z0-9])${token}(?:$|[^a-z0-9])`, "i");
691
+ return re.test(url.hostname) || re.test(url.pathname);
692
+ };
693
+ var slugFromEndpoint = (endpoint) => {
694
+ try {
695
+ const url = new URL(endpoint);
696
+ return url.hostname.replace(/[^a-z0-9]+/gi, "_").toLowerCase();
697
+ } catch {
698
+ return "graphql";
699
+ }
700
+ };
701
+ var formatTypeRef2 = (ref) => Match2.value(ref.kind).pipe(
702
+ Match2.when("NON_NULL", () => ref.ofType ? `${formatTypeRef2(ref.ofType)}!` : "Unknown!"),
703
+ Match2.when("LIST", () => ref.ofType ? `[${formatTypeRef2(ref.ofType)}]` : "[Unknown]"),
704
+ Match2.option,
705
+ Option5.getOrElse(() => ref.name ?? "Unknown")
706
+ );
707
+ var unwrapTypeName2 = (ref) => {
708
+ if (ref.name) return ref.name;
709
+ if (ref.ofType) return unwrapTypeName2(ref.ofType);
710
+ return "Unknown";
711
+ };
712
+ var buildSelectionSet = (ref, types, depth, seen) => {
713
+ if (depth > 2) return "";
714
+ const leafName = unwrapTypeName2(ref);
715
+ if (seen.has(leafName)) return "";
716
+ const objectType = types.get(leafName);
717
+ if (!objectType?.fields) return "";
718
+ const kind = objectType.kind;
719
+ if (kind === "SCALAR" || kind === "ENUM") return "";
720
+ seen.add(leafName);
721
+ const subFields = objectType.fields.filter((f) => !f.name.startsWith("__")).slice(0, 12).map((f) => {
722
+ const sub = buildSelectionSet(f.type, types, depth + 1, seen);
723
+ return sub ? `${f.name} ${sub}` : f.name;
724
+ });
725
+ seen.delete(leafName);
726
+ return subFields.length > 0 ? `{ ${subFields.join(" ")} }` : "";
727
+ };
728
+ var operationNameForField = (fieldName) => fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
729
+ var buildOperationStringForField = (kind, field, types) => {
730
+ const opType = kind === "query" ? "query" : "mutation";
731
+ const opName = operationNameForField(field.name);
732
+ const varDefs = field.args.map((arg) => {
733
+ const typeName = formatTypeRef2(arg.type);
734
+ return `$${arg.name}: ${typeName}`;
735
+ });
736
+ const argPasses = field.args.map((arg) => `${arg.name}: $${arg.name}`);
737
+ const selectionSet = buildSelectionSet(field.type, types, 0, /* @__PURE__ */ new Set());
738
+ const varDefsStr = varDefs.length > 0 ? `(${varDefs.join(", ")})` : "";
739
+ const argPassStr = argPasses.length > 0 ? `(${argPasses.join(", ")})` : "";
740
+ return `${opType} ${opName}${varDefsStr} { ${field.name}${argPassStr}${selectionSet ? ` ${selectionSet}` : ""} }`;
741
+ };
742
+ var prepareOperations = (fields, introspection) => {
743
+ const typeMap = /* @__PURE__ */ new Map();
744
+ for (const t of introspection.__schema.types) {
745
+ typeMap.set(t.name, t);
746
+ }
747
+ const fieldMap = /* @__PURE__ */ new Map();
748
+ const schema = introspection.__schema;
749
+ for (const rootKind of ["query", "mutation"]) {
750
+ const typeName = rootKind === "query" ? schema.queryType?.name : schema.mutationType?.name;
751
+ if (!typeName) continue;
752
+ const rootType = typeMap.get(typeName);
753
+ if (!rootType?.fields) continue;
754
+ for (const f of rootType.fields) {
755
+ if (!f.name.startsWith("__")) {
756
+ fieldMap.set(`${rootKind}.${f.name}`, { kind: rootKind, field: f });
757
+ }
758
+ }
759
+ }
760
+ return fields.map((extracted) => {
761
+ const prefix = extracted.kind === "mutation" ? "mutation" : "query";
762
+ const toolName = `${prefix}.${extracted.fieldName}`;
763
+ const description = Option5.getOrElse(
764
+ extracted.description,
765
+ () => `GraphQL ${extracted.kind}: ${extracted.fieldName} -> ${extracted.returnTypeName}`
766
+ );
767
+ const key = `${extracted.kind}.${extracted.fieldName}`;
768
+ const entry = fieldMap.get(key);
769
+ const operationString = entry ? buildOperationStringForField(entry.kind, entry.field, typeMap) : `${extracted.kind} ${operationNameForField(extracted.fieldName)} { ${extracted.fieldName} }`;
770
+ const binding = OperationBinding.make({
771
+ kind: extracted.kind,
772
+ fieldName: extracted.fieldName,
773
+ operationString,
774
+ variableNames: extracted.arguments.map((a) => a.name)
775
+ });
776
+ return {
777
+ toolName,
778
+ description,
779
+ inputSchema: Option5.getOrUndefined(extracted.inputSchema),
780
+ binding
781
+ };
782
+ });
783
+ };
784
+ var annotationsFor = (binding) => {
785
+ if (binding.kind === "mutation") {
786
+ return {
787
+ requiresApproval: true,
788
+ approvalDescription: `mutation ${binding.fieldName}`
789
+ };
790
+ }
791
+ return {};
792
+ };
793
+ var renderAuthTemplate = (template, value) => {
794
+ if (template.kind === "oauth2") {
795
+ const header = template.header ?? "Authorization";
796
+ const prefix = template.prefix ?? "Bearer ";
797
+ return { headers: { [header]: `${prefix}${value}` }, queryParams: {} };
798
+ }
799
+ const rendered = template.prefix ? `${template.prefix}${value}` : value;
800
+ if (template.in === "query") {
801
+ return { headers: {}, queryParams: { [template.name]: rendered } };
802
+ }
803
+ return { headers: { [template.name]: rendered }, queryParams: {} };
804
+ };
805
+ var buildToolDefs = (prepared) => prepared.map((p) => ({
806
+ name: ToolName.make(p.toolName),
807
+ description: p.description,
808
+ inputSchema: p.inputSchema,
809
+ annotations: annotationsFor(p.binding)
810
+ }));
811
+ var toStoredOperations = (slug, prepared) => prepared.map((p) => ({
812
+ toolName: p.toolName,
813
+ integration: String(slug),
814
+ binding: p.binding
815
+ }));
816
+ var introspectHeadersForConnection = (config, credentialValue) => {
817
+ const headers = { ...config.headers ?? {} };
818
+ const queryParams = { ...config.queryParams ?? {} };
819
+ if (credentialValue !== null) {
820
+ const template = config.authenticationTemplate[0];
821
+ if (template) {
822
+ const rendered = renderAuthTemplate(template, credentialValue);
823
+ Object.assign(headers, rendered.headers);
824
+ Object.assign(queryParams, rendered.queryParams);
825
+ }
826
+ }
827
+ return { headers, queryParams };
828
+ };
829
+ var introspectForConnection = (config, credentialValue, httpClientLayer) => {
830
+ if (config.introspectionJson) {
831
+ return parseIntrospectionJson(config.introspectionJson);
832
+ }
833
+ const auth = introspectHeadersForConnection(config, credentialValue);
834
+ return introspect(
835
+ config.endpoint,
836
+ Object.keys(auth.headers).length > 0 ? auth.headers : void 0,
837
+ Object.keys(auth.queryParams).length > 0 ? auth.queryParams : void 0
838
+ ).pipe(Effect5.provide(httpClientLayer));
839
+ };
840
+ var materializeOperations = (ctx, integration, config, credential, httpClientLayer) => Effect5.gen(function* () {
841
+ const template = config.authenticationTemplate.find(
842
+ (t) => t.slug === String(credential.template)
843
+ );
844
+ const headers = { ...config.headers ?? {} };
845
+ const queryParams = {
846
+ ...config.queryParams ?? {}
847
+ };
848
+ if (template && credential.value !== null) {
849
+ const rendered = renderAuthTemplate(template, credential.value);
850
+ Object.assign(headers, rendered.headers);
851
+ Object.assign(queryParams, rendered.queryParams);
852
+ }
853
+ const introspection = config.introspectionJson ? yield* parseIntrospectionJson(config.introspectionJson) : yield* introspect(
854
+ config.endpoint,
855
+ Object.keys(headers).length > 0 ? headers : void 0,
856
+ Object.keys(queryParams).length > 0 ? queryParams : void 0
857
+ ).pipe(Effect5.provide(httpClientLayer));
858
+ const { result } = yield* extract(introspection).pipe(
859
+ Effect5.catch(
860
+ () => Effect5.succeed({
861
+ result: { fields: [] }
862
+ })
863
+ )
864
+ );
865
+ const prepared = prepareOperations(result.fields, introspection);
866
+ const stored = toStoredOperations(IntegrationSlug.make(integration), prepared);
867
+ yield* ctx.storage.replaceOperations(integration, stored);
868
+ return stored;
869
+ });
870
+ var graphqlApiKeyLabel = (placement) => `API key (${placement.name || (placement.carrier === "header" ? "header" : "query")})`;
871
+ var describeGraphqlAuthMethods = (record) => {
872
+ const config = Option5.getOrUndefined(decodeGraphqlIntegrationConfigOption(record.config));
873
+ if (!config) return [];
874
+ return config.authenticationTemplate.map((template) => {
875
+ const slug = template.slug;
876
+ if (template.kind === "oauth2") {
877
+ return {
878
+ id: slug,
879
+ label: "OAuth",
880
+ kind: "oauth",
881
+ template: slug,
882
+ oauth: {}
883
+ };
884
+ }
885
+ const placement = {
886
+ carrier: template.in,
887
+ name: template.name,
888
+ prefix: template.prefix ?? ""
889
+ };
890
+ return {
891
+ id: slug,
892
+ label: graphqlApiKeyLabel(placement),
893
+ kind: "apikey",
894
+ template: slug,
895
+ placements: [placement]
896
+ };
897
+ });
898
+ };
899
+ var describeGraphqlIntegrationDisplay = (record) => {
900
+ const config = Option5.getOrUndefined(decodeGraphqlIntegrationConfigOption(record.config));
901
+ return { url: config?.endpoint };
902
+ };
903
+ var shortId = () => Math.random().toString(36).slice(2, 8);
904
+ var freshCustomSlug = (taken) => {
905
+ let candidate = `custom_${shortId()}`;
906
+ while (taken.has(candidate)) candidate = `custom_${shortId()}`;
907
+ return candidate;
908
+ };
909
+ var mergeGraphqlAuthTemplate = (existing, incoming) => {
910
+ const result = existing.map((entry) => entry);
911
+ const taken = new Set(result.map((entry) => entry.slug));
912
+ for (const entry of incoming) {
913
+ const requested = entry.slug.trim();
914
+ const existingIndex = result.findIndex((current) => current.slug === requested);
915
+ if (requested.length > 0 && existingIndex >= 0) {
916
+ result[existingIndex] = entry;
917
+ continue;
918
+ }
919
+ const slug = requested.length > 0 && !taken.has(requested) ? requested : freshCustomSlug(taken);
920
+ taken.add(slug);
921
+ result.push({ ...entry, slug });
922
+ }
923
+ return result;
924
+ };
925
+ var makeGraphqlExtension = (ctx) => {
926
+ const buildConfig = (input) => GraphqlIntegrationConfig.make({
927
+ endpoint: input.endpoint,
928
+ name: input.name?.trim() || slugFromEndpoint(input.endpoint),
929
+ ...input.introspectionJson !== void 0 ? { introspectionJson: input.introspectionJson } : {},
930
+ ...input.headers !== void 0 ? { headers: input.headers } : {},
931
+ ...input.queryParams !== void 0 ? { queryParams: input.queryParams } : {},
932
+ authenticationTemplate: input.authenticationTemplate ?? []
933
+ });
934
+ const addIntegrationInternal = (input) => Effect5.gen(function* () {
935
+ const slug = IntegrationSlug.make(input.slug ?? slugFromEndpoint(input.endpoint));
936
+ const existing = yield* ctx.core.integrations.get(slug);
937
+ if (existing) {
938
+ return yield* new IntegrationAlreadyExistsError({ slug });
939
+ }
940
+ return yield* addIntegrationTransaction(input, slug);
941
+ });
942
+ const addIntegrationTransaction = (input, slug) => ctx.transaction(
943
+ Effect5.gen(function* () {
944
+ const baseConfig = buildConfig(input);
945
+ if (baseConfig.introspectionJson === void 0) {
946
+ yield* ctx.core.integrations.register({
947
+ slug,
948
+ description: baseConfig.name,
949
+ config: baseConfig,
950
+ canRemove: true,
951
+ canRefresh: true
952
+ });
953
+ return { slug: String(slug), name: baseConfig.name, toolCount: 0 };
954
+ }
955
+ const introspection = yield* parseIntrospectionJson(baseConfig.introspectionJson);
956
+ const { result } = yield* extract(introspection);
957
+ const prepared = prepareOperations(result.fields, introspection);
958
+ const config = GraphqlIntegrationConfig.make({
959
+ ...baseConfig,
960
+ introspectionJson: JSON.stringify({ data: introspection })
961
+ });
962
+ yield* ctx.storage.replaceOperations(String(slug), toStoredOperations(slug, prepared));
963
+ yield* ctx.core.integrations.register({
964
+ slug,
965
+ description: config.name,
966
+ config,
967
+ canRemove: true,
968
+ canRefresh: true
969
+ });
970
+ return {
971
+ slug: String(slug),
972
+ name: config.name,
973
+ toolCount: prepared.length
974
+ };
975
+ })
976
+ );
977
+ const configureIntegration = (slug, input) => Effect5.gen(function* () {
978
+ const record = yield* ctx.core.integrations.get(IntegrationSlug.make(slug));
979
+ if (!record) return;
980
+ const current = Option5.getOrElse(
981
+ // best-effort: re-decode the stored config, falling back to an
982
+ // endpoint-only config if it was never set.
983
+ yield* decodeGraphqlIntegrationConfig(record.config).pipe(Effect5.option),
984
+ () => GraphqlIntegrationConfig.make({
985
+ endpoint: "",
986
+ name: record.description,
987
+ authenticationTemplate: []
988
+ })
989
+ );
990
+ const next = GraphqlIntegrationConfig.make({
991
+ endpoint: input.endpoint ?? current.endpoint,
992
+ name: input.name?.trim() || current.name,
993
+ ...current.introspectionJson !== void 0 ? { introspectionJson: current.introspectionJson } : {},
994
+ ...(input.headers ?? current.headers) !== void 0 ? { headers: input.headers ?? current.headers } : {},
995
+ ...(input.queryParams ?? current.queryParams) !== void 0 ? { queryParams: input.queryParams ?? current.queryParams } : {},
996
+ authenticationTemplate: input.authenticationTemplate ?? current.authenticationTemplate
997
+ });
998
+ yield* ctx.core.integrations.update(IntegrationSlug.make(slug), {
999
+ description: next.name,
1000
+ config: next
1001
+ });
1002
+ });
1003
+ const getConfig = (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(
1004
+ Effect5.map(
1005
+ (record) => record ? Option5.getOrNull(decodeGraphqlIntegrationConfigOption(record.config)) : null
1006
+ )
1007
+ );
1008
+ const configureAuthMethods = (slug, input) => ctx.transaction(
1009
+ Effect5.gen(function* () {
1010
+ const record = yield* ctx.core.integrations.get(IntegrationSlug.make(slug));
1011
+ if (!record) return [];
1012
+ const current = Option5.getOrNull(decodeGraphqlIntegrationConfigOption(record.config));
1013
+ if (!current) return [];
1014
+ const merged = input.mode === "replace" ? input.authenticationTemplate : mergeGraphqlAuthTemplate(
1015
+ current.authenticationTemplate,
1016
+ input.authenticationTemplate
1017
+ );
1018
+ const next = GraphqlIntegrationConfig.make({
1019
+ ...current,
1020
+ authenticationTemplate: merged
1021
+ });
1022
+ yield* ctx.core.integrations.update(IntegrationSlug.make(slug), {
1023
+ config: next
1024
+ });
1025
+ return merged;
1026
+ })
1027
+ );
1028
+ return {
1029
+ /** Register a GraphQL integration (introspects + persists operations). */
1030
+ addIntegration: (input) => addIntegrationInternal(input),
1031
+ /** Read the integration's stored config. */
1032
+ getIntegration: (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(Effect5.map((record) => record ? record.config : null)),
1033
+ /** Read the integration's decoded config (auth templates surfaced). */
1034
+ getConfig,
1035
+ /** Merge-append custom auth methods (custom-method-create flow). */
1036
+ configureAuth: configureAuthMethods,
1037
+ removeIntegration: (slug) => ctx.transaction(
1038
+ Effect5.gen(function* () {
1039
+ yield* ctx.storage.removeOperations(slug);
1040
+ yield* ctx.core.integrations.remove(IntegrationSlug.make(slug)).pipe(Effect5.catchTag("IntegrationRemovalNotAllowedError", () => Effect5.void));
1041
+ })
1042
+ ),
1043
+ configure: configureIntegration
1044
+ };
1045
+ };
1046
+ var graphqlPlugin = definePlugin((options) => {
1047
+ return {
1048
+ id: GRAPHQL_PLUGIN_ID,
1049
+ packageName: "@executor-js/plugin-graphql",
1050
+ integrationPresets: graphqlPresets.map((preset) => ({
1051
+ id: preset.id,
1052
+ name: preset.name,
1053
+ summary: preset.summary,
1054
+ url: preset.url,
1055
+ endpoint: preset.endpoint,
1056
+ ...preset.icon ? { icon: preset.icon } : {},
1057
+ ...preset.featured ? { featured: preset.featured } : {}
1058
+ })),
1059
+ storage: (deps) => makeDefaultGraphqlStore(deps),
1060
+ extension: (ctx) => makeGraphqlExtension(ctx),
1061
+ integrationConfigure: {
1062
+ type: "graphql",
1063
+ schema: GraphqlConfigureInputSchema,
1064
+ configure: ({ ctx, integration, config }) => makeGraphqlExtension(ctx).configure(String(integration), config)
1065
+ },
1066
+ describeAuthMethods: describeGraphqlAuthMethods,
1067
+ describeIntegrationDisplay: describeGraphqlIntegrationDisplay,
1068
+ staticSources: (self) => [
1069
+ {
1070
+ id: "graphql",
1071
+ kind: "executor",
1072
+ name: "GraphQL",
1073
+ tools: [
1074
+ {
1075
+ name: "getIntegration",
1076
+ description: "Inspect an existing GraphQL integration, including endpoint, static headers/query params, and auth templates. Use this before repairing an integration with `graphql.configure` or creating a connection.",
1077
+ inputSchema: StaticGetIntegrationInputStandardSchema,
1078
+ outputSchema: StaticGetIntegrationOutputStandardSchema,
1079
+ handler: ({ args }) => {
1080
+ const input = args;
1081
+ return Effect5.map(
1082
+ self.getIntegration(input.slug),
1083
+ (integration) => ToolResult.ok({ integration })
1084
+ );
1085
+ }
1086
+ },
1087
+ {
1088
+ name: "addIntegration",
1089
+ description: "Add a GraphQL endpoint to the catalog and register its operations. Introspects the endpoint (or uses provided introspection JSON). After adding, create an owner-scoped connection against the integration to materialize its per-connection tools. For API keys / bearer tokens, declare an `authenticationTemplate` and create a connection whose value is the token.",
1090
+ annotations: {
1091
+ requiresApproval: true,
1092
+ approvalDescription: "Add a GraphQL integration"
1093
+ },
1094
+ inputSchema: StaticAddIntegrationInputStandardSchema,
1095
+ outputSchema: StaticAddIntegrationOutputStandardSchema,
1096
+ handler: ({ args }) => {
1097
+ const input = args;
1098
+ return self.addIntegration(input).pipe(
1099
+ Effect5.map((result) => ToolResult.ok({ slug: result.slug, name: result.name })),
1100
+ Effect5.catchTags({
1101
+ GraphqlIntrospectionError: ({ message }) => Effect5.succeed(graphqlToolFailure("graphql_introspection_failed", message)),
1102
+ GraphqlExtractionError: ({ message }) => Effect5.succeed(graphqlToolFailure("graphql_extraction_failed", message)),
1103
+ IntegrationAlreadyExistsError: ({ slug }) => Effect5.succeed(
1104
+ graphqlToolFailure(
1105
+ "integration_already_exists",
1106
+ `Integration ${slug} already exists; update it instead of re-adding.`
1107
+ )
1108
+ )
1109
+ })
1110
+ );
1111
+ }
1112
+ }
1113
+ ]
1114
+ }
1115
+ ],
1116
+ // -----------------------------------------------------------------------
1117
+ // Per-connection tool production. THIS is where a GraphQL integration is
1118
+ // introspected — when a connection is created (or refreshed), with that
1119
+ // connection's credential — yielding one ToolDef per operation. Registering
1120
+ // the integration in the catalog makes no network call; discovery is
1121
+ // deferred to here, exactly how MCP defers tool discovery to connect time.
1122
+ // The introspected schema is identical across connections, so `invokeTool`
1123
+ // re-derives the same operation bindings; only the credential differs.
1124
+ // -----------------------------------------------------------------------
1125
+ resolveTools: ({
1126
+ config,
1127
+ getValue
1128
+ }) => Effect5.gen(function* () {
1129
+ const decoded = yield* decodeGraphqlIntegrationConfig(config).pipe(Effect5.option);
1130
+ if (Option5.isNone(decoded)) return { tools: [] };
1131
+ const graphqlConfig = decoded.value;
1132
+ const credentialValue = graphqlConfig.introspectionJson === void 0 ? yield* getValue().pipe(Effect5.catch(() => Effect5.succeed(null))) : null;
1133
+ const introspection = yield* introspectForConnection(
1134
+ graphqlConfig,
1135
+ credentialValue,
1136
+ options?.httpClientLayer ?? httpClientLayerFallback
1137
+ ).pipe(Effect5.option);
1138
+ if (Option5.isNone(introspection)) return { tools: [] };
1139
+ const extracted = yield* extract(introspection.value).pipe(Effect5.option);
1140
+ if (Option5.isNone(extracted)) return { tools: [] };
1141
+ const prepared = prepareOperations(extracted.value.result.fields, introspection.value);
1142
+ return {
1143
+ tools: buildToolDefs(prepared),
1144
+ definitions: extracted.value.definitions
1145
+ };
1146
+ }).pipe(Effect5.catch(() => Effect5.succeed({ tools: [] }))),
1147
+ // -----------------------------------------------------------------------
1148
+ // Invoke one of a connection's tools. Look up the operation by integration
1149
+ // + tool name, render the credential through the connection's auth
1150
+ // template, and execute the GraphQL request.
1151
+ // -----------------------------------------------------------------------
1152
+ invokeTool: ({ ctx, toolRow, credential, args }) => Effect5.gen(function* () {
1153
+ const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
1154
+ const integration = toolRow.integration;
1155
+ const toolName = toolRow.name;
1156
+ const config = yield* decodeGraphqlIntegrationConfig(credential.config).pipe(
1157
+ Effect5.mapError(
1158
+ () => new GraphqlInvocationError({
1159
+ message: `Invalid GraphQL integration config for "${integration}"`,
1160
+ statusCode: Option5.none()
1161
+ })
1162
+ )
1163
+ );
1164
+ let op = yield* ctx.storage.getOperation(integration, toolName);
1165
+ if (!op) {
1166
+ op = yield* materializeOperations(
1167
+ ctx,
1168
+ integration,
1169
+ config,
1170
+ credential,
1171
+ httpClientLayer
1172
+ ).pipe(Effect5.map((ops) => ops.find((o) => o.toolName === toolName) ?? null));
1173
+ }
1174
+ if (!op) {
1175
+ return yield* new GraphqlInvocationError({
1176
+ message: `No GraphQL operation found for tool "${integration}.${toolName}"`,
1177
+ statusCode: Option5.none()
1178
+ });
1179
+ }
1180
+ const headers = { ...config.headers ?? {} };
1181
+ const queryParams = {
1182
+ ...config.queryParams ?? {}
1183
+ };
1184
+ const template = config.authenticationTemplate.find(
1185
+ (t) => t.slug === String(credential.template)
1186
+ );
1187
+ if (template) {
1188
+ if (credential.value === null) {
1189
+ return yield* new GraphqlAuthRequiredError({
1190
+ code: template.kind === "oauth2" ? "oauth_connection_missing" : "connection_value_missing",
1191
+ message: template.kind === "oauth2" ? `Missing OAuth connection value for GraphQL integration "${integration}" (connection "${credential.connection}")` : `Missing credential value for GraphQL integration "${integration}" (connection "${credential.connection}")`,
1192
+ owner: credential.owner,
1193
+ integration,
1194
+ connection: String(credential.connection),
1195
+ credentialKind: template.kind === "oauth2" ? "oauth" : "secret",
1196
+ credentialLabel: template.kind === "oauth2" ? "OAuth sign-in" : "API key",
1197
+ template: String(credential.template)
1198
+ });
1199
+ }
1200
+ const rendered = renderAuthTemplate(template, credential.value);
1201
+ Object.assign(headers, rendered.headers);
1202
+ Object.assign(queryParams, rendered.queryParams);
1203
+ }
1204
+ const result = yield* invokeWithLayer(
1205
+ op.binding,
1206
+ args ?? {},
1207
+ config.endpoint,
1208
+ headers,
1209
+ queryParams,
1210
+ httpClientLayer
1211
+ );
1212
+ const errors = decodeGraphqlErrors(result.errors);
1213
+ if (errors !== void 0 && errors.length > 0) {
1214
+ const firstMessage = extractGraphqlErrorMessage(errors);
1215
+ return ToolResult.fail({
1216
+ code: "graphql_errors",
1217
+ message: firstMessage !== void 0 ? firstMessage : "GraphQL request returned errors",
1218
+ details: { errors }
1219
+ });
1220
+ }
1221
+ if (result.status < 200 || result.status >= 300) {
1222
+ if (result.status === 401 || result.status === 403) {
1223
+ return authToolFailure({
1224
+ code: "connection_rejected",
1225
+ status: result.status,
1226
+ message: `Upstream rejected credentials for GraphQL integration "${integration}" with HTTP ${result.status}. Re-authenticate or update the connection before retrying this tool.`,
1227
+ source: { id: integration, scope: credential.owner },
1228
+ credential: { kind: "upstream", label: "Upstream authorization" },
1229
+ upstream: {
1230
+ status: result.status,
1231
+ details: {
1232
+ data: result.data,
1233
+ errors: result.errors
1234
+ }
1235
+ }
1236
+ });
1237
+ }
1238
+ return ToolResult.fail({
1239
+ code: "graphql_http_error",
1240
+ status: result.status,
1241
+ message: `GraphQL request failed with HTTP ${result.status}`,
1242
+ details: {
1243
+ status: result.status,
1244
+ data: result.data,
1245
+ errors: result.errors
1246
+ }
1247
+ });
1248
+ }
1249
+ return ToolResult.ok(result.data);
1250
+ }).pipe(
1251
+ Effect5.catchTag(
1252
+ "GraphqlAuthRequiredError",
1253
+ (error) => Effect5.succeed(graphqlAuthToolFailure(error))
1254
+ )
1255
+ ),
1256
+ // Per-connection cleanup. Operation bindings are catalog-level (shared
1257
+ // across an integration's connections), so removing a single connection
1258
+ // leaves them in place; the executor drops the connection's tool rows.
1259
+ removeConnection: () => Effect5.void,
1260
+ detect: ({ ctx, url }) => Effect5.gen(function* () {
1261
+ const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
1262
+ const trimmed = url.trim();
1263
+ if (!trimmed) return null;
1264
+ const parsed = yield* Effect5.try({
1265
+ try: () => new URL(trimmed),
1266
+ catch: (cause) => cause
1267
+ }).pipe(Effect5.option);
1268
+ if (Option5.isNone(parsed)) return null;
1269
+ const ok = yield* introspect(trimmed).pipe(
1270
+ Effect5.provide(httpClientLayer),
1271
+ Effect5.map(() => true),
1272
+ Effect5.catch(() => Effect5.succeed(false))
1273
+ );
1274
+ const slug = slugFromEndpoint(trimmed);
1275
+ if (ok) {
1276
+ return IntegrationDetectionResult.make({
1277
+ kind: "graphql",
1278
+ confidence: "high",
1279
+ endpoint: trimmed,
1280
+ name: slug,
1281
+ slug
1282
+ });
1283
+ }
1284
+ if (urlMatchesToken(parsed.value, "graphql")) {
1285
+ return IntegrationDetectionResult.make({
1286
+ kind: "graphql",
1287
+ confidence: "low",
1288
+ endpoint: trimmed,
1289
+ name: slug,
1290
+ slug
1291
+ });
1292
+ }
1293
+ return null;
1294
+ })
1295
+ };
1296
+ });
1297
+ var httpClientLayerFallback = FetchHttpClient.layer;
1298
+
1299
+ export {
1300
+ introspect,
1301
+ parseIntrospectionJson,
1302
+ extract,
1303
+ invoke,
1304
+ invokeWithLayer,
1305
+ makeDefaultGraphqlStore,
1306
+ describeGraphqlAuthMethods,
1307
+ graphqlPlugin
1308
+ };
1309
+ //# sourceMappingURL=chunk-ADQTI2OK.js.map