The pnpm stub command auto-generates code templates to reduce repetitive work. You can quickly create Practice scripts or new Entities to speed up development.
Commands
practice - Generate Practice Script
Generate scripts for temporary experiments or data processing.
pnpm stub practice < nam e >
Example :
pnpm stub practice test-api
# Generated:
# src/practices/p1-test-api.ts
# Automatically opens in VS Code
# Run command copied to clipboard
Generated file :
src/practices/p1-test-api.ts
import { Sonamu } from "sonamu" ;
console . clear ();
console . log ( "p1-test-api.ts" );
Sonamu . runScript ( async () => {
// TODO
});
Run :
# Paste from clipboard (Cmd+V)
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-test-api.js
entity - Generate Entity
Create a new Entity. By default, template cones are automatically generated along with the Entity.
pnpm stub entity < entityI d > [options]
Options :
Option Description Example (none) Generate Entity with template cones (default behavior) pnpm stub entity Product--aiGenerate Entity with LLM-powered cones (ANTHROPIC_API_KEY required) pnpm stub entity Product --ai--no-conesGenerate Entity only, skip cone generation pnpm stub entity Product --no-cones
Examples :
# Default: Generate Entity with template cones
pnpm stub entity Product
# ✓ Entity 'Product' created with template cones
# 💡 Tip: Run 'pnpm sonamu cone gen Product' to improve with AI
# Generate with AI-powered cones
pnpm stub entity Product --ai
# ✓ Entity 'Product' created
# 🌟 Generating AI-powered cones...
# ✅ Done (1234 tokens)
# Generate Entity without cones
pnpm stub entity Product --no-cones
# ✓ Entity 'Product' created without cones
Template cones can be generated without ANTHROPIC_API_KEY. You can later upgrade them using the cone gen command with AI.
Generated file :
src/entities/Product.entity.ts
import type { EntityType } from "sonamu" ;
export const ProductEntity = {
properties: [
{
name: "id" ,
type: "int" ,
primaryKey: true ,
autoIncrement: true ,
},
{
name: "title" ,
type: "string" ,
length: 255 ,
},
{
name: "created_at" ,
type: "datetime" ,
default: "CURRENT_TIMESTAMP" ,
},
],
} satisfies EntityType ;
Practice Scripts
Use Cases
Practice scripts are useful for these tasks:
Purpose Description Example API Testing Test external API calls p1-test-payment-api.tsData Migration One-time data transformation p2-migrate-user-data.tsDebugging Validate specific logic p3-debug-cache-issue.tsData Generation Create test data p4-create-sample-posts.tsAnalysis Check data statistics p5-analyze-user-activity.ts
Automatic Numbering
Practice files are automatically numbered :
When you create a new practice, it automatically gets the highest number + 1.
Practical Examples
1. API Testing
src/practices/p1-test-payment-api.ts
import { Sonamu } from "sonamu" ;
console . clear ();
console . log ( "p1-test-payment-api.ts" );
Sonamu . runScript ( async () => {
const paymentService = new PaymentService ();
// Test payment attempt
const result = await paymentService . charge ({
amount: 10000 ,
orderId: "TEST-001" ,
});
console . log ( "Payment result:" , result );
});
2. Data Migration
src/practices/p2-migrate-user-data.ts
import { Sonamu } from "sonamu" ;
import { UserModel } from "../models/User.model" ;
console . clear ();
console . log ( "p2-migrate-user-data.ts" );
Sonamu . runScript ( async () => {
// Get all users
const users = await UserModel . findAll ();
for ( const user of users ) {
// Migrate legacy field to new field
await UserModel . update ( user . id , {
new_field: transformData ( user . legacy_field ),
});
console . log ( `✓ Migrated user ${ user . id } ` );
}
console . log ( ` \n ✓ Total: ${ users . length } users migrated` );
});
3. Batch Jobs
src/practices/p3-send-welcome-emails.ts
import { Sonamu } from "sonamu" ;
import { UserModel } from "../models/User.model" ;
console . clear ();
console . log ( "p3-send-welcome-emails.ts" );
Sonamu . runScript ( async () => {
// New users
const newUsers = await UserModel . findMany ({
where: { welcome_email_sent: false },
});
console . log ( `Sending welcome emails to ${ newUsers . length } users...` );
for ( const user of newUsers ) {
try {
await sendWelcomeEmail ( user . email );
await UserModel . update ( user . id , {
welcome_email_sent: true ,
});
console . log ( `✓ Sent to ${ user . email } ` );
} catch ( error ) {
console . error ( `✗ Failed to send to ${ user . email } :` , error );
}
}
});
Entity Generation
Basic Structure
Entities generated with stub entity include basic fields:
export const ProductEntity = {
properties: [
{ name: "id" , type: "int" , primaryKey: true , autoIncrement: true },
{ name: "title" , type: "string" , length: 255 },
{ name: "created_at" , type: "datetime" , default: "CURRENT_TIMESTAMP" },
],
} satisfies EntityType ;
Customization
Add needed fields after generation:
export const ProductEntity = {
properties: [
// Basic fields
{ name: "id" , type: "int" , primaryKey: true , autoIncrement: true },
{ name: "title" , type: "string" , length: 255 },
// Additional fields
{ name: "description" , type: "text" , nullable: true },
{ name: "price" , type: "decimal" , precision: 10 , scale: 2 },
{ name: "stock" , type: "int" , default: 0 },
{ name: "category_id" , type: "int" },
// Timestamps
{ name: "created_at" , type: "datetime" , default: "CURRENT_TIMESTAMP" },
{ name: "updated_at" , type: "datetime" , onUpdate: "CURRENT_TIMESTAMP" },
],
indexes: [
{ fields: [ "category_id" ] },
{ fields: [ "title" ], type: "fulltext" },
],
belongsTo: [
{ entityId: "Category" , as: "category" },
],
} satisfies EntityType ;
Development Workflow
1. Validate Ideas
# Quickly create practice
pnpm stub practice test-idea
# Write code and run
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-test-idea.js
# Delete or keep after verification
2. Add Entity
# Generate Entity
pnpm stub entity Order
# Write Entity definition
# src/entities/Order.entity.ts
# Create and apply migration
pnpm migrate run
# Scaffold Model
pnpm scaffold model Order
3. Data Operations
# Generate Practice script
pnpm stub practice import-orders
# Write data processing logic
# src/practices/p1-import-orders.ts
# Run
pnpm node -r dotenv/config --enable-source-maps dist/practices/p1-import-orders.js
Practical Tips
1. Reuse Practice Scripts
// ✅ Make functions reusable
async function processUsers ( filter : any ) {
const users = await UserModel . findMany ({ where: filter });
// Processing logic...
}
Sonamu . runScript ( async () => {
// Reuse with various filters
await processUsers ({ role: "admin" });
await processUsers ({ role: "user" });
});
2. Error Handling
Sonamu . runScript ( async () => {
try {
await riskyOperation ();
} catch ( error ) {
console . error ( "Error occurred:" , error );
process . exit ( 1 ); // Exit on failure
}
});
3. Progress Display
Sonamu . runScript ( async () => {
const users = await UserModel . findAll ();
for ( let i = 0 ; i < users . length ; i ++ ) {
await processUser ( users [ i ]);
// Show progress
const progress = Math . round ((( i + 1 ) / users . length ) * 100 );
console . log ( `Progress: ${ progress } % ( ${ i + 1 } / ${ users . length } )` );
}
});
4. Cleanup Practices
# Delete completed practices
rm src/practices/p1-old-practice.ts
# Or move to archive directory
mkdir src/practices/archived
mv src/practices/p1- * .ts src/practices/archived/
Cautions
Cautions when using Practice scripts :
Production data : Practice uses real DB. Be careful!
Backup : Always backup before important data operations.
Transactions : Use transactions when modifying data.
Testing : Test in development environment before production run.
Next Steps
scaffold Auto-generate Models with code scaffolding
Entity Learn more about Entity definitions