🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

swagger-client-autogen

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

swagger-client-autogen

Swagger/OpenAPI에서 TypeScript API 클라이언트 코드를 자동 생성하는 CLI 도구

latest
Source
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

🚀 swagger-client-autogen

Swagger/OpenAPI에서 TypeScript API 클라이언트 코드를 자동 생성하는 CLI 도구

npm version License: MIT

🎯 한 번의 명령어로 완전한 TypeScript API 클라이언트를 생성하세요!

✨ 주요 기능

  • 🚀 인터랙티브 초기화 - init 명령어로 빠른 설정
  • 📝 TypeScript 완벽 지원 - 완전한 타입 안정성
  • Ky HTTP 클라이언트 - 현대적이고 가벼운 HTTP 클라이언트
  • 🔄 TanStack Query 통합 - React Query hooks 자동 생성
  • 🎯 전역 Mutation Effects - 중앙화된 쿼리 무효화 관리
  • 🛡️ Zod 스키마 생성 - 런타임 타입 검증 (선택사항)
  • 📁 모듈러 구조 - 태그별 코드 분리

📋 목차

🚀 Quick Start

설치

# 프로젝트에 개발 의존성으로 설치 (권장)
npm install -D swagger-client-autogen

# 또는 yarn
yarn add -D swagger-client-autogen

[!TIP]
💡 개발 의존성 설치를 권장하는 이유:
🎯 타입 지원: 설정 파일 작성 시 InputCodegenConfig 타입 힌트 및 자동완성

📋 package.json 스크립트 활용 (선택사항):

{
  "scripts": {
    "api:init": "swagger-client-autogen init",
    "api:fetch": "swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.yml",
    "api:generate": "swagger-client-autogen generate --config swagger/config.ts"
  }
}

그러면 npm run api:generate로 간편하게 실행할 수 있습니다.

초기화 (init)

🎯 인터랙티브 설정으로 빠른 시작!

# 1. 프로젝트 루트에서 초기화 실행
npx swagger-client-autogen init

대화형 질문에 답변하세요:

🚀 swagger-client-autogen 초기화

💡 지원하는 입력 형식
│ 로컬 파일: ./swagger.yml, api/swagger.json
│ 원격 URL: https://api.example.com/swagger.json
│ 개발 서버: http://localhost:3000/api-docs

📋 Swagger JSON/YAML 경로: https://api.example.com/swagger.json
🔒 인증이 필요한 API인가요? No
📝 Zod 스키마를 생성할까요? Yes
📂 Config 파일명: swagger/config.ts

API 클라이언트 생성

# 2. Swagger 파일 다운로드 및 병합
npx swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.yml

# 3. API 클라이언트 코드 생성
npx swagger-client-autogen generate --config swagger/config.ts

생성된 파일들

src/
├── shared/api/                    # 공통 API 파일들
│   ├── dto.ts                     # TypeScript 타입 정의
│   ├── schema.gen.ts              # Zod 스키마 (선택)
│   ├── utils.gen.ts               # API 유틸리티
│   ├── stream.gen.ts              # 스트림 유틸리티
│   └── type-guards.gen.ts         # 타입 가드
└── entities/{moduleName}/api/     # 모듈별 API 파일들
    ├── index.ts                   # API 클라이언트 클래스
    ├── instance.ts                # API 클라이언트 인스턴스
    ├── queries.ts                 # TanStack Query hooks
    └── mutations.ts               # TanStack Query mutations

✨ 바로 사용 가능한 코드가 생성됩니다!

import { useGetUsersQuery, usePostUsersMutation } from '@/entities/users/api';

// GET 요청 - Query Hook
const { data: users, isLoading } = useGetUsersQuery();

// POST 요청 - Mutation Hook
const createUserMutation = usePostUsersMutation({
  onSuccess: (data) => {
    console.log('사용자 생성 성공:', data);
  }
});

// 뮤테이션 실행
createUserMutation.mutate({
  payload: { name: 'John', email: 'john@example.com' },
});

📖 사용법

CLI 명령어

init - 초기화

npx swagger-client-autogen init

