Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .claude/agents/tidy-first.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
name: tidy-first
description: Refactoring specialist applying Kent Beck's Tidy First principles. Proactively invoked when adding new features, implementing functionality, code reviews, and refactoring. Evaluates whether to tidy code BEFORE making behavioral changes. Also responds to Korean prompts (기능 추가, 기능 구현, 새 기능, 리팩토링, 코드 정리, 코드 리뷰).
tools: Read, Grep, Glob, Bash, Edit
model: inherit
---

You are a refactoring specialist focused on Kent Beck's "Tidy First?" principles.

## Language Support

Respond in the same language as the user's prompt:
- If the user writes in Korean, respond in Korean
- If the user writes in English, respond in English

## When to Activate

**Proactively engage when the user wants to:**
- Add a new feature or functionality
- Implement new behavior
- Modify existing features
- Review or refactor code

**Your first task**: Before any behavioral change, analyze the target code area and recommend tidying opportunities that would make the feature implementation easier.

## Core Principles

### The Tidy First? Question
ALWAYS ask this question before adding features:
- Tidy first if: cost of tidying < reduction in future change costs
- Tidying should be a minutes-to-hours activity
- Always separate structural changes from behavioral changes
- Make the change easy, then make the easy change

### Tidying Types
1. **Guard Clauses**: Convert nested conditionals to early returns
2. **Dead Code**: Remove unreachable or unused code
3. **Normalize Symmetries**: Make similar code patterns consistent
4. **Extract Functions**: Break complex logic into focused functions
5. **Readability**: Improve naming and structure
6. **Cohesion Order**: Place related code close together
7. **Explaining Variables**: Add descriptive variables for complex expressions

## Work Process

1. **Analyze**: Read code and identify Tidy First opportunities
2. **Evaluate**: Assess tidying cost vs benefit (determine if tidying is worthwhile)
3. **Verify Tests**: Ensure existing tests pass
4. **Apply**: Apply only one tidying type at a time
5. **Validate**: Re-run tests after changes (`pnpm test`)
6. **Suggest Commit**: Propose commit message in Conventional Commits format

## Project Rules Compliance

Follow this project's code style:

- **Effect Library**: Maintain `Effect.gen`, `pipe`, `Data.TaggedError` style
- **Type Safety**: Never use `any` type - use `unknown` with type guards or Effect Schema
- **Linting**: Follow Biome lint rules (`pnpm lint`)
- **TDD**: Respect Red → Green → Refactor cycle

## Important Principles

- **Keep it small**: Each tidying should take minutes to hours
- **Safety first**: Only make structural changes that don't alter behavior
- **Tests required**: Verify all tests pass after every change
- **Separate commits**: Keep structural and behavioral changes in separate commits
- **Incremental improvement**: Apply only one tidying type at a time

## Commit Message Format

```
refactor: [tidying type] - [change description]

Examples:
refactor: guard clauses - convert nested if statements to early returns
refactor: dead code - remove unused helper function
refactor: extract function - separate complex validation logic into validateInput
```
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,4 @@ coverage/
# Next.js example build outputs
**/.next/
**/out/
.vercel/

# Claude
CLAUDE.md
.vercel/
93 changes: 93 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# SOLAPI SDK for Node.js

**Generated:** 2026-01-21
**Commit:** 9df35df
**Branch:** master

## OVERVIEW

Server-side SDK for SMS/LMS/MMS and Kakao messaging in Korea. Uses Effect library for type-safe functional programming with Data.TaggedError-based error handling.

## STRUCTURE

```
solapi-nodejs/
├── src/
│ ├── index.ts # SolapiMessageService facade (entry point)
│ ├── errors/ # Data.TaggedError types
│ ├── lib/ # Core utilities (fetcher, auth, error handler)
│ ├── models/ # Schemas, requests, responses (see models/AGENTS.md)
│ ├── services/ # Domain services (see services/AGENTS.md)
│ └── types/ # Shared type definitions
├── test/ # Mirrors src/ structure
├── examples/ # Usage examples (excluded from build)
└── debug/ # Debug scripts
```

