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