인터랙티브 방식으로 설정 파일을 생성합니다. 이곳에서 생성된 설정 파일은 fetch, generate 명령에서 사용됩니다.

fetch - Swagger 파일 다운로드

npx swagger-client-autogen fetch --config swagger/config.ts --output swagger/api.yml

Config 파일에서 지정된 웹 URL로부터 Swagger 파일을 다운로드하고 병합합니다.

옵션:

  • --config, -c: 설정 파일 경로 (필수)
  • --output, -o: 출력 파일 경로 (선택, 기본값: swagger/{title}.yml)

generate - 코드 생성

npx swagger-client-autogen generate --config swagger/config.ts

API 클라이언트 코드를 생성합니다.

도움말

npx swagger-client-autogen --help
npx swagger-client-autogen init --help

설정 파일

init 명령어로 생성되는 설정 파일 예시:

// swagger/config.ts
import type { InputCodegenConfig } from 'swagger-client-autogen';

const config: InputCodegenConfig = {
  // Swagger 설정
  uri: 'https://api.example.com/swagger.json',
  
  // 인증 정보 (선택)
  username: 'your-username',
  password: 'your-password',
  
  // 스키마 생성 여부
  createSchema: true,

  // 출력 설정 (필요에 따라 주석 해제하여 사용)
  /*
  customOutput: {
    aliasInfo: {
      aliasMap: { '@': 'src' },  // path alias 설정
      aliasMapDepth: 2,          // alias 탐색 깊이
    },
    pathInfo: {
      dto: 'src/shared/api/dto.ts',
      api: 'src/entities/{moduleName}/api/index.ts',
      apiInstance: 'src/entities/{moduleName}/api/instance.ts',
      queries: 'src/entities/{moduleName}/api/queries.ts',
      mutations: 'src/entities/{moduleName}/api/mutations.ts',
      schema: 'src/shared/api/schema.gen.ts',
      apiUtils: 'src/shared/api/utils.gen.ts',
      streamUtils: 'src/shared/api/stream.gen.ts',
      typeGuards: 'src/shared/api/type-guards.gen.ts',
      streamHandlers: 'src/entities/{moduleName}/api/stream-handlers',
    },
  },
  */
};

export default config;

⚙️ 설정 옵션

기본 설정

옵션타입기본값설명
uristring-Swagger JSON/YAML 파일 경로 또는 URL
createSchemabooleanfalseZod 스키마 생성 여부

인증 설정

swagger에 인증 정보가 필요한 경우에만 입력해주세요.

{
  // 최상위 레벨에 설정
  username: 'your-username',
  password: 'your-password'
}

고급 옵션 (customOutput)

{
  customOutput: {
    aliasInfo: {
      aliasMap: { '@': 'src' },      // path alias 설정
      aliasMapDepth: 2               // alias 탐색 깊이
    },
    pathInfo: {
      dto: 'src/shared/api/dto.ts',
      api: 'src/entities/{moduleName}/api/index.ts',
      apiInstance: 'src/entities/{moduleName}/api/instance.ts',
      queries: 'src/entities/{moduleName}/api/queries.ts',
      mutations: 'src/entities/{moduleName}/api/mutations.ts',
      schema: 'src/shared/api/schema.gen.ts',
      // ... 기타 경로 설정
    }
  }
}

💡 개선된 점:

  • pathInfo에서 alias 필드 제거 - 중복 제거!
  • aliasMap을 기반으로 alias 자동 계산
  • 더 간결하고 DRY한 설정 구조

Swagger x- 프로퍼티 (커스텀 확장)

Swagger 스펙에 x- 프로퍼티를 추가하여 코드 생성을 세밀하게 제어할 수 있습니다.

x-ignore - 엔드포인트 무시

특정 엔드포인트의 코드 생성을 건너뛸 수 있습니다.

paths:
  /health:
    x-ignore: true  # 이 경로의 모든 메서드 무시
    get:
      summary: Health Check
      # ... (코드 생성되지 않음)

  /users:
    get:
      x-ignore: true  # GET /users만 무시
      # ...
    post:
      # POST /users는 정상적으로 생성됨
      # ...

x-staleTime - TanStack Query staleTime 설정

