@webiny/api-graphql 0.0.0-unstable.0d717d18dd → 0.0.0-unstable.5387e5f7c8
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/engine/GraphQLEngine.d.ts +10 -8
- package/engine/GraphQLEngine.js +37 -51
- package/engine/GraphQLEngine.js.map +1 -1
- package/engine/GraphQLEngineFeature.js +2 -2
- package/engine/GraphQLEngineFeature.js.map +1 -1
- package/engine/GraphQLRoute.d.ts +14 -4
- package/engine/GraphQLRoute.js +15 -5
- package/engine/GraphQLRoute.js.map +1 -1
- package/engine/GraphQLSchemaCacheFeature.d.ts +8 -0
- package/engine/GraphQLSchemaCacheFeature.js +11 -0
- package/engine/GraphQLSchemaCacheFeature.js.map +1 -0
- package/engine/GraphQLSchemaKeyVerificationFeature.d.ts +9 -0
- package/engine/GraphQLSchemaKeyVerificationFeature.js +11 -0
- package/engine/GraphQLSchemaKeyVerificationFeature.js.map +1 -0
- package/engine/MemoryGraphQLSchemaCache.d.ts +15 -0
- package/engine/MemoryGraphQLSchemaCache.js +32 -0
- package/engine/MemoryGraphQLSchemaCache.js.map +1 -0
- package/engine/MemoryGraphQLSchemaKeyVerification.d.ts +13 -0
- package/engine/MemoryGraphQLSchemaKeyVerification.js +23 -0
- package/engine/MemoryGraphQLSchemaKeyVerification.js.map +1 -0
- package/engine/abstractions.d.ts +29 -0
- package/engine/abstractions.js +3 -1
- package/engine/abstractions.js.map +1 -1
- package/engine/createSchemaCacheKey.d.ts +8 -0
- package/engine/createSchemaCacheKey.js +23 -0
- package/engine/createSchemaCacheKey.js.map +1 -0
- package/engine/index.d.ts +2 -2
- package/engine/index.js +2 -2
- package/exports/api/graphql.d.ts +1 -0
- package/exports/api/graphql.js +1 -0
- package/features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.js +4 -3
- package/features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.js.map +1 -1
- package/features/GraphQLSchemaBuilder/GraphQLSchemaComposer.d.ts +6 -2
- package/features/GraphQLSchemaBuilder/GraphQLSchemaComposer.js +50 -6
- package/features/GraphQLSchemaBuilder/GraphQLSchemaComposer.js.map +1 -1
- package/features/GraphQLSchemaBuilder/abstractions.d.ts +6 -1
- package/features/GraphQLSchemaBuilder/abstractions.js.map +1 -1
- package/graphql/abstractions.core.d.ts +8 -6
- package/graphql/abstractions.core.js.map +1 -1
- package/graphql/abstractions.public.d.ts +7 -0
- package/graphql/abstractions.public.js.map +1 -1
- package/graphql/staticSchemaKey.d.ts +5 -0
- package/graphql/staticSchemaKey.js +4 -0
- package/graphql/staticSchemaKey.js.map +1 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/package.json +15 -16
- package/engine/GraphQLContextEnhancer.d.ts +0 -12
- package/engine/GraphQLContextEnhancer.js +0 -5
- package/engine/GraphQLContextEnhancer.js.map +0 -1
- package/engine/GraphQLContextualSchema.d.ts +0 -14
- package/engine/GraphQLContextualSchema.js +0 -5
- package/engine/GraphQLContextualSchema.js.map +0 -1
- package/registerLegacyPluginsViaGqlContextEnhancer.d.ts +0 -12
- package/registerLegacyPluginsViaGqlContextEnhancer.js +0 -21
- package/registerLegacyPluginsViaGqlContextEnhancer.js.map +0 -1
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { Container } from "@webiny/di";
|
|
2
2
|
import { GraphQLEngine as GraphQLEngineAbstraction } from "./abstractions.js";
|
|
3
|
+
import { GraphQLSchemaCache } from "./abstractions.js";
|
|
3
4
|
import type { IGraphQLSchemaComposer } from "../features/GraphQLSchemaBuilder/abstractions.js";
|
|
4
|
-
import type { IGraphQLContextEnhancer } from "./GraphQLContextEnhancer.js";
|
|
5
|
-
import type { IGraphQLContextualSchema } from "./GraphQLContextualSchema.js";
|
|
6
5
|
declare class GraphQLEngineImpl implements GraphQLEngineAbstraction.Interface {
|
|
7
6
|
private composer;
|
|
8
7
|
private container;
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
constructor(composer: IGraphQLSchemaComposer, container: Container, enhancers: IGraphQLContextEnhancer[], contextualSchemas: IGraphQLContextualSchema[]);
|
|
8
|
+
private schemaCache;
|
|
9
|
+
constructor(composer: IGraphQLSchemaComposer, container: Container, schemaCache: GraphQLSchemaCache.Interface | undefined);
|
|
12
10
|
execute(body: any): Promise<any>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The same composer output always builds the same executable schema, so with a cache in the
|
|
13
|
+
* root container it's built once and reused. When every schema factory has a schema key, the key
|
|
14
|
+
* comes from those and the factories only run on a cache miss. Otherwise they run on every
|
|
15
|
+
* request, and the key comes from what they produced.
|
|
16
|
+
*/
|
|
17
|
+
private resolveSchema;
|
|
16
18
|
private executeOne;
|
|
17
19
|
}
|
|
18
20
|
export declare const GraphQLEngine: typeof GraphQLEngineImpl & {
|
package/engine/GraphQLEngine.js
CHANGED
|
@@ -1,63 +1,55 @@
|
|
|
1
1
|
import { graphql } from "graphql";
|
|
2
|
-
import { makeExecutableSchema
|
|
2
|
+
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
3
3
|
import { mergeResolvers } from "@graphql-tools/merge";
|
|
4
4
|
import { RequestContainer } from "@webiny/event-handler-core";
|
|
5
|
-
import { GraphQLEngine } from "./abstractions.js";
|
|
6
|
-
import {
|
|
7
|
-
import { GraphQLContextualSchema } from "./GraphQLContextualSchema.js";
|
|
5
|
+
import { GraphQLEngine, GraphQLSchemaCache } from "./abstractions.js";
|
|
6
|
+
import { createSchemaCacheKey } from "./createSchemaCacheKey.js";
|
|
8
7
|
import { GraphQLSchemaComposer } from "../features/GraphQLSchemaBuilder/abstractions.js";
|
|
9
8
|
import { ResolverDecoration } from "../ResolverDecoration.js";
|
|
10
9
|
import { createRequestBody } from "../createRequestBody.js";
|
|
10
|
+
const buildStaticSchema = (schemaConfig)=>{
|
|
11
|
+
const resolverDecoration = new ResolverDecoration();
|
|
12
|
+
if (schemaConfig.resolverDecorators) resolverDecoration.addDecorators(schemaConfig.resolverDecorators);
|
|
13
|
+
const typeDefs = `type Query\ntype Mutation\n${schemaConfig.typeDefs ?? ""}`;
|
|
14
|
+
const resolvers = mergeResolvers([
|
|
15
|
+
schemaConfig.resolvers ?? {}
|
|
16
|
+
]);
|
|
17
|
+
return makeExecutableSchema({
|
|
18
|
+
typeDefs,
|
|
19
|
+
resolvers: resolverDecoration.decorateResolvers(resolvers),
|
|
20
|
+
assumeValidSDL: true,
|
|
21
|
+
inheritResolversFromInterfaces: true,
|
|
22
|
+
updateResolversInPlace: true
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const composeAndBuild = async (composer)=>{
|
|
26
|
+
const schemaConfig = await composer.build();
|
|
27
|
+
return buildStaticSchema(schemaConfig);
|
|
28
|
+
};
|
|
11
29
|
class GraphQLEngineImpl {
|
|
12
|
-
constructor(composer, container,
|
|
30
|
+
constructor(composer, container, schemaCache){
|
|
13
31
|
this.composer = composer;
|
|
14
32
|
this.container = container;
|
|
15
|
-
this.
|
|
16
|
-
this.contextualSchemas = contextualSchemas;
|
|
33
|
+
this.schemaCache = schemaCache;
|
|
17
34
|
}
|
|
18
35
|
async execute(body) {
|
|
19
|
-
const ctx =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
if (schemaConfig.resolverDecorators) resolverDecoration.addDecorators(schemaConfig.resolverDecorators);
|
|
24
|
-
const typeDefs = `type Query\ntype Mutation\n${schemaConfig.typeDefs ?? ""}`;
|
|
25
|
-
const staticSchema = makeExecutableSchema({
|
|
26
|
-
typeDefs,
|
|
27
|
-
resolvers: resolverDecoration.decorateResolvers(mergeResolvers([
|
|
28
|
-
schemaConfig.resolvers ?? {}
|
|
29
|
-
])),
|
|
30
|
-
assumeValidSDL: true,
|
|
31
|
-
inheritResolversFromInterfaces: true
|
|
32
|
-
});
|
|
33
|
-
const schema = await this.buildSchema(staticSchema, extraSchemas);
|
|
36
|
+
const ctx = {
|
|
37
|
+
container: this.container
|
|
38
|
+
};
|
|
39
|
+
const schema = await this.resolveSchema();
|
|
34
40
|
const parsed = createRequestBody(body);
|
|
35
41
|
if (!Array.isArray(parsed)) return this.executeOne(parsed, schema, ctx);
|
|
36
42
|
const results = [];
|
|
37
43
|
for (const b of parsed)results.push(await this.executeOne(b, schema, ctx));
|
|
38
44
|
return results;
|
|
39
45
|
}
|
|
40
|
-
async
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
async buildContextualSchemas(ctx) {
|
|
48
|
-
if (0 === this.contextualSchemas.length) return [];
|
|
49
|
-
const schemas = [];
|
|
50
|
-
for (const s of this.contextualSchemas)schemas.push(await s.build(ctx));
|
|
51
|
-
return schemas;
|
|
52
|
-
}
|
|
53
|
-
async buildSchema(staticSchema, extraSchemas) {
|
|
54
|
-
if (0 === extraSchemas.length) return staticSchema;
|
|
55
|
-
return mergeSchemas({
|
|
56
|
-
schemas: [
|
|
57
|
-
staticSchema,
|
|
58
|
-
...extraSchemas
|
|
59
|
-
]
|
|
60
|
-
});
|
|
46
|
+
async resolveSchema() {
|
|
47
|
+
if (!this.schemaCache) return composeAndBuild(this.composer);
|
|
48
|
+
const factoriesKey = await this.composer.getSchemaKey();
|
|
49
|
+
if (factoriesKey) return this.schemaCache.getOrBuild(`factories:${factoriesKey}`, ()=>composeAndBuild(this.composer));
|
|
50
|
+
const schemaConfig = await this.composer.build();
|
|
51
|
+
const contentKey = createSchemaCacheKey(schemaConfig);
|
|
52
|
+
return this.schemaCache.getOrBuild(`content:${contentKey}`, async ()=>buildStaticSchema(schemaConfig));
|
|
61
53
|
}
|
|
62
54
|
async executeOne(body, schema, ctx) {
|
|
63
55
|
const { query, variables, operationName } = body;
|
|
@@ -77,15 +69,9 @@ const GraphQLEngine_GraphQLEngine = GraphQLEngine.createImplementation({
|
|
|
77
69
|
GraphQLSchemaComposer,
|
|
78
70
|
RequestContainer,
|
|
79
71
|
[
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
multiple: true
|
|
83
|
-
}
|
|
84
|
-
],
|
|
85
|
-
[
|
|
86
|
-
GraphQLContextualSchema,
|
|
72
|
+
GraphQLSchemaCache,
|
|
87
73
|
{
|
|
88
|
-
|
|
74
|
+
optional: true
|
|
89
75
|
}
|
|
90
76
|
]
|
|
91
77
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine/GraphQLEngine.js","sources":["../../src/engine/GraphQLEngine.ts"],"sourcesContent":["import { graphql } from \"graphql\";\nimport { makeExecutableSchema
|
|
1
|
+
{"version":3,"file":"engine/GraphQLEngine.js","sources":["../../src/engine/GraphQLEngine.ts"],"sourcesContent":["import { graphql } from \"graphql\";\nimport { makeExecutableSchema } from \"@graphql-tools/schema\";\nimport { mergeResolvers } from \"@graphql-tools/merge\";\nimport { Container } from \"@webiny/di\";\nimport { RequestContainer } from \"@webiny/event-handler-core\";\nimport { GraphQLEngine as GraphQLEngineAbstraction } from \"./abstractions.js\";\nimport { GraphQLSchemaCache } from \"./abstractions.js\";\nimport { createSchemaCacheKey } from \"./createSchemaCacheKey.js\";\nimport { GraphQLSchemaComposer } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\nimport { ResolverDecoration } from \"~/ResolverDecoration.js\";\nimport { createRequestBody } from \"~/createRequestBody.js\";\nimport type { IGraphQLSchemaComposer } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\nimport type { GraphQLRequestBody } from \"~/types.js\";\nimport type { GraphQLSchema } from \"graphql\";\n\nconst buildStaticSchema = (schemaConfig: IGraphQLSchema): GraphQLSchema => {\n const resolverDecoration = new ResolverDecoration();\n if (schemaConfig.resolverDecorators) {\n resolverDecoration.addDecorators(schemaConfig.resolverDecorators);\n }\n\n // Always provide base root types so that `extend type Query/Mutation` works without\n // requiring callers to define them. With assumeValidSDL:true, empty base types are\n // allowed at build time; graphql() then returns schema-validation errors at execution\n // time (e.g. \"Type Query must define one or more fields.\") when no fields are registered.\n const typeDefs = `type Query\\ntype Mutation\\n${schemaConfig.typeDefs ?? \"\"}`;\n const resolvers = mergeResolvers([schemaConfig.resolvers ?? {}]);\n\n return makeExecutableSchema({\n typeDefs,\n resolvers: resolverDecoration.decorateResolvers(resolvers),\n assumeValidSDL: true,\n inheritResolversFromInterfaces: true,\n // The schema built from typeDefs above is new and nothing else holds it, so resolvers go\n // onto it directly. Without this, graphql-tools rebuilds the whole schema to attach them,\n // which was about half of the schema build on a cold Lambda.\n updateResolversInPlace: true\n });\n};\n\nconst composeAndBuild = async (composer: IGraphQLSchemaComposer): Promise<GraphQLSchema> => {\n const schemaConfig = await composer.build();\n return buildStaticSchema(schemaConfig);\n};\n\nclass GraphQLEngineImpl implements GraphQLEngineAbstraction.Interface {\n constructor(\n private composer: IGraphQLSchemaComposer,\n private container: Container,\n private schemaCache: GraphQLSchemaCache.Interface | undefined\n ) {}\n\n async execute(body: any): Promise<any> {\n const ctx: Record<string, any> = { container: this.container };\n const schema = await this.resolveSchema();\n const parsed = createRequestBody(body);\n\n if (!Array.isArray(parsed)) {\n return this.executeOne(parsed, schema, ctx);\n }\n // Run sequentially so per-request state (e.g. ctx.debug.logs) is scoped per query.\n const results = [];\n for (const b of parsed) {\n results.push(await this.executeOne(b, schema, ctx));\n }\n return results;\n }\n\n /**\n * The same composer output always builds the same executable schema, so with a cache in the\n * root container it's built once and reused. When every schema factory has a schema key, the key\n * comes from those and the factories only run on a cache miss. Otherwise they run on every\n * request, and the key comes from what they produced.\n */\n private async resolveSchema(): Promise<GraphQLSchema> {\n if (!this.schemaCache) {\n return composeAndBuild(this.composer);\n }\n\n const factoriesKey = await this.composer.getSchemaKey();\n if (factoriesKey) {\n return this.schemaCache.getOrBuild(`factories:${factoriesKey}`, () => {\n return composeAndBuild(this.composer);\n });\n }\n\n const schemaConfig = await this.composer.build();\n const contentKey = createSchemaCacheKey(schemaConfig);\n return this.schemaCache.getOrBuild(`content:${contentKey}`, async () => {\n return buildStaticSchema(schemaConfig);\n });\n }\n\n private async executeOne(\n body: GraphQLRequestBody,\n schema: GraphQLSchema,\n ctx: Record<string, any>\n ): Promise<any> {\n const { query, variables, operationName } = body;\n\n return graphql({\n schema,\n source: query,\n rootValue: {},\n contextValue: ctx,\n variableValues: variables ?? undefined,\n operationName: operationName ?? undefined\n });\n }\n}\n\nexport const GraphQLEngine = GraphQLEngineAbstraction.createImplementation({\n implementation: GraphQLEngineImpl,\n dependencies: [\n GraphQLSchemaComposer,\n RequestContainer,\n [GraphQLSchemaCache, { optional: true }]\n ]\n});\n"],"names":["buildStaticSchema","schemaConfig","resolverDecoration","ResolverDecoration","typeDefs","resolvers","mergeResolvers","makeExecutableSchema","composeAndBuild","composer","GraphQLEngineImpl","container","schemaCache","body","ctx","schema","parsed","createRequestBody","Array","results","b","factoriesKey","contentKey","createSchemaCacheKey","query","variables","operationName","graphql","undefined","GraphQLEngine","GraphQLEngineAbstraction","GraphQLSchemaComposer","RequestContainer","GraphQLSchemaCache"],"mappings":";;;;;;;;;AAgBA,MAAMA,oBAAoB,CAACC;IACvB,MAAMC,qBAAqB,IAAIC;IAC/B,IAAIF,aAAa,kBAAkB,EAC/BC,mBAAmB,aAAa,CAACD,aAAa,kBAAkB;IAOpE,MAAMG,WAAW,CAAC,2BAA2B,EAAEH,aAAa,QAAQ,IAAI,IAAI;IAC5E,MAAMI,YAAYC,eAAe;QAACL,aAAa,SAAS,IAAI,CAAC;KAAE;IAE/D,OAAOM,qBAAqB;QACxBH;QACA,WAAWF,mBAAmB,iBAAiB,CAACG;QAChD,gBAAgB;QAChB,gCAAgC;QAIhC,wBAAwB;IAC5B;AACJ;AAEA,MAAMG,kBAAkB,OAAOC;IAC3B,MAAMR,eAAe,MAAMQ,SAAS,KAAK;IACzC,OAAOT,kBAAkBC;AAC7B;AAEA,MAAMS;IACF,YACYD,QAAgC,EAChCE,SAAoB,EACpBC,WAAqD,CAC/D;aAHUH,QAAQ,GAARA;aACAE,SAAS,GAATA;aACAC,WAAW,GAAXA;IACT;IAEH,MAAM,QAAQC,IAAS,EAAgB;QACnC,MAAMC,MAA2B;YAAE,WAAW,IAAI,CAAC,SAAS;QAAC;QAC7D,MAAMC,SAAS,MAAM,IAAI,CAAC,aAAa;QACvC,MAAMC,SAASC,kBAAkBJ;QAEjC,IAAI,CAACK,MAAM,OAAO,CAACF,SACf,OAAO,IAAI,CAAC,UAAU,CAACA,QAAQD,QAAQD;QAG3C,MAAMK,UAAU,EAAE;QAClB,KAAK,MAAMC,KAAKJ,OACZG,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,CAACC,GAAGL,QAAQD;QAElD,OAAOK;IACX;IAQA,MAAc,gBAAwC;QAClD,IAAI,CAAC,IAAI,CAAC,WAAW,EACjB,OAAOX,gBAAgB,IAAI,CAAC,QAAQ;QAGxC,MAAMa,eAAe,MAAM,IAAI,CAAC,QAAQ,CAAC,YAAY;QACrD,IAAIA,cACA,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,UAAU,EAAEA,cAAc,EAAE,IACrDb,gBAAgB,IAAI,CAAC,QAAQ;QAI5C,MAAMP,eAAe,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK;QAC9C,MAAMqB,aAAaC,qBAAqBtB;QACxC,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAEqB,YAAY,EAAE,UACjDtB,kBAAkBC;IAEjC;IAEA,MAAc,WACVY,IAAwB,EACxBE,MAAqB,EACrBD,GAAwB,EACZ;QACZ,MAAM,EAAEU,KAAK,EAAEC,SAAS,EAAEC,aAAa,EAAE,GAAGb;QAE5C,OAAOc,QAAQ;YACXZ;YACA,QAAQS;YACR,WAAW,CAAC;YACZ,cAAcV;YACd,gBAAgBW,aAAaG;YAC7B,eAAeF,iBAAiBE;QACpC;IACJ;AACJ;AAEO,MAAMC,8BAAgBC,cAAAA,oBAA6C,CAAC;IACvE,gBAAgBpB;IAChB,cAAc;QACVqB;QACAC;QACA;YAACC;YAAoB;gBAAE,UAAU;YAAK;SAAE;KAC3C;AACL"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createFeature } from "@webiny/feature/api";
|
|
2
2
|
import { GraphQLSchemaComposerFeature } from "../features/GraphQLSchemaBuilder/feature.js";
|
|
3
3
|
import { GraphQLEngine } from "./GraphQLEngine.js";
|
|
4
|
-
import {
|
|
4
|
+
import { GraphQLRouteDefinition } from "./GraphQLRoute.js";
|
|
5
5
|
const GraphQLEngineFeature = createFeature({
|
|
6
6
|
name: "GraphQLEngine",
|
|
7
7
|
register (container) {
|
|
8
8
|
GraphQLSchemaComposerFeature.register(container);
|
|
9
9
|
container.register(GraphQLEngine);
|
|
10
|
-
container.register(
|
|
10
|
+
container.register(GraphQLRouteDefinition);
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
13
|
export { GraphQLEngineFeature };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine/GraphQLEngineFeature.js","sources":["../../src/engine/GraphQLEngineFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { GraphQLSchemaComposerFeature } from \"~/features/GraphQLSchemaBuilder/feature.js\";\nimport { GraphQLEngine } from \"./GraphQLEngine.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"engine/GraphQLEngineFeature.js","sources":["../../src/engine/GraphQLEngineFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\n\nimport { GraphQLSchemaComposerFeature } from \"~/features/GraphQLSchemaBuilder/feature.js\";\nimport { GraphQLEngine } from \"./GraphQLEngine.js\";\nimport { GraphQLRouteDefinition } from \"./GraphQLRoute.js\";\n\nexport const GraphQLEngineFeature = createFeature({\n name: \"GraphQLEngine\",\n register(container) {\n GraphQLSchemaComposerFeature.register(container);\n container.register(GraphQLEngine);\n container.register(GraphQLRouteDefinition);\n }\n});\n"],"names":["GraphQLEngineFeature","createFeature","container","GraphQLSchemaComposerFeature","GraphQLEngine","GraphQLRouteDefinition"],"mappings":";;;;AAMO,MAAMA,uBAAuBC,cAAc;IAC9C,MAAM;IACN,UAASC,SAAS;QACdC,6BAA6B,QAAQ,CAACD;QACtCA,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;IACvB;AACJ"}
|
package/engine/GraphQLRoute.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpRouteDefinition, HttpRouteHandler } from "@webiny/event-handler-core";
|
|
2
2
|
import type { IHttpRequest, IHttpResponse } from "@webiny/event-handler-core";
|
|
3
3
|
import type { IGraphQLEngine } from "./abstractions.js";
|
|
4
|
-
declare class GraphQLRouteImpl implements
|
|
4
|
+
declare class GraphQLRouteImpl implements HttpRouteHandler.Interface {
|
|
5
5
|
private engine;
|
|
6
|
-
readonly method = "POST";
|
|
7
|
-
readonly path = "/graphql";
|
|
8
6
|
constructor(engine: IGraphQLEngine);
|
|
9
7
|
handle(request: IHttpRequest): Promise<IHttpResponse>;
|
|
10
8
|
}
|
|
11
9
|
export declare const GraphQLRoute: typeof GraphQLRouteImpl & {
|
|
12
10
|
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core").IHttpRoute>;
|
|
13
11
|
};
|
|
12
|
+
declare class GraphQLRouteDefinitionImpl implements HttpRouteDefinition.Interface {
|
|
13
|
+
readonly name = "graphql";
|
|
14
|
+
readonly method = "POST";
|
|
15
|
+
readonly path = "/graphql";
|
|
16
|
+
readonly handler: typeof GraphQLRouteImpl & {
|
|
17
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core").IHttpRoute>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** What the router matches on. Zero dependencies, so building it costs nothing. */
|
|
21
|
+
export declare const GraphQLRouteDefinition: typeof GraphQLRouteDefinitionImpl & {
|
|
22
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core").IHttpRouteDefinition>;
|
|
23
|
+
};
|
|
14
24
|
export {};
|
package/engine/GraphQLRoute.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HttpRouteDefinition, HttpRouteHandler } from "@webiny/event-handler-core";
|
|
2
2
|
import { GraphQLEngine } from "./abstractions.js";
|
|
3
3
|
class GraphQLRouteImpl {
|
|
4
4
|
constructor(engine){
|
|
5
5
|
this.engine = engine;
|
|
6
|
-
this.method = "POST";
|
|
7
|
-
this.path = "/graphql";
|
|
8
6
|
}
|
|
9
7
|
async handle(request) {
|
|
10
8
|
const result = await this.engine.execute(request.body);
|
|
@@ -17,12 +15,24 @@ class GraphQLRouteImpl {
|
|
|
17
15
|
};
|
|
18
16
|
}
|
|
19
17
|
}
|
|
20
|
-
const GraphQLRoute =
|
|
18
|
+
const GraphQLRoute = HttpRouteHandler.createImplementation({
|
|
21
19
|
implementation: GraphQLRouteImpl,
|
|
22
20
|
dependencies: [
|
|
23
21
|
GraphQLEngine
|
|
24
22
|
]
|
|
25
23
|
});
|
|
26
|
-
|
|
24
|
+
class GraphQLRouteDefinitionImpl {
|
|
25
|
+
constructor(){
|
|
26
|
+
this.name = "graphql";
|
|
27
|
+
this.method = "POST";
|
|
28
|
+
this.path = "/graphql";
|
|
29
|
+
this.handler = GraphQLRoute;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const GraphQLRouteDefinition = HttpRouteDefinition.createImplementation({
|
|
33
|
+
implementation: GraphQLRouteDefinitionImpl,
|
|
34
|
+
dependencies: []
|
|
35
|
+
});
|
|
36
|
+
export { GraphQLRoute, GraphQLRouteDefinition };
|
|
27
37
|
|
|
28
38
|
//# sourceMappingURL=GraphQLRoute.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine/GraphQLRoute.js","sources":["../../src/engine/GraphQLRoute.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"engine/GraphQLRoute.js","sources":["../../src/engine/GraphQLRoute.ts"],"sourcesContent":["import { HttpRouteDefinition, HttpRouteHandler } from \"@webiny/event-handler-core\";\nimport { GraphQLEngine } from \"./abstractions.js\";\nimport type { IHttpRequest, IHttpResponse } from \"@webiny/event-handler-core\";\nimport type { IGraphQLEngine } from \"./abstractions.js\";\n\nclass GraphQLRouteImpl implements HttpRouteHandler.Interface {\n constructor(private engine: IGraphQLEngine) {}\n\n async handle(request: IHttpRequest): Promise<IHttpResponse> {\n const result = await this.engine.execute(request.body);\n return {\n statusCode: 200,\n headers: { \"Content-Type\": \"application/json\" },\n body: result\n };\n }\n}\n\nexport const GraphQLRoute = HttpRouteHandler.createImplementation({\n implementation: GraphQLRouteImpl,\n dependencies: [GraphQLEngine]\n});\n\nclass GraphQLRouteDefinitionImpl implements HttpRouteDefinition.Interface {\n readonly name = \"graphql\";\n readonly method = \"POST\";\n readonly path = \"/graphql\";\n readonly handler = GraphQLRoute;\n}\n\n/** What the router matches on. Zero dependencies, so building it costs nothing. */\nexport const GraphQLRouteDefinition = HttpRouteDefinition.createImplementation({\n implementation: GraphQLRouteDefinitionImpl,\n dependencies: []\n});\n"],"names":["GraphQLRouteImpl","engine","request","result","GraphQLRoute","HttpRouteHandler","GraphQLEngine","GraphQLRouteDefinitionImpl","GraphQLRouteDefinition","HttpRouteDefinition"],"mappings":";;AAKA,MAAMA;IACF,YAAoBC,MAAsB,CAAE;aAAxBA,MAAM,GAANA;IAAyB;IAE7C,MAAM,OAAOC,OAAqB,EAA0B;QACxD,MAAMC,SAAS,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAACD,QAAQ,IAAI;QACrD,OAAO;YACH,YAAY;YACZ,SAAS;gBAAE,gBAAgB;YAAmB;YAC9C,MAAMC;QACV;IACJ;AACJ;AAEO,MAAMC,eAAeC,iBAAiB,oBAAoB,CAAC;IAC9D,gBAAgBL;IAChB,cAAc;QAACM;KAAc;AACjC;AAEA,MAAMC;;aACO,IAAI,GAAG;aACP,MAAM,GAAG;aACT,IAAI,GAAG;aACP,OAAO,GAAGH;;AACvB;AAGO,MAAMI,yBAAyBC,oBAAoB,oBAAoB,CAAC;IAC3E,gBAAgBF;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register in the ROOT container. The request container registers the GraphQL engine again on every
|
|
3
|
+
* request, so a cache registered there would start empty each time.
|
|
4
|
+
*/
|
|
5
|
+
export declare const GraphQLSchemaCacheFeature: {
|
|
6
|
+
name: string;
|
|
7
|
+
register(container: import("@webiny/di").Container): void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/api";
|
|
2
|
+
import { MemoryGraphQLSchemaCache } from "./MemoryGraphQLSchemaCache.js";
|
|
3
|
+
const GraphQLSchemaCacheFeature = createFeature({
|
|
4
|
+
name: "GraphQLSchemaCache",
|
|
5
|
+
register (container) {
|
|
6
|
+
container.register(MemoryGraphQLSchemaCache).inSingletonScope();
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
export { GraphQLSchemaCacheFeature };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=GraphQLSchemaCacheFeature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine/GraphQLSchemaCacheFeature.js","sources":["../../src/engine/GraphQLSchemaCacheFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { MemoryGraphQLSchemaCache } from \"./MemoryGraphQLSchemaCache.js\";\n\n/**\n * Register in the ROOT container. The request container registers the GraphQL engine again on every\n * request, so a cache registered there would start empty each time.\n */\nexport const GraphQLSchemaCacheFeature = createFeature({\n name: \"GraphQLSchemaCache\",\n register(container) {\n container.register(MemoryGraphQLSchemaCache).inSingletonScope();\n }\n});\n"],"names":["GraphQLSchemaCacheFeature","createFeature","container","MemoryGraphQLSchemaCache"],"mappings":";;AAOO,MAAMA,4BAA4BC,cAAc;IACnD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,0BAA0B,gBAAgB;IACjE;AACJ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register in the ROOT container of test handlers, next to GraphQLSchemaCacheFeature. Every request
|
|
3
|
+
* then also runs each keyed factory on its own and checks its output against its schema key, so a
|
|
4
|
+
* factory whose key misses a dependency fails the tests instead of serving a stale schema.
|
|
5
|
+
*/
|
|
6
|
+
export declare const GraphQLSchemaKeyVerificationFeature: {
|
|
7
|
+
name: string;
|
|
8
|
+
register(container: import("@webiny/di").Container): void;
|
|
9
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/api";
|
|
2
|
+
import { MemoryGraphQLSchemaKeyVerification } from "./MemoryGraphQLSchemaKeyVerification.js";
|
|
3
|
+
const GraphQLSchemaKeyVerificationFeature = createFeature({
|
|
4
|
+
name: "GraphQLSchemaKeyVerification",
|
|
5
|
+
register (container) {
|
|
6
|
+
container.register(MemoryGraphQLSchemaKeyVerification).inSingletonScope();
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
export { GraphQLSchemaKeyVerificationFeature };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=GraphQLSchemaKeyVerificationFeature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine/GraphQLSchemaKeyVerificationFeature.js","sources":["../../src/engine/GraphQLSchemaKeyVerificationFeature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { MemoryGraphQLSchemaKeyVerification } from \"./MemoryGraphQLSchemaKeyVerification.js\";\n\n/**\n * Register in the ROOT container of test handlers, next to GraphQLSchemaCacheFeature. Every request\n * then also runs each keyed factory on its own and checks its output against its schema key, so a\n * factory whose key misses a dependency fails the tests instead of serving a stale schema.\n */\nexport const GraphQLSchemaKeyVerificationFeature = createFeature({\n name: \"GraphQLSchemaKeyVerification\",\n register(container) {\n container.register(MemoryGraphQLSchemaKeyVerification).inSingletonScope();\n }\n});\n"],"names":["GraphQLSchemaKeyVerificationFeature","createFeature","container","MemoryGraphQLSchemaKeyVerification"],"mappings":";;AAQO,MAAMA,sCAAsCC,cAAc;IAC7D,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,oCAAoC,gBAAgB;IAC3E;AACJ"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql";
|
|
2
|
+
import { GraphQLSchemaCache as Abstraction } from "./abstractions.js";
|
|
3
|
+
/**
|
|
4
|
+
* Keeps the most recently used schemas in memory, dropping the least recently used one once it holds
|
|
5
|
+
* `MAX_ENTRIES`. It stores the build itself, so concurrent requests for a missing key share one
|
|
6
|
+
* build, and a failed build is forgotten.
|
|
7
|
+
*/
|
|
8
|
+
declare class MemoryGraphQLSchemaCacheImpl implements Abstraction.Interface {
|
|
9
|
+
private readonly schemas;
|
|
10
|
+
getOrBuild(key: string, build: () => Promise<GraphQLSchema>): Promise<GraphQLSchema>;
|
|
11
|
+
}
|
|
12
|
+
export declare const MemoryGraphQLSchemaCache: typeof MemoryGraphQLSchemaCacheImpl & {
|
|
13
|
+
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IGraphQLSchemaCache>;
|
|
14
|
+
};
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GraphQLSchemaCache } from "./abstractions.js";
|
|
2
|
+
const MAX_ENTRIES = 10;
|
|
3
|
+
class MemoryGraphQLSchemaCacheImpl {
|
|
4
|
+
getOrBuild(key, build) {
|
|
5
|
+
const cached = this.schemas.get(key);
|
|
6
|
+
if (cached) {
|
|
7
|
+
this.schemas.delete(key);
|
|
8
|
+
this.schemas.set(key, cached);
|
|
9
|
+
return cached;
|
|
10
|
+
}
|
|
11
|
+
const schema = build().catch((error)=>{
|
|
12
|
+
if (this.schemas.get(key) === schema) this.schemas.delete(key);
|
|
13
|
+
throw error;
|
|
14
|
+
});
|
|
15
|
+
this.schemas.set(key, schema);
|
|
16
|
+
if (this.schemas.size > MAX_ENTRIES) {
|
|
17
|
+
const oldestKey = this.schemas.keys().next().value;
|
|
18
|
+
this.schemas.delete(oldestKey);
|
|
19
|
+
}
|
|
20
|
+
return schema;
|
|
21
|
+
}
|
|
22
|
+
constructor(){
|
|
23
|
+
this.schemas = new Map();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const MemoryGraphQLSchemaCache = GraphQLSchemaCache.createImplementation({
|
|
27
|
+
implementation: MemoryGraphQLSchemaCacheImpl,
|
|
28
|
+
dependencies: []
|
|
29
|
+
});
|
|
30
|
+
export { MemoryGraphQLSchemaCache };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=MemoryGraphQLSchemaCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine/MemoryGraphQLSchemaCache.js","sources":["../../src/engine/MemoryGraphQLSchemaCache.ts"],"sourcesContent":["import type { GraphQLSchema } from \"graphql\";\nimport { GraphQLSchemaCache as Abstraction } from \"./abstractions.js\";\n\n/*\n * A built schema is several megabytes. Tenants with the same models produce the same key and share\n * one entry, so a handful of entries covers the usual case without letting a process that serves\n * many differently shaped tenants grow without bound.\n */\nconst MAX_ENTRIES = 10;\n\n/**\n * Keeps the most recently used schemas in memory, dropping the least recently used one once it holds\n * `MAX_ENTRIES`. It stores the build itself, so concurrent requests for a missing key share one\n * build, and a failed build is forgotten.\n */\nclass MemoryGraphQLSchemaCacheImpl implements Abstraction.Interface {\n private readonly schemas = new Map<string, Promise<GraphQLSchema>>();\n\n public getOrBuild(key: string, build: () => Promise<GraphQLSchema>): Promise<GraphQLSchema> {\n const cached = this.schemas.get(key);\n if (cached) {\n // Re-insert, so the map's insertion order doubles as the recently-used order.\n this.schemas.delete(key);\n this.schemas.set(key, cached);\n return cached;\n }\n\n const schema = build().catch(error => {\n if (this.schemas.get(key) === schema) {\n this.schemas.delete(key);\n }\n throw error;\n });\n this.schemas.set(key, schema);\n\n if (this.schemas.size > MAX_ENTRIES) {\n const oldestKey = this.schemas.keys().next().value as string;\n this.schemas.delete(oldestKey);\n }\n\n return schema;\n }\n}\n\nexport const MemoryGraphQLSchemaCache = Abstraction.createImplementation({\n implementation: MemoryGraphQLSchemaCacheImpl,\n dependencies: []\n});\n"],"names":["MAX_ENTRIES","MemoryGraphQLSchemaCacheImpl","key","build","cached","schema","error","oldestKey","Map","MemoryGraphQLSchemaCache","Abstraction"],"mappings":";AAQA,MAAMA,cAAc;AAOpB,MAAMC;IAGK,WAAWC,GAAW,EAAEC,KAAmC,EAA0B;QACxF,MAAMC,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAACF;QAChC,IAAIE,QAAQ;YAER,IAAI,CAAC,OAAO,CAAC,MAAM,CAACF;YACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAACA,KAAKE;YACtB,OAAOA;QACX;QAEA,MAAMC,SAASF,QAAQ,KAAK,CAACG,CAAAA;YACzB,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAACJ,SAASG,QAC1B,IAAI,CAAC,OAAO,CAAC,MAAM,CAACH;YAExB,MAAMI;QACV;QACA,IAAI,CAAC,OAAO,CAAC,GAAG,CAACJ,KAAKG;QAEtB,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAGL,aAAa;YACjC,MAAMO,YAAY,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,KAAK;YAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAACA;QACxB;QAEA,OAAOF;IACX;;aAzBiB,OAAO,GAAG,IAAIG;;AA0BnC;AAEO,MAAMC,2BAA2BC,mBAAAA,oBAAgC,CAAC;IACrE,gBAAgBT;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GraphQLSchemaKeyVerification as Abstraction } from "./abstractions.js";
|
|
2
|
+
/**
|
|
3
|
+
* Remembers the output each factory produced under each schema key, and throws when the same factory
|
|
4
|
+
* produces something else under a key it used before.
|
|
5
|
+
*/
|
|
6
|
+
declare class MemoryGraphQLSchemaKeyVerificationImpl implements Abstraction.Interface {
|
|
7
|
+
private readonly outputs;
|
|
8
|
+
check(params: Abstraction.Check): void;
|
|
9
|
+
}
|
|
10
|
+
export declare const MemoryGraphQLSchemaKeyVerification: typeof MemoryGraphQLSchemaKeyVerificationImpl & {
|
|
11
|
+
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IGraphQLSchemaKeyVerification>;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GraphQLSchemaKeyVerification } from "./abstractions.js";
|
|
2
|
+
class MemoryGraphQLSchemaKeyVerificationImpl {
|
|
3
|
+
check(params) {
|
|
4
|
+
const { factory, key, output } = params;
|
|
5
|
+
const id = JSON.stringify([
|
|
6
|
+
factory,
|
|
7
|
+
key
|
|
8
|
+
]);
|
|
9
|
+
const previous = this.outputs.get(id);
|
|
10
|
+
if (void 0 === previous) return void this.outputs.set(id, output);
|
|
11
|
+
if (previous !== output) throw new Error(`GraphQL schema factory "${factory}" produced a different schema under the same schema key "${key}". Its getSchemaKey() is missing something its output depends on.`);
|
|
12
|
+
}
|
|
13
|
+
constructor(){
|
|
14
|
+
this.outputs = new Map();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const MemoryGraphQLSchemaKeyVerification = GraphQLSchemaKeyVerification.createImplementation({
|
|
18
|
+
implementation: MemoryGraphQLSchemaKeyVerificationImpl,
|
|
19
|
+
dependencies: []
|
|
20
|
+
});
|
|
21
|
+
export { MemoryGraphQLSchemaKeyVerification };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=MemoryGraphQLSchemaKeyVerification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine/MemoryGraphQLSchemaKeyVerification.js","sources":["../../src/engine/MemoryGraphQLSchemaKeyVerification.ts"],"sourcesContent":["import { GraphQLSchemaKeyVerification as Abstraction } from \"./abstractions.js\";\n\n/**\n * Remembers the output each factory produced under each schema key, and throws when the same factory\n * produces something else under a key it used before.\n */\nclass MemoryGraphQLSchemaKeyVerificationImpl implements Abstraction.Interface {\n private readonly outputs = new Map<string, string>();\n\n public check(params: Abstraction.Check): void {\n const { factory, key, output } = params;\n const id = JSON.stringify([factory, key]);\n const previous = this.outputs.get(id);\n\n if (previous === undefined) {\n this.outputs.set(id, output);\n return;\n }\n\n if (previous !== output) {\n throw new Error(\n `GraphQL schema factory \"${factory}\" produced a different schema under the same schema key \"${key}\". Its getSchemaKey() is missing something its output depends on.`\n );\n }\n }\n}\n\nexport const MemoryGraphQLSchemaKeyVerification = Abstraction.createImplementation({\n implementation: MemoryGraphQLSchemaKeyVerificationImpl,\n dependencies: []\n});\n"],"names":["MemoryGraphQLSchemaKeyVerificationImpl","params","factory","key","output","id","JSON","previous","undefined","Error","Map","MemoryGraphQLSchemaKeyVerification","Abstraction"],"mappings":";AAMA,MAAMA;IAGK,MAAMC,MAAyB,EAAQ;QAC1C,MAAM,EAAEC,OAAO,EAAEC,GAAG,EAAEC,MAAM,EAAE,GAAGH;QACjC,MAAMI,KAAKC,KAAK,SAAS,CAAC;YAACJ;YAASC;SAAI;QACxC,MAAMI,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,CAACF;QAElC,IAAIE,AAAaC,WAAbD,UAAwB,YACxB,IAAI,CAAC,OAAO,CAAC,GAAG,CAACF,IAAID;QAIzB,IAAIG,aAAaH,QACb,MAAM,IAAIK,MACN,CAAC,wBAAwB,EAAEP,QAAQ,yDAAyD,EAAEC,IAAI,iEAAiE,CAAC;IAGhL;;aAjBiB,OAAO,GAAG,IAAIO;;AAkBnC;AAEO,MAAMC,qCAAqCC,6BAAAA,oBAAgC,CAAC;IAC/E,gBAAgBZ;IAChB,cAAc,EAAE;AACpB"}
|
package/engine/abstractions.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { GraphQLSchema } from "graphql";
|
|
1
2
|
export interface IGraphQLEngine {
|
|
2
3
|
execute(body: any): Promise<any>;
|
|
3
4
|
}
|
|
@@ -5,3 +6,31 @@ export declare const GraphQLEngine: import("@webiny/di").Abstraction<IGraphQLEng
|
|
|
5
6
|
export declare namespace GraphQLEngine {
|
|
6
7
|
type Interface = IGraphQLEngine;
|
|
7
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* GraphQLSchemaCache - Keeps built executable schemas across requests, keyed by what they were built
|
|
11
|
+
* from. Register it in the root container, so it lives as long as the handler.
|
|
12
|
+
*/
|
|
13
|
+
export interface IGraphQLSchemaCache {
|
|
14
|
+
getOrBuild(key: string, build: () => Promise<GraphQLSchema>): Promise<GraphQLSchema>;
|
|
15
|
+
}
|
|
16
|
+
export declare const GraphQLSchemaCache: import("@webiny/di").Abstraction<IGraphQLSchemaCache>;
|
|
17
|
+
export declare namespace GraphQLSchemaCache {
|
|
18
|
+
type Interface = IGraphQLSchemaCache;
|
|
19
|
+
}
|
|
20
|
+
export interface IGraphQLSchemaKeyCheck {
|
|
21
|
+
factory: string;
|
|
22
|
+
key: string;
|
|
23
|
+
output: string;
|
|
24
|
+
}
|
|
25
|
+
export interface IGraphQLSchemaKeyVerification {
|
|
26
|
+
check(params: IGraphQLSchemaKeyCheck): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* GraphQLSchemaKeyVerification - Fails when a schema factory produces different output under the same
|
|
30
|
+
* schema key, which would serve a stale schema. Register it in the root container of test handlers.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GraphQLSchemaKeyVerification: import("@webiny/di").Abstraction<IGraphQLSchemaKeyVerification>;
|
|
33
|
+
export declare namespace GraphQLSchemaKeyVerification {
|
|
34
|
+
type Interface = IGraphQLSchemaKeyVerification;
|
|
35
|
+
type Check = IGraphQLSchemaKeyCheck;
|
|
36
|
+
}
|
package/engine/abstractions.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createAbstraction } from "@webiny/feature/api";
|
|
2
2
|
const GraphQLEngine = createAbstraction("GraphQLEngine");
|
|
3
|
-
|
|
3
|
+
const GraphQLSchemaCache = createAbstraction("GraphQLSchemaCache");
|
|
4
|
+
const GraphQLSchemaKeyVerification = createAbstraction("GraphQLSchemaKeyVerification");
|
|
5
|
+
export { GraphQLEngine, GraphQLSchemaCache, GraphQLSchemaKeyVerification };
|
|
4
6
|
|
|
5
7
|
//# sourceMappingURL=abstractions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine/abstractions.js","sources":["../../src/engine/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\nexport interface IGraphQLEngine {\n execute(body: any): Promise<any>;\n}\n\nexport const GraphQLEngine = createAbstraction<IGraphQLEngine>(\"GraphQLEngine\");\n\nexport namespace GraphQLEngine {\n export type Interface = IGraphQLEngine;\n}\n"],"names":["GraphQLEngine","createAbstraction"],"mappings":";
|
|
1
|
+
{"version":3,"file":"engine/abstractions.js","sources":["../../src/engine/abstractions.ts"],"sourcesContent":["import type { GraphQLSchema } from \"graphql\";\nimport { createAbstraction } from \"@webiny/feature/api\";\n\nexport interface IGraphQLEngine {\n execute(body: any): Promise<any>;\n}\n\nexport const GraphQLEngine = createAbstraction<IGraphQLEngine>(\"GraphQLEngine\");\n\nexport namespace GraphQLEngine {\n export type Interface = IGraphQLEngine;\n}\n\n/**\n * GraphQLSchemaCache - Keeps built executable schemas across requests, keyed by what they were built\n * from. Register it in the root container, so it lives as long as the handler.\n */\nexport interface IGraphQLSchemaCache {\n getOrBuild(key: string, build: () => Promise<GraphQLSchema>): Promise<GraphQLSchema>;\n}\n\nexport const GraphQLSchemaCache = createAbstraction<IGraphQLSchemaCache>(\"GraphQLSchemaCache\");\n\nexport namespace GraphQLSchemaCache {\n export type Interface = IGraphQLSchemaCache;\n}\n\nexport interface IGraphQLSchemaKeyCheck {\n factory: string;\n key: string;\n output: string;\n}\n\nexport interface IGraphQLSchemaKeyVerification {\n check(params: IGraphQLSchemaKeyCheck): void;\n}\n\n/**\n * GraphQLSchemaKeyVerification - Fails when a schema factory produces different output under the same\n * schema key, which would serve a stale schema. Register it in the root container of test handlers.\n */\nexport const GraphQLSchemaKeyVerification = createAbstraction<IGraphQLSchemaKeyVerification>(\n \"GraphQLSchemaKeyVerification\"\n);\n\nexport namespace GraphQLSchemaKeyVerification {\n export type Interface = IGraphQLSchemaKeyVerification;\n export type Check = IGraphQLSchemaKeyCheck;\n}\n"],"names":["GraphQLEngine","createAbstraction","GraphQLSchemaCache","GraphQLSchemaKeyVerification"],"mappings":";AAOO,MAAMA,gBAAgBC,kBAAkC;AAcxD,MAAMC,qBAAqBD,kBAAuC;AAoBlE,MAAME,+BAA+BF,kBACxC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IGraphQLSchema } from "../graphql/abstractions.public.js";
|
|
2
|
+
/**
|
|
3
|
+
* The key identifies what an executable schema is built from: the type definitions, which paths
|
|
4
|
+
* have a resolver, and how many decorators each path has. Resolver functions themselves can't be
|
|
5
|
+
* compared, but every one of them resolves its dependencies when it runs, so two requests that
|
|
6
|
+
* produce the same key produce schemas that behave the same.
|
|
7
|
+
*/
|
|
8
|
+
export declare const createSchemaCacheKey: (schema: IGraphQLSchema) => string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const collectResolverPaths = (resolvers, prefix, paths)=>{
|
|
3
|
+
for (const [key, value] of Object.entries(resolvers)){
|
|
4
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
5
|
+
if ("function" == typeof value) paths.push(path);
|
|
6
|
+
else if (value && "object" == typeof value) collectResolverPaths(value, path, paths);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
const createSchemaCacheKey = (schema)=>{
|
|
10
|
+
const resolverPaths = [];
|
|
11
|
+
collectResolverPaths(schema.resolvers ?? {}, "", resolverPaths);
|
|
12
|
+
const decoratorPaths = Object.entries(schema.resolverDecorators ?? {}).map(([path, decorators])=>`${path}:${decorators.length}`);
|
|
13
|
+
const hash = createHash("sha1");
|
|
14
|
+
hash.update(String(schema.typeDefs ?? ""));
|
|
15
|
+
hash.update("\n#resolvers\n");
|
|
16
|
+
hash.update(resolverPaths.sort().join("\n"));
|
|
17
|
+
hash.update("\n#decorators\n");
|
|
18
|
+
hash.update(decoratorPaths.sort().join("\n"));
|
|
19
|
+
return hash.digest("hex");
|
|
20
|
+
};
|
|
21
|
+
export { createSchemaCacheKey };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=createSchemaCacheKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine/createSchemaCacheKey.js","sources":["../../src/engine/createSchemaCacheKey.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\n\nconst collectResolverPaths = (resolvers: Record<string, any>, prefix: string, paths: string[]) => {\n for (const [key, value] of Object.entries(resolvers)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (typeof value === \"function\") {\n paths.push(path);\n } else if (value && typeof value === \"object\") {\n collectResolverPaths(value, path, paths);\n }\n }\n};\n\n/**\n * The key identifies what an executable schema is built from: the type definitions, which paths\n * have a resolver, and how many decorators each path has. Resolver functions themselves can't be\n * compared, but every one of them resolves its dependencies when it runs, so two requests that\n * produce the same key produce schemas that behave the same.\n */\nexport const createSchemaCacheKey = (schema: IGraphQLSchema): string => {\n const resolverPaths: string[] = [];\n collectResolverPaths(schema.resolvers ?? {}, \"\", resolverPaths);\n\n const decoratorPaths = Object.entries(schema.resolverDecorators ?? {}).map(\n ([path, decorators]) => `${path}:${decorators.length}`\n );\n\n const hash = createHash(\"sha1\");\n hash.update(String(schema.typeDefs ?? \"\"));\n hash.update(\"\\n#resolvers\\n\");\n hash.update(resolverPaths.sort().join(\"\\n\"));\n hash.update(\"\\n#decorators\\n\");\n hash.update(decoratorPaths.sort().join(\"\\n\"));\n\n return hash.digest(\"hex\");\n};\n"],"names":["collectResolverPaths","resolvers","prefix","paths","key","value","Object","path","createSchemaCacheKey","schema","resolverPaths","decoratorPaths","decorators","hash","createHash","String"],"mappings":";AAGA,MAAMA,uBAAuB,CAACC,WAAgCC,QAAgBC;IAC1E,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAO,OAAO,CAACL,WAAY;QAClD,MAAMM,OAAOL,SAAS,GAAGA,OAAO,CAAC,EAAEE,KAAK,GAAGA;QAC3C,IAAI,AAAiB,cAAjB,OAAOC,OACPF,MAAM,IAAI,CAACI;aACR,IAAIF,SAAS,AAAiB,YAAjB,OAAOA,OACvBL,qBAAqBK,OAAOE,MAAMJ;IAE1C;AACJ;AAQO,MAAMK,uBAAuB,CAACC;IACjC,MAAMC,gBAA0B,EAAE;IAClCV,qBAAqBS,OAAO,SAAS,IAAI,CAAC,GAAG,IAAIC;IAEjD,MAAMC,iBAAiBL,OAAO,OAAO,CAACG,OAAO,kBAAkB,IAAI,CAAC,GAAG,GAAG,CACtE,CAAC,CAACF,MAAMK,WAAW,GAAK,GAAGL,KAAK,CAAC,EAAEK,WAAW,MAAM,EAAE;IAG1D,MAAMC,OAAOC,WAAW;IACxBD,KAAK,MAAM,CAACE,OAAON,OAAO,QAAQ,IAAI;IACtCI,KAAK,MAAM,CAAC;IACZA,KAAK,MAAM,CAACH,cAAc,IAAI,GAAG,IAAI,CAAC;IACtCG,KAAK,MAAM,CAAC;IACZA,KAAK,MAAM,CAACF,eAAe,IAAI,GAAG,IAAI,CAAC;IAEvC,OAAOE,KAAK,MAAM,CAAC;AACvB"}
|
package/engine/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./abstractions.js";
|
|
2
|
-
export * from "./GraphQLContextEnhancer.js";
|
|
3
|
-
export * from "./GraphQLContextualSchema.js";
|
|
4
2
|
export * from "./GraphQLRoute.js";
|
|
5
3
|
export * from "./GraphQLEngineFeature.js";
|
|
4
|
+
export * from "./GraphQLSchemaCacheFeature.js";
|
|
5
|
+
export * from "./GraphQLSchemaKeyVerificationFeature.js";
|
package/engine/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from "./abstractions.js";
|
|
2
|
-
export * from "./GraphQLContextEnhancer.js";
|
|
3
|
-
export * from "./GraphQLContextualSchema.js";
|
|
4
2
|
export * from "./GraphQLRoute.js";
|
|
5
3
|
export * from "./GraphQLEngineFeature.js";
|
|
4
|
+
export * from "./GraphQLSchemaCacheFeature.js";
|
|
5
|
+
export * from "./GraphQLSchemaKeyVerificationFeature.js";
|
package/exports/api/graphql.d.ts
CHANGED
package/exports/api/graphql.js
CHANGED
|
@@ -8,10 +8,11 @@ class GraphQLSchemaBuilder {
|
|
|
8
8
|
const pathParts = path.split(".");
|
|
9
9
|
const graphqlResolver = (parent, args, context, info)=>{
|
|
10
10
|
const resolvedDeps = dependencies.map((dep)=>{
|
|
11
|
-
const [abstraction] = Array.isArray(dep) ? dep : [
|
|
12
|
-
dep
|
|
11
|
+
const [abstraction, options] = Array.isArray(dep) ? dep : [
|
|
12
|
+
dep,
|
|
13
|
+
void 0
|
|
13
14
|
];
|
|
14
|
-
return context.container.resolve(abstraction);
|
|
15
|
+
return options?.multiple ? context.container.resolveAll(abstraction) : context.container.resolve(abstraction);
|
|
15
16
|
});
|
|
16
17
|
const actualResolver = resolver(...resolvedDeps);
|
|
17
18
|
return actualResolver({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.js","sources":["../../../src/features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.ts"],"sourcesContent":["import type { GraphQLSchemaBuilder as Abstraction } from \"./abstractions.js\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\nimport type { ResolverDecorator, ResolverDecorators, Resolvers } from \"~/types.js\";\nimport type { Dependency } from \"@webiny/di\";\n\nexport class GraphQLSchemaBuilder implements Abstraction.Interface {\n private readonly typeDefsArray: string[] = [];\n private readonly resolvers: Resolvers<any> = {};\n private readonly resolverDecorators: ResolverDecorators = {};\n\n addTypeDefs(typeDefs: string): this {\n this.typeDefsArray.push(typeDefs);\n return this;\n }\n\n addResolver<TArgs = any>(config: Abstraction.Config<TArgs>): this {\n const { path, dependencies = [], resolver } = config;\n\n const pathParts = path.split(\".\");\n\n const graphqlResolver = (parent: any, args: TArgs, context: any, info: any) => {\n const resolvedDeps = dependencies.map((dep: Dependency) => {\n const [abstraction] = Array.isArray(dep) ? dep : [dep];\n return context.container.resolve(abstraction);\n });\n\n const actualResolver = resolver(...resolvedDeps);\n\n return actualResolver({ parent, args, context, info });\n };\n\n this.setResolverAtPath(pathParts, graphqlResolver);\n\n return this;\n }\n\n addLegacyResolvers(resolvers: Record<string, any>, prefix = \"\"): this {\n for (const [key, value] of Object.entries(resolvers)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (typeof value === \"function\") {\n const fn = value;\n this.addResolver({\n path,\n dependencies: [],\n resolver:\n () =>\n ({ parent, args, context, info }: any) =>\n fn(parent, args, context, info)\n });\n } else if (typeof value === \"object\" && value !== null) {\n this.addLegacyResolvers(value, path);\n }\n }\n return this;\n }\n\n /**\n * @internal This method needs revisiting, to align with DI concepts.\n */\n addResolverDecorator(path: string, decorator: ResolverDecorator): this {\n if (!this.resolverDecorators[path]) {\n this.resolverDecorators[path] = [];\n }\n this.resolverDecorators[path].push(decorator);\n return this;\n }\n\n build(): IGraphQLSchema {\n return {\n typeDefs: this.typeDefsArray.join(\"\\n\"),\n resolvers: this.resolvers,\n resolverDecorators: this.resolverDecorators\n };\n }\n\n private setResolverAtPath(pathParts: string[], resolver: any): void {\n let current: any = this.resolvers;\n\n for (let i = 0; i < pathParts.length - 1; i++) {\n const part = pathParts[i];\n if (!current[part]) {\n current[part] = {};\n }\n current = current[part];\n }\n\n const finalKey = pathParts[pathParts.length - 1];\n current[finalKey] = resolver;\n }\n}\n"],"names":["GraphQLSchemaBuilder","typeDefs","config","path","dependencies","resolver","pathParts","graphqlResolver","parent","args","context","info","resolvedDeps","dep","abstraction","Array","actualResolver","resolvers","prefix","key","value","Object","fn","decorator","current","i","part","finalKey"],"mappings":"AAKO,MAAMA;IAKT,YAAYC,QAAgB,EAAQ;QAChC,IAAI,CAAC,aAAa,CAAC,IAAI,CAACA;QACxB,OAAO,IAAI;IACf;IAEA,YAAyBC,MAAiC,EAAQ;QAC9D,MAAM,EAAEC,IAAI,EAAEC,eAAe,EAAE,EAAEC,QAAQ,EAAE,GAAGH;QAE9C,MAAMI,YAAYH,KAAK,KAAK,CAAC;QAE7B,MAAMI,kBAAkB,CAACC,QAAaC,MAAaC,SAAcC;YAC7D,MAAMC,eAAeR,aAAa,GAAG,CAAC,CAACS;
|
|
1
|
+
{"version":3,"file":"features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.js","sources":["../../../src/features/GraphQLSchemaBuilder/GraphQLSchemaBuilder.ts"],"sourcesContent":["import type { GraphQLSchemaBuilder as Abstraction } from \"./abstractions.js\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\nimport type { ResolverDecorator, ResolverDecorators, Resolvers } from \"~/types.js\";\nimport type { Dependency } from \"@webiny/di\";\n\nexport class GraphQLSchemaBuilder implements Abstraction.Interface {\n private readonly typeDefsArray: string[] = [];\n private readonly resolvers: Resolvers<any> = {};\n private readonly resolverDecorators: ResolverDecorators = {};\n\n addTypeDefs(typeDefs: string): this {\n this.typeDefsArray.push(typeDefs);\n return this;\n }\n\n addResolver<TArgs = any>(config: Abstraction.Config<TArgs>): this {\n const { path, dependencies = [], resolver } = config;\n\n const pathParts = path.split(\".\");\n\n const graphqlResolver = (parent: any, args: TArgs, context: any, info: any) => {\n const resolvedDeps = dependencies.map((dep: Dependency) => {\n /*\n * `Dependency` allows `[abstraction, { multiple: true }]`, same as anywhere else in\n * the container. This used to destructure the tuple, throw the options away and\n * always single-resolve, so a resolver asking for every implementation of an\n * abstraction received one object and failed on `.map`.\n */\n const [abstraction, options] = Array.isArray(dep) ? dep : [dep, undefined];\n\n return options?.multiple\n ? context.container.resolveAll(abstraction)\n : context.container.resolve(abstraction);\n });\n\n const actualResolver = resolver(...resolvedDeps);\n\n return actualResolver({ parent, args, context, info });\n };\n\n this.setResolverAtPath(pathParts, graphqlResolver);\n\n return this;\n }\n\n addLegacyResolvers(resolvers: Record<string, any>, prefix = \"\"): this {\n for (const [key, value] of Object.entries(resolvers)) {\n const path = prefix ? `${prefix}.${key}` : key;\n if (typeof value === \"function\") {\n const fn = value;\n this.addResolver({\n path,\n dependencies: [],\n resolver:\n () =>\n ({ parent, args, context, info }: any) =>\n fn(parent, args, context, info)\n });\n } else if (typeof value === \"object\" && value !== null) {\n this.addLegacyResolvers(value, path);\n }\n }\n return this;\n }\n\n /**\n * @internal This method needs revisiting, to align with DI concepts.\n */\n addResolverDecorator(path: string, decorator: ResolverDecorator): this {\n if (!this.resolverDecorators[path]) {\n this.resolverDecorators[path] = [];\n }\n this.resolverDecorators[path].push(decorator);\n return this;\n }\n\n build(): IGraphQLSchema {\n return {\n typeDefs: this.typeDefsArray.join(\"\\n\"),\n resolvers: this.resolvers,\n resolverDecorators: this.resolverDecorators\n };\n }\n\n private setResolverAtPath(pathParts: string[], resolver: any): void {\n let current: any = this.resolvers;\n\n for (let i = 0; i < pathParts.length - 1; i++) {\n const part = pathParts[i];\n if (!current[part]) {\n current[part] = {};\n }\n current = current[part];\n }\n\n const finalKey = pathParts[pathParts.length - 1];\n current[finalKey] = resolver;\n }\n}\n"],"names":["GraphQLSchemaBuilder","typeDefs","config","path","dependencies","resolver","pathParts","graphqlResolver","parent","args","context","info","resolvedDeps","dep","abstraction","options","Array","undefined","actualResolver","resolvers","prefix","key","value","Object","fn","decorator","current","i","part","finalKey"],"mappings":"AAKO,MAAMA;IAKT,YAAYC,QAAgB,EAAQ;QAChC,IAAI,CAAC,aAAa,CAAC,IAAI,CAACA;QACxB,OAAO,IAAI;IACf;IAEA,YAAyBC,MAAiC,EAAQ;QAC9D,MAAM,EAAEC,IAAI,EAAEC,eAAe,EAAE,EAAEC,QAAQ,EAAE,GAAGH;QAE9C,MAAMI,YAAYH,KAAK,KAAK,CAAC;QAE7B,MAAMI,kBAAkB,CAACC,QAAaC,MAAaC,SAAcC;YAC7D,MAAMC,eAAeR,aAAa,GAAG,CAAC,CAACS;gBAOnC,MAAM,CAACC,aAAaC,QAAQ,GAAGC,MAAM,OAAO,CAACH,OAAOA,MAAM;oBAACA;oBAAKI;iBAAU;gBAE1E,OAAOF,SAAS,WACVL,QAAQ,SAAS,CAAC,UAAU,CAACI,eAC7BJ,QAAQ,SAAS,CAAC,OAAO,CAACI;YACpC;YAEA,MAAMI,iBAAiBb,YAAYO;YAEnC,OAAOM,eAAe;gBAAEV;gBAAQC;gBAAMC;gBAASC;YAAK;QACxD;QAEA,IAAI,CAAC,iBAAiB,CAACL,WAAWC;QAElC,OAAO,IAAI;IACf;IAEA,mBAAmBY,SAA8B,EAAEC,SAAS,EAAE,EAAQ;QAClE,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAO,OAAO,CAACJ,WAAY;YAClD,MAAMhB,OAAOiB,SAAS,GAAGA,OAAO,CAAC,EAAEC,KAAK,GAAGA;YAC3C,IAAI,AAAiB,cAAjB,OAAOC,OAAsB;gBAC7B,MAAME,KAAKF;gBACX,IAAI,CAAC,WAAW,CAAC;oBACbnB;oBACA,cAAc,EAAE;oBAChB,UACI,IACA,CAAC,EAAEK,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,IAAI,EAAO,GACjCa,GAAGhB,QAAQC,MAAMC,SAASC;gBACtC;YACJ,OAAO,IAAI,AAAiB,YAAjB,OAAOW,SAAsBA,AAAU,SAAVA,OACpC,IAAI,CAAC,kBAAkB,CAACA,OAAOnB;QAEvC;QACA,OAAO,IAAI;IACf;IAKA,qBAAqBA,IAAY,EAAEsB,SAA4B,EAAQ;QACnE,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAACtB,KAAK,EAC9B,IAAI,CAAC,kBAAkB,CAACA,KAAK,GAAG,EAAE;QAEtC,IAAI,CAAC,kBAAkB,CAACA,KAAK,CAAC,IAAI,CAACsB;QACnC,OAAO,IAAI;IACf;IAEA,QAAwB;QACpB,OAAO;YACH,UAAU,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YAClC,WAAW,IAAI,CAAC,SAAS;YACzB,oBAAoB,IAAI,CAAC,kBAAkB;QAC/C;IACJ;IAEQ,kBAAkBnB,SAAmB,EAAED,QAAa,EAAQ;QAChE,IAAIqB,UAAe,IAAI,CAAC,SAAS;QAEjC,IAAK,IAAIC,IAAI,GAAGA,IAAIrB,UAAU,MAAM,GAAG,GAAGqB,IAAK;YAC3C,MAAMC,OAAOtB,SAAS,CAACqB,EAAE;YACzB,IAAI,CAACD,OAAO,CAACE,KAAK,EACdF,OAAO,CAACE,KAAK,GAAG,CAAC;YAErBF,UAAUA,OAAO,CAACE,KAAK;QAC3B;QAEA,MAAMC,WAAWvB,SAAS,CAACA,UAAU,MAAM,GAAG,EAAE;QAChDoB,OAAO,CAACG,SAAS,GAAGxB;IACxB;;aA3FiB,aAAa,GAAa,EAAE;aAC5B,SAAS,GAAmB,CAAC;aAC7B,kBAAkB,GAAuB,CAAC;;AA0F/D"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { Container } from "@webiny/di";
|
|
2
2
|
import { GraphQLSchemaComposer as Abstraction } from "./abstractions.js";
|
|
3
|
+
import { GraphQLSchemaKeyVerification } from "../../engine/abstractions.js";
|
|
3
4
|
import type { IGraphQLSchema } from "../../graphql/abstractions.public.js";
|
|
4
5
|
declare class GraphQLSchemaComposerImpl implements Abstraction.Interface {
|
|
5
6
|
private container;
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
private verification;
|
|
8
|
+
constructor(container: Container, verification: GraphQLSchemaKeyVerification.Interface | undefined);
|
|
9
|
+
build(): Promise<IGraphQLSchema>;
|
|
10
|
+
getSchemaKey(): Promise<string | null>;
|
|
11
|
+
private getFactories;
|
|
8
12
|
}
|
|
9
13
|
export declare const GraphQLSchemaComposer: typeof GraphQLSchemaComposerImpl & {
|
|
10
14
|
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IGraphQLSchemaComposer>;
|
|
@@ -1,24 +1,68 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { RequestContainer } from "@webiny/event-handler-core";
|
|
2
3
|
import { GraphQLSchemaComposer } from "./abstractions.js";
|
|
3
4
|
import { CoreGraphQLSchemaFactory, GraphQLSchemaFactory } from "../../graphql/abstractions.js";
|
|
5
|
+
import { GraphQLSchemaKeyVerification } from "../../engine/abstractions.js";
|
|
6
|
+
import { createSchemaCacheKey } from "../../engine/createSchemaCacheKey.js";
|
|
4
7
|
import { GraphQLSchemaBuilder } from "./GraphQLSchemaBuilder.js";
|
|
8
|
+
function isKeyed(factory) {
|
|
9
|
+
return "function" == typeof factory.getSchemaKey;
|
|
10
|
+
}
|
|
11
|
+
async function getFactoryOutput(factory) {
|
|
12
|
+
const builder = new GraphQLSchemaBuilder();
|
|
13
|
+
await factory.execute(builder);
|
|
14
|
+
const schema = builder.build();
|
|
15
|
+
return createSchemaCacheKey(schema);
|
|
16
|
+
}
|
|
17
|
+
async function verifyKeys(verification, factories, keys) {
|
|
18
|
+
for(let i = 0; i < factories.length; i++){
|
|
19
|
+
const output = await getFactoryOutput(factories[i]);
|
|
20
|
+
verification.check({
|
|
21
|
+
factory: factories[i].constructor.name,
|
|
22
|
+
key: keys[i],
|
|
23
|
+
output
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
5
27
|
class GraphQLSchemaComposerImpl {
|
|
6
|
-
constructor(container){
|
|
28
|
+
constructor(container, verification){
|
|
7
29
|
this.container = container;
|
|
30
|
+
this.verification = verification;
|
|
8
31
|
}
|
|
9
|
-
async build(
|
|
32
|
+
async build() {
|
|
10
33
|
const builder = new GraphQLSchemaBuilder();
|
|
34
|
+
for (const factory of this.getFactories())await factory.execute(builder);
|
|
35
|
+
return builder.build();
|
|
36
|
+
}
|
|
37
|
+
async getSchemaKey() {
|
|
38
|
+
const factories = this.getFactories();
|
|
39
|
+
if (!factories.every(isKeyed)) return null;
|
|
40
|
+
const pendingKeys = factories.map((factory)=>factory.getSchemaKey());
|
|
41
|
+
const keys = await Promise.all(pendingKeys);
|
|
42
|
+
if (this.verification) await verifyKeys(this.verification, factories, keys);
|
|
43
|
+
const hash = createHash("sha1");
|
|
44
|
+
hash.update(keys.join("\n"));
|
|
45
|
+
return hash.digest("hex");
|
|
46
|
+
}
|
|
47
|
+
getFactories() {
|
|
11
48
|
const coreSchemas = this.container.resolveAll(CoreGraphQLSchemaFactory);
|
|
12
49
|
const userSchemas = this.container.resolveAll(GraphQLSchemaFactory);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
50
|
+
return [
|
|
51
|
+
...coreSchemas,
|
|
52
|
+
...userSchemas
|
|
53
|
+
];
|
|
16
54
|
}
|
|
17
55
|
}
|
|
18
56
|
const GraphQLSchemaComposer_GraphQLSchemaComposer = GraphQLSchemaComposer.createImplementation({
|
|
19
57
|
implementation: GraphQLSchemaComposerImpl,
|
|
20
58
|
dependencies: [
|
|
21
|
-
RequestContainer
|
|
59
|
+
RequestContainer,
|
|
60
|
+
[
|
|
61
|
+
GraphQLSchemaKeyVerification,
|
|
62
|
+
{
|
|
63
|
+
optional: true
|
|
64
|
+
}
|
|
65
|
+
]
|
|
22
66
|
]
|
|
23
67
|
});
|
|
24
68
|
export { GraphQLSchemaComposer_GraphQLSchemaComposer as GraphQLSchemaComposer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/GraphQLSchemaBuilder/GraphQLSchemaComposer.js","sources":["../../../src/features/GraphQLSchemaBuilder/GraphQLSchemaComposer.ts"],"sourcesContent":["import { Container } from \"@webiny/di\";\nimport { RequestContainer } from \"@webiny/event-handler-core\";\nimport { GraphQLSchemaComposer as Abstraction } from \"./abstractions.js\";\nimport { GraphQLSchemaFactory, CoreGraphQLSchemaFactory } from \"~/graphql/abstractions.js\";\nimport { GraphQLSchemaBuilder } from \"./GraphQLSchemaBuilder.js\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\n\
|
|
1
|
+
{"version":3,"file":"features/GraphQLSchemaBuilder/GraphQLSchemaComposer.js","sources":["../../../src/features/GraphQLSchemaBuilder/GraphQLSchemaComposer.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { Container } from \"@webiny/di\";\nimport { RequestContainer } from \"@webiny/event-handler-core\";\nimport { GraphQLSchemaComposer as Abstraction } from \"./abstractions.js\";\nimport { GraphQLSchemaFactory, CoreGraphQLSchemaFactory } from \"~/graphql/abstractions.js\";\nimport { GraphQLSchemaKeyVerification } from \"~/engine/abstractions.js\";\nimport { createSchemaCacheKey } from \"~/engine/createSchemaCacheKey.js\";\nimport { GraphQLSchemaBuilder } from \"./GraphQLSchemaBuilder.js\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\n\ntype SchemaFactory = CoreGraphQLSchemaFactory.Interface | GraphQLSchemaFactory.Interface;\n\ntype KeyedSchemaFactory = SchemaFactory & {\n getSchemaKey(): string | Promise<string>;\n};\n\nfunction isKeyed(factory: SchemaFactory): factory is KeyedSchemaFactory {\n return typeof factory.getSchemaKey === \"function\";\n}\n\n// The output of one factory on its own, reduced to the same key the engine uses for a full schema.\nasync function getFactoryOutput(factory: SchemaFactory): Promise<string> {\n const builder = new GraphQLSchemaBuilder();\n await factory.execute(builder);\n const schema = builder.build();\n return createSchemaCacheKey(schema);\n}\n\n// Runs each factory on its own and checks its output against its schema key.\nasync function verifyKeys(\n verification: GraphQLSchemaKeyVerification.Interface,\n factories: SchemaFactory[],\n keys: string[]\n): Promise<void> {\n for (let i = 0; i < factories.length; i++) {\n const output = await getFactoryOutput(factories[i]);\n verification.check({\n factory: factories[i].constructor.name,\n key: keys[i],\n output\n });\n }\n}\n\nclass GraphQLSchemaComposerImpl implements Abstraction.Interface {\n constructor(\n private container: Container,\n private verification: GraphQLSchemaKeyVerification.Interface | undefined\n ) {}\n\n async build(): Promise<IGraphQLSchema> {\n const builder = new GraphQLSchemaBuilder();\n\n for (const factory of this.getFactories()) {\n await factory.execute(builder);\n }\n\n return builder.build();\n }\n\n async getSchemaKey(): Promise<string | null> {\n const factories = this.getFactories();\n if (!factories.every(isKeyed)) {\n return null;\n }\n\n const pendingKeys = factories.map(factory => factory.getSchemaKey());\n const keys = await Promise.all(pendingKeys);\n\n if (this.verification) {\n await verifyKeys(this.verification, factories, keys);\n }\n\n const hash = createHash(\"sha1\");\n hash.update(keys.join(\"\\n\"));\n return hash.digest(\"hex\");\n }\n\n private getFactories(): SchemaFactory[] {\n const coreSchemas = this.container.resolveAll(CoreGraphQLSchemaFactory);\n const userSchemas = this.container.resolveAll(GraphQLSchemaFactory);\n return [...coreSchemas, ...userSchemas];\n }\n}\n\nexport const GraphQLSchemaComposer = Abstraction.createImplementation({\n implementation: GraphQLSchemaComposerImpl,\n dependencies: [RequestContainer, [GraphQLSchemaKeyVerification, { optional: true }]]\n});\n"],"names":["isKeyed","factory","getFactoryOutput","builder","GraphQLSchemaBuilder","schema","createSchemaCacheKey","verifyKeys","verification","factories","keys","i","output","GraphQLSchemaComposerImpl","container","pendingKeys","Promise","hash","createHash","coreSchemas","CoreGraphQLSchemaFactory","userSchemas","GraphQLSchemaFactory","GraphQLSchemaComposer","Abstraction","RequestContainer","GraphQLSchemaKeyVerification"],"mappings":";;;;;;;AAgBA,SAASA,QAAQC,OAAsB;IACnC,OAAO,AAAgC,cAAhC,OAAOA,QAAQ,YAAY;AACtC;AAGA,eAAeC,iBAAiBD,OAAsB;IAClD,MAAME,UAAU,IAAIC;IACpB,MAAMH,QAAQ,OAAO,CAACE;IACtB,MAAME,SAASF,QAAQ,KAAK;IAC5B,OAAOG,qBAAqBD;AAChC;AAGA,eAAeE,WACXC,YAAoD,EACpDC,SAA0B,EAC1BC,IAAc;IAEd,IAAK,IAAIC,IAAI,GAAGA,IAAIF,UAAU,MAAM,EAAEE,IAAK;QACvC,MAAMC,SAAS,MAAMV,iBAAiBO,SAAS,CAACE,EAAE;QAClDH,aAAa,KAAK,CAAC;YACf,SAASC,SAAS,CAACE,EAAE,CAAC,WAAW,CAAC,IAAI;YACtC,KAAKD,IAAI,CAACC,EAAE;YACZC;QACJ;IACJ;AACJ;AAEA,MAAMC;IACF,YACYC,SAAoB,EACpBN,YAAgE,CAC1E;aAFUM,SAAS,GAATA;aACAN,YAAY,GAAZA;IACT;IAEH,MAAM,QAAiC;QACnC,MAAML,UAAU,IAAIC;QAEpB,KAAK,MAAMH,WAAW,IAAI,CAAC,YAAY,GACnC,MAAMA,QAAQ,OAAO,CAACE;QAG1B,OAAOA,QAAQ,KAAK;IACxB;IAEA,MAAM,eAAuC;QACzC,MAAMM,YAAY,IAAI,CAAC,YAAY;QACnC,IAAI,CAACA,UAAU,KAAK,CAACT,UACjB,OAAO;QAGX,MAAMe,cAAcN,UAAU,GAAG,CAACR,CAAAA,UAAWA,QAAQ,YAAY;QACjE,MAAMS,OAAO,MAAMM,QAAQ,GAAG,CAACD;QAE/B,IAAI,IAAI,CAAC,YAAY,EACjB,MAAMR,WAAW,IAAI,CAAC,YAAY,EAAEE,WAAWC;QAGnD,MAAMO,OAAOC,WAAW;QACxBD,KAAK,MAAM,CAACP,KAAK,IAAI,CAAC;QACtB,OAAOO,KAAK,MAAM,CAAC;IACvB;IAEQ,eAAgC;QACpC,MAAME,cAAc,IAAI,CAAC,SAAS,CAAC,UAAU,CAACC;QAC9C,MAAMC,cAAc,IAAI,CAAC,SAAS,CAAC,UAAU,CAACC;QAC9C,OAAO;eAAIH;eAAgBE;SAAY;IAC3C;AACJ;AAEO,MAAME,8CAAwBC,sBAAAA,oBAAgC,CAAC;IAClE,gBAAgBX;IAChB,cAAc;QAACY;QAAkB;YAACC;YAA8B;gBAAE,UAAU;YAAK;SAAE;KAAC;AACxF"}
|
|
@@ -31,7 +31,12 @@ export declare namespace GraphQLSchemaBuilder {
|
|
|
31
31
|
type Config<TArgs = any> = ResolverConfig<TArgs>;
|
|
32
32
|
}
|
|
33
33
|
export interface IGraphQLSchemaComposer {
|
|
34
|
-
build(
|
|
34
|
+
build(): Promise<IGraphQLSchema>;
|
|
35
|
+
/**
|
|
36
|
+
* Combines every factory's schema key. Returns null when any factory has no key, because its
|
|
37
|
+
* output can then only be known by running it.
|
|
38
|
+
*/
|
|
39
|
+
getSchemaKey(): Promise<string | null>;
|
|
35
40
|
}
|
|
36
41
|
export declare const GraphQLSchemaComposer: import("@webiny/di").Abstraction<IGraphQLSchemaComposer>;
|
|
37
42
|
export declare namespace GraphQLSchemaComposer {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/GraphQLSchemaBuilder/abstractions.js","sources":["../../../src/features/GraphQLSchemaBuilder/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { ResolverDecorator, TypeDefs } from \"~/types.js\";\nimport type { Dependency } from \"@webiny/di\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\n\nexport interface ResolverConfig<TArgs = any, TParent = any> {\n path: string;\n dependencies?: Dependency[];\n resolver: (\n ...resolvedDeps: any[]\n ) => (params: { parent: TParent; args: TArgs; context: any; info: any }) => any;\n}\n\nexport interface IGraphQLSchemaBuilder {\n addTypeDefs(typeDefs: TypeDefs): this;\n addResolver<TArgs = any, TParent = any>(config: ResolverConfig<TArgs, TParent>): this;\n /**\n * Bridge for legacy (parent, args, ctx, info) resolver objects.\n * Walks a nested resolvers map and registers each leaf as an addResolver() call.\n */\n addLegacyResolvers(resolvers: Record<string, any>, prefix?: string): this;\n /**\n * @internal This method needs revisiting, to align with DI concepts.\n */\n addResolverDecorator(path: string, decorator: ResolverDecorator): this;\n build(): IGraphQLSchema;\n}\n\nexport const GraphQLSchemaBuilder =\n createAbstraction<IGraphQLSchemaBuilder>(\"GraphQLSchemaBuilder\");\n\nexport namespace GraphQLSchemaBuilder {\n export type Interface = IGraphQLSchemaBuilder;\n export type Config<TArgs = any> = ResolverConfig<TArgs>;\n}\n\nexport interface IGraphQLSchemaComposer {\n build(
|
|
1
|
+
{"version":3,"file":"features/GraphQLSchemaBuilder/abstractions.js","sources":["../../../src/features/GraphQLSchemaBuilder/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { ResolverDecorator, TypeDefs } from \"~/types.js\";\nimport type { Dependency } from \"@webiny/di\";\nimport type { IGraphQLSchema } from \"~/graphql/abstractions.public.js\";\n\nexport interface ResolverConfig<TArgs = any, TParent = any> {\n path: string;\n dependencies?: Dependency[];\n resolver: (\n ...resolvedDeps: any[]\n ) => (params: { parent: TParent; args: TArgs; context: any; info: any }) => any;\n}\n\nexport interface IGraphQLSchemaBuilder {\n addTypeDefs(typeDefs: TypeDefs): this;\n addResolver<TArgs = any, TParent = any>(config: ResolverConfig<TArgs, TParent>): this;\n /**\n * Bridge for legacy (parent, args, ctx, info) resolver objects.\n * Walks a nested resolvers map and registers each leaf as an addResolver() call.\n */\n addLegacyResolvers(resolvers: Record<string, any>, prefix?: string): this;\n /**\n * @internal This method needs revisiting, to align with DI concepts.\n */\n addResolverDecorator(path: string, decorator: ResolverDecorator): this;\n build(): IGraphQLSchema;\n}\n\nexport const GraphQLSchemaBuilder =\n createAbstraction<IGraphQLSchemaBuilder>(\"GraphQLSchemaBuilder\");\n\nexport namespace GraphQLSchemaBuilder {\n export type Interface = IGraphQLSchemaBuilder;\n export type Config<TArgs = any> = ResolverConfig<TArgs>;\n}\n\nexport interface IGraphQLSchemaComposer {\n build(): Promise<IGraphQLSchema>;\n /**\n * Combines every factory's schema key. Returns null when any factory has no key, because its\n * output can then only be known by running it.\n */\n getSchemaKey(): Promise<string | null>;\n}\n\nexport const GraphQLSchemaComposer =\n createAbstraction<IGraphQLSchemaComposer>(\"GraphQLSchemaComposer\");\n\nexport namespace GraphQLSchemaComposer {\n export type Interface = IGraphQLSchemaComposer;\n}\n"],"names":["GraphQLSchemaBuilder","createAbstraction","GraphQLSchemaComposer"],"mappings":";AA4BO,MAAMA,uBACTC,kBAAyC;AAgBtC,MAAMC,wBACTD,kBAA0C"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { GraphQLSchemaBuilder } from "../features/GraphQLSchemaBuilder/abstractions.js";
|
|
2
|
-
/**
|
|
3
|
-
* Internal — ctx is a legacy bridge, not exposed to public implementors.
|
|
4
|
-
* TODO: remove ctx? once all registerLegacyPluginsViaGqlContextEnhancer callers are migrated —
|
|
5
|
-
* at that point GraphQLSchemaComposer no longer needs to thread ctx through to schema factories.
|
|
6
|
-
*/
|
|
7
2
|
export interface ICoreGraphQLSchemaFactory {
|
|
8
|
-
execute(builder: GraphQLSchemaBuilder.Interface
|
|
3
|
+
execute(builder: GraphQLSchemaBuilder.Interface): Promise<GraphQLSchemaBuilder.Interface>;
|
|
4
|
+
/**
|
|
5
|
+
* Returns what this factory's type definitions and resolver paths depend on: the same key must
|
|
6
|
+
* always mean the same output. When every factory has one, the GraphQL engine runs the factories
|
|
7
|
+
* only if no schema is cached under the combined key yet. Leave it out and the factories run on
|
|
8
|
+
* every request. Use `staticSchemaKey()` for a factory whose output never changes.
|
|
9
|
+
*/
|
|
10
|
+
getSchemaKey?(): string | Promise<string>;
|
|
9
11
|
}
|
|
10
12
|
export declare const CoreGraphQLSchemaFactory: import("@webiny/di").Abstraction<ICoreGraphQLSchemaFactory>;
|
|
11
13
|
export declare namespace CoreGraphQLSchemaFactory {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql/abstractions.core.js","sources":["../../src/graphql/abstractions.core.ts"],"sourcesContent":["/**\n * This file contains abstraction for use by the core Webiny team, or anyone contributing to the webiny-js repository.\n */\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type { GraphQLSchemaBuilder } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\n\n/**\n
|
|
1
|
+
{"version":3,"file":"graphql/abstractions.core.js","sources":["../../src/graphql/abstractions.core.ts"],"sourcesContent":["/**\n * This file contains abstraction for use by the core Webiny team, or anyone contributing to the webiny-js repository.\n */\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type { GraphQLSchemaBuilder } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\n\nexport interface ICoreGraphQLSchemaFactory {\n execute(builder: GraphQLSchemaBuilder.Interface): Promise<GraphQLSchemaBuilder.Interface>;\n /**\n * Returns what this factory's type definitions and resolver paths depend on: the same key must\n * always mean the same output. When every factory has one, the GraphQL engine runs the factories\n * only if no schema is cached under the combined key yet. Leave it out and the factories run on\n * every request. Use `staticSchemaKey()` for a factory whose output never changes.\n */\n getSchemaKey?(): string | Promise<string>;\n}\n\nexport const CoreGraphQLSchemaFactory = createAbstraction<ICoreGraphQLSchemaFactory>(\n \"CoreGraphQLSchemaFactory\"\n);\nexport namespace CoreGraphQLSchemaFactory {\n export type Interface = ICoreGraphQLSchemaFactory;\n export type SchemaBuilder = GraphQLSchemaBuilder.Interface;\n export type Return = Promise<GraphQLSchemaBuilder.Interface>;\n}\n"],"names":["CoreGraphQLSchemaFactory","createAbstraction"],"mappings":";AAiBO,MAAMA,2BAA2BC,kBACpC"}
|
|
@@ -8,6 +8,13 @@ export interface IGraphQLSchema {
|
|
|
8
8
|
/** Define custom GraphQL schema extensions. */
|
|
9
9
|
export interface IGraphQLSchemaFactory {
|
|
10
10
|
execute(builder: GraphQLSchemaBuilder.Interface): Promise<GraphQLSchemaBuilder.Interface>;
|
|
11
|
+
/**
|
|
12
|
+
* Returns what this factory's type definitions and resolver paths depend on: the same key must
|
|
13
|
+
* always mean the same output. When every factory has one, the GraphQL engine runs the factories
|
|
14
|
+
* only if no schema is cached under the combined key yet. Leave it out and the factories run on
|
|
15
|
+
* every request. Use `staticSchemaKey()` for a factory whose output never changes.
|
|
16
|
+
*/
|
|
17
|
+
getSchemaKey?(): string | Promise<string>;
|
|
11
18
|
}
|
|
12
19
|
/** Define custom GraphQL schema extensions. */
|
|
13
20
|
export declare const GraphQLSchemaFactory: import("@webiny/di").Abstraction<IGraphQLSchemaFactory>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql/abstractions.public.js","sources":["../../src/graphql/abstractions.public.ts"],"sourcesContent":["/**\n * This file contains abstraction for use by third party developers.\n */\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type {\n Resolvers as IResolvers,\n TypeDefs as ITypeDefs,\n ResolverDecorators as IResolverDecorators\n} from \"~/types.js\";\nimport type { GraphQLSchemaBuilder } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\n\nexport interface IGraphQLSchema {\n typeDefs?: ITypeDefs;\n resolvers?: IResolvers<any>;\n resolverDecorators?: IResolverDecorators;\n}\n\n/** Define custom GraphQL schema extensions. */\nexport interface IGraphQLSchemaFactory {\n execute(builder: GraphQLSchemaBuilder.Interface): Promise<GraphQLSchemaBuilder.Interface>;\n}\n\n/** Define custom GraphQL schema extensions. */\nexport const GraphQLSchemaFactory =\n createAbstraction<IGraphQLSchemaFactory>(\"GraphQLSchemaFactory\");\n\nexport namespace GraphQLSchemaFactory {\n export type Interface = IGraphQLSchemaFactory;\n export type SchemaBuilder = GraphQLSchemaBuilder.Interface;\n export type Return = Promise<GraphQLSchemaBuilder.Interface>;\n}\n"],"names":["GraphQLSchemaFactory","createAbstraction"],"mappings":";
|
|
1
|
+
{"version":3,"file":"graphql/abstractions.public.js","sources":["../../src/graphql/abstractions.public.ts"],"sourcesContent":["/**\n * This file contains abstraction for use by third party developers.\n */\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type {\n Resolvers as IResolvers,\n TypeDefs as ITypeDefs,\n ResolverDecorators as IResolverDecorators\n} from \"~/types.js\";\nimport type { GraphQLSchemaBuilder } from \"~/features/GraphQLSchemaBuilder/abstractions.js\";\n\nexport interface IGraphQLSchema {\n typeDefs?: ITypeDefs;\n resolvers?: IResolvers<any>;\n resolverDecorators?: IResolverDecorators;\n}\n\n/** Define custom GraphQL schema extensions. */\nexport interface IGraphQLSchemaFactory {\n execute(builder: GraphQLSchemaBuilder.Interface): Promise<GraphQLSchemaBuilder.Interface>;\n /**\n * Returns what this factory's type definitions and resolver paths depend on: the same key must\n * always mean the same output. When every factory has one, the GraphQL engine runs the factories\n * only if no schema is cached under the combined key yet. Leave it out and the factories run on\n * every request. Use `staticSchemaKey()` for a factory whose output never changes.\n */\n getSchemaKey?(): string | Promise<string>;\n}\n\n/** Define custom GraphQL schema extensions. */\nexport const GraphQLSchemaFactory =\n createAbstraction<IGraphQLSchemaFactory>(\"GraphQLSchemaFactory\");\n\nexport namespace GraphQLSchemaFactory {\n export type Interface = IGraphQLSchemaFactory;\n export type SchemaBuilder = GraphQLSchemaBuilder.Interface;\n export type Return = Promise<GraphQLSchemaBuilder.Interface>;\n}\n"],"names":["GraphQLSchemaFactory","createAbstraction"],"mappings":";AA8BO,MAAMA,uBACTC,kBAAyC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql/staticSchemaKey.js","sources":["../../src/graphql/staticSchemaKey.ts"],"sourcesContent":["/**\n * Schema key for a factory whose type definitions and resolver paths never change. The id only has\n * to be unique among factories.\n */\nexport const staticSchemaKey = (id: string) => {\n return (): string => `static:${id}`;\n};\n"],"names":["staticSchemaKey","id"],"mappings":"AAIO,MAAMA,kBAAkB,CAACC,KACrB,IAAc,CAAC,OAAO,EAAEA,IAAI"}
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export * from "./errors.js";
|
|
2
|
-
export { registerLegacyPluginsViaGqlContextEnhancer } from "./registerLegacyPluginsViaGqlContextEnhancer.js";
|
|
3
2
|
export * from "./responses.js";
|
|
4
3
|
export * from "./utils/index.js";
|
|
5
4
|
export * from "./plugins/index.js";
|
|
@@ -8,5 +7,6 @@ export * from "./processRequestBody.js";
|
|
|
8
7
|
export * from "./createResolverDecorator.js";
|
|
9
8
|
export * from "./ResolverDecoration.js";
|
|
10
9
|
export * from "./engine/index.js";
|
|
10
|
+
export * from "./graphql/staticSchemaKey.js";
|
|
11
11
|
export default _default;
|
|
12
12
|
declare function _default(): never[];
|
package/index.js
CHANGED
|
@@ -7,8 +7,8 @@ export * from "./processRequestBody.js";
|
|
|
7
7
|
export * from "./createResolverDecorator.js";
|
|
8
8
|
export * from "./ResolverDecoration.js";
|
|
9
9
|
export * from "./engine/index.js";
|
|
10
|
+
export * from "./graphql/staticSchemaKey.js";
|
|
10
11
|
const src = ()=>[];
|
|
11
|
-
export { registerLegacyPluginsViaGqlContextEnhancer } from "./registerLegacyPluginsViaGqlContextEnhancer.js";
|
|
12
12
|
export default src;
|
|
13
13
|
|
|
14
14
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["export * from \"./errors.js\";\nexport
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["export * from \"./errors.js\";\nexport * from \"./responses.js\";\nexport * from \"./utils/index.js\";\nexport * from \"./plugins/index.js\";\nexport * from \"./createRequestBody.js\";\nexport * from \"./processRequestBody.js\";\nexport * from \"./createResolverDecorator.js\";\nexport * from \"./ResolverDecoration.js\";\nexport * from \"./engine/index.js\";\nexport * from \"./graphql/staticSchemaKey.js\";\n\n// Backward-compat stub — the Fastify GraphQL route was replaced by GraphQLRoute\n// registered via GraphQLEngineFeature. This default export is a no-op.\nexport default () => [];\n"],"names":[],"mappings":";;;;;;;;;;AAaA,YAAgB,IAAK,EAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-graphql",
|
|
3
|
-
"version": "0.0.0-unstable.
|
|
3
|
+
"version": "0.0.0-unstable.5387e5f7c8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -18,27 +18,26 @@
|
|
|
18
18
|
"Adrian Smijulj <adrian@webiny.com>"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@graphql-tools/merge": "9.2.
|
|
22
|
-
"@graphql-tools/resolvers-composition": "7.0.
|
|
23
|
-
"@graphql-tools/schema": "10.0
|
|
24
|
-
"@graphql-tools/utils": "
|
|
25
|
-
"@webiny/api": "0.0.0-unstable.
|
|
26
|
-
"@webiny/di": "
|
|
27
|
-
"@webiny/error": "0.0.0-unstable.
|
|
28
|
-
"@webiny/event-handler-core": "0.0.0-unstable.
|
|
29
|
-
"@webiny/feature": "0.0.0-unstable.
|
|
30
|
-
"@webiny/
|
|
31
|
-
"@webiny/
|
|
32
|
-
"@webiny/utils": "0.0.0-unstable.0d717d18dd",
|
|
21
|
+
"@graphql-tools/merge": "9.2.3",
|
|
22
|
+
"@graphql-tools/resolvers-composition": "7.0.36",
|
|
23
|
+
"@graphql-tools/schema": "10.1.0",
|
|
24
|
+
"@graphql-tools/utils": "12.0.0",
|
|
25
|
+
"@webiny/api": "0.0.0-unstable.5387e5f7c8",
|
|
26
|
+
"@webiny/di": "2.0.0",
|
|
27
|
+
"@webiny/error": "0.0.0-unstable.5387e5f7c8",
|
|
28
|
+
"@webiny/event-handler-core": "0.0.0-unstable.5387e5f7c8",
|
|
29
|
+
"@webiny/feature": "0.0.0-unstable.5387e5f7c8",
|
|
30
|
+
"@webiny/plugins": "0.0.0-unstable.5387e5f7c8",
|
|
31
|
+
"@webiny/utils": "0.0.0-unstable.5387e5f7c8",
|
|
33
32
|
"graphql": "16.14.2",
|
|
34
|
-
"graphql-scalars": "
|
|
33
|
+
"graphql-scalars": "2.0.0",
|
|
35
34
|
"zod": "4.4.3"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|
|
38
|
-
"@webiny/build-tools": "0.0.0-unstable.
|
|
37
|
+
"@webiny/build-tools": "0.0.0-unstable.5387e5f7c8",
|
|
39
38
|
"rimraf": "6.1.3",
|
|
40
39
|
"typescript": "7.0.2",
|
|
41
|
-
"vitest": "4.1.
|
|
40
|
+
"vitest": "4.1.11"
|
|
42
41
|
},
|
|
43
42
|
"publishConfig": {
|
|
44
43
|
"access": "public"
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Allows packages to add properties to the GraphQL resolver contextValue.
|
|
3
|
-
* Register implementations to make things like `context.security`, `context.tenancy`
|
|
4
|
-
* available to resolvers alongside the base `context.container`.
|
|
5
|
-
*/
|
|
6
|
-
export interface IGraphQLContextEnhancer {
|
|
7
|
-
enhance(ctx: Record<string, any>): void | Promise<void>;
|
|
8
|
-
}
|
|
9
|
-
export declare const GraphQLContextEnhancer: import("@webiny/di").Abstraction<IGraphQLContextEnhancer>;
|
|
10
|
-
export declare namespace GraphQLContextEnhancer {
|
|
11
|
-
type Interface = IGraphQLContextEnhancer;
|
|
12
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"engine/GraphQLContextEnhancer.js","sources":["../../src/engine/GraphQLContextEnhancer.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\n/**\n * Allows packages to add properties to the GraphQL resolver contextValue.\n * Register implementations to make things like `context.security`, `context.tenancy`\n * available to resolvers alongside the base `context.container`.\n */\nexport interface IGraphQLContextEnhancer {\n enhance(ctx: Record<string, any>): void | Promise<void>;\n}\n\nexport const GraphQLContextEnhancer =\n createAbstraction<IGraphQLContextEnhancer>(\"GraphQLContextEnhancer\");\n\nexport namespace GraphQLContextEnhancer {\n export type Interface = IGraphQLContextEnhancer;\n}\n"],"names":["GraphQLContextEnhancer","createAbstraction"],"mappings":";AAWO,MAAMA,yBACTC,kBAA2C"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { GraphQLSchema } from "graphql";
|
|
2
|
-
/**
|
|
3
|
-
* A schema that is built after context enhancement — use when schema content depends on
|
|
4
|
-
* runtime data (e.g. CMS content models fetched from the database).
|
|
5
|
-
*
|
|
6
|
-
* Registered schemas are merged with the static schema from GraphQLSchemaComposer.
|
|
7
|
-
*/
|
|
8
|
-
export interface IGraphQLContextualSchema {
|
|
9
|
-
build(ctx: Record<string, any>): Promise<GraphQLSchema>;
|
|
10
|
-
}
|
|
11
|
-
export declare const GraphQLContextualSchema: import("@webiny/di").Abstraction<IGraphQLContextualSchema>;
|
|
12
|
-
export declare namespace GraphQLContextualSchema {
|
|
13
|
-
type Interface = IGraphQLContextualSchema;
|
|
14
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"engine/GraphQLContextualSchema.js","sources":["../../src/engine/GraphQLContextualSchema.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { GraphQLSchema } from \"graphql\";\n\n/**\n * A schema that is built after context enhancement — use when schema content depends on\n * runtime data (e.g. CMS content models fetched from the database).\n *\n * Registered schemas are merged with the static schema from GraphQLSchemaComposer.\n */\nexport interface IGraphQLContextualSchema {\n build(ctx: Record<string, any>): Promise<GraphQLSchema>;\n}\n\nexport const GraphQLContextualSchema =\n createAbstraction<IGraphQLContextualSchema>(\"GraphQLContextualSchema\");\n\nexport namespace GraphQLContextualSchema {\n export type Interface = IGraphQLContextualSchema;\n}\n"],"names":["GraphQLContextualSchema","createAbstraction"],"mappings":";AAaO,MAAMA,0BACTC,kBAA4C"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Container } from "@webiny/di";
|
|
2
|
-
/**
|
|
3
|
-
* Escape hatch for legacy plugin-based code that hasn't been migrated to createFeature() yet.
|
|
4
|
-
*
|
|
5
|
-
* Accepts any plugin array (same shape as the old createHandler({ plugins: [...] })) and
|
|
6
|
-
* applies them via a GraphQLContextEnhancer so they still receive the context object.
|
|
7
|
-
*
|
|
8
|
-
* Usage in createLambdaHandler request callback:
|
|
9
|
-
* registerLegacyPluginsViaGqlContextEnhancer(container, extensions());
|
|
10
|
-
* registerLegacyPluginsViaGqlContextEnhancer(container, [myPlugin(), anotherPlugin()]);
|
|
11
|
-
*/
|
|
12
|
-
export declare function registerLegacyPluginsViaGqlContextEnhancer(container: Container, plugins: any | any[]): void;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { PluginsContainer } from "@webiny/plugins";
|
|
2
|
-
import { GraphQLContextEnhancer } from "./engine/index.js";
|
|
3
|
-
function registerLegacyPluginsViaGqlContextEnhancer(container, plugins) {
|
|
4
|
-
const flat = [
|
|
5
|
-
plugins
|
|
6
|
-
].flat(1 / 0).filter(Boolean);
|
|
7
|
-
let initialized = false;
|
|
8
|
-
const enhancer = {
|
|
9
|
-
async enhance (ctx) {
|
|
10
|
-
if (initialized) return;
|
|
11
|
-
initialized = true;
|
|
12
|
-
if (!ctx.plugins) ctx.plugins = new PluginsContainer([]);
|
|
13
|
-
for (const plugin of flat)if ("function" == typeof plugin.apply) await plugin.apply(ctx);
|
|
14
|
-
else if (ctx.plugins) ctx.plugins.register(plugin);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
container.registerInstance(GraphQLContextEnhancer, enhancer);
|
|
18
|
-
}
|
|
19
|
-
export { registerLegacyPluginsViaGqlContextEnhancer };
|
|
20
|
-
|
|
21
|
-
//# sourceMappingURL=registerLegacyPluginsViaGqlContextEnhancer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registerLegacyPluginsViaGqlContextEnhancer.js","sources":["../src/registerLegacyPluginsViaGqlContextEnhancer.ts"],"sourcesContent":["import type { Container } from \"@webiny/di\";\nimport { PluginsContainer } from \"@webiny/plugins\";\nimport { GraphQLContextEnhancer } from \"./engine/index.js\";\nimport type { IGraphQLContextEnhancer } from \"./engine/index.js\";\n\n/**\n * Escape hatch for legacy plugin-based code that hasn't been migrated to createFeature() yet.\n *\n * Accepts any plugin array (same shape as the old createHandler({ plugins: [...] })) and\n * applies them via a GraphQLContextEnhancer so they still receive the context object.\n *\n * Usage in createLambdaHandler request callback:\n * registerLegacyPluginsViaGqlContextEnhancer(container, extensions());\n * registerLegacyPluginsViaGqlContextEnhancer(container, [myPlugin(), anotherPlugin()]);\n */\nexport function registerLegacyPluginsViaGqlContextEnhancer(\n container: Container,\n plugins: any | any[]\n): void {\n const flat = [plugins].flat(Infinity as 1).filter(Boolean);\n let initialized = false;\n\n const enhancer: IGraphQLContextEnhancer = {\n async enhance(ctx: Record<string, any>): Promise<void> {\n if (initialized) {\n return;\n }\n initialized = true;\n\n // Legacy plugins assume ctx.plugins exists (they call ctx.plugins.register() directly).\n // Mirror the contract of the old createHandler which always initialised PluginsContainer.\n if (!ctx.plugins) {\n ctx.plugins = new PluginsContainer([]);\n }\n\n for (const plugin of flat) {\n if (typeof plugin.apply === \"function\") {\n await plugin.apply(ctx);\n } else if (ctx.plugins) {\n ctx.plugins.register(plugin);\n }\n }\n }\n };\n\n container.registerInstance(GraphQLContextEnhancer, enhancer);\n}\n"],"names":["registerLegacyPluginsViaGqlContextEnhancer","container","plugins","flat","Infinity","Boolean","initialized","enhancer","ctx","PluginsContainer","plugin","GraphQLContextEnhancer"],"mappings":";;AAeO,SAASA,2CACZC,SAAoB,EACpBC,OAAoB;IAEpB,MAAMC,OAAO;QAACD;KAAQ,CAAC,IAAI,CAACE,OAAe,MAAM,CAACC;IAClD,IAAIC,cAAc;IAElB,MAAMC,WAAoC;QACtC,MAAM,SAAQC,GAAwB;YAClC,IAAIF,aACA;YAEJA,cAAc;YAId,IAAI,CAACE,IAAI,OAAO,EACZA,IAAI,OAAO,GAAG,IAAIC,iBAAiB,EAAE;YAGzC,KAAK,MAAMC,UAAUP,KACjB,IAAI,AAAwB,cAAxB,OAAOO,OAAO,KAAK,EACnB,MAAMA,OAAO,KAAK,CAACF;iBAChB,IAAIA,IAAI,OAAO,EAClBA,IAAI,OAAO,CAAC,QAAQ,CAACE;QAGjC;IACJ;IAEAT,UAAU,gBAAgB,CAACU,wBAAwBJ;AACvD"}
|