## WHERE TO LOOK

| Task | Location | Notes |
|------|----------|-------|
| Add new message type | `src/models/base/messages/` | Extend MessageType union |
| Add new service | `src/services/` | Extend DefaultService |
| Add new error type | `src/errors/defaultError.ts` | Extend Data.TaggedError |
| Add utility function | `src/lib/` | Follow Effect patterns |
| Add Kakao BMS type | `src/models/base/kakao/bms/` | Add to BMS_REQUIRED_FIELDS |
| Fix API request issue | `src/lib/defaultFetcher.ts` | HTTP client with retry |
| Understand error flow | `src/lib/effectErrorHandler.ts` | Effect → Promise conversion |

## CONVENTIONS

**Effect Library (MANDATORY)**:
- All errors: `Data.TaggedError` with environment-aware `toString()`
- Async operations: `Effect.gen` + `Effect.tryPromise`, never wrap with try-catch
- Validation: `Effect Schema` with `Schema.filter`, `Schema.transform`
- Error execution: `runSafePromise()` / `runSafeSync()` from effectErrorHandler

**TypeScript**:
- **NEVER use `any`** — use `unknown` + type guards or Effect Schema
- Strict mode enforced (`noUnusedLocals`, `noUnusedParameters`)
- Path aliases: `@models`, `@lib`, `@services`, `@errors`, `@internal-types`

**Testing**:
- Unit: `vitest` with `Schema.decodeUnknownEither()` for validation tests
- E2E: `@effect/vitest` with `it.effect()` and `Effect.gen`
- Run: `pnpm test` / `pnpm test:watch`

## ANTI-PATTERNS

| Pattern | Why Bad | Do Instead |
|---------|---------|------------|
| `any` type | Loses type safety | `unknown` + type guards |
| `as any`, `@ts-ignore` | Suppresses errors | Fix the type issue |
| try-catch around Effect | Loses Effect benefits | Use `Effect.catchTag` |
| Direct `throw new Error()` | Inconsistent error handling | Use `Data.TaggedError` |
| Empty catch blocks | Swallows errors | Handle or propagate |

## COMMANDS

```bash
pnpm dev # Watch mode (tsup)
pnpm build # Lint + build
pnpm lint # Biome check with auto-fix
pnpm test # Run tests once
pnpm test:watch # Watch mode
pnpm docs # Generate TypeDoc
```

## ARCHITECTURE NOTES

**Service Facade Pattern**: `SolapiMessageService` aggregates 7 domain services via `bindServices()` dynamic method binding. All services extend `DefaultService`.

**Error Flow**:
```
API Response
→ defaultFetcher (creates Effect errors)
→ runSafePromise (converts to Promise)
→ toCompatibleError (preserves properties on Error)
→ Consumer
```

**Production vs Development**: Error messages stripped of stack traces and detailed context in production (`process.env.NODE_ENV === 'production'`).

**Retry Logic**: `defaultFetcher.ts` implements 3x retry with exponential backoff for retryable errors (connection refused, reset, 503).
98 changes: 98 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

SOLAPI SDK for Node.js - A server-side SDK for sending SMS, LMS, MMS, and Kakao messages (Alimtalk/Friendtalk) in Korea. Compatible with SOLAPI family services (CoolSMS, etc).

## Commands

```bash
# Development
pnpm dev # Watch mode with tsup
pnpm build # Lint + build (production)
pnpm lint # Biome check with auto-fix

# Testing
pnpm test # Run all tests once
pnpm test:watch # Watch mode
pnpm vitest run <path> # Run specific test file

# Documentation
pnpm docs # Generate TypeDoc documentation
```

## Architecture

### Entry Point & Service Facade
`SolapiMessageService` (src/index.ts) is the main SDK entry point. It aggregates all domain services and exposes their methods via delegation pattern using `bindServices()`.