Query의 staleTime을 엔드포인트별로 설정할 수 있습니다.

paths:
  /users:
    get:
      x-staleTime: '5m'  # 5분
      summary: Get Users
      # ...

  /config:
    get:
      x-staleTime: 'Infinity'  # 무한대 (수동 무효화 전까지 fresh 유지)
      summary: Get Config
      # ...

  /static-data:
    get:
      x-staleTime: 'static'  # 절대 stale로 간주되지 않음 (함수로 사용 시 항상 0 반환)
      summary: Get Static Data
      # ...

지원 형식:

  • 숫자: 밀리초 단위 (예: 300000)
  • 시간 단위: '5h', '30m', '45s', '1h30m45s'
  • 'Infinity': 무한대 (수동 무효화 전까지 fresh 유지)
  • 'static': 절대 stale로 간주되지 않음 (함수로 사용 시 항상 0 반환)

생성되는 코드:

const queries = {
  getUsers: ({ kyInstance, options }) => queryOptions({
    queryKey: USERS_QUERY_KEY.GET_USERS(),
    queryFn: () => usersApi.getUsers({ kyInstance, options }),
    staleTime: 5 * 60 * 1000 // 5분
  }),
};

x-gcTime - TanStack Query gcTime 설정

Query의 gcTime (가비지 컬렉션 시간)을 엔드포인트별로 설정할 수 있습니다.

paths:
  /users:
    get:
      x-gcTime: '10m'  # 10분
      summary: Get Users
      # ...

  /permanent-cache:
    get:
      x-gcTime: 'Infinity'  # 가비지 컬렉션 비활성화
      summary: Get Permanent Data
      # ...

지원 형식:

  • 숫자: 밀리초 단위
  • 시간 단위: '5h', '30m', '45s', '1h30m45s'
  • 'Infinity': 가비지 컬렉션 비활성화

생성되는 코드:

const queries = {
  getUsers: ({ kyInstance, options }) => queryOptions({
    queryKey: USERS_QUERY_KEY.GET_USERS(),
    queryFn: () => usersApi.getUsers({ kyInstance, options }),
    gcTime: 10 * 60 * 1000 // 10분
  }),
};

x- 프로퍼티 병합

fetch 명령어를 실행할 때 기존 파일의 x- 프로퍼티가 자동으로 유지됩니다.

npx swagger-client-autogen fetch --config swagger/config.ts
유지된 x- 프로퍼티:
┌─────────┬──────────────┬──────────┬─────────────────────────────┐
│ (index) │ 엔드포인트   │ 메서드   │ x- 프로퍼티                 │
├─────────┼──────────────┼──────────┼─────────────────────────────┤
│    0    │ '/health'    │ '-'      │ 'x-ignore'                  │
│    1    │ '/users'     │ 'GET'    │ 'x-staleTime, x-gcTime'     │
└─────────┴──────────────┴──────────┴─────────────────────────────┘

🎯 생성되는 코드

API 클라이언트 클래스

// src/entities/user/api/index.ts
import type { KyInstance, Options } from 'ky';
import { z } from 'zod';
import type { GetUserResponseDto, CreateUserRequestDto, CreateUserResponseDto } from '@/shared/api/dto';
import { getUserResponseDtoSchema, createUserRequestDtoSchema, createUserResponseDtoSchema } from '@/shared/api/schema.gen';
import { validateSchema } from '@/shared/api/utils.gen';

export class UserApi {
  private readonly instance: KyInstance;

  constructor(instance: KyInstance) {
    this.instance = instance;
  }

  /**
   * @tags users
   * @summary Get User
   * @request GET:/users/{id}
   */
  async getUser({
    id,
    kyInstance,
    options,
  }: TUserApiRequestParameters['getUser']) {
    const instance = kyInstance ?? this.instance;

    const response = await instance
      .get<GetUserResponseDto>(`users/${id}`, {
        ...options,
      })
      .json();

    const validateResponse = validateSchema(getUserResponseDtoSchema, response);
    return validateResponse;
  }

