@luvio/graphql-parser 0.99.0 → 0.100.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/gql.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Exposes a tagged template literal to parse graphql operation string
3
+ * "gql" is the only publicly exposed method
4
+ * @module gql
5
+ */
6
+ import type { DefinitionNode, DocumentNode } from 'graphql/language';
7
+ export declare type AstResolver = (astReference: any) => DocumentNode | undefined;
8
+ /**
9
+ * we should look into optimizing this before it turns into a memory hog
10
+ * weakmaps, or limiting the size of the cache, or something
11
+ */
12
+ export declare const docMap: Map<string, DocumentNode>;
13
+ /**
14
+ * Opaque reference map to return keys to userland
15
+ * As a user shouldn't have access to the Document
16
+ */
17
+ export declare const referenceMap: WeakMap<Object, DocumentNode>;
18
+ export declare let addMetaschemaDirectives: boolean;
19
+ /**
20
+ * Insert string and fragment substitutions with the actual nodes
21
+ * @param inputString
22
+ * @param substitutions - string | fragment DocumentNode
23
+ * @returns { operation string, fragment docs [] }
24
+ */
25
+ export declare function processSubstitutions(inputString: ReadonlyArray<string>, substitutions: (string | object)[]): {
26
+ operationString: string;
27
+ fragments: DefinitionNode[];
28
+ } | null;
29
+ /**
30
+ * strips Document node and nested definitions of location references
31
+ */
32
+ export declare function stripLocation(node: any): any;
33
+ /**
34
+ *
35
+ * @param astReference - ast reference passed from user land
36
+ */
37
+ export declare const astResolver: AstResolver;
38
+ /**
39
+ *
40
+ * @param literals - operation query string
41
+ * @param subs - all other substitutions
42
+ * @returns an opaque reference to the parsed document
43
+ */
44
+ export declare function gql(literals: ReadonlyArray<string> | string, ...subs: (string | object)[]): object | null;
45
+ /**
46
+ * Enable the parser to add corresponding metaschema directives for backwards compatibility
47
+ */
48
+ export declare function enableAddMetaschemaDirective(): void;
49
+ export declare function disableAddMetaschemaDirective(): void;
@@ -10502,7 +10502,7 @@ function transform$9(node, transformState) {
10502
10502
  }
10503
10503
  return ret;
10504
10504
  }
