@lde/search-api-graphql 0.20.3 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-schema.d.ts.map +1 -1
- package/dist/build-schema.js +191 -30
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-schema.d.ts","sourceRoot":"","sources":["../src/build-schema.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"build-schema.d.ts","sourceRoot":"","sources":["../src/build-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAWL,aAAa,EAQd,MAAM,SAAS,CAAC;AACjB,OAAO,EAKL,KAAK,YAAY,EAEjB,KAAK,WAAW,EAChB,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAmBrB,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;AA+ED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,aAAa,CAuoBf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAER"}
|
package/dist/build-schema.js
CHANGED
|
@@ -1,9 +1,58 @@
|
|
|
1
|
-
import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLInputObjectType, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, printSchema, } from 'graphql';
|
|
2
|
-
import { AND_KEY, DEFAULT_LABEL_FIELD, facetableFields, filterableFields, filterOn, filterOperatorFor, ID_FIELD, OR_KEY, isRangeFacet, labelFieldNameOf, nestedReferenceType, outputFields, pageForOffset, sortableFields, unixSecondsToIso, } from '@lde/search/adapter';
|
|
1
|
+
import { GraphQLBoolean, GraphQLEnumType, GraphQLError, GraphQLFloat, GraphQLInputObjectType, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLSchema, GraphQLString, printSchema, } from 'graphql';
|
|
2
|
+
import { AND_KEY, DEFAULT_LABEL_FIELD, facetableFields, filterableFields, filterOn, filterOperatorFor, ID_FIELD, isAbsoluteIri, OR_KEY, isRangeFacet, labelFieldNameOf, 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)));
|
|
6
6
|
const scalarOutput = (scalar, field) => field.required === true ? new GraphQLNonNull(scalar) : scalar;
|
|
7
|
+
/**
|
|
8
|
+
* The IRI scalar: an absolute IRI, serialized as a string.
|
|
9
|
+
*
|
|
10
|
+
* Its job is to make a filter’s **element type** tell a consumer what the filter
|
|
11
|
+
* keys on, so “which of this type’s fields accept an IRI” is answerable by
|
|
12
|
+
* introspection instead of by a hardcoded, per-deployment list of predicates
|
|
13
|
+
* that drifts whenever a field is added. Wire-compatible with `String` – but
|
|
14
|
+
* deliberately NOT the same type, since the whole point is that the two are
|
|
15
|
+
* distinguishable. Consequence for consumers: a variable must be declared
|
|
16
|
+
* `[IRI!]`, not `[String!]`, because GraphQL checks variable usage nominally.
|
|
17
|
+
*
|
|
18
|
+
* **Input coercion validates; output serialization does not.** Rejecting a bare
|
|
19
|
+
* token on the way in is what turns `where: { material: { in: ["boerenbont"] } }`
|
|
20
|
+
* from a silent empty result into an error saying why – the same job
|
|
21
|
+
* `argsToQuery` already does for an out-of-range `perPage`.
|
|
22
|
+
*
|
|
23
|
+
* It validates on the way **out** too, and deliberately: a type that is enforced
|
|
24
|
+
* in one direction only is not a type a consumer can rely on, and the coarse
|
|
25
|
+
* discovery strategy reads `IRI` as the promise that a value is a selection key.
|
|
26
|
+
* Serving a non-IRI under it would make the promise false exactly where a
|
|
27
|
+
* consumer acts on it. The projection applies the same rule at the source, so
|
|
28
|
+
* the only way to reach this error is an index written before it existed – which
|
|
29
|
+
* a reindex fixes, and which failing names instead of hiding.
|
|
30
|
+
*
|
|
31
|
+
* What counts as an IRI is {@link isAbsoluteIri} – shared with the projection,
|
|
32
|
+
* so the surface cannot promise something the index contradicts.
|
|
33
|
+
*/
|
|
34
|
+
const iriScalar = new GraphQLScalarType({
|
|
35
|
+
name: 'IRI',
|
|
36
|
+
description: 'An absolute IRI (RFC 3987), serialized as a string. Any scheme – `https:`, `urn:`, `doi:`, `ark:` – not only HTTP. A field or filter typed `IRI` keys on identity, never on a literal.',
|
|
37
|
+
specifiedByURL: 'https://www.rfc-editor.org/rfc/rfc3987',
|
|
38
|
+
serialize: (value) => assertIriOut(GraphQLString.serialize(value)),
|
|
39
|
+
parseValue: (value) => assertIri(GraphQLString.parseValue(value)),
|
|
40
|
+
parseLiteral: (node, variables) => assertIri(GraphQLString.parseLiteral(node, variables)),
|
|
41
|
+
});
|
|
42
|
+
function assertIri(value) {
|
|
43
|
+
if (!isAbsoluteIri(value)) {
|
|
44
|
+
throw new GraphQLError(`IRI cannot represent “${value}”: an IRI needs a scheme (for example “https:”, “urn:” or “doi:”) and no whitespace. A value like this is usually a label or a token, which selects nothing on a field that keys on identity.`);
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
/** Outbound the fault is the index, not the caller, so the message points at
|
|
49
|
+
* the fix rather than at the query. */
|
|
50
|
+
function assertIriOut(value) {
|
|
51
|
+
if (!isAbsoluteIri(value)) {
|
|
52
|
+
throw new GraphQLError(`IRI cannot serialize “${value}”: the index holds a value that is not an absolute IRI on a field that keys on identity. The projection drops such values at the source, so this document predates that rule – reindex it.`);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
7
56
|
/** SCREAMING_SNAKE_CASE for an enum value name, e.g. `datePosted` → `DATE_POSTED`. */
|
|
8
57
|
function screamingSnake(name) {
|
|
9
58
|
return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toUpperCase();
|
|
@@ -35,25 +84,37 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
35
84
|
value: { type: new GraphQLNonNull(GraphQLString) },
|
|
36
85
|
},
|
|
37
86
|
});
|
|
38
|
-
// A
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
},
|
|
87
|
+
// A value facet bucket: a selection key, its count, and (for reference facets)
|
|
88
|
+
// the engine-resolved data label; null for token/free-string facets whose
|
|
89
|
+
// display the consumer owns. Only the key’s type varies between the two – a
|
|
90
|
+
// facet keys on whatever its field keys on – so both are built from here and
|
|
91
|
+
// cannot drift apart.
|
|
92
|
+
const valueBucketFields = (keyType) => ({
|
|
93
|
+
value: { type: new GraphQLNonNull(keyType) },
|
|
94
|
+
count: { type: new GraphQLNonNull(GraphQLInt) },
|
|
95
|
+
label: {
|
|
96
|
+
type: new GraphQLList(new GraphQLNonNull(languageString)),
|
|
97
|
+
resolve: (bucket, _args, context) => {
|
|
98
|
+
const label = bucket.label;
|
|
99
|
+
return label
|
|
100
|
+
? toLanguageStrings(label, context.acceptLanguage, languageOrder)
|
|
101
|
+
: null;
|
|
54
102
|
},
|
|
55
103
|
},
|
|
56
104
|
});
|
|
105
|
+
const valueBucket = new GraphQLObjectType({
|
|
106
|
+
name: 'ValueBucket',
|
|
107
|
+
fields: valueBucketFields(GraphQLString),
|
|
108
|
+
});
|
|
109
|
+
// A reference facet’s bucket, whose `value` is an `IRI` because the field it
|
|
110
|
+
// buckets keys on identity. This is what makes the round trip typed end to
|
|
111
|
+
// end: the bucket a consumer selects feeds straight into the ‹Target›Filter
|
|
112
|
+
// that selects it, with no `String`-to-`IRI` boundary in between – the
|
|
113
|
+
// contract BooleanBucket already keeps for `is`.
|
|
114
|
+
const iriBucket = new GraphQLObjectType({
|
|
115
|
+
name: 'IriBucket',
|
|
116
|
+
fields: valueBucketFields(iriScalar),
|
|
117
|
+
});
|
|
57
118
|
// A numeric range-facet bin: half-open `[min, max)` bounds (max null on an
|
|
58
119
|
// open-ended top bin) and the count of documents in it.
|
|
59
120
|
const rangeBucket = new GraphQLObjectType({
|
|
@@ -92,12 +153,63 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
92
153
|
name: 'SortDirection',
|
|
93
154
|
values: { ASC: { value: 'asc' }, DESC: { value: 'desc' } },
|
|
94
155
|
});
|
|
95
|
-
|
|
96
|
-
|
|
156
|
+
// Membership filters, split by what the field keys on. A `keyword` field
|
|
157
|
+
// holds literals (an accession number, an ISO 8601 string, a token); a
|
|
158
|
+
// `reference` holds identity. One shared `StringFilter` said neither, which
|
|
159
|
+
// left `where: { material: { in: ["boerenbont"] } }` valid, silent and empty.
|
|
160
|
+
const keywordFilter = new GraphQLInputObjectType({
|
|
161
|
+
name: 'KeywordFilter',
|
|
162
|
+
description: 'Matches a field holding literal values.',
|
|
97
163
|
fields: {
|
|
98
164
|
in: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
|
|
99
165
|
},
|
|
100
166
|
});
|
|
167
|
+
// The fallback for a reference whose referent is nameable as no type: a
|
|
168
|
+
// canonical vocabulary URI, a licence, a content URL. Its `in` element type is
|
|
169
|
+
// what the COARSE discovery strategy matches on – select every criterion field
|
|
170
|
+
// whose filter takes IRIs, and you have the complete reference-field set with
|
|
171
|
+
// no origin collection at all.
|
|
172
|
+
const iriFilter = new GraphQLInputObjectType({
|
|
173
|
+
name: 'IRIFilter',
|
|
174
|
+
description: 'Matches a field holding IRIs that belong to no collection this API serves.',
|
|
175
|
+
fields: { in: { type: new GraphQLList(new GraphQLNonNull(iriScalar)) } },
|
|
176
|
+
});
|
|
177
|
+
/**
|
|
178
|
+
* The filter input for IRIs of one named target, `‹TypeName›Filter` – created
|
|
179
|
+
* once per target and shared by every field pointing at it, INCLUDING the
|
|
180
|
+
* target’s own `id`. That sharing is the whole mechanism: a consumer resolves
|
|
181
|
+
* the collection it is browsing to its filter type through `‹Type›Where.id`,
|
|
182
|
+
* then selects the criterion fields of every other type whose filter is that
|
|
183
|
+
* same type. The name is COMPARED, never parsed – a generic consumer needs no
|
|
184
|
+
* more knowledge of `TermFilter` than it already needs of `terms`.
|
|
185
|
+
*/
|
|
186
|
+
const targetFilters = new Map();
|
|
187
|
+
function targetFilter(typeName) {
|
|
188
|
+
const existing = targetFilters.get(typeName);
|
|
189
|
+
if (existing !== undefined) {
|
|
190
|
+
return existing;
|
|
191
|
+
}
|
|
192
|
+
const name = `${typeName}Filter`;
|
|
193
|
+
// graphql-js would reject the duplicate too, but only once the whole schema
|
|
194
|
+
// is assembled and without saying which declaration caused it. Checked
|
|
195
|
+
// against every name already claimed – the built-in filters, the `IRI`
|
|
196
|
+
// scalar, and every root/reference type – since a declaration is free to
|
|
197
|
+
// name a type `TermFilter` beside a root type `Term`.
|
|
198
|
+
if (name === keywordFilter.name ||
|
|
199
|
+
name === iriFilter.name ||
|
|
200
|
+
name === iriScalar.name ||
|
|
201
|
+
takenTypeNames.has(name)) {
|
|
202
|
+
throw new Error(`Type “${typeName}” would be filtered through “${name}”, which is already the name of another type; rename one of them.`);
|
|
203
|
+
}
|
|
204
|
+
takenTypeNames.add(name);
|
|
205
|
+
const filter = new GraphQLInputObjectType({
|
|
206
|
+
name,
|
|
207
|
+
description: `Matches a field holding IRIs of ${typeName}.`,
|
|
208
|
+
fields: { in: { type: new GraphQLList(new GraphQLNonNull(iriScalar)) } },
|
|
209
|
+
});
|
|
210
|
+
targetFilters.set(typeName, filter);
|
|
211
|
+
return filter;
|
|
212
|
+
}
|
|
101
213
|
const intRange = rangeInput('IntRange', GraphQLInt);
|
|
102
214
|
const floatRange = rangeInput('FloatRange', GraphQLFloat);
|
|
103
215
|
const dateRange = rangeInput('DateRange', GraphQLString);
|
|
@@ -145,7 +257,13 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
145
257
|
* stays the id-plus-label pair its strategy carries.
|
|
146
258
|
*/
|
|
147
259
|
function registerReferenceType(field, owner) {
|
|
148
|
-
if (field.kind !== 'reference' ||
|
|
260
|
+
if (field.kind !== 'reference' ||
|
|
261
|
+
field.ref === undefined ||
|
|
262
|
+
// An `idOnly` reference carries the IRI and nothing else, so it surfaces
|
|
263
|
+
// as the IRI itself rather than as an object – there is no type to
|
|
264
|
+
// register, and no name needed to register one under.
|
|
265
|
+
field.ref.strategy === 'idOnly' ||
|
|
266
|
+
field.ref.typeName === undefined) {
|
|
149
267
|
return;
|
|
150
268
|
}
|
|
151
269
|
if (referenceTypes.has(field.ref.typeName)) {
|
|
@@ -176,14 +294,16 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
176
294
|
// the registration order is (the graph is acyclic by searchSchema).
|
|
177
295
|
fields: () => nested === undefined
|
|
178
296
|
? {
|
|
297
|
+
id: { type: new GraphQLNonNull(iriScalar) },
|
|
179
298
|
// The same word the label source declares its label field
|
|
180
299
|
// under (`label` by default): one resolved label, one name for
|
|
181
300
|
// it wherever it surfaces.
|
|
182
|
-
id: { type: new GraphQLNonNull(GraphQLString) },
|
|
183
301
|
[labelKeyOf(field)]: labelList((source) => source.label),
|
|
184
302
|
}
|
|
185
303
|
: {
|
|
186
|
-
|
|
304
|
+
// Nullable, and load-bearing: a referent needs no identity, so
|
|
305
|
+
// a blank-node one nests exactly like a named one, minus this.
|
|
306
|
+
id: { type: iriScalar },
|
|
187
307
|
...Object.fromEntries(outputFields(nested).map((nestedField) => [
|
|
188
308
|
nestedField.name,
|
|
189
309
|
outputFieldConfig(nestedField),
|
|
@@ -222,6 +342,30 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
222
342
|
}
|
|
223
343
|
: { type: scalarOutput(GraphQLString, field) };
|
|
224
344
|
case 'reference': {
|
|
345
|
+
// `idOnly`: the IRI itself, not an object wrapping it – so a field whose
|
|
346
|
+
// referent this deployment does not describe (a vocabulary URI, a
|
|
347
|
+
// licence, a content URL) reads as the flat list of IRIs it is, while
|
|
348
|
+
// still declaring what it holds.
|
|
349
|
+
//
|
|
350
|
+
// The IR still carries a `Reference` here, as it does for every
|
|
351
|
+
// non-inline reference (see `SearchValue`): the strategy is a statement
|
|
352
|
+
// about the SURFACE, not about the port, so the flattening belongs on
|
|
353
|
+
// this side rather than in each adapter’s result reconstruction. A
|
|
354
|
+
// referent carrying no IRI drops out instead of nulling the field.
|
|
355
|
+
if (field.ref?.strategy === 'idOnly') {
|
|
356
|
+
const iriOf = (value) => value?.id;
|
|
357
|
+
return field.array === true
|
|
358
|
+
? {
|
|
359
|
+
type: nonNullListOf(iriScalar),
|
|
360
|
+
resolve: (source) => (source[field.name] ?? [])
|
|
361
|
+
.map(iriOf)
|
|
362
|
+
.filter((iri) => iri !== undefined),
|
|
363
|
+
}
|
|
364
|
+
: {
|
|
365
|
+
type: scalarOutput(iriScalar, field),
|
|
366
|
+
resolve: (source) => iriOf(source[field.name]) ?? null,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
225
369
|
const referenceType = referenceTypes.get(field.ref?.typeName ?? '');
|
|
226
370
|
return field.array === true
|
|
227
371
|
? {
|
|
@@ -256,10 +400,18 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
256
400
|
};
|
|
257
401
|
}
|
|
258
402
|
}
|
|
403
|
+
/** The filter input one field accepts, fixed by what the field keys on: a
|
|
404
|
+
* `reference` keys on identity (its target’s filter, or `IRIFilter` when it
|
|
405
|
+
* names no target), a `keyword` on literals. */
|
|
259
406
|
function whereFieldType(field) {
|
|
260
407
|
switch (filterOperatorFor(field.kind)) {
|
|
261
408
|
case 'in':
|
|
262
|
-
|
|
409
|
+
if (field.kind !== 'reference') {
|
|
410
|
+
return keywordFilter;
|
|
411
|
+
}
|
|
412
|
+
return field.ref?.typeName === undefined
|
|
413
|
+
? iriFilter
|
|
414
|
+
: targetFilter(field.ref.typeName);
|
|
263
415
|
case 'range':
|
|
264
416
|
return field.kind === 'integer'
|
|
265
417
|
? intRange
|
|
@@ -277,7 +429,7 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
277
429
|
name: typeName,
|
|
278
430
|
fields: () => {
|
|
279
431
|
const fields = {
|
|
280
|
-
id: { type: new GraphQLNonNull(
|
|
432
|
+
id: { type: new GraphQLNonNull(iriScalar) },
|
|
281
433
|
};
|
|
282
434
|
for (const field of outputFields(searchType)) {
|
|
283
435
|
fields[field.name] = outputFieldConfig(field);
|
|
@@ -299,7 +451,11 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
299
451
|
*/
|
|
300
452
|
const fieldKeys = () => {
|
|
301
453
|
const fields = {
|
|
302
|
-
|
|
454
|
+
// Typed self-referentially – `TermWhere.id: TermFilter` – so a consumer
|
|
455
|
+
// can resolve “the collection I am browsing” to “the filter type that
|
|
456
|
+
// accepts its IRIs”, which is what makes the refined discovery strategy
|
|
457
|
+
// work without hardcoding a single domain name.
|
|
458
|
+
[ID_FIELD]: { type: targetFilter(typeName) },
|
|
303
459
|
};
|
|
304
460
|
for (const field of filterable) {
|
|
305
461
|
fields[field.name] = {
|
|
@@ -362,8 +518,8 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
362
518
|
},
|
|
363
519
|
});
|
|
364
520
|
// Keyed facets object: one field per facetable field, typed by its kind
|
|
365
|
-
// (range fields → [RangeBucket!], boolean fields → [BooleanBucket!],
|
|
366
|
-
// [ValueBucket!]). Only the selected
|
|
521
|
+
// (range fields → [RangeBucket!], boolean fields → [BooleanBucket!],
|
|
522
|
+
// reference fields → [IriBucket!], else [ValueBucket!]). Only the selected
|
|
367
523
|
// fields are resolved (GraphQL prunes the rest), so the selection IS the
|
|
368
524
|
// request; how they are computed – skip-own-filter, batched into one
|
|
369
525
|
// engine dispatch – lives in facet-batch.ts.
|
|
@@ -419,11 +575,16 @@ export function buildGraphQLSchema(schema, options = {}) {
|
|
|
419
575
|
};
|
|
420
576
|
}
|
|
421
577
|
/** The bucket type a facet’s kind earns: bins for a range facet, a real
|
|
422
|
-
* boolean for a boolean one, a
|
|
578
|
+
* boolean for a boolean one, an `IRI` key for a reference, a literal key
|
|
579
|
+
* otherwise. The reference test is the one {@link whereFieldType} applies, so
|
|
580
|
+
* a bucket and the filter it feeds cannot disagree about what the field keys
|
|
581
|
+
* on. */
|
|
423
582
|
function bucketTypeFor(field) {
|
|
424
583
|
if (isRangeFacet(field))
|
|
425
584
|
return rangeBucket;
|
|
426
|
-
|
|
585
|
+
if (field.kind === 'boolean')
|
|
586
|
+
return booleanBucket;
|
|
587
|
+
return field.kind === 'reference' ? iriBucket : valueBucket;
|
|
427
588
|
}
|
|
428
589
|
/** The keyed facets object for one type (only called with ≥ 1 facetable field). */
|
|
429
590
|
function facetsTypeFor(typeName, facetable) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lde/search-api-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@escape.tech/graphql-armor-cost-limit": "^2.4.3",
|
|
38
38
|
"@escape.tech/graphql-armor-max-depth": "^2.4.2",
|
|
39
39
|
"@graphql-yoga/render-graphiql": "^5.21.2",
|
|
40
|
-
"@lde/search": "^0.
|
|
40
|
+
"@lde/search": "^0.19.0",
|
|
41
41
|
"commander": "^15.0.0",
|
|
42
42
|
"dataloader": "^2.2.3",
|
|
43
43
|
"graphql": "^16.9.0",
|