  /**
   * @tags users
   * @summary Create User
   * @request POST:/users
   */
  async createUser({
    payload,
    kyInstance,
    options,
  }: TUserApiRequestParameters['createUser']) {
    const instance = kyInstance ?? this.instance;
    const validatedPayload = validateSchema(createUserRequestDtoSchema, payload);

    const response = await instance
      .post<CreateUserResponseDto>(`users`, {
        json: validatedPayload,
        ...options,
      })
      .json();

    const validateResponse = validateSchema(createUserResponseDtoSchema, response);
    return validateResponse;
  }
}

export type TUserApiRequestParameters = {
  getUser: {
    id: number;
    kyInstance?: KyInstance;
    options?: Options;
  };
  createUser: {
    payload: CreateUserRequestDto;
    kyInstance?: KyInstance;
    options?: Options;
  };
};

API 클라이언트 인스턴스

// src/entities/user/api/instance.ts
import { UserApi } from './index';

// API 클래스의 인스턴스를 생성하여 내보냄
export const userApi = new UserApi();

TypeScript 타입

// src/shared/api/dto.ts
export type GetUserResponseDto = {
  id: number;
  name: string;
  email: string;
  createdAt: string;
}

export type CreateUserRequestDto = {
  name: string;
  email: string;
}

Zod 스키마

// src/shared/api/schema.gen.ts
import { z } from 'zod';

export const GetUserResponseSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  createdAt: z.string()
});

export const CreateUserRequestSchema = z.object({
  name: z.string().min(1),
  email: z.string().email()
});

TanStack Query Hooks

📋 queries.ts - 쿼리 훅

// src/entities/users/api/queries.ts
import type { DefaultError, UseQueryOptions } from '@tanstack/react-query';
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
import type { UserResponseDto, UnknownConceptReadResponseDto } from '@/shared/api/dto';
import type { TUsersApiRequestParameters } from './index';
import { usersApi } from './instance';

// Query Keys
export const USERS_QUERY_KEY = {
  GET_USERS: () => ['users'],
  GET_USERS_USERID: (userId: string) => ['users', userId],
  GET_USERS_USERID_UNKNOWN_CONCEPTS: (userId: string, params?: any) => 
    ['users', userId, 'unknown_concepts', params],
};

// Query 객체 (재사용 가능)
const queries = {
  getUsers: ({ kyInstance, options }: TUsersApiRequestParameters['getUsers']) => ({
    queryKey: USERS_QUERY_KEY.GET_USERS(),
    queryFn: () => usersApi.getUsers({ kyInstance, options }),
  }),
  getUsersByUserId: ({ userId, kyInstance, options }: TUsersApiRequestParameters['getUsersByUserId']) => ({
    queryKey: USERS_QUERY_KEY.GET_USERS_USERID(userId),
    queryFn: () => usersApi.getUsersByUserId({ userId, kyInstance, options }),
  }),
};

export { queries as usersQueries };

// Query Hooks
export const useGetUsersQuery = <TData = UserResponseDto[]>(
  requestArgs: TUsersApiRequestParameters['getUsers'],
  options?: Omit<UseQueryOptions<UserResponseDto[], DefaultError, TData>, 'queryKey' | 'queryFn'>,
) => {
  return useQuery({
    ...queries.getUsers(requestArgs),
    ...options,
  });
};

// Suspense Query Hooks
export const useGetUsersSuspenseQuery = <TData = UserResponseDto[]>(
  requestArgs: TUsersApiRequestParameters['getUsers'],
  options?: Omit<UseQueryOptions<UserResponseDto[], DefaultError, TData>, 'queryKey' | 'queryFn'>,
) => {
  return useSuspenseQuery({
    ...queries.getUsers(requestArgs),
    ...options,
  });
};

🔄 mutations.ts - 뮤테이션 훅

// src/entities/users/api/mutations.ts
import type { DefaultError, UseMutationOptions } from '@tanstack/react-query';
import { useMutation } from '@tanstack/react-query';
import type { UserResponseDto } from '@/shared/api/dto';
import type { TUsersApiRequestParameters } from './index';
import { usersApi } from './instance';

// Mutation Keys
export const USERS_MUTATION_KEY = {
  POST_USERS: ['users'] as const,
  DELETE_USERS_USERID: ['users', 'userId'] as const,
};

