New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

grats

Package Overview
Dependencies
Maintainers
1
Versions
240
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

grats - npm Package Compare versions

Comparing version 0.0.4 to 0.0.5

10

dist/package.json
{
"name": "grats",
"version": "0.0.4",
"version": "0.0.5",
"main": "dist/src/index.js",

@@ -12,3 +12,3 @@ "bin": "dist/src/cli.js",

"scripts": {
"test": "ts-node --esm src/tests/test.ts",
"test": "ts-node src/tests/test.ts",
"integration-tests": "node src/tests/integration.mjs",

@@ -38,3 +38,7 @@ "build": "tsc --build",

},
"packageManager": "pnpm@8.1.1"
"packageManager": "pnpm@8.1.1",
"engines": {
"node": ">=16 <=21",
"pnpm": "^8"
}
}

@@ -65,3 +65,3 @@ /**

export declare function pluralTypeMissingParameter(): string;
export declare function expectedIdentifer(): string;
export declare function expectedIdentifier(): string;
export declare function killsParentOnExceptionWithWrongConfig(): string;

@@ -82,1 +82,7 @@ export declare function killsParentOnExceptionOnNullable(): string;

export declare function unresolvedTypeReference(): string;
export declare function expectedTypeAnnotationOnContext(): string;
export declare function expectedTypeAnnotationOfReferenceOnContext(): string;
export declare function expectedTypeAnnotationOnContextToBeResolvable(): string;
export declare function expectedTypeAnnotationOnContextToHaveDeclaration(): string;
export declare function unexpectedParamSpreadForContextParam(): string;
export declare function multipleContextTypes(): string;
"use strict";
exports.__esModule = true;
exports.defaultArgPropertyMissingName = exports.defaultArgElementIsNotAssignment = exports.defaultValueIsNotLiteral = exports.ambiguousNumberType = exports.expectedOneNonNullishType = exports.propertyFieldMissingType = exports.cannotResolveSymbolForDescription = exports.promiseMissingTypeArg = exports.methodMissingType = exports.gqlEntityMissingName = exports.enumVariantMissingInitializer = exports.enumVariantNotStringLiteral = exports.enumTagOnInvalidNode = exports.argNotTyped = exports.argNameNotLiteral = exports.argIsNotProperty = exports.argumentParamIsNotObject = exports.argumentParamIsMissingType = exports.typeNameDoesNotMatchExpected = exports.typeNameTypeNotStringLiteral = exports.typeNameMissingTypeAnnotation = exports.typeNameInitializerWrong = exports.typeNameInitializeNotString = exports.typeNameMissingInitializer = exports.typeNameNotDeclaration = exports.typeTagOnAliasOfNonObject = exports.typeTagOnUnamedClass = exports.inputFieldUntyped = exports.inputTypeFieldNotProperty = exports.inputTypeNotLiteral = exports.functionFieldNotNamedExport = exports.functionFieldDefaultExport = exports.functionFieldNotNamed = exports.functionFieldParentTypeNotValid = exports.functionFieldParentTypeMissing = exports.functionFieldNotTopLevel = exports.invalidReturnTypeForFunctionField = exports.invalidParentArgForFunctionField = exports.expectedUnionTypeReference = exports.expectedUnionTypeNode = exports.invalidUnionTagUsage = exports.invalidInputTagUsage = exports.invalidEnumTagUsage = exports.invalidInterfaceTagUsage = exports.invalidScalarTagUsage = exports.invalidTypeTagUsage = exports.invalidGratsTag = exports.wrongCasingForGratsTag = exports.killsParentOnExceptionOnWrongNode = exports.fieldTagOnWrongNode = void 0;
exports.unresolvedTypeReference = exports.invalidTypePassedToFieldFunction = exports.parameterPropertyMissingType = exports.parameterPropertyNotPublic = exports.parameterWithoutModifiers = exports.duplicateInterfaceTag = exports.duplicateTag = exports.implementsTagOnTypeAlias = exports.implementsTagOnInterface = exports.implementsTagOnClass = exports.implementsTagMissingValue = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedIdentifer = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = void 0;
exports.multipleContextTypes = exports.unexpectedParamSpreadForContextParam = exports.expectedTypeAnnotationOnContextToHaveDeclaration = exports.expectedTypeAnnotationOnContextToBeResolvable = exports.expectedTypeAnnotationOfReferenceOnContext = exports.expectedTypeAnnotationOnContext = exports.unresolvedTypeReference = exports.invalidTypePassedToFieldFunction = exports.parameterPropertyMissingType = exports.parameterPropertyNotPublic = exports.parameterWithoutModifiers = exports.duplicateInterfaceTag = exports.duplicateTag = exports.implementsTagOnTypeAlias = exports.implementsTagOnInterface = exports.implementsTagOnClass = exports.implementsTagMissingValue = exports.mergedInterfaces = exports.nonNullTypeCannotBeOptional = exports.killsParentOnExceptionOnNullable = exports.killsParentOnExceptionWithWrongConfig = exports.expectedIdentifier = exports.pluralTypeMissingParameter = exports.unknownGraphQLType = exports.unsupportedTypeLiteral = exports.defaultArgPropertyMissingInitializer = void 0;
var Extractor_1 = require("./Extractor");

@@ -72,3 +72,3 @@ // TODO: Move these to short URLS that are easier to keep from breaking.

function invalidParentArgForFunctionField() {
return "Expected `@".concat(Extractor_1.FIELD_TAG, "` function to have a first argument representing the type to extend.");
return "Expected `@".concat(Extractor_1.FIELD_TAG, "` function to have a first argument representing the type to extend. If you don't need access to the parent object in the function, you can name the variable `_` to indicate that it is unused. e.g. `function myField(_: ParentType) {}`");
}

@@ -153,7 +153,7 @@ exports.invalidParentArgForFunctionField = invalidParentArgForFunctionField;

function argumentParamIsMissingType() {
return "Expected GraphQL field arguments to have a TypeScript type. If there are no arguments, you can use `args: never`.";
return "Expected GraphQL field arguments to have a TypeScript type. If there are no arguments, you can use `args: unknown`.";
}
exports.argumentParamIsMissingType = argumentParamIsMissingType;
function argumentParamIsNotObject() {
return "Expected GraphQL field arguments to be typed using a literal object: `{someField: string}`.";
return "Expected GraphQL field arguments to be typed using a literal object: `{someField: string}`. If there are no arguments, you can use `args: unknown`.";
}

@@ -241,6 +241,6 @@ exports.argumentParamIsNotObject = argumentParamIsNotObject;

exports.pluralTypeMissingParameter = pluralTypeMissingParameter;
function expectedIdentifer() {
function expectedIdentifier() {
return "Expected an identifier.";
}
exports.expectedIdentifer = expectedIdentifer;
exports.expectedIdentifier = expectedIdentifier;
function killsParentOnExceptionWithWrongConfig() {

@@ -318,1 +318,27 @@ return "Unexpected `@".concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` tag. `@").concat(Extractor_1.KILLS_PARENT_ON_EXCEPTION_TAG, "` is only supported when the Grats config `nullableByDefault` is enabled.");

exports.unresolvedTypeReference = unresolvedTypeReference;
function expectedTypeAnnotationOnContext() {
return "Expected context parameter to have a type annotation. Grats validates that your context parameter is type-safe by checking all context values reference the same type declaration.";
}
exports.expectedTypeAnnotationOnContext = expectedTypeAnnotationOnContext;
function expectedTypeAnnotationOfReferenceOnContext() {
return "Expected context parameter's type to be a type reference Grats validates that your context parameter is type-safe by checking all context values reference the same type declaration.";
}
exports.expectedTypeAnnotationOfReferenceOnContext = expectedTypeAnnotationOfReferenceOnContext;
function expectedTypeAnnotationOnContextToBeResolvable() {
// TODO: Provide guidance?
// TODO: I don't think we have a test case that triggers this error.
return "Unable to resolve context parameter type. Grats validates that your context parameter is type-safe by checking all context values reference the same type declaration.";
}
exports.expectedTypeAnnotationOnContextToBeResolvable = expectedTypeAnnotationOnContextToBeResolvable;
function expectedTypeAnnotationOnContextToHaveDeclaration() {
return "Unable to locate the declaration of the context parameter's type. Grats validates that your context parameter is type-safe by checking all context values reference the same type declaration. Did you forget to import or define this type?";
}
exports.expectedTypeAnnotationOnContextToHaveDeclaration = expectedTypeAnnotationOnContextToHaveDeclaration;
function unexpectedParamSpreadForContextParam() {
return "Unexpected spread parameter in context parameter position. Grats expects the context parameter to be a single, explicitly typed, argument.";
}
exports.unexpectedParamSpreadForContextParam = unexpectedParamSpreadForContextParam;
function multipleContextTypes() {
return "Context argument's type does not match. Grats expects all resolvers that read the context argument to use the same type for that argument. Did you use the incorrect type in one of your resolvers?";
}
exports.multipleContextTypes = multipleContextTypes;

@@ -84,3 +84,3 @@ import { FieldDefinitionNode, InputValueDefinitionNode, NamedTypeNode, NameNode, TypeNode, StringValueNode, ConstValueNode, ConstDirectiveNode, EnumValueDefinitionNode, ConstObjectFieldNode, ConstObjectValueNode, ConstListValueNode } from "graphql";

collectArrayLiteral(node: ts.ArrayLiteralExpression): ConstListValueNode | null;
cellectObjectLiteral(node: ts.ObjectLiteralExpression): ConstObjectValueNode | null;
collectObjectLiteral(node: ts.ObjectLiteralExpression): ConstObjectValueNode | null;
collectObjectField(node: ts.ObjectLiteralElementLike): ConstObjectFieldNode | null;

@@ -93,2 +93,3 @@ collectArg(node: ts.TypeElement, defaults?: Map<string, ts.Expression> | null): InputValueDefinitionNode | null;

entityName(node: ts.ClassDeclaration | ts.MethodDeclaration | ts.MethodSignature | ts.PropertyDeclaration | ts.InterfaceDeclaration | ts.PropertySignature | ts.EnumDeclaration | ts.TypeAliasDeclaration | ts.FunctionDeclaration | ts.ParameterDeclaration, tag: ts.JSDocTag): NameNode | null;
validateContextParameter(node: ts.ParameterDeclaration): null | undefined;
methodDeclaration(node: ts.MethodDeclaration | ts.MethodSignature): FieldDefinitionNode | null;

@@ -95,0 +96,0 @@ collectMethodType(node: ts.TypeNode): TypeNode | null;

@@ -313,2 +313,6 @@ "use strict";

}
var context = node.parameters[2];
if (context != null) {
this.validateContextParameter(context);
}
var description = this.collectDescription(funcName);

@@ -579,7 +583,6 @@ if (!ts.isSourceFile(node.parent)) {

Extractor.prototype.symbolHasGqlTag = function (node) {
var _a;
var symbol = this.ctx.checker.getSymbolAtLocation(node);
if (symbol == null)
return false;
var declaration = (_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a[0];
var declaration = this.ctx.findSymbolDeclaration(symbol);
if (declaration == null)

@@ -721,3 +724,3 @@ return false;

}
if (argsType.kind === ts.SyntaxKind.NeverKeyword) {
if (argsType.kind === ts.SyntaxKind.UnknownKeyword) {
return [];

@@ -791,3 +794,3 @@ }

else if (ts.isObjectLiteralExpression(node)) {
return this.cellectObjectLiteral(node);
return this.collectObjectLiteral(node);
}

@@ -827,3 +830,3 @@ else if (ts.isArrayLiteralExpression(node)) {

};
Extractor.prototype.cellectObjectLiteral = function (node) {
Extractor.prototype.collectObjectLiteral = function (node) {
var e_8, _a;

@@ -1033,2 +1036,41 @@ var fields = [];

};
// Ensure the type of the ctx param resolves to the declaration
// annotated with `@gqlContext`.
Extractor.prototype.validateContextParameter = function (node) {
if (node.type == null) {
return this.report(node, E.expectedTypeAnnotationOnContext());
}
if (node.type.kind === ts.SyntaxKind.UnknownKeyword) {
// If the user just needs to define the argument to get to a later parameter,
// they can use `ctx: unknown` to safely avoid triggering a Grats error.
return;
}
if (!ts.isTypeReferenceNode(node.type)) {
return this.report(node.type, E.expectedTypeAnnotationOfReferenceOnContext());
}
// Check for ...
if (node.dotDotDotToken != null) {
return this.report(node.dotDotDotToken, E.unexpectedParamSpreadForContextParam());
}
var symbol = this.ctx.checker.getSymbolAtLocation(node.type.typeName);
if (symbol == null) {
return this.report(node.type.typeName, E.expectedTypeAnnotationOnContextToBeResolvable());
}
var declaration = this.ctx.findSymbolDeclaration(symbol);
if (declaration == null) {
return this.report(node.type.typeName, E.expectedTypeAnnotationOnContextToHaveDeclaration());
}
if (this.ctx.gqlContext == null) {
// This is the first typed context value we've seen...
this.ctx.gqlContext = {
declaration: declaration,
firstReference: node.type.typeName
};
}
else if (this.ctx.gqlContext.declaration !== declaration) {
return this.report(node.type.typeName, E.multipleContextTypes(), [
this.related(this.ctx.gqlContext.firstReference, "A different type reference was used here"),
]);
}
};
Extractor.prototype.methodDeclaration = function (node) {

@@ -1053,2 +1095,6 @@ var tag = this.findTag(node, exports.FIELD_TAG);

}
var context = node.parameters[1];
if (context != null) {
this.validateContextParameter(context);
}
var description = this.collectDescription(node.name);

@@ -1252,3 +1298,3 @@ var id = this.expectIdentifier(node.name);

}
return this.report(node, E.expectedIdentifer());
return this.report(node, E.expectedIdentifier());
};

@@ -1255,0 +1301,0 @@ Extractor.prototype.findTag = function (node, tagName) {

@@ -18,2 +18,10 @@ import { DefinitionNode, DocumentNode, FieldDefinitionNode, Location, NameNode } from "graphql";

/**
* Information about the GraphQL context type. We track the first value we see,
* and then validate that any other values we see are the same.
*/
type GqlContext = {
declaration: ts.Node;
firstReference: ts.Node;
};
/**
* Used to track TypeScript references.

@@ -36,2 +44,3 @@ *

_unresolvedTypes: Map<NameNode, ts.Symbol>;
gqlContext: GqlContext | null;
hasTypename: Set<string>;

@@ -42,2 +51,4 @@ constructor(options: ts.ParsedCommandLine, checker: ts.TypeChecker, host: ts.CompilerHost);

markUnresolvedType(node: ts.Node, name: NameNode): void;
findSymbolDeclaration(startSymbol: ts.Symbol): ts.Declaration | null;
resolveSymbol(startSymbol: ts.Symbol): ts.Symbol;
resolveTypes(doc: DocumentNode): DiagnosticsResult<DocumentNode>;

@@ -44,0 +55,0 @@ handleAbstractDefinitions(docs: GratsDefinitionNode[]): DiagnosticsResult<DefinitionNode[]>;

@@ -52,2 +52,5 @@ "use strict";

this._unresolvedTypes = new Map();
// The resolver context declaration, if it has been encountered.
// Gets mutated by Extractor.
this.gqlContext = null;
this.hasTypename = new Set();

@@ -79,7 +82,23 @@ this._options = options;

}
if (symbol.flags & ts.SymbolFlags.Alias) {
// Follow any aliases to get the real type declaration.
this._unresolvedTypes.set(name, this.resolveSymbol(symbol));
};
TypeContext.prototype.findSymbolDeclaration = function (startSymbol) {
var _a;
var symbol = this.resolveSymbol(startSymbol);
var declaration = (_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a[0];
return declaration !== null && declaration !== void 0 ? declaration : null;
};
// Follow symbol aliases until we find the original symbol. Accounts for
// cyclical aliases.
TypeContext.prototype.resolveSymbol = function (startSymbol) {
var symbol = startSymbol;
var visitedSymbols = new Set();
while (ts.SymbolFlags.Alias & symbol.flags) {
if (visitedSymbols.has(symbol)) {
throw new Error("Cyclical alias detected. Breaking resolution.");
}
visitedSymbols.add(symbol);
symbol = this.checker.getAliasedSymbol(symbol);
}
this._unresolvedTypes.set(name, symbol);
return symbol;
};

@@ -86,0 +105,0 @@ TypeContext.prototype.resolveTypes = function (doc) {

{
"name": "grats",
"version": "0.0.4",
"version": "0.0.5",
"main": "dist/src/index.js",

@@ -32,4 +32,8 @@ "bin": "dist/src/cli.js",

"packageManager": "pnpm@8.1.1",
"engines": {
"node": ">=16 <=21",
"pnpm": "^8"
},
"scripts": {
"test": "ts-node --esm src/tests/test.ts",
"test": "ts-node src/tests/test.ts",
"integration-tests": "node src/tests/integration.mjs",

@@ -36,0 +40,0 @@ "build": "tsc --build",

@@ -11,3 +11,3 @@ # -=[ ALPHA SOFTWARE ]=-

When you write your GraphQL server in TypeScript, your fields and resovlers
When you write your GraphQL server in TypeScript, your fields and resolvers
are _already_ annotated with type information. _Grats leverages your existing

@@ -18,3 +18,3 @@ type annotations to automatically extract an executable GraphQL schema from your

By making your TypeScript implementation the source of truth, you never have to
worry about valiating that your implementiaton matches your schema. Your
worry about valuating that your implementation matches your schema. Your
implementation _is_ your schema!

@@ -30,9 +30,9 @@

* [@mofeiZ](https://github.com/mofeiZ) and [@alunyov](https://github/alunyov) for their Relay hack-week project exploring a similar idea.
* [@josephsavona](https://github.com/josephsavona) for input on the design of [Relay Resolvers](https://relay.dev/docs/guides/relay-resolvers/) which inspired this project.
* [@bradzacher](https://github.com/bradzacher) for tips on how to handle TypeScript ASTs.
* Everyone who worked on Meta's Hack GraphQL server, the developer experince of which inspired this project.
* A number of other projects which seem to have explored similar ideas in the past:
* [ts2gql](https://github.com/convoyinc/ts2gql)
* [ts2graphql](https://github.com/cevek/ts2graphql)
* [typegraphql-reflection-poc](https://github.com/MichalLytek/typegraphql-reflection-poc)
- [@mofeiZ](https://github.com/mofeiZ) and [@alunyov](https://github/alunyov) for their Relay hack-week project exploring a similar idea.
- [@josephsavona](https://github.com/josephsavona) for input on the design of [Relay Resolvers](https://relay.dev/docs/guides/relay-resolvers/) which inspired this project.
- [@bradzacher](https://github.com/bradzacher) for tips on how to handle TypeScript ASTs.
- Everyone who worked on Meta's Hack GraphQL server, the developer experince of which inspired this project.
- A number of other projects which seem to have explored similar ideas in the past:
- [ts2gql](https://github.com/convoyinc/ts2gql)
- [ts2graphql](https://github.com/cevek/ts2graphql)
- [typegraphql-reflection-poc](https://github.com/MichalLytek/typegraphql-reflection-poc)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc