@suss/adapter-typescript 0.24.1 → 0.24.3

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/index.cjs CHANGED
@@ -12893,6 +12893,7 @@ function parameterName(segment, convention) {
12893
12893
  }
12894
12894
 
12895
12895
  // src/discovery/graphqlClientConstruction.ts
12896
+ var import_graphql2 = require("graphql");
12896
12897
  var import_picomatch2 = __toESM(require("picomatch"), 1);
12897
12898
  var import_ts_morph58 = require("ts-morph");
12898
12899
  var import_behavioral_ir5 = require("@suss/behavioral-ir");
@@ -12904,15 +12905,20 @@ function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
12904
12905
  const workspace = sole !== null ? boundWorkspaceFor(sole, packs) : null;
12905
12906
  const soleClient = sole !== null ? workspace !== null ? { ...sole, workspace } : sole : null;
12906
12907
  let registry = null;
12908
+ let defined = null;
12907
12909
  for (const summary of summaries) {
12908
12910
  if (!(0, import_behavioral_ir5.isGraphqlOperationBinding)(summary.identity.boundaryBinding)) {
12909
12911
  continue;
12910
12912
  }
12911
- const existing = (0, import_behavioral_ir5.readGraphqlMetadata)(summary) ?? {};
12913
+ const found = (0, import_behavioral_ir5.readGraphqlMetadata)(summary) ?? {};
12914
+ const existing = withoutFragmentsDefinedElsewhere(found, () => {
12915
+ defined ??= fragmentsRegisteredIn(sourceFiles);
12916
+ return defined;
12917
+ });
12912
12918
  const scoped = scopedWorkspaceFor(summary.location.file, scopes);
12913
12919
  const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : soleClient;
12914
12920
  const dangling = (existing.unresolvedFragments?.length ?? 0) > 0;
12915
- if (client === null && !dangling) {
12921
+ if (client === null && !dangling && existing === found) {
12916
12922
  continue;
12917
12923
  }
12918
12924
  if (dangling && registry === null) {
@@ -12925,6 +12931,56 @@ function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
12925
12931
  });
12926
12932
  }
12927
12933
  }
