@lde/search-api-graphql 0.21.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
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"}
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;AAqGD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,aAAa,CA4pBf;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAER"}
@@ -41,18 +41,39 @@ const iriScalar = new GraphQLScalarType({
41
41
  });
42
42
  function assertIri(value) {
43
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.`);
44
+ throw userError(`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
45
  }
46
46
  return value;
47
47
  }
48
48
  /** Outbound the fault is the index, not the caller, so the message points at
49
- * the fix rather than at the query. */
49
+ * the fix rather than at the query – and it carries no `BAD_USER_INPUT`,
50
+ * since there is nothing the query could have done differently. */
50
51
  function assertIriOut(value) {
51
52
  if (!isAbsoluteIri(value)) {
52
53
  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
  }
54
55
  return value;
55
56
  }
57
+ /**
58
+ * An error the CALLER can fix, marked as such: a `GraphQLError` carrying
59
+ * `extensions.code = 'BAD_USER_INPUT'`.
60
+ *
61
+ * Both halves matter, and both are about the consumer rather than about us. A
62
+ * plain `Error` thrown from a resolver is a server fault as far as the
63
+ * transport is concerned, so graphql-yoga masks it to `“Unexpected error.”`;
64
+ * the message then survives only in the API container’s log, where a
65
+ * presentation-layer developer building against a hosted endpoint cannot read
66
+ * it. Throwing a `GraphQLError` keeps the sentence that says what was wrong.
67
+ * The `code` says whose fault it is, so a client can distinguish “fix your
68
+ * query” from “retry later” without matching on prose – the convention every
69
+ * major GraphQL server shares. `“Unexpected error.”` is left for what the name
70
+ * says: faults we did not anticipate.
71
+ */
72
+ function userError(message) {
73
+ return new GraphQLError(message, {
74
+ extensions: { code: 'BAD_USER_INPUT' },
75
+ });
76
+ }
56
77
  /** SCREAMING_SNAKE_CASE for an enum value name, e.g. `datePosted` → `DATE_POSTED`. */
57
78
  function screamingSnake(name) {
58
79
  return name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toUpperCase();
@@ -545,11 +566,31 @@ export function buildGraphQLSchema(schema, options = {}) {
545
566
  return {
546
567
  type: new GraphQLNonNull(resultType),
547
568
  args: {
548
- query: { type: GraphQLString },
549
- where: { type: whereInput },
550
- orderBy: { type: orderByInput },
551
- page: { type: GraphQLInt, defaultValue: 1 },
552
- perPage: { type: GraphQLInt, defaultValue: 20 },
569
+ query: {
570
+ type: GraphQLString,
571
+ description: 'Free-text query. Omit it to browse by filter alone.',
572
+ },
573
+ where: {
574
+ type: whereInput,
575
+ description: 'Conditions every result must satisfy.',
576
+ },
577
+ orderBy: {
578
+ type: orderByInput,
579
+ description: 'Sort order. Defaults to relevance for a free-text query.',
580
+ },
581
+ page: {
582
+ type: GraphQLInt,
583
+ defaultValue: 1,
584
+ // The bound in the SDL, so the playground’s own documentation
585
+ // answers “how large may a page be?” before a request has to fail
586
+ // to say it.
587
+ description: '1-based page number; at least 1.',
588
+ },
589
+ perPage: {
590
+ type: GraphQLInt,
591
+ defaultValue: 20,
592
+ description: `Results per page, between 0 and ${maxPerPage}. Use 0 for a facet-only query: no results are fetched, and the facet counts still come back.`,
593
+ },
553
594
  },
554
595
  resolve: async (_source, args, context) => {
555
596
  const built = argsToQuery(args, context, searchType, maxPerPage);
@@ -636,11 +677,11 @@ function argsToQuery(args, context, searchType, maxPerPage) {
636
677
  const perPage = args.perPage ?? 20;
637
678
  const page = args.page ?? 1;
638
679
  if (page < 1) {
639
- throw new Error(`page must be at least 1; got ${page}.`);
680
+ throw userError(`page must be at least 1; got ${page}.`);
640
681
  }
641
682
  // perPage: 0 is a legitimate facet-only query (no hits, page pins to 1).
642
683
  if (perPage < 0 || perPage > maxPerPage) {
643
- throw new Error(`perPage must be between 0 and ${maxPerPage}; got ${perPage}.`);
684
+ throw userError(`perPage must be between 0 and ${maxPerPage}; got ${perPage}.`);
644
685
  }
645
686
  return {
646
687
  text: args.query,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search-api-graphql",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
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",