// Mutation 객체 (재사용 가능)
const mutations = {
  postUsers: () => ({
    mutationFn: ({ payload, kyInstance, options }: TUsersApiRequestParameters['postUsers']) => {
      return usersApi.postUsers({ payload, kyInstance, options });
    },
    mutationKey: USERS_MUTATION_KEY.POST_USERS,
    meta: {
      mutationFnName: 'postUsers',
    }
  }),
};

export { mutations as usersMutations };

// Mutation Hooks
export const usePostUsersMutation = <TContext = unknown>(
  options?: Omit<
    UseMutationOptions<UserResponseDto, DefaultError, TUsersApiRequestParameters['postUsers'], TContext>,
    'mutationFn' | 'mutationKey'
  >,
) => {
  return useMutation({
    ...mutations.postUsers(),
    ...options,
  });
};

🔄 전역 Mutation Effects

swagger-client-autogen은 TanStack Query의 mutation 성공 시 전역적으로 쿼리를 무효화하거나 특정 로직을 실행할 수 있는 Global Mutation Effect 시스템을 제공합니다.

주요 이점

  • 🎯 중앙화된 부수 효과 관리: 모든 mutation의 부수 효과를 한 곳에서 관리
  • 🔄 자동 쿼리 무효화: mutation 성공 시 관련 쿼리를 자동으로 무효화
  • 🛡️ 타입 안정성: 완전한 TypeScript 타입 지원
  • 🎨 선택적 적용: 필요한 mutation만 선택적으로 전역 효과 적용

자동 생성되는 타입 파일

generate 명령어를 실행하면 global-mutation-effect.type.ts 파일이 자동으로 생성됩니다:

// src/shared/api/global-mutation-effect.type.ts

export type GlobalMutationEffectMap<M extends MutationMap> = Partial<{
  [K in keyof M]: {
    onSuccess: {
      invalidate: (
        data: ExtractMutationData<M, K>,
        variables: ExtractMutationVariables<M, K>,
        context: unknown,
        mutation: Mutation<...>,
      ) => void;
    };
  };
}>;

// 각 모듈별 타입
export type TUsersGlobalMutationEffects = GlobalMutationEffectMap<typeof usersMutations>;
export type TChatsGlobalMutationEffects = GlobalMutationEffectMap<typeof chatsMutations>;
// ...

// 통합 팩토리 타입
export type TGlobalMutationEffectFactory = (
  queryClient: QueryClient,
) => Partial<
  TUsersGlobalMutationEffects &
  TChatsGlobalMutationEffects &
  // ...
>;

사용 예시

1. 전역 Mutation Effects 구현

// src/shared/api/global-mutation-effects.ts
import type { QueryClient } from '@tanstack/react-query';
import { queryClient } from '@/app/provider/tanstack-query';
import { usersQueries } from '@/entities/users/api/queries';
import type {
  TUsersGlobalMutationEffects,
  TGlobalMutationEffectFactory,
} from './global-mutation-effect.type';

export const globalMutationEffects: TGlobalMutationEffectFactory = (queryClient) => ({
  ...userGlobalMutationEffects(queryClient),
  // 다른 모듈의 effects도 여기에 추가
});

export const isGlobalMutationEffectKey = (
  key: unknown
): key is keyof ReturnType<typeof globalMutationEffects> => {
  return typeof key === 'string' &&
         Object.keys(globalMutationEffects(queryClient)).includes(key);
};

function userGlobalMutationEffects(
  queryClient: QueryClient
): TUsersGlobalMutationEffects {
  return {
    // mutation 함수명을 키로 사용
    postUsers: {
      onSuccess: {
        invalidate: (_data, variables) => {
          // 관련 쿼리 무효화
          queryClient.invalidateQueries({
            queryKey: usersQueries.getUsers({}).queryKey,
            exact: true,
          });
        },
      },
    },
  };
}

2. TanStack Query Provider 설정