12934
+ function fragmentsRegisteredIn(sourceFiles) {
12935
+ const registered = /* @__PURE__ */ new Set();
12936
+ for (const sourceFile of sourceFiles) {
12937
+ sourceFile.forEachDescendant((node) => {
12938
+ if (!import_ts_morph58.Node.isCallExpression(node)) {
12939
+ return;
12940
+ }
12941
+ const argument = node.getArguments()[0];
12942
+ if (argument === void 0 || !import_ts_morph58.Node.isNoSubstitutionTemplateLiteral(argument)) {
12943
+ return;
12944
+ }
12945
+ for (const name of fragmentOnlyDocumentNames(argument.getLiteralText())) {
12946
+ registered.add(name);
12947
+ }
12948
+ });
12949
+ }
12950
+ return registered;
12951
+ }
12952
+ function fragmentOnlyDocumentNames(text) {
12953
+ if (!text.includes("fragment")) {
12954
+ return [];
12955
+ }
12956
+ let parsed;
12957
+ try {
12958
+ parsed = (0, import_graphql2.parse)(text);
12959
+ } catch {
12960
+ return [];
12961
+ }
12962
+ const names = [];
12963
+ for (const definition of parsed.definitions) {
12964
+ if (definition.kind !== "FragmentDefinition") {
12965
+ return [];
12966
+ }
12967
+ names.push(definition.name.value);
12968
+ }
12969
+ return names;
12970
+ }
12971
+ function withoutFragmentsDefinedElsewhere(metadata, definedInProject) {
12972
+ const unresolved = metadata.unresolvedFragments ?? [];
12973
+ if (unresolved.length === 0) {
12974
+ return metadata;
12975
+ }
12976
+ const defined = definedInProject();
12977
+ const left = unresolved.filter((name) => !defined.has(name));
12978
+ if (left.length === unresolved.length) {
12979
+ return metadata;
12980
+ }
12981
+ const { unresolvedFragments: _dropped, ...rest } = metadata;
12982
+ return left.length === 0 ? rest : { ...rest, unresolvedFragments: left };
12983
+ }
12928
12984
  function compileOperationScopes(packs) {
12929
12985
  return packs.flatMap((pack) => pack.graphqlOperationScopes ?? []).map((scope) => {
12930
12986
  const matchers2 = scope.files.map(
@@ -13979,7 +14035,7 @@ function isJsxOrFragment(node) {
13979
14035
  }
13980
14036
 
13981
14037
  // src/graphqlContract.ts
13982
- var import_graphql2 = require("graphql");
14038
+ var import_graphql3 = require("graphql");
13983
14039
  var parseCache = /* @__PURE__ */ new Map();
13984
14040
  function parseSchema(sdl) {
13985
14041
  const cached = parseCache.get(sdl);
@@ -13987,7 +14043,7 @@ function parseSchema(sdl) {
13987
14043
  return cached;
13988
14044
  }
13989
14045
  try {
13990
- const doc = (0, import_graphql2.parse)(sdl);
14046
+ const doc = (0, import_graphql3.parse)(sdl);
13991
14047
  parseCache.set(sdl, doc);
13992
14048
  return doc;
13993
14049
  } catch (_err) {
@@ -14009,7 +14065,7 @@ function deriveGraphqlContract(sdl, typeName, fieldName, framework) {
14009
14065
  args: (field.arguments ?? []).map((arg) => ({
14010
14066
  name: arg.name.value,
14011
14067
  type: typeNodeToShape(arg.type),
14012
- required: arg.type.kind === import_graphql2.Kind.NON_NULL_TYPE
14068
+ required: arg.type.kind === import_graphql3.Kind.NON_NULL_TYPE
14013
14069
  })),
14014
14070
  provenance: "derived",
14015
14071
  framework
@@ -14017,7 +14073,7 @@ function deriveGraphqlContract(sdl, typeName, fieldName, framework) {
14017
14073
  }
14018
14074
  function findField(doc, typeName, fieldName) {
14019
14075
  for (const def of doc.definitions) {
14020
- if (def.kind !== import_graphql2.Kind.OBJECT_TYPE_DEFINITION && def.kind !== import_graphql2.Kind.OBJECT_TYPE_EXTENSION) {
14076
+ if (def.kind !== import_graphql3.Kind.OBJECT_TYPE_DEFINITION && def.kind !== import_graphql3.Kind.OBJECT_TYPE_EXTENSION) {
14021
14077
  continue;
14022
14078
  }
14023
14079
  const node = def;
@@ -14033,10 +14089,10 @@ function findField(doc, typeName, fieldName) {
14033
14089
  return null;
14034
14090
  }
14035
14091
  function typeNodeToShape(node) {
14036
- if (node.kind === import_graphql2.Kind.NON_NULL_TYPE) {
14092
+ if (node.kind === import_graphql3.Kind.NON_NULL_TYPE) {
14037
14093
  return typeNodeToShape(node.type);
14038
14094
  }
14039
- if (node.kind === import_graphql2.Kind.LIST_TYPE) {
14095
+ if (node.kind === import_graphql3.Kind.LIST_TYPE) {
14040
14096
  return { type: "array", items: typeNodeToShape(node.type) };
14041
14097
  }
14042
14098
  return scalarOrRef(node);
package/dist/index.js CHANGED
@@ -12914,6 +12914,9 @@ function parameterName(segment, convention) {
12914
12914
  }
12915
12915
 
12916
12916
  // src/discovery/graphqlClientConstruction.ts
12917
+ import {
12918
+ parse as graphqlParse2
12919
+ } from "graphql";
12917
12920
  import picomatch2 from "picomatch";
12918
12921
  import {
12919
12922
  Node as Node55
@@ -12931,15 +12934,20 @@ function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
12931
12934
  const workspace = sole !== null ? boundWorkspaceFor(sole, packs) : null;
12932
12935
  const soleClient = sole !== null ? workspace !== null ? { ...sole, workspace } : sole : null;
12933
12936
  let registry = null;
12937
+ let defined = null;
12934
12938
  for (const summary of summaries) {
12935
12939
  if (!isGraphqlOperationBinding(summary.identity.boundaryBinding)) {
12936
12940
  continue;
12937
12941
  }
12938
- const existing = readGraphqlMetadata(summary) ?? {};
12942
+ const found = readGraphqlMetadata(summary) ?? {};
12943
+ const existing = withoutFragmentsDefinedElsewhere(found, () => {
12944
+ defined ??= fragmentsRegisteredIn(sourceFiles);
12945
+ return defined;
12946
+ });
12939
12947
  const scoped = scopedWorkspaceFor(summary.location.file, scopes);
12940
12948
  const client = scoped !== null ? { uri: null, uriRef: null, workspace: scoped } : soleClient;
12941
12949
  const dangling = (existing.unresolvedFragments?.length ?? 0) > 0;
12942
- if (client === null && !dangling) {
12950
+ if (client === null && !dangling && existing === found) {
12943
12951
  continue;
12944
12952
  }
12945
12953
  if (dangling && registry === null) {
@@ -12952,6 +12960,56 @@ function stampGraphqlClientRefs(summaries, sourceFiles, packs, resolution) {
12952
12960
  });
12953
12961
  }
12954
12962
  }
12963
+ function fragmentsRegisteredIn(sourceFiles) {
12964
+ const registered = /* @__PURE__ */ new Set();
12965
+ for (const sourceFile of sourceFiles) {
12966
+ sourceFile.forEachDescendant((node) => {
12967
+ if (!Node55.isCallExpression(node)) {
12968
+ return;
12969
+ }
12970
+ const argument = node.getArguments()[0];
12971
+ if (argument === void 0 || !Node55.isNoSubstitutionTemplateLiteral(argument)) {
12972
+ return;
12973
+ }
12974
+ for (const name of fragmentOnlyDocumentNames(argument.getLiteralText())) {
12975
+ registered.add(name);
12976
+ }
12977
+ });
12978
+ }
12979
+ return registered;
12980
+ }
12981
+ function fragmentOnlyDocumentNames(text) {
12982
+ if (!text.includes("fragment")) {
12983
+ return [];
12984
+ }
12985
+ let parsed;
12986
+ try {
12987
+ parsed = graphqlParse2(text);
12988
+ } catch {
12989
+ return [];
12990
+ }
12991
+ const names = [];
12992
+ for (const definition of parsed.definitions) {
12993
+ if (definition.kind !== "FragmentDefinition") {
12994
+ return [];
12995
+ }
12996
+ names.push(definition.name.value);
12997
+ }
12998
+ return names;
12999
+ }
13000
+ function withoutFragmentsDefinedElsewhere(metadata, definedInProject) {
13001
+ const unresolved = metadata.unresolvedFragments ?? [];
13002
+ if (unresolved.length === 0) {
13003
+ return metadata;
13004
+ }
13005
+ const defined = definedInProject();
13006
+ const left = unresolved.filter((name) => !defined.has(name));
13007
+ if (left.length === unresolved.length) {
13008
+ return metadata;
13009
+ }
13010
+ const { unresolvedFragments: _dropped, ...rest } = metadata;
13011
+ return left.length === 0 ? rest : { ...rest, unresolvedFragments: left };
13012
+ }
12955
13013
  function compileOperationScopes(packs) {
12956
13014
  return packs.flatMap((pack) => pack.graphqlOperationScopes ?? []).map((scope) => {
12957
13015
  const matchers2 = scope.files.map(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-typescript",
3
- "version": "0.24.1",
3
+ "version": "0.24.3",
4
4
  "type": "module",
5
5
  "description": "Read a TypeScript or JavaScript codebase with suss and check what each handler returns against every caller, spec and template it can see.",
6
6
  "keywords": [
@@ -48,11 +48,11 @@
48
48
  "typecheck": "tsc --noEmit"
49
49
  },
50
50
  "dependencies": {
51
- "@suss/behavioral-ir": "0.24.1",
52
- "@suss/datalog": "0.24.1",
53
- "@suss/extractor": "0.24.1",
54
- "@suss/resolution": "0.24.1",
55
- "@suss/values": "0.24.1",
51
+ "@suss/behavioral-ir": "0.24.3",
52
+ "@suss/datalog": "0.24.3",
53
+ "@suss/extractor": "0.24.3",
54
+ "@suss/resolution": "0.24.3",
55
+ "@suss/values": "0.24.3",
56
56
  "graphql": "^16.8.0",
57
57
  "picomatch": "^4.0.4",
58
58
  "ts-morph": "^23.0.0",