feat: base code
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/persona?schema=public&connect_timeout=300"
|
||||||
|
ANTHROPIC_API_KEY=
|
||||||
24
.eslintrc.json
Normal file
24
.eslintrc.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"next/core-web-vitals",
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
|
"@typescript-eslint/consistent-type-definitions": "error",
|
||||||
|
"react-hooks/rules-of-hooks": "error",
|
||||||
|
"react-hooks/exhaustive-deps": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/.yarn/** linguist-vendored
|
||||||
|
/.yarn/releases/* binary
|
||||||
|
/.yarn/plugins/**/* binary
|
||||||
|
/.pnp.* binary linguist-generated
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -128,3 +128,7 @@ dist
|
|||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
.editorconfig
|
||||||
|
|
||||||
|
personas/
|
||||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 80,
|
||||||
|
"jsxSingleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
5
.yarnrc.yml
Normal file
5
.yarnrc.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
compressionLevel: mixed
|
||||||
|
|
||||||
|
enableGlobalCache: false
|
||||||
|
|
||||||
|
nodeLinker: node-modules
|
||||||
3
commitlint.config.ts
Normal file
3
commitlint.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['@commitlint/config-conventional']
|
||||||
|
};
|
||||||
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
container_name: personadb
|
||||||
|
image: postgres:15
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: persona
|
||||||
|
ports:
|
||||||
|
- "5433:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
40
package.json
Normal file
40
package.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "purchases-personas",
|
||||||
|
"scripts": {
|
||||||
|
"start": "prisma generate && node dist/index.js",
|
||||||
|
"dev": "nodemon src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"lint": "next lint",
|
||||||
|
"format": "prettier --config .prettierrc '**/*.{ts,json,md}' --write",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"prepare": "husky install",
|
||||||
|
"audit": "audit-ci",
|
||||||
|
"generate": "prisma generate",
|
||||||
|
"migrate": "prisma migrate dev"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@anthropic-ai/sdk": "^0.32.1",
|
||||||
|
"@prisma/client": "^5.22.0",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"express": "^4.21.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^18.4.3",
|
||||||
|
"@commitlint/config-conventional": "^18.4.3",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
||||||
|
"@typescript-eslint/parser": "^6.12.0",
|
||||||
|
"audit-ci": "^6.6.1",
|
||||||
|
"eslint": "^8",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"husky": "^8.0.3",
|
||||||
|
"lint-staged": "^15.1.0",
|
||||||
|
"nodemon": "^3.0.2",
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"prisma": "^5.8.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
29
prisma/migrations/20241123210705_init/migration.sql
Normal file
29
prisma/migrations/20241123210705_init/migration.sql
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"age" INTEGER NOT NULL,
|
||||||
|
"persona" JSONB NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "items" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"amount" DOUBLE PRECISION NOT NULL,
|
||||||
|
"datetime" TIMESTAMP(3) NOT NULL,
|
||||||
|
"location" TEXT NOT NULL,
|
||||||
|
"notes" TEXT,
|
||||||
|
"userId" INTEGER NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "items_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "items" ADD CONSTRAINT "items_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "postgresql"
|
||||||
35
prisma/schema.prisma
Normal file
35
prisma/schema.prisma
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
age Int
|
||||||
|
persona Json
|
||||||
|
items Item[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Item {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
amount Float
|
||||||
|
datetime DateTime
|
||||||
|
location String
|
||||||
|
notes String?
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@map("items")
|
||||||
|
}
|
||||||
11
src/index.ts
Normal file
11
src/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { makeRequest } from './utils/anthropicClient';
|
||||||
|
import { savePersonaJson } from './utils/savePersonaJson';
|
||||||
|
import { savePersonaDb } from './utils/savePersonaDb';
|
||||||
|
|
||||||
|
const personaPromise = makeRequest();
|
||||||
|
|
||||||
|
personaPromise.then(persona => {
|
||||||
|
savePersonaDb(persona.text).then(id => savePersonaJson(persona.text, id));
|
||||||
|
|
||||||
|
console.log('New persona:', persona.text);
|
||||||
|
});
|
||||||
105
src/utils/anthropicClient.ts
Normal file
105
src/utils/anthropicClient.ts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS IS THE PROMPT IN HUMAN-READABLE FORMAT:
|
||||||
|
|
||||||
|
Generate a JSON object for a persona with the following schema:
|
||||||
|
{
|
||||||
|
"core": {
|
||||||
|
"age": <number>,
|
||||||
|
"name": <string>,
|
||||||
|
"occupation": {
|
||||||
|
"title": <string>,
|
||||||
|
"level": <string>,
|
||||||
|
"income": <number>,
|
||||||
|
"location": <string>,
|
||||||
|
"schedule": <array of strings>
|
||||||
|
},
|
||||||
|
"home": {
|
||||||
|
"type": <string>,
|
||||||
|
"ownership": <string>,
|
||||||
|
"location": <string>,
|
||||||
|
"commute_distance_km": <number>
|
||||||
|
},
|
||||||
|
"household": {
|
||||||
|
"status": <string>,
|
||||||
|
"members": <array of strings>,
|
||||||
|
"pets": <array of objects>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routines": {
|
||||||
|
"weekday": <object with 24 hour timeline>,
|
||||||
|
"weekend": <array of regular activities>,
|
||||||
|
"commute": {
|
||||||
|
"method": <string>,
|
||||||
|
"route": <array of locations>,
|
||||||
|
"regular_stops": <array of objects>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"preferences": {
|
||||||
|
"diet": <array of strings>,
|
||||||
|
"brands": <array of objects with loyalty scores>,
|
||||||
|
"price_sensitivity": <number 1-10>,
|
||||||
|
"payment_methods": <array of strings>,
|
||||||
|
"shopping": {
|
||||||
|
"grocery_stores": <array of objects with frequency>,
|
||||||
|
"coffee_shops": <array of objects with frequency>,
|
||||||
|
"restaurants": <array of objects with frequency>,
|
||||||
|
"retail": <array of objects with frequency>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"finances": {
|
||||||
|
"subscriptions": <array of objects with amounts and dates>,
|
||||||
|
"regular_bills": <array of objects with amounts and dates>,
|
||||||
|
"spending_patterns": {
|
||||||
|
"impulsive_score": <number 1-10>,
|
||||||
|
"categories": <object with category preferences>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"habits": {
|
||||||
|
"exercise": <array of objects with schedule>,
|
||||||
|
"social": <array of objects with frequency>,
|
||||||
|
"entertainment": <array of objects with frequency>,
|
||||||
|
"vices": <array of objects with frequency>
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"stress_triggers": <array of strings>,
|
||||||
|
"reward_behaviors": <array of strings>,
|
||||||
|
"upcoming_events": <array of objects with dates>,
|
||||||
|
"recent_changes": <array of strings>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Generate realistic values for all fields, including specific store names, brands, and locations. All temporal data should be relative to the current week. Frequencies should be specified as times per week. Include coordinates for locations. Use ISO timestamps for dates.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const personaCreationPrompt =
|
||||||
|
'Generate a detailed JSON object for a realistic persona following this schema, populating all fields with specific real-world values including store names, brands, coordinates, and locations. Use frequencies per week, ISO timestamps relative to the current week, and include coordinates for all locations. The output should be a complete JSON object with core demographic details (age, job, housing, household), daily routines, shopping preferences with actual store names and visit frequencies, brand loyalties with scores, diet, payment preferences, exercise habits, social patterns, entertainment choices, upcoming events, and stress factors. The data should reflect the current week for all temporal values. Schema: {"core":{age:<number>,"name": <string>,occupation:{title:<string>,level:<string>,income:<number>,location:<string>,schedule:<array of strings>},home:{type:<string>,ownership:<string>,location:<string>,commute_distance_km:<number>},household:{status:<string>,members:<array of strings>,pets:<array of objects>}},"routines":{weekday:<object with 24 hour timeline>,weekend:<array of regular activities>,commute:{method:<string>,route:<array of locations>,regular_stops:<array of objects>}},"preferences":{diet:<array of strings>,brands:<array of objects with loyalty scores>,price_sensitivity:<number 1-10>,payment_methods:<array of strings>,shopping:{grocery_stores:<array of objects with frequency>,coffee_shops:<array of objects with frequency>,restaurants:<array of objects with frequency>,retail:<array of objects with frequency>}},"finances":{subscriptions:<array of objects with amounts and dates>,regular_bills:<array of objects with amounts and dates>,spending_patterns:{impulsive_score:<number 1-10>,categories:<object with category preferences>}},"habits":{exercise:<array of objects with schedule>,social:<array of objects with frequency>,entertainment:<array of objects with frequency>,vices:<array of objects with frequency>},"context":{stress_triggers:<array of strings>,reward_behaviors:<array of strings>,upcoming_events:<array of objects with dates>,recent_changes:<array of strings>}}';
|
||||||
|
|
||||||
|
export async function makeRequest() {
|
||||||
|
if (!process.env.ANTHROPIC_API_KEY) {
|
||||||
|
throw Error('Anthropic API key missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const anthropic = new Anthropic({
|
||||||
|
apiKey: process.env.ANTHROPIC_API_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await anthropic.messages.create({
|
||||||
|
model: 'claude-3-5-sonnet-20241022',
|
||||||
|
max_tokens: 2000,
|
||||||
|
messages: [{ role: 'user', content: personaCreationPrompt }]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
response.stop_reason &&
|
||||||
|
response.stop_reason !== 'end_turn' &&
|
||||||
|
response.stop_reason !== 'stop_sequence'
|
||||||
|
) {
|
||||||
|
throw Error(response.stop_reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Message ID:', response.id);
|
||||||
|
|
||||||
|
return response.content[0] as { text: string };
|
||||||
|
}
|
||||||
5
src/utils/parsePersona.ts
Normal file
5
src/utils/parsePersona.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Persona } from './types';
|
||||||
|
|
||||||
|
export function parsePersona(persona: string) {
|
||||||
|
return JSON.parse(persona) as Persona;
|
||||||
|
}
|
||||||
3
src/utils/prismaClient.ts
Normal file
3
src/utils/prismaClient.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
export const prisma = new PrismaClient();
|
||||||
17
src/utils/savePersonaDb.ts
Normal file
17
src/utils/savePersonaDb.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { prisma } from './prismaClient';
|
||||||
|
|
||||||
|
export async function savePersonaDb(persona: string) {
|
||||||
|
const personaObject = JSON.parse(persona);
|
||||||
|
|
||||||
|
const result = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
name: personaObject['core']['name'],
|
||||||
|
age: personaObject['core']['age'],
|
||||||
|
persona: JSON.stringify(persona)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Persona ${result.name} inserted with ID ${result.id}`);
|
||||||
|
|
||||||
|
return result.id;
|
||||||
|
}
|
||||||
10
src/utils/savePersonaJson.ts
Normal file
10
src/utils/savePersonaJson.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { parsePersona } from './parsePersona';
|
||||||
|
|
||||||
|
export function savePersonaJson(persona: string, id: number) {
|
||||||
|
fs.promises.writeFile(`personas/${id}.json`, persona, 'utf8');
|
||||||
|
|
||||||
|
const personaObject = parsePersona(persona);
|
||||||
|
|
||||||
|
console.log(`Persona ${personaObject.core.name} saved as persona/${id}.json`);
|
||||||
|
}
|
||||||
118
src/utils/types.ts
Normal file
118
src/utils/types.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
type Pet = {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FrequencyObject = {
|
||||||
|
name: string;
|
||||||
|
frequency: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SubscriptionBill = {
|
||||||
|
name: string;
|
||||||
|
amount: number;
|
||||||
|
date: string | Date;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActivityObject = {
|
||||||
|
name: string;
|
||||||
|
frequency: number;
|
||||||
|
schedule?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type BrandLoyalty = {
|
||||||
|
name: string;
|
||||||
|
loyaltyScore: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Event = {
|
||||||
|
name: string;
|
||||||
|
date: string | Date;
|
||||||
|
details?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TimelineActivity = {
|
||||||
|
activity: string;
|
||||||
|
duration: string;
|
||||||
|
location?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RegularStop = {
|
||||||
|
location: string;
|
||||||
|
purpose: string;
|
||||||
|
frequency: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SpendingCategories = {
|
||||||
|
[category: string]: {
|
||||||
|
preference: number;
|
||||||
|
frequency: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Persona = {
|
||||||
|
core: {
|
||||||
|
age: number;
|
||||||
|
name: string;
|
||||||
|
occupation: {
|
||||||
|
title: string;
|
||||||
|
level: string;
|
||||||
|
income: number;
|
||||||
|
location: string;
|
||||||
|
schedule: string[];
|
||||||
|
};
|
||||||
|
home: {
|
||||||
|
type: string;
|
||||||
|
ownership: string;
|
||||||
|
location: string;
|
||||||
|
commute_distance_km: number;
|
||||||
|
};
|
||||||
|
household: {
|
||||||
|
status: string;
|
||||||
|
members: string[];
|
||||||
|
pets: Pet[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
routines: {
|
||||||
|
weekday: {
|
||||||
|
[hour: string]: TimelineActivity;
|
||||||
|
};
|
||||||
|
weekend: ActivityObject[];
|
||||||
|
commute: {
|
||||||
|
method: string;
|
||||||
|
route: string[];
|
||||||
|
regular_stops: RegularStop[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
preferences: {
|
||||||
|
diet: string[];
|
||||||
|
brands: BrandLoyalty[];
|
||||||
|
price_sensitivity: number;
|
||||||
|
payment_methods: string[];
|
||||||
|
shopping: {
|
||||||
|
grocery_stores: FrequencyObject[];
|
||||||
|
coffee_shops: FrequencyObject[];
|
||||||
|
restaurants: FrequencyObject[];
|
||||||
|
retail: FrequencyObject[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
finances: {
|
||||||
|
subscriptions: SubscriptionBill[];
|
||||||
|
regular_bills: SubscriptionBill[];
|
||||||
|
spending_patterns: {
|
||||||
|
impulsive_score: number;
|
||||||
|
categories: SpendingCategories;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
habits: {
|
||||||
|
exercise: ActivityObject[];
|
||||||
|
social: ActivityObject[];
|
||||||
|
entertainment: ActivityObject[];
|
||||||
|
vices: ActivityObject[];
|
||||||
|
};
|
||||||
|
context: {
|
||||||
|
stress_triggers: string[];
|
||||||
|
reward_behaviors: string[];
|
||||||
|
upcoming_events: Event[];
|
||||||
|
recent_changes: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2017",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["es6"],
|
||||||
|
"allowJs": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"typeRoots": ["./node_modules/@types"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*", "utils/anthropicClient.ts"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user