@laitszkin/apollo-toolkit 4.1.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/bin/apollo-toolkit.ts +4 -0
- package/dist/bin/apollo-toolkit.js +4 -0
- package/package.json +7 -2
- package/packages/cli/dist/help-text-builder.d.ts +23 -0
- package/packages/cli/dist/help-text-builder.js +166 -0
- package/packages/cli/dist/index.d.ts +6 -17
- package/packages/cli/dist/index.js +52 -246
- package/packages/cli/dist/installer.d.ts +1 -0
- package/packages/cli/dist/installer.js +20 -7
- package/packages/cli/dist/parsers/install-parser.d.ts +15 -0
- package/packages/cli/dist/parsers/install-parser.js +87 -0
- package/packages/cli/dist/parsers/parser-utils.d.ts +9 -0
- package/packages/cli/dist/parsers/parser-utils.js +16 -0
- package/packages/cli/dist/parsers/tool-parser.d.ts +16 -0
- package/packages/cli/dist/parsers/tool-parser.js +58 -0
- package/packages/cli/dist/parsers/types.d.ts +50 -0
- package/packages/cli/dist/parsers/types.js +1 -0
- package/packages/cli/dist/parsers/uninstall-parser.d.ts +15 -0
- package/packages/cli/dist/parsers/uninstall-parser.js +67 -0
- package/packages/cli/dist/tool-registration.d.ts +2 -0
- package/packages/cli/dist/tool-registration.js +2 -0
- package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/cli/dist/types.d.ts +3 -1
- package/packages/cli/dist/updater.js +11 -5
- package/packages/cli/help-text-builder.ts +180 -0
- package/packages/cli/index.ts +59 -251
- package/packages/cli/installer.ts +19 -7
- package/packages/cli/package.json +6 -3
- package/packages/cli/parsers/install-parser.ts +94 -0
- package/packages/cli/parsers/parser-utils.ts +17 -0
- package/packages/cli/parsers/tool-parser.ts +65 -0
- package/packages/cli/parsers/types.ts +56 -0
- package/packages/cli/parsers/uninstall-parser.ts +75 -0
- package/packages/cli/tool-registration.ts +3 -0
- package/packages/cli/types.ts +6 -1
- package/packages/cli/updater.ts +11 -5
- package/packages/tool-registry/dist/registry.js +3 -4
- package/packages/tool-registry/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tool-registry/dist/types.d.ts +2 -9
- package/packages/tool-registry/package.json +3 -3
- package/packages/tool-registry/registry.ts +3 -4
- package/packages/tool-registry/tsconfig.json +6 -2
- package/packages/tool-registry/types.ts +3 -9
- package/packages/tool-utils/app-error.ts +97 -0
- package/packages/tool-utils/dist/app-error.d.ts +49 -0
- package/packages/tool-utils/dist/app-error.js +80 -0
- package/packages/tool-utils/dist/index.d.ts +5 -0
- package/packages/tool-utils/dist/index.js +3 -0
- package/packages/tool-utils/dist/platform-adapter.d.ts +48 -0
- package/packages/tool-utils/dist/platform-adapter.js +73 -0
- package/packages/tool-utils/dist/schema.d.ts +68 -0
- package/packages/tool-utils/dist/schema.js +67 -0
- package/packages/tool-utils/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tool-utils/index.ts +12 -0
- package/packages/tool-utils/package.json +3 -3
- package/packages/tool-utils/platform-adapter.ts +112 -0
- package/packages/tool-utils/schema.ts +122 -0
- package/packages/tools/architecture/dist/index.d.ts +13 -0
- package/packages/tools/architecture/dist/index.js +55 -57
- package/packages/tools/architecture/dist/index.test.js +17 -4
- package/packages/tools/architecture/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/architecture/index.test.ts +27 -14
- package/packages/tools/architecture/index.ts +85 -88
- package/packages/tools/architecture/package.json +3 -3
- package/packages/tools/codegraph/dist/index.js +21 -17
- package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/codegraph/index.ts +21 -17
- package/packages/tools/codegraph/package.json +3 -3
- package/packages/tools/create-review-report/dist/index.d.ts +1 -2
- package/packages/tools/create-review-report/dist/index.js +46 -77
- package/packages/tools/create-review-report/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/create-review-report/index.ts +52 -81
- package/packages/tools/create-review-report/package.json +3 -3
- package/packages/tools/create-specs/dist/index.d.ts +1 -2
- package/packages/tools/create-specs/dist/index.js +70 -123
- package/packages/tools/create-specs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/create-specs/index.ts +82 -128
- package/packages/tools/create-specs/package.json +3 -3
- package/packages/tools/docs-to-voice/dist/index.d.ts +1 -2
- package/packages/tools/docs-to-voice/dist/index.js +116 -219
- package/packages/tools/docs-to-voice/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/docs-to-voice/index.ts +265 -385
- package/packages/tools/docs-to-voice/package.json +3 -3
- package/packages/tools/enforce-video-aspect-ratio/dist/index.d.ts +1 -2
- package/packages/tools/enforce-video-aspect-ratio/dist/index.js +77 -154
- package/packages/tools/enforce-video-aspect-ratio/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/enforce-video-aspect-ratio/index.ts +87 -172
- package/packages/tools/enforce-video-aspect-ratio/package.json +3 -3
- package/packages/tools/eval/dist/index.js +7 -0
- package/packages/tools/eval/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/eval/index.ts +8 -0
- package/packages/tools/eval/package.json +3 -3
- package/packages/tools/extract-conversations/dist/index.d.ts +1 -2
- package/packages/tools/extract-conversations/dist/index.js +31 -29
- package/packages/tools/extract-conversations/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/extract-conversations/index.ts +37 -30
- package/packages/tools/extract-conversations/package.json +3 -3
- package/packages/tools/extract-pdf-text/dist/index.d.ts +1 -2
- package/packages/tools/extract-pdf-text/dist/index.js +44 -65
- package/packages/tools/extract-pdf-text/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/extract-pdf-text/index.ts +55 -74
- package/packages/tools/extract-pdf-text/package.json +3 -3
- package/packages/tools/filter-logs/dist/index.js +60 -84
- package/packages/tools/filter-logs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/filter-logs/index.ts +67 -97
- package/packages/tools/filter-logs/package.json +3 -3
- package/packages/tools/find-github-issues/dist/index.d.ts +10 -0
- package/packages/tools/find-github-issues/dist/index.js +34 -5
- package/packages/tools/find-github-issues/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/find-github-issues/index.ts +37 -5
- package/packages/tools/find-github-issues/package.json +3 -3
- package/packages/tools/generate-storyboard-images/dist/index.d.ts +1 -2
- package/packages/tools/generate-storyboard-images/dist/index.js +98 -173
- package/packages/tools/generate-storyboard-images/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/generate-storyboard-images/index.ts +100 -188
- package/packages/tools/generate-storyboard-images/package.json +3 -3
- package/packages/tools/open-github-issue/dist/index.d.ts +13 -0
- package/packages/tools/open-github-issue/dist/index.js +67 -68
- package/packages/tools/open-github-issue/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/open-github-issue/index.ts +71 -72
- package/packages/tools/open-github-issue/package.json +3 -3
- package/packages/tools/read-github-issue/dist/index.d.ts +16 -1
- package/packages/tools/read-github-issue/dist/index.js +32 -40
- package/packages/tools/read-github-issue/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/read-github-issue/index.ts +32 -45
- package/packages/tools/read-github-issue/package.json +3 -3
- package/packages/tools/render-error-book/dist/index.d.ts +1 -2
- package/packages/tools/render-error-book/dist/index.js +74 -95
- package/packages/tools/render-error-book/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/render-error-book/index.ts +88 -103
- package/packages/tools/render-error-book/package.json +3 -3
- package/packages/tools/render-katex/dist/index.d.ts +1 -2
- package/packages/tools/render-katex/dist/index.js +70 -157
- package/packages/tools/render-katex/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/render-katex/index.ts +138 -222
- package/packages/tools/render-katex/package.json +3 -3
- package/packages/tools/review-threads/dist/index.d.ts +12 -0
- package/packages/tools/review-threads/dist/index.js +83 -86
- package/packages/tools/review-threads/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/review-threads/index.ts +90 -84
- package/packages/tools/review-threads/package.json +3 -3
- package/packages/tools/search-logs/dist/index.js +100 -136
- package/packages/tools/search-logs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/search-logs/index.ts +113 -145
- package/packages/tools/search-logs/package.json +3 -3
- package/packages/tools/sync-memory-index/dist/index.js +34 -28
- package/packages/tools/sync-memory-index/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/sync-memory-index/index.ts +37 -28
- package/packages/tools/sync-memory-index/package.json +3 -3
- package/packages/tools/validate-openai-agent-config/dist/index.js +13 -7
- package/packages/tools/validate-openai-agent-config/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/validate-openai-agent-config/index.ts +13 -7
- package/packages/tools/validate-openai-agent-config/package.json +3 -3
- package/packages/tools/validate-skill-frontmatter/dist/index.js +12 -6
- package/packages/tools/validate-skill-frontmatter/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/validate-skill-frontmatter/index.ts +12 -6
- package/packages/tools/validate-skill-frontmatter/package.json +3 -3
- package/packages/tui/dist/index.d.ts +2 -1
- package/packages/tui/dist/index.js +1 -0
- package/packages/tui/dist/stdio-adapter.d.ts +36 -0
- package/packages/tui/dist/stdio-adapter.js +69 -0
- package/packages/tui/dist/terminal.js +3 -1
- package/packages/tui/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tui/dist/types.d.ts +17 -0
- package/packages/tui/index.ts +2 -1
- package/packages/tui/package.json +6 -5
- package/packages/tui/stdio-adapter.ts +85 -0
- package/packages/tui/terminal.ts +3 -1
- package/packages/tui/tsconfig.json +5 -2
- package/packages/tui/types.ts +19 -0
- package/resources/project-architecture/assets/architecture.css +2 -1
- package/resources/project-architecture/atlas/atlas.history.log +1 -0
- package/resources/project-architecture/atlas/atlas.history.undo.json +13 -2
- package/resources/project-architecture/atlas/atlas.history.undo.stack.json +610 -0
- package/resources/project-architecture/atlas/atlas.index.yaml +81 -5
- package/resources/project-architecture/atlas/features/cli-dispatch.yaml +43 -0
- package/resources/project-architecture/atlas/features/terminal-ui.yaml +29 -0
- package/resources/project-architecture/atlas/features/tool-registry.yaml +22 -0
- package/resources/project-architecture/atlas/features/tool-utils.yaml +22 -0
- package/resources/project-architecture/features/cli-dispatch/arg-parser.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/help-builder.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/index.html +64 -0
- package/resources/project-architecture/features/cli-dispatch/installer-core.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/tool-discovery.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/update-checker.html +40 -0
- package/resources/project-architecture/features/terminal-ui/banner-display.html +40 -0
- package/resources/project-architecture/features/terminal-ui/index.html +50 -0
- package/resources/project-architecture/features/terminal-ui/interactive-prompts.html +40 -0
- package/resources/project-architecture/features/terminal-ui/terminal-detection.html +40 -0
- package/resources/project-architecture/features/tool-registry/formatter.html +40 -0
- package/resources/project-architecture/features/tool-registry/index.html +43 -0
- package/resources/project-architecture/features/tool-registry/registry-core.html +40 -0
- package/resources/project-architecture/features/tool-utils/index.html +43 -0
- package/resources/project-architecture/features/tool-utils/log-utils.html +40 -0
- package/resources/project-architecture/features/tool-utils/skill-discovery.html +40 -0
- package/resources/project-architecture/index.html +365 -121
- package/scripts/rewrite-imports.mjs +2 -2
- package/scripts/test.sh +144 -8
- package/skills/design/SKILL.md +57 -64
- package/skills/design/assets/templates/DESIGN.md +12 -0
- package/skills/design/references/code-smells.md +94 -0
- package/skills/design/references/module-boundary-adjustment.md +126 -0
- package/skills/design/references/module-internal-restructuring.md +132 -0
- package/skills/design/references/module-internal-simplification.md +164 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Module Boundary Adjustment (T3) — Patterns
|
|
2
|
+
|
|
3
|
+
Changes that affect a module's public API, data contract, or cross-module coupling.
|
|
4
|
+
Requires dedicated test coverage — define test strategy in CHECKLIST.md before implementing.
|
|
5
|
+
|
|
6
|
+
## Extract Module from God Module
|
|
7
|
+
|
|
8
|
+
A large module containing >2 distinct responsibilities is split along ownership or lifecycle boundaries.
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
Before:
|
|
12
|
+
module: "order-service"
|
|
13
|
+
responsibilities: [payment, fulfillment, notification, audit]
|
|
14
|
+
|
|
15
|
+
After:
|
|
16
|
+
module: "order-service"
|
|
17
|
+
responsibilities: [order lifecycle, payment orchestration]
|
|
18
|
+
module: "fulfillment-service"
|
|
19
|
+
responsibilities: [inventory check, shipping, tracking]
|
|
20
|
+
module: "notification-service"
|
|
21
|
+
responsibilities: [email, SMS, push for order events]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Verification**:
|
|
25
|
+
- Integration tests confirm cross-module interactions still produce the same business outcomes
|
|
26
|
+
- Each new module has independent test coverage for its internal logic
|
|
27
|
+
- Contracts between modules (events, data types) are versioned or explicit
|
|
28
|
+
|
|
29
|
+
## Change to Cross-Module Data Contract
|
|
30
|
+
|
|
31
|
+
Modifying the shape of data passed between modules — a field added, renamed, removed, or changed type.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
// Contract: events/order-fulfilled.ts
|
|
35
|
+
// Before:
|
|
36
|
+
interface OrderFulfilled {
|
|
37
|
+
orderId: string;
|
|
38
|
+
trackingNumber: string;
|
|
39
|
+
carrier: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// After:
|
|
43
|
+
interface OrderFulfilled {
|
|
44
|
+
orderId: string;
|
|
45
|
+
trackingNumber: string;
|
|
46
|
+
carrier: 'fedex' | 'ups' | 'usps'; // narrowed from string
|
|
47
|
+
estimatedDelivery?: string; // added optional field
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Test obligations**:
|
|
52
|
+
- Producer publishes all required fields
|
|
53
|
+
- Consumer handles the new optional field gracefully (absence = backward compatible)
|
|
54
|
+
- Consumer reject messages for invalid carrier values
|
|
55
|
+
|
|
56
|
+
## Remodeling Invariant Enforcement
|
|
57
|
+
|
|
58
|
+
An invariant previously enforced at the database or UI layer must move to the application module boundary.
|
|
59
|
+
|
|
60
|
+
| Scenario | Approach |
|
|
61
|
+
|---|---|
|
|
62
|
+
| DB constraint moved to application | Add validation in service layer; dual-run until migration verified |
|
|
63
|
+
| UI-only validation promoted to API | Add middleware guard; test both valid and invalid input |
|
|
64
|
+
| Soft enforcement → hard enforcement | Roll out with logging first, then error-throwing after observation period |
|
|
65
|
+
|
|
66
|
+
## Public API Signature Change
|
|
67
|
+
|
|
68
|
+
A module's exported function, class, or type changes in a way that requires all callers to update.
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// Before:
|
|
72
|
+
export function createUser(name: string, email: string, role: string): User;
|
|
73
|
+
|
|
74
|
+
// After:
|
|
75
|
+
export function createUser(params: CreateUserParams): User;
|
|
76
|
+
// where CreateUserParams = { name: string; email: string; role: Role };
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Migration strategy** (choose one and document):
|
|
80
|
+
1. **Deprecate-and-copy**: Old function marked `@deprecated` → new function added → callers migrate one by one → old function removed after N cycles
|
|
81
|
+
2. **Shim layer**: A thin adapter maps new interface to old, or old to new, during a transition window
|
|
82
|
+
3. **Flag gate**: Both implementations coexist behind a feature flag; toggle after callers are confirmed updated
|
|
83
|
+
|
|
84
|
+
## Module-to-Event Boundary
|
|
85
|
+
|
|
86
|
+
A direct RPC call between two modules migrates to event-driven communication.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Before: synchronous RPC
|
|
90
|
+
// fulfillment.ts
|
|
91
|
+
const notification = new NotificationService();
|
|
92
|
+
notification.sendEmail(order.userEmail, 'Shipped');
|
|
93
|
+
|
|
94
|
+
// After: event-driven
|
|
95
|
+
// fulfillment.ts
|
|
96
|
+
events.publish({ type: 'order.shipped', data: { orderId, userEmail } });
|
|
97
|
+
// notification service subscribes to 'order.shipped' independently
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Test obligations**:
|
|
101
|
+
- Integration test: publish event → assert subscriber executes expected side-effect
|
|
102
|
+
- Contract test: event schema is agreed between publisher and subscriber
|
|
103
|
+
- Resilience test: subscriber failure does not affect publisher health
|
|
104
|
+
|
|
105
|
+
## Interface Extraction
|
|
106
|
+
|
|
107
|
+
A class's public methods become an explicit interface to decouple callers from implementation.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Before:
|
|
111
|
+
export class PaymentGateway {
|
|
112
|
+
async charge(amount: number, token: string) { ... }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// After:
|
|
116
|
+
export interface PaymentProvider {
|
|
117
|
+
charge(amount: number, token: string): Promise<PaymentResult>;
|
|
118
|
+
}
|
|
119
|
+
export class StripeGateway implements PaymentProvider { ... }
|
|
120
|
+
export class FakeGateway implements PaymentProvider { ... } // for tests
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Verification**:
|
|
124
|
+
- Unit tests pass against `FakeGateway`
|
|
125
|
+
- Integration tests cover `StripeGateway` against sandbox
|
|
126
|
+
- Existing callers are updated to depend on the interface, not the class
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Module-Internal Restructuring (T2) — Patterns
|
|
2
|
+
|
|
3
|
+
Restructuring that crosses files but stays within the same module boundary.
|
|
4
|
+
Existing integration tests validate behavior — include in the design's task decomposition.
|
|
5
|
+
|
|
6
|
+
## Extract Shared Logic
|
|
7
|
+
|
|
8
|
+
A utility or helper repeated across multiple files within the same module.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// Before
|
|
12
|
+
// file-a.ts: const formatDate = (d) => ...;
|
|
13
|
+
// file-b.ts: const formatDate = (d) => ...;
|
|
14
|
+
|
|
15
|
+
// After
|
|
16
|
+
// shared/date.ts: export const formatDate = (d) => ...;
|
|
17
|
+
// file-a.ts, file-b.ts: import { formatDate } from '../shared/date';
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Placement**: Put shared utilities at the lowest level that all consumers can import without crossing module boundaries.
|
|
21
|
+
|
|
22
|
+
**Verify**: Integration tests pass; grep confirms all callers updated.
|
|
23
|
+
|
|
24
|
+
## Consolidate Scattered State
|
|
25
|
+
|
|
26
|
+
State of the same concept spread across multiple files in the module.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Before
|
|
30
|
+
// config.ts — export const API_TIMEOUT = 5000;
|
|
31
|
+
// constants.ts — export const DEFAULT_TIMEOUT = 5000;
|
|
32
|
+
// settings.ts — const timeout = process.env.API_TIMEOUT ?? 5000;
|
|
33
|
+
|
|
34
|
+
// After
|
|
35
|
+
// config.ts — all timeout definitions in one place:
|
|
36
|
+
// export const API_TIMEOUT = process.env.API_TIMEOUT ?? 5000;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Rule of thumb**: If changing one value means updating N files, the state is scattered.
|
|
40
|
+
|
|
41
|
+
## Reorganize File Boundaries
|
|
42
|
+
|
|
43
|
+
A single file has grown to contain multiple concerns; split along cohesion lines.
|
|
44
|
+
|
|
45
|
+
| Symptom | Split Strategy |
|
|
46
|
+
|---|---|
|
|
47
|
+
| File has "// validation" and "// formatting" section blocks | One file per concern |
|
|
48
|
+
| File exports 8+ unrelated functions | Group by what they operate on |
|
|
49
|
+
| File contains both types and logic | Types → `types.ts`, logic → `logic.ts` |
|
|
50
|
+
| File has >300 lines and covers >3 distinct error scenarios | Split by failure mode |
|
|
51
|
+
|
|
52
|
+
**Naming convention**: `{concern}.ts`, not `{concern}Utils.ts` — the file is the home, not a utility drawer.
|
|
53
|
+
|
|
54
|
+
## Extract Function Group
|
|
55
|
+
|
|
56
|
+
Several functions share a common prefix or operate on the same sub-concept.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// Before
|
|
60
|
+
// order-validator.ts
|
|
61
|
+
// function validateLineItem(item) { ... }
|
|
62
|
+
// function validateShipping(addr) { ... }
|
|
63
|
+
// function validatePromo(code) { ... }
|
|
64
|
+
|
|
65
|
+
// After
|
|
66
|
+
// order-validator.ts — orchestrates validation
|
|
67
|
+
// validation/line-item.ts — validateLineItem
|
|
68
|
+
// validation/shipping.ts — validateShipping
|
|
69
|
+
// validation/promo.ts — validatePromo
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Flatten Function Chain
|
|
73
|
+
|
|
74
|
+
A → B → C → D where each is a single-caller delegation.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Before
|
|
78
|
+
function handleRequest(req) { return validate(parse(req)); }
|
|
79
|
+
function validate(data) { return checkSchema(data); }
|
|
80
|
+
function checkSchema(data) { return schema.parse(data); }
|
|
81
|
+
|
|
82
|
+
// After
|
|
83
|
+
function handleRequest(req) { return schema.parse(req); }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Skip if** the chain exists for testability (each function individually mockable) — that's a valid reason.
|
|
87
|
+
|
|
88
|
+
## Migrate to Early Data Validation
|
|
89
|
+
|
|
90
|
+
Input validation scattered across processing steps → consolidated at the module boundary.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Before
|
|
94
|
+
function process(data) {
|
|
95
|
+
const parsed = parse(data); // validates format
|
|
96
|
+
const enriched = enrich(parsed); // validates completeness
|
|
97
|
+
const saved = save(enriched); // validates constraints
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// After
|
|
101
|
+
function process(data) {
|
|
102
|
+
const validated = validate(data); // single validation pass
|
|
103
|
+
return save(parse(enrich(validated)));
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Standardize Error Handling
|
|
108
|
+
|
|
109
|
+
Mixed error patterns (throwing, returning `null`, returning `Result` types) unified within the module.
|
|
110
|
+
|
|
111
|
+
| Inconsistent | Standardized |
|
|
112
|
+
|---|---|
|
|
113
|
+
| Some functions throw, some return null | All return `Result<T, E>` or all throw |
|
|
114
|
+
| Some validate with `assert`, some with `if...throw` | Uniform validation pattern |
|
|
115
|
+
| Callers don't know which errors to handle | Documented error types per function group |
|
|
116
|
+
|
|
117
|
+
## Remove Implicit Module Coupling
|
|
118
|
+
|
|
119
|
+
Two files in the same module import internal symbols from each other's implementation files rather than going through the module's index/barrel.
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Before
|
|
123
|
+
// features/orders/fulfill.ts
|
|
124
|
+
// import { picker } from './internal/warehouse';
|
|
125
|
+
|
|
126
|
+
// After
|
|
127
|
+
// features/orders/index.ts re-exports warehouse's public API
|
|
128
|
+
// features/orders/fulfill.ts
|
|
129
|
+
// import { picker } from '../orders'; // through barrel
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Verify**: No file in the module imports from another file's private path — all go through the module boundary.
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Module-Internal Simplification (T1) — Patterns
|
|
2
|
+
|
|
3
|
+
Simplifications confined to a single function or file that do not change the module's public API.
|
|
4
|
+
Existing unit tests validate behavioral preservation — these are the safest refactorings.
|
|
5
|
+
|
|
6
|
+
## Guard Clause Extraction
|
|
7
|
+
|
|
8
|
+
Replace nested conditionals with early returns.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
// Before
|
|
12
|
+
function processOrder(order) {
|
|
13
|
+
if (order) {
|
|
14
|
+
if (order.isPaid) {
|
|
15
|
+
if (!order.isCancelled) {
|
|
16
|
+
// main logic
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// After
|
|
23
|
+
function processOrder(order) {
|
|
24
|
+
if (!order) return;
|
|
25
|
+
if (!order.isPaid) return;
|
|
26
|
+
if (order.isCancelled) return;
|
|
27
|
+
// main logic
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Test coverage**: All code paths already exercised by unit tests.
|
|
32
|
+
**Verify**: `npm test -- --related <file>`
|
|
33
|
+
|
|
34
|
+
## Dead Code Removal
|
|
35
|
+
|
|
36
|
+
Remove unused exports, unreachable branches, and commented-out blocks.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// Before
|
|
40
|
+
export function legacyFormatter(data) { /* no callers found */ }
|
|
41
|
+
function process() {
|
|
42
|
+
// if (experimental) { ... } // commented out 6 months ago
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// After
|
|
46
|
+
// Remove legacyFormatter entirely
|
|
47
|
+
function process() { /* clean body */ }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Verify**: `grep -r "legacyFormatter" src/` confirms zero callers; tests still pass.
|
|
51
|
+
|
|
52
|
+
## Predicate Simplification
|
|
53
|
+
|
|
54
|
+
Collapse duplicated or tautological conditions.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Before
|
|
58
|
+
if (user != null && user !== undefined && user.role != null) { ... }
|
|
59
|
+
|
|
60
|
+
// After
|
|
61
|
+
if (user?.role) { ... }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Inline Redundant Variable
|
|
65
|
+
|
|
66
|
+
A variable that exists solely to hold a single-use expression.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Before
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
log(now);
|
|
72
|
+
|
|
73
|
+
// After
|
|
74
|
+
log(Date.now());
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Does not apply when** the variable name carries meaning that clarifies the expression.
|
|
78
|
+
|
|
79
|
+
## Switch-to-Map / Switch-to-Polymorphism
|
|
80
|
+
|
|
81
|
+
Replace long switch statements with lookup maps.
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Before
|
|
85
|
+
function format(type, value) {
|
|
86
|
+
switch (type) {
|
|
87
|
+
case 'date': return formatDate(value);
|
|
88
|
+
case 'currency': return formatCurrency(value);
|
|
89
|
+
case 'percent': return formatPercent(value);
|
|
90
|
+
default: return String(value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// After
|
|
95
|
+
const FORMATTERS = {
|
|
96
|
+
date: formatDate,
|
|
97
|
+
currency: formatCurrency,
|
|
98
|
+
percent: formatPercent,
|
|
99
|
+
};
|
|
100
|
+
function format(type, value) {
|
|
101
|
+
return (FORMATTERS[type] ?? String)(value);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Remove Redundant Branch
|
|
106
|
+
|
|
107
|
+
When a branch body is identical to the fallback, merge.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Before
|
|
111
|
+
function greet(name) {
|
|
112
|
+
if (name) {
|
|
113
|
+
return `Hello, ${name}!`;
|
|
114
|
+
}
|
|
115
|
+
return `Hello, world!`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// After
|
|
119
|
+
function greet(name) {
|
|
120
|
+
return `Hello, ${name || 'world'}!`;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Flatten Unnecessary Async
|
|
125
|
+
|
|
126
|
+
A function declared `async` but containing no `await` calls.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
// Before
|
|
130
|
+
async function getVersion() {
|
|
131
|
+
return pkg.version;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// After
|
|
135
|
+
function getVersion() {
|
|
136
|
+
return pkg.version;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Skip if** the function is an interface/abstract override that must return a Promise for polymorphic consistency.
|
|
141
|
+
|
|
142
|
+
## Merge Adjacent Operations
|
|
143
|
+
|
|
144
|
+
Two sequential loops or filter-map chains over the same collection.
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Before
|
|
148
|
+
const active = users.filter(u => u.active);
|
|
149
|
+
const names = active.map(u => u.name);
|
|
150
|
+
|
|
151
|
+
// After
|
|
152
|
+
const names = users.filter(u => u.active).map(u => u.name);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Remove Accidental Complexity
|
|
156
|
+
|
|
157
|
+
Extraneous indirection introduced by premature abstraction or framework boilerplate.
|
|
158
|
+
|
|
159
|
+
| Anti-pattern | Refactored |
|
|
160
|
+
|---|---|
|
|
161
|
+
| Single-function class that holds no state | Convert to plain function |
|
|
162
|
+
| Wrapper function that only adds logging | Inline logging at call site |
|
|
163
|
+
| Factory that always produces the same concrete type | Use constructor directly |
|
|
164
|
+
| Interface with exactly one implementation | Remove interface, keep implementation |
|