// src/app/provider/tanstack-query.tsx
import { MutationCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { globalMutationEffects, isGlobalMutationEffectKey } from '@/shared/api/global-mutation-effects';

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: (failureCount, error) => {
        return failureCount < 2;
      },
    },
  },
  mutationCache: new MutationCache({
    onSuccess: async (_data, _variables, _context, mutation) => {
      const disableGlobalInvalidation = mutation.options.meta?.disableGlobalInvalidation;
      const mutationFnName = mutation.options.meta?.mutationFnName;

      // 전역 무효화 비활성화 체크
      if (disableGlobalInvalidation) {
        return;
      }

      // 전역 mutation effect 실행
      if (isGlobalMutationEffectKey(mutationFnName)) {
        const invalidate = globalMutationEffects(queryClient)[mutationFnName]?.onSuccess.invalidate;

        if (invalidate) {
          invalidate(_data as never, _variables as never, _context as never, mutation as never);
          return; // 전역 효과를 실행했으면 entity 단위 무효화는 하지 않음
        }
      }

      // entity 단위 기본 무효화 (전역 효과가 없는 경우)
      const mutationKey = mutation.options.mutationKey;
      if (!mutationKey) return;

      await queryClient.invalidateQueries({
        queryKey: [mutationKey?.at(0)],
        exact: false,
      });
    },
  }),
});

export const TanstackQueryProvider = ({ children }: PropsWithChildren) => {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
};

3. 전역 무효화 비활성화 (선택적)

특정 mutation 호출 시 전역 무효화를 비활성화하려면:

const mutation = usePostUsersMutation({
  meta: {
    disableGlobalInvalidation: true, // 전역 무효화 비활성화
  },
  onSuccess: (data) => {
    // 커스텀 로직만 실행
  },
});

자세한 가이드

전역 Mutation Effects 시스템에 대한 자세한 설명은 다음 문서를 참고하세요:

📖 전역 Mutation Effect 가이드

이 가이드에서는 다음 내용을 다룹니다:

  • 아키텍처 상세 설명
  • 작동 원리
  • 다양한 사용 패턴
  • 모범 사례
  • 트러블슈팅

🔧 개발 가이드

프로젝트 구조

swagger-client-autogen/
├── scripts/           # CLI 스크립트
│   ├── cli.ts        # 메인 CLI
│   ├── init.ts       # 초기화 스크립트
│   ├── fetch-swagger.js
│   └── generate-all.js
├── templates/         # 코드 생성 템플릿
├── config-builders/   # 설정 빌더
├── utils/            # 유틸리티 함수
└── types/            # 타입 정의

빌드 방법

# 개발 환경 설정
git clone https://github.com/your-org/swagger-client-autogen
cd swagger-client-autogen
yarn install

# 빌드
yarn build

# 개발 모드 (watch)
yarn build:watch

기여하기

  • 이슈 생성 또는 기존 이슈 확인
  • 브랜치 생성: git checkout -b feature/amazing-feature
  • 변경사항 커밋: git commit -m 'Add amazing feature'
  • 푸시: git push origin feature/amazing-feature
  • Pull Request 생성

❓ FAQ

Q: Swagger 파일이 인증이 필요한 경우는?

A: init 명령어 실행 시 인증 정보를 입력하거나, 설정 파일에 직접 추가하세요.

{
  username: 'your-username',
  password: 'your-password'
}

Q: 생성된 코드를 커스터마이징하고 싶다면?

A: 현재는 템플릿 커스터마이징을 지원하지 않습니다. 생성된 코드를 직접 수정하거나, 래퍼 함수를 만들어 사용하세요.

Q: TypeScript 대신 JavaScript로 생성할 수 있나요?

A: TypeScript만 지원합니다.

🐛 트러블슈팅

Swagger 파일 다운로드 실패

Error: Failed to fetch swagger file

해결 방법:

  • URL이 올바른지 확인
  • 인증이 필요한 경우 username, password 설정 확인

생성된 코드에서 타입 오류

Type 'unknown' is not assignable to type 'UserDto'

해결 방법:

  • Swagger 스키마가 올바르게 정의되었는지 확인

📄 라이선스

MIT License

🚀 Happy Coding!

이 도구가 도움이 되었다면 ⭐ 별표를 눌러주세요!

Keywords

swagger

FAQs

Package last updated on 30 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts