@effect-gql/core 1.0.0 → 1.1.1
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/README.md +27 -1
- package/builder/index.cjs +17 -2
- package/builder/index.cjs.map +1 -1
- package/builder/index.d.cts +3 -2
- package/builder/index.d.ts +3 -2
- package/builder/index.js +17 -2
- package/builder/index.js.map +1 -1
- package/index.cjs +141 -74
- package/index.cjs.map +1 -1
- package/index.d.cts +9 -3
- package/index.d.ts +9 -3
- package/index.js +143 -76
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/{schema-builder-Cvdq7Kz_.d.cts → schema-builder-DKvkzU_M.d.cts} +2 -0
- package/{schema-builder-Cvdq7Kz_.d.ts → schema-builder-DKvkzU_M.d.ts} +2 -0
- package/server/index.cjs +88 -47
- package/server/index.cjs.map +1 -1
- package/server/index.d.cts +5 -3
- package/server/index.d.ts +5 -3
- package/server/index.js +91 -50
- package/server/index.js.map +1 -1
package/index.cjs
CHANGED
|
@@ -6,7 +6,6 @@ var S2 = require('effect/Schema');
|
|
|
6
6
|
var AST = require('effect/SchemaAST');
|
|
7
7
|
var DataLoader = require('dataloader');
|
|
8
8
|
var platform = require('@effect/platform');
|
|
9
|
-
var graphqlWs = require('graphql-ws');
|
|
10
9
|
|
|
11
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
11
|
|
|
@@ -837,7 +836,14 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
|
|
|
837
836
|
* Register an object type from a schema
|
|
838
837
|
*/
|
|
839
838
|
objectType(config) {
|
|
840
|
-
const {
|
|
839
|
+
const {
|
|
840
|
+
schema,
|
|
841
|
+
description,
|
|
842
|
+
implements: implementsInterfaces,
|
|
843
|
+
directives,
|
|
844
|
+
cacheControl,
|
|
845
|
+
fields
|
|
846
|
+
} = config;
|
|
841
847
|
const name = config.name ?? getSchemaName(schema);
|
|
842
848
|
if (!name) {
|
|
843
849
|
throw new Error(
|
|
@@ -845,7 +851,14 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
|
|
|
845
851
|
);
|
|
846
852
|
}
|
|
847
853
|
const newTypes = new Map(this.state.types);
|
|
848
|
-
newTypes.set(name, {
|
|
854
|
+
newTypes.set(name, {
|
|
855
|
+
name,
|
|
856
|
+
schema,
|
|
857
|
+
description,
|
|
858
|
+
implements: implementsInterfaces,
|
|
859
|
+
directives,
|
|
860
|
+
cacheControl
|
|
861
|
+
});
|
|
849
862
|
let newObjectFields = this.state.objectFields;
|
|
850
863
|
if (fields) {
|
|
851
864
|
newObjectFields = new Map(this.state.objectFields);
|
|
@@ -1214,6 +1227,7 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
|
|
|
1214
1227
|
const implementedInterfaces = typeReg.implements?.map((name) => interfaceRegistry.get(name)).filter(Boolean) ?? [];
|
|
1215
1228
|
const graphqlType = new graphql.GraphQLObjectType({
|
|
1216
1229
|
name: typeName,
|
|
1230
|
+
description: typeReg.description,
|
|
1217
1231
|
fields: () => {
|
|
1218
1232
|
const baseFields = schemaToFields(typeReg.schema, sharedCtx);
|
|
1219
1233
|
const additionalFields = this.state.objectFields.get(typeName);
|
|
@@ -1489,7 +1503,8 @@ var LoaderRegistry = class {
|
|
|
1489
1503
|
this.Service,
|
|
1490
1504
|
effect.Effect.gen(function* () {
|
|
1491
1505
|
const instances = {};
|
|
1492
|
-
for (const
|
|
1506
|
+
for (const name of Object.keys(self.definitions)) {
|
|
1507
|
+
const def = self.definitions[name];
|
|
1493
1508
|
instances[name] = yield* createDataLoader(def);
|
|
1494
1509
|
}
|
|
1495
1510
|
return instances;
|
|
@@ -1509,6 +1524,9 @@ var LoaderRegistry = class {
|
|
|
1509
1524
|
/**
|
|
1510
1525
|
* Load a single value by key.
|
|
1511
1526
|
* This is the most common operation in resolvers.
|
|
1527
|
+
*
|
|
1528
|
+
* @internal The internal cast is safe because LoaderInstances<Defs> guarantees
|
|
1529
|
+
* that loaders[name] is a DataLoader with the correct key/value types.
|
|
1512
1530
|
*/
|
|
1513
1531
|
load(name, key) {
|
|
1514
1532
|
const self = this;
|
|
@@ -1521,6 +1539,9 @@ var LoaderRegistry = class {
|
|
|
1521
1539
|
/**
|
|
1522
1540
|
* Load multiple values by keys.
|
|
1523
1541
|
* All keys are batched into a single request.
|
|
1542
|
+
*
|
|
1543
|
+
* @internal The internal cast is safe because LoaderInstances<Defs> guarantees
|
|
1544
|
+
* that loaders[name] is a DataLoader with the correct key/value types.
|
|
1524
1545
|
*/
|
|
1525
1546
|
loadMany(name, keys) {
|
|
1526
1547
|
const self = this;
|
|
@@ -1537,35 +1558,39 @@ var LoaderRegistry = class {
|
|
|
1537
1558
|
});
|
|
1538
1559
|
}
|
|
1539
1560
|
};
|
|
1561
|
+
function createSingleDataLoader(def, context) {
|
|
1562
|
+
return new DataLoader__default.default(async (keys) => {
|
|
1563
|
+
const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
|
|
1564
|
+
return keys.map((key) => {
|
|
1565
|
+
const item = items.find((i) => def.key(i) === key);
|
|
1566
|
+
if (!item) return new Error(`Not found: ${key}`);
|
|
1567
|
+
return item;
|
|
1568
|
+
});
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
function createGroupedDataLoader(def, context) {
|
|
1572
|
+
return new DataLoader__default.default(async (keys) => {
|
|
1573
|
+
const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
|
|
1574
|
+
const map = /* @__PURE__ */ new Map();
|
|
1575
|
+
for (const item of items) {
|
|
1576
|
+
const key = def.groupBy(item);
|
|
1577
|
+
let arr = map.get(key);
|
|
1578
|
+
if (!arr) {
|
|
1579
|
+
arr = [];
|
|
1580
|
+
map.set(key, arr);
|
|
1581
|
+
}
|
|
1582
|
+
arr.push(item);
|
|
1583
|
+
}
|
|
1584
|
+
return keys.map((key) => map.get(key) ?? []);
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1540
1587
|
function createDataLoader(def) {
|
|
1541
1588
|
return effect.Effect.gen(function* () {
|
|
1542
1589
|
const context = yield* effect.Effect.context();
|
|
1543
1590
|
if (def._tag === "single") {
|
|
1544
|
-
|
|
1545
|
-
const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
|
|
1546
|
-
return keys.map((key) => {
|
|
1547
|
-
const item = items.find((i) => def.key(i) === key);
|
|
1548
|
-
if (!item) return new Error(`Not found: ${key}`);
|
|
1549
|
-
return item;
|
|
1550
|
-
});
|
|
1551
|
-
});
|
|
1552
|
-
return loader;
|
|
1591
|
+
return createSingleDataLoader(def, context);
|
|
1553
1592
|
} else {
|
|
1554
|
-
|
|
1555
|
-
const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
|
|
1556
|
-
const map = /* @__PURE__ */ new Map();
|
|
1557
|
-
for (const item of items) {
|
|
1558
|
-
const key = def.groupBy(item);
|
|
1559
|
-
let arr = map.get(key);
|
|
1560
|
-
if (!arr) {
|
|
1561
|
-
arr = [];
|
|
1562
|
-
map.set(key, arr);
|
|
1563
|
-
}
|
|
1564
|
-
arr.push(item);
|
|
1565
|
-
}
|
|
1566
|
-
return keys.map((key) => map.get(key) ?? []);
|
|
1567
|
-
});
|
|
1568
|
-
return loader;
|
|
1593
|
+
return createGroupedDataLoader(def, context);
|
|
1569
1594
|
}
|
|
1570
1595
|
});
|
|
1571
1596
|
}
|
|
@@ -1789,7 +1814,7 @@ var GraphQLRouterConfigFromEnv = effect.Config.all({
|
|
|
1789
1814
|
);
|
|
1790
1815
|
|
|
1791
1816
|
// src/server/graphiql.ts
|
|
1792
|
-
var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
|
|
1817
|
+
var graphiqlHtml = (endpoint, subscriptionEndpoint) => `<!DOCTYPE html>
|
|
1793
1818
|
<html lang="en">
|
|
1794
1819
|
<head>
|
|
1795
1820
|
<meta charset="utf-8" />
|
|
@@ -1817,6 +1842,7 @@ var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
|
|
|
1817
1842
|
<script>
|
|
1818
1843
|
const fetcher = GraphiQL.createFetcher({
|
|
1819
1844
|
url: ${JSON.stringify(endpoint)},
|
|
1845
|
+
subscriptionUrl: ${JSON.stringify(subscriptionEndpoint ?? endpoint)},
|
|
1820
1846
|
});
|
|
1821
1847
|
ReactDOM.createRoot(document.getElementById('graphiql')).render(
|
|
1822
1848
|
React.createElement(GraphiQL, { fetcher })
|
|
@@ -2492,19 +2518,32 @@ var defaultErrorHandler = (cause) => (process.env.NODE_ENV !== "production" ? ef
|
|
|
2492
2518
|
).pipe(effect.Effect.orDie)
|
|
2493
2519
|
)
|
|
2494
2520
|
);
|
|
2495
|
-
var
|
|
2521
|
+
var GraphQLRequestBodySchema = effect.Schema.Struct({
|
|
2522
|
+
query: effect.Schema.String,
|
|
2523
|
+
variables: effect.Schema.optionalWith(effect.Schema.Record({ key: effect.Schema.String, value: effect.Schema.Unknown }), {
|
|
2524
|
+
as: "Option"
|
|
2525
|
+
}),
|
|
2526
|
+
operationName: effect.Schema.optionalWith(effect.Schema.String, { as: "Option" })
|
|
2527
|
+
});
|
|
2528
|
+
var decodeRequestBody = platform.HttpIncomingMessage.schemaBodyJson(GraphQLRequestBodySchema);
|
|
2529
|
+
var parseGraphQLQuery = (query2, extensionsService) => {
|
|
2496
2530
|
try {
|
|
2497
2531
|
const document = graphql.parse(query2);
|
|
2498
|
-
return { ok: true, document };
|
|
2532
|
+
return effect.Effect.succeed({ ok: true, document });
|
|
2499
2533
|
} catch (parseError) {
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2534
|
+
return extensionsService.get().pipe(
|
|
2535
|
+
effect.Effect.flatMap(
|
|
2536
|
+
(extensionData) => platform.HttpServerResponse.json({
|
|
2537
|
+
errors: [{ message: String(parseError) }],
|
|
2538
|
+
extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
|
|
2539
|
+
}).pipe(
|
|
2540
|
+
effect.Effect.orDie,
|
|
2541
|
+
effect.Effect.map((response) => ({ ok: false, response }))
|
|
2542
|
+
)
|
|
2543
|
+
)
|
|
2544
|
+
);
|
|
2506
2545
|
}
|
|
2507
|
-
}
|
|
2546
|
+
};
|
|
2508
2547
|
var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig) => {
|
|
2509
2548
|
if (!complexityConfig) {
|
|
2510
2549
|
return effect.Effect.void;
|
|
@@ -2524,8 +2563,9 @@ var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig
|
|
|
2524
2563
|
)
|
|
2525
2564
|
);
|
|
2526
2565
|
};
|
|
2527
|
-
var
|
|
2528
|
-
|
|
2566
|
+
var isPromiseLike = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
|
|
2567
|
+
var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => {
|
|
2568
|
+
const tryExecute = effect.Effect.try({
|
|
2529
2569
|
try: () => graphql.execute({
|
|
2530
2570
|
schema,
|
|
2531
2571
|
document,
|
|
@@ -2535,33 +2575,34 @@ var executeGraphQLQuery = (schema, document, variables, operationName, runtime)
|
|
|
2535
2575
|
}),
|
|
2536
2576
|
catch: (error) => new Error(String(error))
|
|
2537
2577
|
});
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
()
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
})
|
|
2545
|
-
|
|
2578
|
+
return tryExecute.pipe(
|
|
2579
|
+
effect.Effect.flatMap((executeResult) => {
|
|
2580
|
+
if (isPromiseLike(executeResult)) {
|
|
2581
|
+
return effect.Effect.promise(() => executeResult);
|
|
2582
|
+
}
|
|
2583
|
+
return effect.Effect.succeed(executeResult);
|
|
2584
|
+
})
|
|
2585
|
+
);
|
|
2586
|
+
};
|
|
2587
|
+
var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => {
|
|
2546
2588
|
if (cacheControlConfig?.enabled === false || cacheControlConfig?.calculateHttpHeaders === false) {
|
|
2547
|
-
return void 0;
|
|
2589
|
+
return effect.Effect.succeed(void 0);
|
|
2548
2590
|
}
|
|
2549
2591
|
const operations = document.definitions.filter(
|
|
2550
2592
|
(d) => d.kind === graphql.Kind.OPERATION_DEFINITION
|
|
2551
2593
|
);
|
|
2552
2594
|
const operation = operationName ? operations.find((o) => o.name?.value === operationName) : operations[0];
|
|
2553
2595
|
if (!operation || operation.operation === "mutation") {
|
|
2554
|
-
return void 0;
|
|
2596
|
+
return effect.Effect.succeed(void 0);
|
|
2555
2597
|
}
|
|
2556
|
-
|
|
2598
|
+
return computeCachePolicy({
|
|
2557
2599
|
document,
|
|
2558
2600
|
operation,
|
|
2559
2601
|
schema,
|
|
2560
2602
|
cacheHints,
|
|
2561
2603
|
config: cacheControlConfig ?? {}
|
|
2562
|
-
});
|
|
2563
|
-
|
|
2564
|
-
});
|
|
2604
|
+
}).pipe(effect.Effect.map(toCacheControlHeader));
|
|
2605
|
+
};
|
|
2565
2606
|
var buildGraphQLResponse = (result, extensionData, cacheControlHeader) => {
|
|
2566
2607
|
const finalResult = Object.keys(extensionData).length > 0 ? {
|
|
2567
2608
|
...result,
|
|
@@ -2599,7 +2640,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
|
|
|
2599
2640
|
const extensionsService = yield* makeExtensionsService();
|
|
2600
2641
|
const runtime = yield* effect.Effect.runtime();
|
|
2601
2642
|
const request = yield* platform.HttpServerRequest.HttpServerRequest;
|
|
2602
|
-
const
|
|
2643
|
+
const parsedBody = yield* decodeRequestBody(request);
|
|
2644
|
+
const body = {
|
|
2645
|
+
query: parsedBody.query,
|
|
2646
|
+
variables: parsedBody.variables._tag === "Some" ? parsedBody.variables.value : void 0,
|
|
2647
|
+
operationName: parsedBody.operationName._tag === "Some" ? parsedBody.operationName.value : void 0
|
|
2648
|
+
};
|
|
2603
2649
|
const parseResult = yield* parseGraphQLQuery(body.query, extensionsService);
|
|
2604
2650
|
if (!parseResult.ok) {
|
|
2605
2651
|
return parseResult.response;
|
|
@@ -2608,7 +2654,7 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
|
|
|
2608
2654
|
yield* runParseHooks(extensions, body.query, document).pipe(
|
|
2609
2655
|
effect.Effect.provideService(ExtensionsService, extensionsService)
|
|
2610
2656
|
);
|
|
2611
|
-
const validationRules = resolvedConfig.introspection ? void 0 :
|
|
2657
|
+
const validationRules = resolvedConfig.introspection ? void 0 : graphql.specifiedRules.concat(graphql.NoSchemaIntrospectionCustomRule);
|
|
2612
2658
|
const validationErrors = graphql.validate(schema, document, validationRules);
|
|
2613
2659
|
yield* runValidateHooks(extensions, document, validationErrors).pipe(
|
|
2614
2660
|
effect.Effect.provideService(ExtensionsService, extensionsService)
|
|
@@ -2669,9 +2715,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
|
|
|
2669
2715
|
platform.HttpRouter.post(resolvedConfig.path, graphqlHandler)
|
|
2670
2716
|
);
|
|
2671
2717
|
if (resolvedConfig.graphiql) {
|
|
2672
|
-
const { path, endpoint } = resolvedConfig.graphiql;
|
|
2718
|
+
const { path, endpoint, subscriptionEndpoint } = resolvedConfig.graphiql;
|
|
2673
2719
|
router = router.pipe(
|
|
2674
|
-
platform.HttpRouter.get(
|
|
2720
|
+
platform.HttpRouter.get(
|
|
2721
|
+
path,
|
|
2722
|
+
platform.HttpServerResponse.html(graphiqlHtml(endpoint, subscriptionEndpoint))
|
|
2723
|
+
)
|
|
2675
2724
|
);
|
|
2676
2725
|
}
|
|
2677
2726
|
return router;
|
|
@@ -2846,27 +2895,45 @@ var runConnectionLifecycle = (socket, wsServer, extra) => effect.Effect.gen(func
|
|
|
2846
2895
|
effect.Effect.catchAllCause(() => effect.Effect.void),
|
|
2847
2896
|
effect.Effect.scoped
|
|
2848
2897
|
);
|
|
2898
|
+
var importGraphqlWs = effect.Effect.tryPromise({
|
|
2899
|
+
try: () => import('graphql-ws'),
|
|
2900
|
+
catch: () => new Error(
|
|
2901
|
+
"graphql-ws is required for WebSocket subscriptions. Install it with: npm install graphql-ws"
|
|
2902
|
+
)
|
|
2903
|
+
});
|
|
2849
2904
|
var makeGraphQLWSHandler = (schema, layer, options) => {
|
|
2850
2905
|
const complexityConfig = options?.complexity;
|
|
2851
2906
|
const fieldComplexities = options?.fieldComplexities ?? /* @__PURE__ */ new Map();
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2907
|
+
let wsServerPromise = null;
|
|
2908
|
+
const getOrCreateServer = async () => {
|
|
2909
|
+
if (!wsServerPromise) {
|
|
2910
|
+
wsServerPromise = effect.Effect.runPromise(importGraphqlWs).then(({ makeServer }) => {
|
|
2911
|
+
const serverOptions = {
|
|
2912
|
+
schema,
|
|
2913
|
+
context: async (ctx) => {
|
|
2914
|
+
const extra = ctx.extra;
|
|
2915
|
+
return {
|
|
2916
|
+
runtime: extra.runtime,
|
|
2917
|
+
...extra.connectionParams
|
|
2918
|
+
};
|
|
2919
|
+
},
|
|
2920
|
+
subscribe: async (args) => graphql.subscribe(args),
|
|
2921
|
+
onConnect: makeOnConnectHandler(options),
|
|
2922
|
+
onDisconnect: makeOnDisconnectHandler(options),
|
|
2923
|
+
onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
|
|
2924
|
+
onComplete: makeOnCompleteHandler(options),
|
|
2925
|
+
onError: makeOnErrorHandler(options)
|
|
2926
|
+
};
|
|
2927
|
+
return makeServer(serverOptions);
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
return wsServerPromise;
|
|
2867
2931
|
};
|
|
2868
|
-
const wsServer = graphqlWs.makeServer(serverOptions);
|
|
2869
2932
|
return (socket) => effect.Effect.gen(function* () {
|
|
2933
|
+
const wsServer = yield* effect.Effect.tryPromise({
|
|
2934
|
+
try: () => getOrCreateServer(),
|
|
2935
|
+
catch: (error) => error
|
|
2936
|
+
});
|
|
2870
2937
|
const runtime = yield* effect.Effect.provide(effect.Effect.runtime(), layer);
|
|
2871
2938
|
const extra = {
|
|
2872
2939
|
socket,
|