10505
- function isCustomDirective(node) {
10505
+ function isCustomDirective$1(node) {
10506
10506
  return (node.name.value === CUSTOM_DIRECTIVE_CONNECTION ||
10507
10507
  node.name.value === CUSTOM_DIRECTIVE_RESOURCE);
10508
10508
  }
@@ -10523,7 +10523,7 @@ function transform$8(node, transformState) {
10523
10523
  else {
10524
10524
  // object or custom field node
10525
10525
  if (directives !== undefined && directives.length > 0) {
10526
- const customDirectiveNode = directives.find(isCustomDirective);
10526
+ const customDirectiveNode = directives.find(isCustomDirective$1);
10527
10527
  if (customDirectiveNode === undefined) {
10528
10528
  // transform non client-side directives
10529
10529
  luvioNode.directives = directives.map((directive) => transform$9(directive, transformState));
@@ -10800,6 +10800,322 @@ function transform(root) {
10800
10800
  };
10801
10801
  }
10802
10802
 
10803
+ const { create, keys } = Object;
10804
+
10805
+ const DIRECTIVE_RECORD_CATEGORY = {
10806
+ kind: 'Directive',
10807
+ name: {
10808
+ kind: 'Name',
10809
+ value: 'category',
10810
+ },
10811
+ arguments: [
10812
+ {
10813
+ kind: 'Argument',
10814
+ name: {
10815
+ kind: 'Name',
10816
+ value: 'name',
10817
+ },
10818
+ value: {
10819
+ kind: 'StringValue',
10820
+ value: 'recordQuery',
10821
+ block: false,
10822
+ },
10823
+ },
10824
+ ],
10825
+ };
10826
+ const DIRECTIVE_PARENT_CATEGORY = {
10827
+ kind: 'Directive',
10828
+ name: {
10829
+ kind: 'Name',
10830
+ value: 'category',
10831
+ },
10832
+ arguments: [
10833
+ {
10834
+ kind: 'Argument',
10835
+ name: {
10836
+ kind: 'Name',
10837
+ value: 'name',
10838
+ },
10839
+ value: {
10840
+ kind: 'StringValue',
10841
+ value: 'parentRelationship',
10842
+ block: false,
10843
+ },
10844
+ },
10845
+ ],
10846
+ };
10847
+ function substituteDirectives(directives, index, nodeName) {
10848
+ if (directives[index].name.value === CUSTOM_DIRECTIVE_CONNECTION) {
10849
+ // replace the custom directive node with the metaschema directive node
10850
+ // @ts-ignore - Document is read only
10851
+ directives[index] = DIRECTIVE_RECORD_CATEGORY;
10852
+ }
10853
+ else if (directives[index].name.value === CUSTOM_DIRECTIVE_RESOURCE) {
10854
+ // node gets its type from @category recordQuery
10855
+ if (nodeName === 'node') {
10856
+ // @ts-ignore - Document is read only
10857
+ directives.splice(index, 1);
10858
+ }
10859
+ else {
10860
+ // @ts-ignore - Document is read only
10861
+ directives[index] = DIRECTIVE_PARENT_CATEGORY;
10862
+ }
10863
+ }
10864
+ }
10865
+ /**
10866
+ * Returns true if the directive node is of legacy type
10867
+ * @param node : Directive node
10868
+ * @returns
10869
+ */
10870
+ function isCustomDirective(node) {
10871
+ return (node.name.value === CUSTOM_DIRECTIVE_CONNECTION ||
10872
+ node.name.value === CUSTOM_DIRECTIVE_RESOURCE);
10873
+ }
10874
+ /**
10875
+ * Traverses a selection set and it's nested selections,
10876
+ * to find any legacy custom directives and substitute them with metaschema directives
10877
+ * @param node - SelectionSetNode
10878
+ * @returns SelectionSetNode
10879
+ */
10880
+ function traverseSelectionSet(node) {
10881
+ if (node === undefined) {
10882
+ return;
10883
+ }
10884
+ for (const selection of node.selections) {
10885
+ // FragmentSpreadNode doesn't have a selection set
10886
+ // which should be handled at this methods entry point
10887
+ const { directives, selectionSet } = selection;
10888
+ let selectionName;
10889
+ if (selection.kind !== 'InlineFragment') {
10890
+ selectionName = selection.name.value;
10891
+ }
10892
+ if (directives !== undefined && directives.length > 0) {
10893
+ // we follow this pattern instead of map to preserve the order of directives
10894
+ // order of directives may be significant as per graphql spec
10895
+ const index = directives.findIndex(isCustomDirective);
10896
+ if (index !== -1) {
10897
+ substituteDirectives(directives, index, selectionName);
10898
+ }
10899
+ }
10900
+ traverseSelectionSet(selectionSet);
10901
+ }
10902
+ return node;
10903
+ }
10904
+ /**
10905
+ * Accepts a document node and replaces the legacy custom directives with metaschema directives "in-place"
10906
+ * @param doc
10907
+ */
10908
+ function metaschemaMapper(doc) {
10909
+ // this method is only callable for Executable definitions
10910
+ // such as Operations and Fragments
10911
+ // so we have to explicitly cast the definitions for ts
10912
+ const { definitions } = doc;
10913
+ for (const def of definitions) {
10914
+ const { directives, selectionSet } = def;
10915
+ if (directives !== undefined && directives.length > 0) {
10916
+ // we are making an assumption here that only one custom directive can be applied to a node
10917
+ // we can revisit if this condition changes
10918
+ const index = directives.findIndex(isCustomDirective);
10919
+ if (index !== -1) {
10920
+ substituteDirectives(directives, index, undefined);
10921
+ }
10922
+ }
10923
+ traverseSelectionSet(selectionSet);
10924
+ }
10925
+ }
10926
+
10927
+ /**
10928
+ * we should look into optimizing this before it turns into a memory hog
10929
+ * weakmaps, or limiting the size of the cache, or something
10930
+ */
10931
+ const docMap = new Map();
10932
+ /**
10933
+ * Opaque reference map to return keys to userland
10934
+ * As a user shouldn't have access to the Document
10935
+ */
10936
+ const referenceMap = new WeakMap();
10937
+ let addMetaschemaDirectives = false;
10938
+ /**
10939
+ * Strips characters that are not significant to the validity or execution
10940
+ * of a GraphQL document:
10941
+ * - UnicodeBOM
10942
+ * - WhiteSpace
10943
+ * - LineTerminator
10944
+ * - Comment
10945
+ * - Comma
10946
+ * - BlockString indentation
10947
+ */
10948
+ function operationKeyBuilder(inputString) {
10949
+ return stripIgnoredCharacters(inputString);
10950
+ }
10951
+ /**
10952
+ * Returns document node if cached or else update the cache and return the document node
10953
+ * @param inputString - operation string
10954
+ * @returns DocumentNode
10955
+ */
10956
+ function parseDocument(inputString) {
10957
+ const operationKey = operationKeyBuilder(inputString);
10958
+ const cachedDoc = docMap.get(operationKey);
10959
+ if (cachedDoc !== undefined) {
10960
+ return cachedDoc;
10961
+ }
10962
+ // parse throws an GraphQLError in case of invalid query, should this be in try/catch?
10963
+ const parsedDoc = parse(inputString);
10964
+ if (!parsedDoc || parsedDoc.kind !== 'Document') {
10965
+ if (process.env.NODE_ENV !== 'production') {
10966
+ throw new Error('Invalid graphql doc');
10967
+ }
10968
+ return null;
10969
+ }
10970
+ // in-place substitution for removal of legacy and adding metaschema directives
10971
+ if (addMetaschemaDirectives) {
10972
+ metaschemaMapper(parsedDoc);
10973
+ }
10974
+ const parsedDocNoLoc = stripLocation(parsedDoc);
10975
+ docMap.set(operationKey, parsedDocNoLoc);
10976
+ return parsedDocNoLoc;
10977
+ }
10978
+ /**
10979
+ * If the input string has fragment substitution
10980
+ * Insert the fragments AST to the query document node
10981
+ */
10982
+ function insertFragments(doc, fragments) {
10983
+ fragments.forEach((fragment) => {
10984
+ // @ts-ignore
10985
+ // graphql describes definitions as "readonly"
10986
+ // so we aren't supposed to mutate the document node
10987
+ // but instead of parsing the fragment again, we substitute it's definition in the document node
10988
+ doc.definitions.push(fragment);
10989
+ });
10990
+ return doc;
10991
+ }
10992
+ function createReference(document) {
10993
+ const key = create(null);
10994
+ referenceMap.set(key, document);
10995
+ return key;
10996
+ }
10997
+ /**
10998
+ * Insert string and fragment substitutions with the actual nodes
10999
+ * @param inputString
11000
+ * @param substitutions - string | fragment DocumentNode
11001
+ * @returns { operation string, fragment docs [] }
11002
+ */
11003
+ function processSubstitutions(inputString, substitutions) {
11004
+ let outputString = '';
11005
+ const fragments = [];
11006
+ const subLength = substitutions.length;
11007
+ for (let i = 0; i < subLength; i++) {
11008
+ const substitution = substitutions[i];
11009
+ outputString += inputString[i];
11010
+ if (typeof substitution === 'string' || typeof substitution === 'number') {
11011
+ outputString += substitution;
11012
+ }
11013
+ else if (typeof substitution === 'object') {
11014
+ const doc = referenceMap.get(substitution);
11015
+ if (doc === undefined) {
11016
+ if (process.env.NODE_ENV !== 'production') {
11017
+ throw new Error('Invalid substitution fragment');
11018
+ }
11019
+ return null;
11020
+ }
11021
+ for (const def of doc.definitions) {
11022
+ fragments.push(def);
11023
+ }
11024
+ }
11025
+ else {
11026
+ if (process.env.NODE_ENV !== 'production') {
11027
+ throw new Error('Unsupported substitution type');
11028
+ }
11029
+ return null;
11030
+ }
11031
+ }
11032
+ return {
11033
+ operationString: outputString + inputString[subLength],
11034
+ fragments,
11035
+ };
11036
+ }
11037
+ /**
11038
+ * strips Document node and nested definitions of location references
11039
+ */
11040
+ function stripLocation(node) {
11041
+ if (node.loc) {
11042
+ delete node.loc;
11043
+ }
11044
+ const keys$1 = keys(node);
11045
+ const keysLength = keys$1.length;
11046
+ if (keysLength === 0) {
11047
+ return node;
11048
+ }
11049
+ for (const key of keys$1) {
11050
+ const subNode = node[key];
11051
+ if (subNode && typeof subNode === 'object') {
11052
+ stripLocation(subNode);
11053
+ }
11054
+ }
11055
+ return node;
11056
+ }
11057
+ /**
11058
+ *
11059
+ * @param astReference - ast reference passed from user land
11060
+ */
11061
+ const astResolver = function (astReference) {
11062
+ return referenceMap.get(astReference);
11063
+ };
11064
+ /**
11065
+ *
11066
+ * @param literals - operation query string
11067
+ * @param subs - all other substitutions
11068
+ * @returns an opaque reference to the parsed document
11069
+ */
11070
+ function gql(literals, ...subs) {
11071
+ let inputString;
11072
+ let inputSubstitutionFragments;
11073
+ if (!literals) {
11074
+ if (process.env.NODE_ENV !== 'production') {
11075
+ throw new Error('Invalid query');
11076
+ }
11077
+ return null;
11078
+ }
11079
+ else if (typeof literals === 'string') {
11080
+ inputString = literals.trim();
11081
+ inputSubstitutionFragments = [];
11082
+ }
11083
+ else {
11084
+ // called as template literal
11085
+ const sub = processSubstitutions(literals, subs);
11086
+ // if invalid fragment references found
11087
+ if (sub === null) {
11088
+ return null;
11089
+ }
11090
+ const { operationString, fragments } = sub;
11091
+ inputString = operationString.trim();
11092
+ inputSubstitutionFragments = fragments;
11093
+ }
11094
+ if (inputString.length === 0) {
11095
+ if (process.env.NODE_ENV !== 'production') {
11096
+ throw new Error('Invalid query');
11097
+ }
11098
+ return null;
11099
+ }
11100
+ const document = parseDocument(inputString);
11101
+ if (document === null) {
11102
+ return null;
11103
+ }
11104
+ if (inputSubstitutionFragments.length === 0) {
11105
+ return createReference(document);
11106
+ }
11107
+ return createReference(insertFragments(document, inputSubstitutionFragments));
11108
+ }
11109
+ /**
11110
+ * Enable the parser to add corresponding metaschema directives for backwards compatibility
11111
+ */
11112
+ function enableAddMetaschemaDirective() {
11113
+ addMetaschemaDirectives = true;
11114
+ }
11115
+ function disableAddMetaschemaDirective() {
11116
+ addMetaschemaDirectives = false;
11117
+ }
11118
+
10803
11119
  /*
10804
11120
  Deprecated - Remove after existing usages are removed.
10805
11121
  */
@@ -10811,7 +11127,11 @@ function parseAndVisit(source) {
10811
11127
  exports.GraphQLScalarType = GraphQLScalarType;
10812
11128
  exports.GraphQLSchema = GraphQLSchema;
10813
11129
  exports.Kind = Kind;
11130
+ exports.astResolver = astResolver;
10814
11131
  exports.buildASTSchema = buildASTSchema;
11132
+ exports.disableAddMetaschemaDirective = disableAddMetaschemaDirective;
11133
+ exports.enableAddMetaschemaDirective = enableAddMetaschemaDirective;
11134
+ exports.gql = gql;
10815
11135
  exports.isScalarType = isScalarType;
10816
11136
  exports.parse = parse;
10817
11137
  exports.parseAndVisit = parseAndVisit;