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

@rehearsal/reporter

Package Overview
Dependencies
Maintainers
3
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rehearsal/reporter - npm Package Compare versions

Comparing version 1.0.20-beta to 1.1.0

dist/src/formatters/index.d.ts

7

dist/src/formatters/json-formatter.d.ts

@@ -1,3 +0,6 @@

import type { Report } from '../types.js';
export declare function jsonFormatter(report: Report): string;
import type { Report, FormatterBase } from '../types.js';
export declare class JSONFormatter implements FormatterBase {
static extension: string;
static getReport(report: Report): string;
}
//# sourceMappingURL=json-formatter.d.ts.map

@@ -1,4 +0,7 @@

export function jsonFormatter(report) {
return JSON.stringify(report, null, 2);
export class JSONFormatter {
static getReport(report) {
return JSON.stringify(report, null, 2);
}
}
JSONFormatter.extension = '.json';
//# sourceMappingURL=json-formatter.js.map

@@ -1,3 +0,6 @@

import type { Report } from '../types.js';
export declare function mdFormatter(report: Report): string;
import type { Report, FormatterBase } from '../types.js';
export declare class MarkdownFormatter implements FormatterBase {
static extension: string;
static getReport(report: Report): string;
}
//# sourceMappingURL=md-formatter.d.ts.map

@@ -1,26 +0,28 @@

export function mdFormatter(report) {
const fileNames = [...new Set(report.items.map((item) => item.analysisTarget))];
let text = ``;
text += `### Summary:\n`;
for (const block of report.summary) {
text += `Project Name: ${block.projectName}\n`;
text += `Typescript Version: ${block.tsVersion}\n`;
text += `Base path: ${block.basePath}\n`;
text += `timestamp: ${block.timestamp}\n`;
text += `\n`;
}
text += `### Results:\n`;
for (const fileName of fileNames) {
const items = report.items.filter((item) => item.analysisTarget === fileName);
text += `\n`;
text += `#### File: ${fileName}, issues: ${items.length}:\n`;
for (const item of items) {
export class MarkdownFormatter {
static getReport(report) {
const fileNames = [...new Set(report.items.map((item) => item.analysisTarget))];
let text = ``;
text += `### Summary:\n`;
for (const block of report.summary) {
text += `Project Name: ${block.projectName}\n`;
text += `Typescript Version: ${block.tsVersion}\n`;
text += `timestamp: ${block.timestamp}\n`;
text += `\n`;
text += `**${item.category} ${item.ruleId}**: 'NEED TO BE FIXED MANUALLY'\n`;
text += `${item.hint}\n`;
text += `Code: \`${item.nodeText}\`\n`;
}
text += `### Results:\n`;
for (const fileName of fileNames) {
const items = report.items.filter((item) => item.analysisTarget === fileName);
text += `\n`;
text += `#### File: ${fileName}, issues: ${items.length}:\n`;
for (const item of items) {
text += `\n`;
text += `**${item.category} ${item.ruleId}**: 'REVIEW REQUIRED'\n`;
text += `${item.hint}\n`;
text += `Code: \`${item.nodeText}\`\n`;
}
}
return text;
}
return text;
}
MarkdownFormatter.extension = '.md';
//# sourceMappingURL=md-formatter.js.map

@@ -0,4 +1,5 @@

import type { Report, FormatterBase } from '../types.js';
import type { Log } from 'sarif';
import type { Report } from '../types.js';
export declare class SarifFormatter {
export declare class SarifFormatter implements FormatterBase {
static extension: string;
private report;

@@ -11,4 +12,5 @@ private rules;

constructor(report: Report);
format(): string;
static getReport(report: Report): string;
buildLog(): Log;
private format;
private buildRun;

@@ -22,4 +24,7 @@ private addRule;

private artifactExists;
private buildArtifact;
private kindConverter;
private levelConverter;
private createRun;
}
export declare function sarifFormatter(report: Report): string;
//# sourceMappingURL=sarif-formatter.d.ts.map

@@ -10,5 +10,6 @@ export class SarifFormatter {

}
format() {
return JSON.stringify(this.buildLog(), null, 2);
static getReport(report) {
return new SarifFormatter(report).format();
}
// only exposed for SonarqubeFormatter
buildLog() {

@@ -22,4 +23,7 @@ const runs = this.buildRun();

}
format() {
return JSON.stringify(this.buildLog(), null, 2);
}
buildRun() {
const run = createRun(this.report);
const run = this.createRun(this.report);
for (const item of this.report.items) {

@@ -61,3 +65,3 @@ this.addRule(item.ruleId, item.message);

if (!this.artifactExists(fileName)) {
const newArtifact = buildArtifact(fileName);
const newArtifact = this.buildArtifact(fileName);
this.artifacts.push(newArtifact);

@@ -75,4 +79,4 @@ this.artifactIndexMap[fileName] = this.artifacts.length - 1;

ruleIndex: this.ruleIndexMap[item.ruleId],
level: levelConverter(item.category),
kind: kindConverter(item.category),
level: this.levelConverter(item.category),
kind: this.kindConverter(item.category),
message: {

@@ -110,58 +114,55 @@ text: item.hint,

}
}
function buildArtifact(fileName) {
return {
location: {
uri: fileName,
},
};
}
function kindConverter(category) {
switch (category) {
case 'Warning':
case 'Suggestion':
case 'Message':
return 'review';
default:
return 'fail';
buildArtifact(fileName) {
return {
location: {
uri: fileName,
},
};
}
}
function levelConverter(category) {
switch (category) {
case 'Warning':
return 'warning';
case 'Error':
return 'error';
case 'Suggestion':
return 'note';
case 'Message':
return 'note';
default:
return 'none';
kindConverter(category) {
switch (category) {
case 'Warning':
case 'Suggestion':
case 'Message':
return 'review';
default:
return 'fail';
}
}
}
function createRun(report) {
return {
tool: {
driver: {
name: `${report.summary[0].commandName}`,
informationUri: 'https://github.com/rehearsal-js/rehearsal-js',
rules: [],
levelConverter(category) {
switch (category) {
case 'Warning':
return 'warning';
case 'Error':
return 'error';
case 'Suggestion':
return 'note';
case 'Message':
return 'note';
default:
return 'none';
}
}
createRun(report) {
return {
tool: {
driver: {
name: `rehearsal report`,
informationUri: 'https://github.com/rehearsal-js/rehearsal-js',
rules: [],
},
},
},
artifacts: [],
results: [],
automationDetails: {
description: {
//For sequential runs, the time difference between each run is minimal, and ts version should be the same.
//So printing out the first timestamp and first ts version.
text: `This is the result of ${report.summary[0].commandName} on your product against TypeScript ${report.summary[0].tsVersion} at ${report.summary[0].timestamp}`,
artifacts: [],
results: [],
automationDetails: {
description: {
//For sequential runs, the time difference between each run is minimal, and ts version should be the same.
//So printing out the first timestamp and first ts version.
text: `This is the result of running Rehearsal on your product against TypeScript ${report.summary[0].tsVersion} at ${report.summary[0].timestamp}`,
},
},
},
};
};
}
}
export function sarifFormatter(report) {
const formatter = new SarifFormatter(report);
return formatter.format();
}
SarifFormatter.extension = '.sarif';
//# sourceMappingURL=sarif-formatter.js.map

@@ -1,3 +0,6 @@

import type { Report } from '../types.js';
export declare function sonarqubeFormatter(report: Report): string;
import type { Report, FormatterBase } from '../types.js';
export declare class SonarqubeFormatter implements FormatterBase {
static extension: string;
static getReport(report: Report): string;
}
//# sourceMappingURL=sonarqube-formatter.d.ts.map
import { isAbsolute, resolve } from 'node:path';
import { SarifFormatter } from './sarif-formatter.js';
const SONARQUBE_SEVERITY = {
notApplicable: 'INFO',
pass: 'INFO',
fail: 'BLOCKER',
review: 'CRITICAL',
open: 'MINOR',
informational: 'INFO',
};
const SONARQUBE_TYPE = {
note: 'CODE_SMELL',
warning: 'CODE_SMELL',
error: 'BUG',
};
function getPhysicalLocation(result) {
return (result.locations && result.locations[0].physicalLocation) ?? undefined;
}
function getFilePath(physicalLocation) {
return physicalLocation?.artifactLocation?.uri ?? '';
}
// SonarQube Formatter will convert to SARIF first and then convert to SonarQube format
// We have to assume the default Report shape
export function sonarqubeFormatter(report) {
const issues = [];
const log = new SarifFormatter(report).buildLog();
const results = log.runs[0].results || [];
if (results.length > 0) {
for (const result of results) {
const physicalLocation = getPhysicalLocation(result);
const filePath = physicalLocation ? getFilePath(physicalLocation) : '';
const absolutePath = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath);
const textRange = getTextRange(physicalLocation);
issues.push({
engineId: 'rehearsal-ts',
ruleId: result.ruleId,
severity: SONARQUBE_SEVERITY[result.kind ?? 'notApplicable'],
type: SONARQUBE_TYPE[result.level],
primaryLocation: {
message: result.message.text ?? '',
filePath: absolutePath,
textRange,
},
});
export class SonarqubeFormatter {
// SonarQube Formatter will convert to SARIF first and then convert to SonarQube format
// We have to assume the default Report shape
static getReport(report) {
const issues = [];
const log = new SarifFormatter(report).buildLog();
const results = log.runs[0].results || [];
const SONARQUBE_SEVERITY = {
notApplicable: 'INFO',
pass: 'INFO',
fail: 'BLOCKER',
review: 'CRITICAL',
open: 'MINOR',
informational: 'INFO',
};
const SONARQUBE_TYPE = {
note: 'CODE_SMELL',
warning: 'CODE_SMELL',
error: 'BUG',
};
function getPhysicalLocation(result) {
return (result.locations && result.locations[0].physicalLocation) ?? undefined;
}
function getFilePath(physicalLocation) {
return physicalLocation?.artifactLocation?.uri ?? '';
}
// We bump column and line numbers by 1 for sarif reader.
// Now we revert back the numbers for sonarqube for column.
function decrementByOne(key, location) {
const region = location?.region;
if (region && Number.isInteger(region[key]) && region[key] > 1) {
return region[key] - 1;
}
return 0;
}
function getTextRange(location) {
return {
startLine: location?.region?.startLine || 0,
startColumn: decrementByOne('startColumn', location),
endLine: location?.region?.endLine || 0,
endColumn: decrementByOne('endColumn', location),
};
}
if (results.length > 0) {
for (const result of results) {
const physicalLocation = getPhysicalLocation(result);
const filePath = physicalLocation ? getFilePath(physicalLocation) : '';
const absolutePath = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath);
const textRange = getTextRange(physicalLocation);
issues.push({
engineId: 'rehearsal-ts',
ruleId: result.ruleId,
severity: SONARQUBE_SEVERITY[result.kind ?? 'notApplicable'],
type: SONARQUBE_TYPE[result.level],
primaryLocation: {
message: result.message.text ?? '',
filePath: absolutePath,
textRange,
},
});
}
}
return JSON.stringify({ issues }, null, 2) || '';
}
return JSON.stringify({ issues }, null, 2) || '';
}
function getTextRange(location) {
return {
startLine: location?.region?.startLine || 0,
startColumn: decrementByOne(location, 'startColumn'),
endLine: location?.region?.endLine || 0,
endColumn: decrementByOne(location, 'endColumn'),
};
}
//We bump column and line numbers by 1 for sarif reader. Now we revert back the numbers for sonarqube for column.
function decrementByOne(location, key) {
const region = location?.region;
if (region && Number.isInteger(region[key]) && region[key] > 1) {
return region[key] - 1;
}
return 0;
}
SonarqubeFormatter.extension = '.sonarqube.json';
//# sourceMappingURL=sonarqube-formatter.js.map

@@ -0,8 +1,5 @@

export * from './formatters/index.js';
export { Reporter } from './reporter.js';
export { jsonFormatter } from './formatters/json-formatter.js';
export { mdFormatter } from './formatters/md-formatter.js';
export { sarifFormatter } from './formatters/sarif-formatter.js';
export { sonarqubeFormatter } from './formatters/sonarqube-formatter.js';
export { normalizeFilePath } from './normalize-paths.js';
export type { Location, Report, ReportItem, ReportSummary, ReportFormatter } from './types.js';
export { ReportItemType } from './types.js';
export type { Location, Report, ReportItem, ReportSummary, ReportFormatter, Formatters, } from './types.js';
//# sourceMappingURL=index.d.ts.map

@@ -0,7 +1,4 @@

export * from './formatters/index.js';
export { Reporter } from './reporter.js';
export { jsonFormatter } from './formatters/json-formatter.js';
export { mdFormatter } from './formatters/md-formatter.js';
export { sarifFormatter } from './formatters/sarif-formatter.js';
export { sonarqubeFormatter } from './formatters/sonarqube-formatter.js';
export { normalizeFilePath } from './normalize-paths.js';
export { ReportItemType } from './types.js';
//# sourceMappingURL=index.js.map

@@ -1,9 +0,8 @@

import { type Report, type ReportFormatter, type ReportItem, type Location, type LintErrorLike, type Run } from './types.js';
import type { Report, ReportItem, Location, LintErrorLike, Run, Formatters } from './types.js';
import type { DiagnosticWithLocation, Node } from 'typescript';
import type { Logger } from 'winston';
type ReporterMeta = {
projectName: string;
basePath: string;
commandName: string;
projectRootDir: string;
tsVersion: string;
stemName?: string;
previousFixedCount?: number;

@@ -17,7 +16,7 @@ };

report: Report;
private logger?;
currentRun: Run;
lastRun: Run | undefined;
private uniqueFiles;
constructor(meta: ReporterMeta, logger?: Logger);
private stemName;
constructor(meta: ReporterMeta);
getFileNames(): string[];

@@ -30,26 +29,24 @@ getItemsByAnalysisTarget(fileName: string): ReportItem[];

/**
* Appends am information about provided diagnostic and related node to the report
* Appends a information about provided TS diagnostic and related node to the report
*/
addTSItemToRun(diagnostic: DiagnosticWithLocation, node?: Node, triggeringLocation?: Location, hint?: string, helpUrl?: string, hintAdded?: boolean): void;
/**
* Appends a information about provided Glint diagnostic and related node to the report
*/
addGlintItemToRun(diagnostic: DiagnosticWithLocation, node?: Node, triggeringLocation?: Location, hint?: string, helpUrl?: string, hintAdded?: boolean): void;
addLintItemToRun(fileName: string, lintError: LintErrorLike): void;
incrementRunFixedItemCount(): void;
saveCurrentRunToReport(runBasePath: string, runEntrypoint: string, timestamp?: string): void;
private mergeItems;
saveCurrentRunToReport(timestamp?: string): void;
/**
* Prints the current report using provided formatter (ex. json, pull-request etc.)
* Prints the reports using provided formatter
* json is always printed and set as default formatter
*/
printReport(file: string, formatter: ReportFormatter): string;
/**
* Saves the current report information to the file in simple JSON format
* to be able to load it later with 'load' function
*/
saveReport(file: string): void;
/**
* Loads the report exported by function 'save' from the file
*/
loadRun(file: string): Reporter;
printReport(dirPath: string, formatterTypes?: Formatters[]): string[];
private mergeItems;
private getFormatter;
private resetCurrentRun;
private static isReport;
private getTimestamp;
private normalizeFilePath;
}
export {};
//# sourceMappingURL=reporter.d.ts.map

@@ -1,5 +0,5 @@

import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join, isAbsolute, relative } from 'node:path';
import { writeFileSync, existsSync, mkdirSync } from 'node:fs';
import ts from 'typescript';
import { ReportItemType, } from './types.js';
import { normalizeFilePath } from './normalize-paths.js';
import { JSONFormatter, MarkdownFormatter, SonarqubeFormatter, SarifFormatter, } from './formatters/index.js';
const { DiagnosticCategory, flattenDiagnosticMessageText, SyntaxKind } = ts;

@@ -10,6 +10,7 @@ /**

export class Reporter {
constructor(meta, logger) {
const { projectName, basePath, commandName, tsVersion, previousFixedCount } = meta;
this.basePath = basePath;
this.logger = logger?.child({ service: 'rehearsal-reporter' });
constructor(meta) {
const { projectName, projectRootDir, tsVersion, previousFixedCount } = meta;
// do not include extension in the stemName
this.stemName = meta.stemName || 'rehearsal-report';
this.basePath = projectRootDir;
this.report = {

@@ -21,2 +22,5 @@ summary: [],

this.uniqueFiles = [];
// runSummary !== summary,
// summary is a list of all runs
// runSummary is a summary of the current run
this.currentRun = {

@@ -27,5 +31,2 @@ runSummary: {

timestamp: '',
basePath: '',
entrypoint: '',
commandName,
},

@@ -49,8 +50,8 @@ fixedItemCount: 0,

/**
* Appends am information about provided diagnostic and related node to the report
* Appends a information about provided TS diagnostic and related node to the report
*/
addTSItemToRun(diagnostic, node, triggeringLocation, hint = '', helpUrl = '', hintAdded = true) {
this.currentRun.items.push({
analysisTarget: normalizeFilePath(this.basePath, diagnostic.file.fileName),
type: ReportItemType.ts,
analysisTarget: this.normalizeFilePath(this.basePath, diagnostic.file.fileName),
type: 0,
ruleId: `TS${diagnostic.code}`,

@@ -67,6 +68,24 @@ category: DiagnosticCategory[diagnostic.category],

}
/**
* Appends a information about provided Glint diagnostic and related node to the report
*/
addGlintItemToRun(diagnostic, node, triggeringLocation, hint = '', helpUrl = '', hintAdded = true) {
this.currentRun.items.push({
analysisTarget: this.normalizeFilePath(this.basePath, diagnostic.file.fileName),
type: 2,
ruleId: `Glint${diagnostic.code}`,
category: DiagnosticCategory[diagnostic.category],
message: flattenDiagnosticMessageText(diagnostic.messageText, '. ').replace(this.basePath, '.'),
hint: hint,
hintAdded,
nodeKind: node ? SyntaxKind[node.kind] : undefined,
nodeText: node?.getText(),
helpUrl,
nodeLocation: triggeringLocation || undefined,
});
}
addLintItemToRun(fileName, lintError) {
this.currentRun.items.push({
analysisTarget: normalizeFilePath(this.basePath, fileName),
type: ReportItemType.lint,
analysisTarget: this.normalizeFilePath(this.basePath, fileName),
type: 1,
// errors generated by rules or errors generated by Eslint core

@@ -94,17 +113,4 @@ ruleId: lintError.ruleId || 'error-generated-by-Eslint-core',

}
saveCurrentRunToReport(runBasePath, runEntrypoint, timestamp) {
timestamp =
timestamp ??
new Date().toLocaleString('en-US', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hourCycle: 'h24',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
this.currentRun.runSummary.timestamp = timestamp;
this.currentRun.runSummary.basePath = runBasePath;
this.currentRun.runSummary.entrypoint = runEntrypoint || '';
saveCurrentRunToReport(timestamp) {
this.currentRun.runSummary.timestamp = timestamp || this.getTimestamp();
this.report.summary = [...this.report.summary, { ...this.currentRun.runSummary }];

@@ -118,2 +124,23 @@ this.report.fixedItemCount += this.currentRun.fixedItemCount;

}
/**
* Prints the reports using provided formatter
* json is always printed and set as default formatter
*/
printReport(dirPath, formatterTypes = ['json']) {
const reports = [];
// always print json report
formatterTypes = Array.from(new Set([...formatterTypes, 'json']));
formatterTypes.forEach((format) => {
const [formatter, fileExtension] = this.getFormatter(format);
const report = formatter(this.report);
reports.push(report);
const reportPath = join(dirPath, `${this.stemName}${fileExtension}`);
if (!existsSync(dirPath)) {
mkdirSync(dirPath, { recursive: true });
}
// write each file as formatted to disk
writeFileSync(reportPath, report);
});
return reports;
}
mergeItems() {

@@ -136,49 +163,53 @@ const fileSet = new Set();

}
/**
* Prints the current report using provided formatter (ex. json, pull-request etc.)
*/
printReport(file, formatter) {
const report = formatter(this.report);
if (file) {
writeFileSync(file, report);
getFormatter(formatterType) {
// get the appropriate report from static methods of formatter
switch (formatterType) {
case 'json':
return [
(report) => JSONFormatter.getReport(report),
JSONFormatter.extension,
];
case 'sonarqube':
return [
(report) => SonarqubeFormatter.getReport(report),
SonarqubeFormatter.extension,
];
case 'md':
return [
(report) => MarkdownFormatter.getReport(report),
MarkdownFormatter.extension,
];
case 'sarif':
return [
(report) => SarifFormatter.getReport(report),
SarifFormatter.extension,
];
default:
break;
}
return report;
throw new Error(`Unknown formatter: ${formatterType}`);
}
/**
* Saves the current report information to the file in simple JSON format
* to be able to load it later with 'load' function
*/
saveReport(file) {
this.printReport(file, (report) => JSON.stringify(report, null, 2));
this.logger?.info(`Report saved to: ${file}.`);
}
/**
* Loads the report exported by function 'save' from the file
*/
loadRun(file) {
if (!existsSync(file)) {
this.logger?.error(`Report file not found: ${file}.`);
}
this.logger?.info(`Report file found: ${file}.`);
const content = readFileSync(file, 'utf-8');
const run = JSON.parse(content);
this.currentRun = run;
if (!Reporter.isReport(this.report)) {
this.logger?.error(`Report not loaded: wrong file format`);
return this;
}
this.logger?.info(`Report loaded from file.`);
return this;
}
resetCurrentRun() {
this.currentRun.runSummary.timestamp = '';
this.currentRun.runSummary.basePath = '';
this.currentRun.runSummary.entrypoint = '';
this.currentRun.fixedItemCount = 0;
this.currentRun.items = [];
}
static isReport(report) {
return report && report.summary !== undefined && report.items !== undefined;
getTimestamp() {
return new Date().toLocaleString('en-US', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hourCycle: 'h24',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}
normalizeFilePath(basePath, filepath) {
if (isAbsolute(filepath)) {
return relative(basePath, filepath);
}
return filepath;
}
}
//# sourceMappingURL=reporter.js.map

@@ -0,8 +1,6 @@

export type Formatters = 'json' | 'sonarqube' | 'md' | 'sarif';
export type ReportSummary = Record<string, unknown> & {
projectName: string;
basePath: string;
entrypoint: string;
tsVersion: string;
timestamp: string;
commandName: string;
};

@@ -17,3 +15,4 @@ export interface Location {

ts = 0,
lint = 1
lint = 1,
glint = 2
}

@@ -59,2 +58,6 @@ export type ReportItem = {

}
export declare class FormatterBase {
static extension: string;
static getReport(_report: Report): string;
}
//# sourceMappingURL=types.d.ts.map

@@ -5,3 +5,11 @@ export var ReportItemType;

ReportItemType[ReportItemType["lint"] = 1] = "lint";
})(ReportItemType = ReportItemType || (ReportItemType = {}));
ReportItemType[ReportItemType["glint"] = 2] = "glint";
})(ReportItemType || (ReportItemType = {}));
// ts doesnt allow for static properties on interfaces yet
export class FormatterBase {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
static getReport(_report) {
return '';
}
}
//# sourceMappingURL=types.js.map
{
"name": "@rehearsal/reporter",
"version": "1.0.20-beta",
"version": "1.1.0",
"description": "Rehearsal Reporter",

@@ -32,13 +32,12 @@ "keywords": [

"devDependencies": {
"@types/debug": "^4.1.7",
"@types/eslint": "^8.21.0",
"@types/sarif": "^2.1.4",
"fs-extra": "^11.1.0",
"vitest": "^0.29.2",
"@vitest/coverage-c8": "^0.30.1",
"fs-extra": "^11.1.1",
"vitest": "^0.30.0",
"vitest-mock-extended": "^1.1.3"
},
"peerDependencies": {
"typescript": "^5.0.0"
"typescript": "^5.1"
},
"packageManager": "pnpm@8.2.0",
"packageManager": "pnpm@8.6.7",
"engines": {

@@ -55,4 +54,3 @@ "node": ">=14.16.0"

"lint:tsc-test": "tsc --noEmit --project test/tsconfig.json",
"test": "vitest --run --config ./vitest.config.ts",
"test:slow": "vitest --run",
"test": "vitest --run --config ./vitest.config.ts --coverage",
"test:watch": "vitest --coverage --watch",

@@ -59,0 +57,0 @@ "version": "pnpm version"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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