@lde/search-api-graphql 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"build-schema.d.ts","sourceRoot":"","sources":["../src/build-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,aAAa,EAQd,MAAM,SAAS,CAAC;AACjB,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAYrB,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAGvB,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B;;sDAEkD;IAClD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACjE;AAED;2EAC2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC;yCACqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;uBACmB;IACnB,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,aAAa,KACnB,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC;;kEAE8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;gDAE4C;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,aAAa,CA6Xf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAER"}
1
+ {"version":3,"file":"build-schema.d.ts","sourceRoot":"","sources":["../src/build-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,aAAa,EAQd,MAAM,SAAS,CAAC;AACjB,OAAO,EAIL,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAarB,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAGvB,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B;;sDAEkD;IAClD,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACjE;AAED;2EAC2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC;yCACqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;uBACmB;IACnB,QAAQ,CAAC,aAAa,CAAC,EAAE,CACvB,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,aAAa,KACnB,WAAW,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC;;kEAE8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC;IACvC;;gDAE4C;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAkBD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,aAAa,CAoaf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAER"}
@@ -1,5 +1,5 @@
1
1
  import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLInputObjectType, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, printSchema, } from 'graphql';
2
- import { facetableFields, filterableFields, filterOperatorFor, ID_FIELD, isRangeFacet, outputFields, pageForOffset, sortableFields, unixSecondsToIso, } from '@lde/search/adapter';
2
+ import { facetableFields, filterableFields, filterOperatorFor, ID_FIELD, isRangeFacet, nestedReferenceType, outputFields, pageForOffset, sortableFields, unixSecondsToIso, } from '@lde/search/adapter';
3
3
  import { defaultLanguageOrder, toLanguageStrings, } from './language.js';
4
4
  import { createFacetLoader } from './facet-batch.js';
5
5
  const nonNullListOf = (type) => new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(type)));
@@ -107,28 +107,59 @@ export function buildGraphQLSchema(schema, options = {}) {
107
107
  // and rejects duplicate names schema-wide.
108
108
  const referenceTypes = new Map();
109
109
  const takenTypeNames = new Set(rootTypeNames);
110
- for (const searchType of schema.values()) {
111
- for (const field of outputFields(searchType)) {
112
- if (field.kind !== 'reference' ||
113
- field.ref === undefined ||
114
- referenceTypes.has(field.ref.typeName)) {
115
- continue;
116
- }
117
- const { typeName } = field.ref;
118
- const graphQLName = rootTypeNames.has(typeName)
119
- ? `${typeName}Reference`
120
- : typeName;
121
- if (takenTypeNames.has(graphQLName)) {
122
- throw new Error(`Reference type “${typeName}” (field “${field.name}” of “${searchType.name}”) would be served as “${graphQLName}”, which collides with another type name; rename one.`);
123
- }
124
- takenTypeNames.add(graphQLName);
125
- referenceTypes.set(typeName, new GraphQLObjectType({
126
- name: graphQLName,
127
- fields: {
110
+ /**
111
+ * Register the GraphQL type one reference field is served as, once per
112
+ * referenced shape. A **surfaced inline** reference is served as the nested
113
+ * object its Reference Type declares – its own `output` fields, configured by
114
+ * exactly the same per-kind rules a root type’s fields are – so the response
115
+ * shape matches the data’s shape. Its `id` is nullable: a referent needs no
116
+ * identity, and a blank-node one nests without one. Every other reference
117
+ * stays the id-plus-label pair its strategy carries.
118
+ */
119
+ function registerReferenceType(field, owner) {
120
+ if (field.kind !== 'reference' ||
121
+ field.ref === undefined ||
122
+ referenceTypes.has(field.ref.typeName)) {
123
+ return;
124
+ }
125
+ const { typeName } = field.ref;
126
+ const graphQLName = rootTypeNames.has(typeName)
127
+ ? `${typeName}Reference`
128
+ : typeName;
129
+ if (takenTypeNames.has(graphQLName)) {
130
+ throw new Error(`Reference type “${typeName}” (field “${field.name}” of “${owner.name}”) would be served as “${graphQLName}”, which collides with another type name; rename one.`);
131
+ }
132
+ takenTypeNames.add(graphQLName);
133
+ const nested = nestedReferenceType(schema, field);
134
+ referenceTypes.set(typeName, new GraphQLObjectType({
135
+ name: graphQLName,
136
+ // A thunk, so a Reference Type nesting another one resolves whatever
137
+ // the registration order is (the graph is acyclic by searchSchema).
138
+ fields: () => nested === undefined
139
+ ? {
128
140
  id: { type: new GraphQLNonNull(GraphQLString) },
129
141
  name: labelList((source) => source.label),
142
+ }
143
+ : {
144
+ id: { type: GraphQLString },
145
+ ...Object.fromEntries(outputFields(nested).map((nestedField) => [
146
+ nestedField.name,
147
+ outputFieldConfig(nestedField),
148
+ ])),
130
149
  },
131
- }));
150
+ }));
151
+ if (nested === undefined) {
152
+ return;
153
+ }
154
+ // A nested reference type’s own surfaced references are types too; register
155
+ // them now, so every type exists before the thunks above are called.
156
+ for (const nestedField of outputFields(nested)) {
157
+ registerReferenceType(nestedField, nested);
158
+ }
159
+ }
160
+ for (const searchType of schema.values()) {
161
+ for (const field of outputFields(searchType)) {
162
+ registerReferenceType(field, searchType);
132
163
  }
133
164
  }
134
165
  function outputFieldConfig(field) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-api-graphql",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Engine- and domain-agnostic GraphQL surface for @lde/search: builds an executable GraphQLSchema from a whole SearchSchema at runtime (no codegen) – one root query field per SearchType – served by generic resolvers over any SearchEngine – and serves it as a framework-agnostic fetch handler with a self-contained playground, SDL endpoint, CORS and depth/cost limits. You supply the schema and per-type typeNames; it names neither your domain nor your engine.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -28,7 +28,7 @@
28
28
  "@escape.tech/graphql-armor-cost-limit": "^2.4.3",
29
29
  "@escape.tech/graphql-armor-max-depth": "^2.4.2",
30
30
  "@graphql-yoga/render-graphiql": "^5.21.2",
31
- "@lde/search": "^0.13.0",
31
+ "@lde/search": "^0.15.0",
32
32
  "dataloader": "^2.2.3",
33
33
  "graphql": "^16.0.0",
34
34
  "graphql-yoga": "^5.21.2",