### Service Layer
All services extend `DefaultService` (src/services/defaultService.ts) which provides:
- Base URL configuration (https://api.solapi.com)
- Authentication handling via `AuthenticationParameter`
- HTTP request abstraction via `defaultFetcher`

Domain services:
- `MessageService` / `GroupService` - Message sending and group management
- `KakaoChannelService` / `KakaoTemplateService` - Kakao Alimtalk integration
- `CashService` - Balance inquiries
- `IamService` - Block lists and 080 rejection management
- `StorageService` - File uploads (images, documents)

### Effect Library Integration
This project uses the **Effect** library for functional programming and type-safe error handling:

- All errors extend `Data.TaggedError` with environment-aware `toString()` methods
- Use `Effect.gen` for complex business logic
- Use `pipe` with `Effect.flatMap` for data transformation chains
- Schema validation via Effect Schema for runtime type safety
- Convert Effect to Promise using `runSafePromise` for API compatibility

### Path Aliases
```
@models → src/models
@lib → src/lib
@services → src/services
@errors → src/errors
@internal-types → src/types
@ → src
```

## Code Style Requirements

### TypeScript
- **Never use `any` type** - use `unknown` with type guards, union types, or Effect Schema
- Prefer functional programming style with Effect library
- Run lint after writing code

### TDD Approach
- Follow Red → Green → Refactor cycle
- Separate structural changes from behavioral changes in commits
- Only commit when all tests pass

### Error Handling
- Define errors as Effect Data types (`Data.TaggedError`)
- Provide concise messages in production, detailed in development
- Use structured logging with environment-specific verbosity

## Sub-Agents

### tidy-first
Refactoring specialist applying Kent Beck's "Tidy First?" principles.

**Auto-invocation conditions**:
- Adding new features or functionality
- Implementing new behavior
- Code review requests
- Refactoring tasks

**Core principles**:
- Always separate structural changes from behavioral changes
- Make small, reversible changes only (minutes to hours)
- Maintain test coverage

**Tidying types**: Guard Clauses, Dead Code removal, Pattern normalization, Function extraction, Readability improvements

Works alongside the TDD Approach section's "Separate structural changes from behavioral changes" principle.
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.7/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false },
"files": {
"ignoreUnknown": false,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 16 additions & 2 deletions examples/javascript/common/src/kakao/send/send_bms.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
/**
* 카카오 브랜드 메시지 발송 예제
* 현재 targeting 타입 중 M, N의 경우는 카카오 측에서 인허가된 채널만 사용하실 수 있습니다.
* 카카오 브랜드 메시지(템플릿 기반) 발송 예제
* 이 파일은 templateId를 사용한 템플릿 기반 BMS 발송 예제입니다.
*
* BMS 자유형(템플릿 없이 직접 메시지 구성) 예제는 아래 파일들을 참고하세요:
* - send_bms_free_text.js: TEXT 타입 (텍스트 전용)
* - send_bms_free_text_with_buttons.js: TEXT 타입 + 버튼
* - send_bms_free_image.js: IMAGE 타입 (이미지 포함)
* - send_bms_free_image_with_buttons.js: IMAGE 타입 + 버튼
* - send_bms_free_wide.js: WIDE 타입 (와이드 이미지)
* - send_bms_free_wide_item_list.js: WIDE_ITEM_LIST 타입 (와이드 아이템 리스트)
* - send_bms_free_commerce.js: COMMERCE 타입 (상품 메시지)
* - send_bms_free_carousel_feed.js: CAROUSEL_FEED 타입 (캐러셀 피드)
* - send_bms_free_carousel_commerce.js: CAROUSEL_COMMERCE 타입 (캐러셀 커머스)
* - send_bms_free_premium_video.js: PREMIUM_VIDEO 타입 (프리미엄 비디오)
*
* targeting 타입 중 M, N의 경우는 카카오 측에서 인허가된 채널만 사용하실 수 있습니다.
* 그 외의 모든 채널은 I 타입만 사용 가능합니다.
*/
const {SolapiMessageService} = require('solapi');
Expand Down
Loading