@cakemail-org/cakemail-cli 1.5.0 → 2.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.
Files changed (234) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +40 -0
  3. package/.env.test.example +45 -0
  4. package/CHANGELOG.md +1031 -0
  5. package/README.md +319 -15
  6. package/audit-formats.js +128 -0
  7. package/cakemail.rb +20 -0
  8. package/dist/cli.js +27 -10
  9. package/dist/cli.js.map +1 -1
  10. package/dist/client.d.ts +2 -0
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +16 -6
  13. package/dist/client.js.map +1 -1
  14. package/dist/commands/account.js +1 -1
  15. package/dist/commands/account.js.map +1 -1
  16. package/dist/commands/attributes.js +1 -1
  17. package/dist/commands/attributes.js.map +1 -1
  18. package/dist/commands/campaigns.d.ts.map +1 -1
  19. package/dist/commands/campaigns.js +103 -8
  20. package/dist/commands/campaigns.js.map +1 -1
  21. package/dist/commands/config.d.ts.map +1 -1
  22. package/dist/commands/config.js +63 -4
  23. package/dist/commands/config.js.map +1 -1
  24. package/dist/commands/contacts.d.ts.map +1 -1
  25. package/dist/commands/contacts.js +91 -12
  26. package/dist/commands/contacts.js.map +1 -1
  27. package/dist/commands/emails.js +1 -1
  28. package/dist/commands/emails.js.map +1 -1
  29. package/dist/commands/interests.d.ts +5 -0
  30. package/dist/commands/interests.d.ts.map +1 -0
  31. package/dist/commands/interests.js +172 -0
  32. package/dist/commands/interests.js.map +1 -0
  33. package/dist/commands/lists.d.ts.map +1 -1
  34. package/dist/commands/lists.js +6 -8
  35. package/dist/commands/lists.js.map +1 -1
  36. package/dist/commands/logs.d.ts +5 -0
  37. package/dist/commands/logs.d.ts.map +1 -0
  38. package/dist/commands/logs.js +237 -0
  39. package/dist/commands/logs.js.map +1 -0
  40. package/dist/commands/reports.js +1 -1
  41. package/dist/commands/reports.js.map +1 -1
  42. package/dist/commands/segments.js +1 -1
  43. package/dist/commands/segments.js.map +1 -1
  44. package/dist/commands/senders.d.ts.map +1 -1
  45. package/dist/commands/senders.js +11 -8
  46. package/dist/commands/senders.js.map +1 -1
  47. package/dist/commands/suppressed.js +1 -1
  48. package/dist/commands/suppressed.js.map +1 -1
  49. package/dist/commands/tags.d.ts +5 -0
  50. package/dist/commands/tags.d.ts.map +1 -0
  51. package/dist/commands/tags.js +124 -0
  52. package/dist/commands/tags.js.map +1 -0
  53. package/dist/commands/templates.js +1 -1
  54. package/dist/commands/templates.js.map +1 -1
  55. package/dist/commands/transactional-templates.d.ts +5 -0
  56. package/dist/commands/transactional-templates.d.ts.map +1 -0
  57. package/dist/commands/transactional-templates.js +354 -0
  58. package/dist/commands/transactional-templates.js.map +1 -0
  59. package/dist/commands/webhooks.js +1 -1
  60. package/dist/commands/webhooks.js.map +1 -1
  61. package/dist/utils/auth.d.ts +8 -1
  62. package/dist/utils/auth.d.ts.map +1 -1
  63. package/dist/utils/auth.js +39 -11
  64. package/dist/utils/auth.js.map +1 -1
  65. package/dist/utils/config-file.d.ts +7 -0
  66. package/dist/utils/config-file.d.ts.map +1 -1
  67. package/dist/utils/config-file.js +15 -0
  68. package/dist/utils/config-file.js.map +1 -1
  69. package/dist/utils/config.d.ts +2 -0
  70. package/dist/utils/config.d.ts.map +1 -1
  71. package/dist/utils/config.js +12 -4
  72. package/dist/utils/config.js.map +1 -1
  73. package/dist/utils/errors.js +1 -1
  74. package/dist/utils/errors.js.map +1 -1
  75. package/dist/utils/list-defaults.d.ts +33 -0
  76. package/dist/utils/list-defaults.d.ts.map +1 -0
  77. package/dist/utils/list-defaults.js +52 -0
  78. package/dist/utils/list-defaults.js.map +1 -0
  79. package/dist/utils/output.d.ts.map +1 -1
  80. package/dist/utils/output.js +36 -13
  81. package/dist/utils/output.js.map +1 -1
  82. package/dist/utils/progress.d.ts.map +1 -1
  83. package/dist/utils/progress.js +32 -4
  84. package/dist/utils/progress.js.map +1 -1
  85. package/dist/utils/spinner.d.ts +17 -0
  86. package/dist/utils/spinner.d.ts.map +1 -0
  87. package/dist/utils/spinner.js +43 -0
  88. package/dist/utils/spinner.js.map +1 -0
  89. package/docs/DOCUMENTATION-STANDARD.md +1068 -0
  90. package/docs/README.md +161 -0
  91. package/docs/developer/ARCHITECTURE.md +516 -0
  92. package/docs/developer/AUTH.md +204 -0
  93. package/docs/developer/CONTRIBUTING.md +227 -0
  94. package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
  95. package/docs/developer/PROJECT_INDEX.md +365 -0
  96. package/docs/planning/API_COVERAGE.md +1045 -0
  97. package/docs/planning/BACKLOG.md +1159 -0
  98. package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
  99. package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
  100. package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
  101. package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
  102. package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
  103. package/docs/planning/cakemail-profile-system-plan.md +1121 -0
  104. package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
  105. package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
  106. package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
  107. package/docs/user-manual/.obsidian/app.json +1 -0
  108. package/docs/user-manual/.obsidian/appearance.json +1 -0
  109. package/docs/user-manual/.obsidian/core-plugins.json +33 -0
  110. package/docs/user-manual/.obsidian/workspace.json +167 -0
  111. package/docs/user-manual/01-getting-started/01-installation.md +214 -0
  112. package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
  113. package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
  114. package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
  115. package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
  116. package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
  117. package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
  118. package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
  119. package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
  120. package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
  121. package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
  122. package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
  123. package/docs/user-manual/03-email-operations/01-senders.md +490 -0
  124. package/docs/user-manual/03-email-operations/02-templates.md +444 -0
  125. package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
  126. package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
  127. package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
  128. package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
  129. package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
  130. package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
  131. package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
  132. package/docs/user-manual/05-contact-management/01-lists.md +836 -0
  133. package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
  134. package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
  135. package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
  136. package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
  137. package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
  138. package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
  139. package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
  140. package/docs/user-manual/07-integrations/02-automation.md +326 -0
  141. package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
  142. package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
  143. package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
  144. package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
  145. package/docs/user-manual/09-command-reference/01-config.md +776 -0
  146. package/docs/user-manual/09-command-reference/02-account.md +652 -0
  147. package/docs/user-manual/09-command-reference/03-lists.md +958 -0
  148. package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
  149. package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
  150. package/docs/user-manual/09-command-reference/06-segments.md +894 -0
  151. package/docs/user-manual/09-command-reference/07-senders.md +803 -0
  152. package/docs/user-manual/09-command-reference/08-templates.md +818 -0
  153. package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
  154. package/docs/user-manual/09-command-reference/10-emails.md +807 -0
  155. package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
  156. package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
  157. package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
  158. package/docs/user-manual/09-command-reference/14-interests.md +630 -0
  159. package/docs/user-manual/09-command-reference/15-tags.md +584 -0
  160. package/docs/user-manual/09-command-reference/16-logs.md +656 -0
  161. package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
  162. package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
  163. package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
  164. package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
  165. package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
  166. package/docs/user-manual/11-appendix/04-faq.md +484 -0
  167. package/docs/user-manual/11-appendix/05-glossary.md +250 -0
  168. package/docs/user-manual/README.md +0 -0
  169. package/package.json +13 -47
  170. package/src/cli.ts +125 -0
  171. package/src/client.ts +16 -0
  172. package/src/commands/account.ts +267 -0
  173. package/src/commands/accounts.ts +78 -0
  174. package/src/commands/actions.ts +249 -0
  175. package/src/commands/attributes.ts +139 -0
  176. package/src/commands/campaign-blueprints.ts +106 -0
  177. package/src/commands/campaigns.ts +469 -0
  178. package/src/commands/config.ts +77 -0
  179. package/src/commands/contacts.ts +612 -0
  180. package/src/commands/custom-attributes.ts +127 -0
  181. package/src/commands/dkims.ts +117 -0
  182. package/src/commands/domains.ts +82 -0
  183. package/src/commands/email-apis.ts +569 -0
  184. package/src/commands/emails.ts +197 -0
  185. package/src/commands/forms.ts +283 -0
  186. package/src/commands/interests.ts +155 -0
  187. package/src/commands/links.ts +38 -0
  188. package/src/commands/lists.ts +406 -0
  189. package/src/commands/logos.ts +71 -0
  190. package/src/commands/logs.ts +386 -0
  191. package/src/commands/reports.ts +306 -0
  192. package/src/commands/segments.ts +158 -0
  193. package/src/commands/senders.ts +204 -0
  194. package/src/commands/sub-accounts.ts +271 -0
  195. package/src/commands/suppressed-emails.ts +234 -0
  196. package/src/commands/suppressed.ts +198 -0
  197. package/src/commands/system-emails.ts +85 -0
  198. package/src/commands/tags.ts +146 -0
  199. package/src/commands/tasks.ts +116 -0
  200. package/src/commands/templates.ts +189 -0
  201. package/src/commands/tokens.ts +83 -0
  202. package/src/commands/transactional-emails.ts +374 -0
  203. package/src/commands/transactional-templates.ts +385 -0
  204. package/src/commands/users.ts +506 -0
  205. package/src/commands/webhooks.ts +172 -0
  206. package/src/commands/workflow-blueprints.ts +123 -0
  207. package/src/commands/workflows.ts +265 -0
  208. package/src/types/profile.ts +93 -0
  209. package/src/utils/auth.ts +272 -0
  210. package/src/utils/config-file.ts +96 -0
  211. package/src/utils/config.ts +134 -0
  212. package/src/utils/confirm.ts +32 -0
  213. package/src/utils/defaults.ts +99 -0
  214. package/src/utils/errors.ts +116 -0
  215. package/src/utils/interactive.ts +91 -0
  216. package/src/utils/list-defaults.ts +74 -0
  217. package/src/utils/output.ts +190 -0
  218. package/src/utils/progress.ts +320 -0
  219. package/src/utils/spinner.ts +22 -0
  220. package/tests/IMPLEMENTATION_STATUS.md +258 -0
  221. package/tests/PTY_SETUP.md +118 -0
  222. package/tests/PTY_TESTING_GUIDE.md +507 -0
  223. package/tests/README.md +244 -0
  224. package/tests/fixtures/api-responses/campaigns.json +34 -0
  225. package/tests/fixtures/test-config.json +13 -0
  226. package/tests/helpers/cli-runner.ts +128 -0
  227. package/tests/helpers/mock-server.ts +301 -0
  228. package/tests/helpers/pty-runner.ts +181 -0
  229. package/tests/integration/campaigns-real-api.test.ts +196 -0
  230. package/tests/integration/setup-integration.ts +50 -0
  231. package/tests/pty/campaigns.test.ts +241 -0
  232. package/tests/setup.ts +34 -0
  233. package/tsconfig.json +15 -0
  234. package/vitest.config.ts +28 -0
@@ -0,0 +1,766 @@
1
+ # Performance Optimization
2
+
3
+ Optimize Cakemail CLI operations for speed, efficiency, and resource usage.
4
+
5
+ ## Overview
6
+
7
+ Learn to:
8
+ - Reduce API call overhead
9
+ - Implement intelligent caching
10
+ - Optimize data processing
11
+ - Minimize network requests
12
+ - Use parallel processing effectively
13
+ - Profile and benchmark operations
14
+ - Handle large datasets efficiently
15
+
16
+ ## API Call Optimization
17
+
18
+ ### Request Batching
19
+
20
+ ```bash
21
+ #!/bin/bash
22
+
23
+ # Bad: Multiple individual API calls
24
+ for id in {1..100}; do
25
+ cakemail contacts get 123 "$id" # 100 API calls
26
+ done
27
+
28
+ # Good: Single list call with pagination
29
+ cakemail contacts list 123 --per-page 100 -f json | \
30
+ jq -r '.data[]' # 1 API call
31
+ ```
32
+
33
+ ### Filter Server-Side
34
+
35
+ ```bash
36
+ #!/bin/bash
37
+
38
+ # Bad: Fetch all, filter locally
39
+ cakemail contacts list 123 -f json | \
40
+ jq '.data[] | select(.status == "subscribed")' # Transfers all data
41
+
42
+ # Good: Filter on server
43
+ cakemail contacts list 123 \
44
+ --filter "status==subscribed" \
45
+ -f json # Only transfers matching data
46
+ ```
47
+
48
+ ### Use Appropriate Output Formats
49
+
50
+ ```bash
51
+ #!/bin/bash
52
+
53
+ # For parsing: Use JSON (most efficient)
54
+ campaign_ids=$(cakemail campaigns list -f json | jq -r '.data[].id')
55
+
56
+ # For display: Use table (formatted)
57
+ cakemail campaigns list # Default table format
58
+
59
+ # For export: Use CSV (smallest)
60
+ cakemail contacts list 123 -f csv > contacts.csv
61
+ ```
62
+
63
+ ## Caching Strategies
64
+
65
+ ### Time-Based Cache
66
+
67
+ ```bash
68
+ #!/bin/bash
69
+
70
+ # Cache with TTL (Time To Live)
71
+ CACHE_DIR=".cache"
72
+ CACHE_TTL=3600 # 1 hour
73
+
74
+ mkdir -p "$CACHE_DIR"
75
+
76
+ cached_call() {
77
+ local cache_key="$1"
78
+ shift
79
+ local command="$*"
80
+ local cache_file="$CACHE_DIR/${cache_key}.cache"
81
+ local cache_time_file="$CACHE_DIR/${cache_key}.time"
82
+
83
+ # Check cache exists and is fresh
84
+ if [ -f "$cache_file" ] && [ -f "$cache_time_file" ]; then
85
+ local cached_time=$(cat "$cache_time_file")
86
+ local current_time=$(date +%s)
87
+ local age=$((current_time - cached_time))
88
+
89
+ if [ $age -lt $CACHE_TTL ]; then
90
+ echo "Cache hit: $cache_key (age: ${age}s)" >&2
91
+ cat "$cache_file"
92
+ return 0
93
+ fi
94
+ fi
95
+
96
+ # Cache miss - fetch and store
97
+ echo "Cache miss: $cache_key" >&2
98
+ local result=$(eval "$command")
99
+ echo "$result" | tee "$cache_file"
100
+ date +%s > "$cache_time_file"
101
+ }
102
+
103
+ # Usage
104
+ lists=$(cached_call "lists" cakemail lists list -f json)
105
+ echo "$lists" | jq '.data[].name'
106
+
107
+ # Second call uses cache
108
+ lists=$(cached_call "lists" cakemail lists list -f json) # Instant!
109
+ ```
110
+
111
+ ### Content-Based Cache Invalidation
112
+
113
+ ```bash
114
+ #!/bin/bash
115
+
116
+ # Cache with content hash for invalidation
117
+ cache_with_hash() {
118
+ local cache_key="$1"
119
+ shift
120
+ local command="$*"
121
+ local cache_file=".cache/${cache_key}.cache"
122
+ local hash_file=".cache/${cache_key}.hash"
123
+
124
+ # Calculate content hash
125
+ local current_hash=$(echo "$command" | md5sum | cut -d' ' -f1)
126
+
127
+ # Check if hash matches
128
+ if [ -f "$hash_file" ] && [ "$(cat "$hash_file")" = "$current_hash" ]; then
129
+ if [ -f "$cache_file" ]; then
130
+ cat "$cache_file"
131
+ return 0
132
+ fi
133
+ fi
134
+
135
+ # Fetch and cache
136
+ eval "$command" | tee "$cache_file"
137
+ echo "$current_hash" > "$hash_file"
138
+ }
139
+
140
+ # Usage - cache automatically invalidates when command changes
141
+ cache_with_hash "campaigns-recent" \
142
+ cakemail campaigns list --filter "sent_at>=2024-01-01" -f json
143
+ ```
144
+
145
+ ### Multi-Level Cache
146
+
147
+ ```bash
148
+ #!/bin/bash
149
+
150
+ # Memory + Disk cache for maximum performance
151
+ declare -A MEMORY_CACHE
152
+
153
+ multi_level_cache() {
154
+ local key="$1"
155
+ shift
156
+ local command="$*"
157
+
158
+ # L1: Memory cache
159
+ if [ -n "${MEMORY_CACHE[$key]}" ]; then
160
+ echo "L1 cache hit" >&2
161
+ echo "${MEMORY_CACHE[$key]}"
162
+ return 0
163
+ fi
164
+
165
+ # L2: Disk cache
166
+ local disk_cache=".cache/${key}.cache"
167
+ if [ -f "$disk_cache" ]; then
168
+ local age=$(($(date +%s) - $(stat -f%m "$disk_cache" 2>/dev/null || stat -c%Y "$disk_cache")))
169
+ if [ $age -lt 3600 ]; then
170
+ echo "L2 cache hit" >&2
171
+ local result=$(cat "$disk_cache")
172
+ MEMORY_CACHE[$key]="$result"
173
+ echo "$result"
174
+ return 0
175
+ fi
176
+ fi
177
+
178
+ # L3: API call
179
+ echo "API call" >&2
180
+ local result=$(eval "$command")
181
+
182
+ # Store in both caches
183
+ MEMORY_CACHE[$key]="$result"
184
+ echo "$result" > "$disk_cache"
185
+ echo "$result"
186
+ }
187
+
188
+ # Usage
189
+ for i in {1..10}; do
190
+ multi_level_cache "list-123" cakemail lists get 123 -f json
191
+ # First call: API call
192
+ # Subsequent calls: L1 cache hit (instant)
193
+ done
194
+ ```
195
+
196
+ ## Parallel Processing
197
+
198
+ ### Optimal Concurrency
199
+
200
+ ```bash
201
+ #!/bin/bash
202
+
203
+ # Find optimal concurrency level
204
+ find_optimal_concurrency() {
205
+ local task="$1"
206
+ local test_items=(1 2 3 4 5 6 7 8 9 10)
207
+
208
+ for concurrency in 1 2 3 5 10; do
209
+ echo "Testing concurrency: $concurrency"
210
+
211
+ local start_time=$(date +%s)
212
+
213
+ # Run tasks in parallel
214
+ local count=0
215
+ for item in "${test_items[@]}"; do
216
+ # Wait if at max concurrency
217
+ while [ $(jobs -r | wc -l) -ge $concurrency ]; do
218
+ sleep 0.1
219
+ done
220
+
221
+ eval "$task $item" &
222
+ ((count++))
223
+ done
224
+
225
+ wait
226
+
227
+ local end_time=$(date +%s)
228
+ local duration=$((end_time - start_time))
229
+
230
+ echo "Concurrency $concurrency: ${duration}s"
231
+ done
232
+ }
233
+
234
+ # Test function
235
+ test_task() {
236
+ sleep 1 # Simulate API call
237
+ }
238
+
239
+ find_optimal_concurrency "test_task"
240
+ ```
241
+
242
+ ### Parallel with Progress Tracking
243
+
244
+ ```bash
245
+ #!/bin/bash
246
+
247
+ # Parallel processing with real-time progress
248
+ parallel_with_progress() {
249
+ local items=("$@")
250
+ local total=${#items[@]}
251
+ local completed=0
252
+ local max_jobs=5
253
+
254
+ # Progress file
255
+ local progress_file=$(mktemp)
256
+ echo "0" > "$progress_file"
257
+
258
+ for item in "${items[@]}"; do
259
+ # Wait if at max concurrency
260
+ while [ $(jobs -r | wc -l) -ge $max_jobs ]; do
261
+ # Update progress display
262
+ completed=$(cat "$progress_file")
263
+ local percent=$((completed * 100 / total))
264
+ printf "\rProgress: %d/%d (%d%%) " "$completed" "$total" "$percent"
265
+ sleep 0.1
266
+ done
267
+
268
+ # Process in background
269
+ (
270
+ process_item "$item"
271
+
272
+ # Update progress counter
273
+ flock "$progress_file" bash -c "echo \$((\$(cat '$progress_file') + 1)) > '$progress_file'"
274
+ ) &
275
+ done
276
+
277
+ # Wait for completion
278
+ wait
279
+
280
+ completed=$(cat "$progress_file")
281
+ printf "\rProgress: %d/%d (100%%) \n" "$completed" "$total"
282
+
283
+ rm "$progress_file"
284
+ }
285
+
286
+ process_item() {
287
+ local campaign_id="$1"
288
+ cakemail reports campaign "$campaign_id" -f json > "report-${campaign_id}.json"
289
+ }
290
+
291
+ # Usage
292
+ campaigns=(790 791 792 793 794 795 796 797 798 799)
293
+ parallel_with_progress "${campaigns[@]}"
294
+ ```
295
+
296
+ ## Data Processing Optimization
297
+
298
+ ### Stream Processing
299
+
300
+ ```bash
301
+ #!/bin/bash
302
+
303
+ # Memory-efficient streaming
304
+ stream_process_large_list() {
305
+ local list_id="$1"
306
+
307
+ echo "Streaming contacts..."
308
+
309
+ # Process without loading into memory
310
+ cakemail contacts list "$list_id" -f json | \
311
+ jq -c '.data[]' | \
312
+ while read -r contact; do
313
+ # Process each contact individually
314
+ local email=$(echo "$contact" | jq -r '.email')
315
+ local status=$(echo "$contact" | jq -r '.status')
316
+
317
+ if [ "$status" = "bounced" ]; then
318
+ echo "$email" >> bounced.txt
319
+ fi
320
+ done
321
+
322
+ echo "Processing complete"
323
+ }
324
+
325
+ stream_process_large_list 123
326
+ ```
327
+
328
+ ### Batch Processing with Chunking
329
+
330
+ ```bash
331
+ #!/bin/bash
332
+
333
+ # Process in optimal-sized chunks
334
+ chunk_process() {
335
+ local input_file="$1"
336
+ local chunk_size=1000
337
+ local temp_dir=$(mktemp -d)
338
+
339
+ # Split into chunks
340
+ split -l $chunk_size "$input_file" "$temp_dir/chunk-"
341
+
342
+ # Process chunks in parallel
343
+ for chunk in "$temp_dir"/chunk-*; do
344
+ (
345
+ process_chunk "$chunk"
346
+ ) &
347
+
348
+ # Limit concurrent chunks
349
+ while [ $(jobs -r | wc -l) -ge 3 ]; do
350
+ sleep 1
351
+ done
352
+ done
353
+
354
+ wait
355
+ rm -rf "$temp_dir"
356
+ }
357
+
358
+ process_chunk() {
359
+ local chunk="$1"
360
+ while read -r line; do
361
+ # Process line
362
+ echo "Processing: $line"
363
+ done < "$chunk"
364
+ }
365
+ ```
366
+
367
+ ### Indexed Lookup
368
+
369
+ ```bash
370
+ #!/bin/bash
371
+
372
+ # Build index for fast lookups
373
+ build_contact_index() {
374
+ local list_id="$1"
375
+ local index_file=".index-${list_id}.db"
376
+
377
+ echo "Building index..."
378
+
379
+ # Create simple key-value index
380
+ cakemail contacts list "$list_id" -f json | \
381
+ jq -r '.data[] | "\(.email)|\(.id)"' > "$index_file"
382
+
383
+ echo "Index built: $index_file"
384
+ }
385
+
386
+ lookup_contact_id() {
387
+ local email="$1"
388
+ local index_file="$2"
389
+
390
+ # O(1) lookup with grep
391
+ grep "^${email}|" "$index_file" | cut -d'|' -f2
392
+ }
393
+
394
+ # Usage
395
+ build_contact_index 123
396
+
397
+ # Fast lookups
398
+ contact_id=$(lookup_contact_id "user@example.com" ".index-123.db")
399
+ echo "Contact ID: $contact_id"
400
+ ```
401
+
402
+ ## Network Optimization
403
+
404
+ ### Connection Reuse
405
+
406
+ ```bash
407
+ #!/bin/bash
408
+
409
+ # Reuse HTTP connections (via curl)
410
+ CURL_OPTS="--keepalive-time 60 --max-time 30"
411
+
412
+ optimized_api_call() {
413
+ local endpoint="$1"
414
+ curl $CURL_OPTS "https://api.cakemail.com/$endpoint"
415
+ }
416
+
417
+ # Multiple calls reuse connection
418
+ for i in {1..10}; do
419
+ optimized_api_call "campaigns"
420
+ done
421
+ ```
422
+
423
+ ### Request Compression
424
+
425
+ ```bash
426
+ #!/bin/bash
427
+
428
+ # Enable gzip compression
429
+ export CAKEMAIL_COMPRESSION=true
430
+
431
+ # Or via curl
432
+ curl --compressed https://api.cakemail.com/campaigns
433
+ ```
434
+
435
+ ### DNS Caching
436
+
437
+ ```bash
438
+ #!/bin/bash
439
+
440
+ # Cache DNS lookups
441
+ export HOSTALIASES=/tmp/hosts
442
+ echo "api.cakemail.com $(dig +short api.cakemail.com | head -1)" > /tmp/hosts
443
+
444
+ # DNS lookup now cached
445
+ ```
446
+
447
+ ## Profiling and Benchmarking
448
+
449
+ ### Command Timing
450
+
451
+ ```bash
452
+ #!/bin/bash
453
+
454
+ # Measure execution time
455
+ time_command() {
456
+ local description="$1"
457
+ shift
458
+ local command="$*"
459
+
460
+ echo "Benchmarking: $description"
461
+
462
+ local start=$(date +%s%N)
463
+ eval "$command" > /dev/null
464
+ local end=$(date +%s%N)
465
+
466
+ local duration=$(( (end - start) / 1000000 )) # Convert to ms
467
+ echo "Duration: ${duration}ms"
468
+ }
469
+
470
+ # Usage
471
+ time_command "List campaigns" cakemail campaigns list
472
+ time_command "List with filter" cakemail campaigns list --filter "status==sent"
473
+ ```
474
+
475
+ ### Performance Profiling
476
+
477
+ ```bash
478
+ #!/bin/bash
479
+
480
+ # Profile script performance
481
+ profile_script() {
482
+ PS4='+ $(date +%s.%N) ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
483
+ set -x
484
+
485
+ # Your script here
486
+ cakemail lists list
487
+ cakemail campaigns list
488
+ cakemail senders list
489
+
490
+ set +x
491
+ }
492
+
493
+ # Run and analyze output
494
+ profile_script 2>&1 | awk '{print $2, $NF}' | sort -n
495
+ ```
496
+
497
+ ### Load Testing
498
+
499
+ ```bash
500
+ #!/bin/bash
501
+
502
+ # Test CLI under load
503
+ load_test() {
504
+ local concurrency="$1"
505
+ local requests="$2"
506
+ local completed=0
507
+
508
+ echo "Load test: $requests requests, $concurrency concurrent"
509
+
510
+ local start_time=$(date +%s)
511
+
512
+ for i in $(seq 1 "$requests"); do
513
+ # Wait if at max concurrency
514
+ while [ $(jobs -r | wc -l) -ge $concurrency ]; do
515
+ sleep 0.1
516
+ done
517
+
518
+ # Execute request
519
+ (
520
+ cakemail campaigns list -f json > /dev/null
521
+ echo "1"
522
+ ) &
523
+ done
524
+
525
+ wait
526
+
527
+ local end_time=$(date +%s)
528
+ local duration=$((end_time - start_time))
529
+ local rps=$(( requests / duration ))
530
+
531
+ echo "Completed: $requests requests in ${duration}s"
532
+ echo "Throughput: ${rps} requests/second"
533
+ }
534
+
535
+ # Run load tests
536
+ load_test 5 100
537
+ load_test 10 100
538
+ ```
539
+
540
+ ## Resource Management
541
+
542
+ ### Memory Optimization
543
+
544
+ ```bash
545
+ #!/bin/bash
546
+
547
+ # Monitor memory usage
548
+ monitor_memory() {
549
+ local pid=$1
550
+
551
+ while kill -0 $pid 2>/dev/null; do
552
+ local mem=$(ps -o rss= -p $pid)
553
+ local mem_mb=$((mem / 1024))
554
+ echo "Memory: ${mem_mb}MB"
555
+ sleep 1
556
+ done
557
+ }
558
+
559
+ # Start process
560
+ ./large-export.sh &
561
+ PID=$!
562
+
563
+ monitor_memory $PID
564
+ ```
565
+
566
+ ### Disk Space Management
567
+
568
+ ```bash
569
+ #!/bin/bash
570
+
571
+ # Clean old cache files
572
+ clean_cache() {
573
+ local cache_dir=".cache"
574
+ local max_age_days=7
575
+
576
+ echo "Cleaning cache older than $max_age_days days..."
577
+
578
+ find "$cache_dir" -type f -mtime +$max_age_days -delete
579
+
580
+ local freed=$(du -sh "$cache_dir" | cut -f1)
581
+ echo "Cache size: $freed"
582
+ }
583
+
584
+ clean_cache
585
+ ```
586
+
587
+ ### Rate Limit Optimization
588
+
589
+ ```bash
590
+ #!/bin/bash
591
+
592
+ # Adaptive rate limiting
593
+ adaptive_rate_limit() {
594
+ local success_count=0
595
+ local failure_count=0
596
+ local delay=0.5
597
+
598
+ for item in "$@"; do
599
+ if process_item "$item"; then
600
+ ((success_count++))
601
+
602
+ # Speed up if successful
603
+ if [ $((success_count % 10)) -eq 0 ]; then
604
+ delay=$(echo "$delay * 0.9" | bc)
605
+ [ $(echo "$delay < 0.1" | bc) -eq 1 ] && delay=0.1
606
+ fi
607
+ else
608
+ ((failure_count++))
609
+
610
+ # Slow down on failure
611
+ delay=$(echo "$delay * 1.5" | bc)
612
+ echo "Rate limit hit, slowing to ${delay}s"
613
+ fi
614
+
615
+ sleep "$delay"
616
+ done
617
+
618
+ echo "Completed: $success_count success, $failure_count failures"
619
+ }
620
+ ```
621
+
622
+ ## Query Optimization
623
+
624
+ ### Minimize Data Transfer
625
+
626
+ ```bash
627
+ #!/bin/bash
628
+
629
+ # Bad: Fetch all fields
630
+ cakemail campaigns list -f json | jq '.data[].id'
631
+
632
+ # Good: Request only needed fields (if API supports)
633
+ cakemail campaigns list -f json | jq '.data[] | {id, name}'
634
+ ```
635
+
636
+ ### Smart Pagination
637
+
638
+ ```bash
639
+ #!/bin/bash
640
+
641
+ # Fetch only what you need
642
+ fetch_recent_campaigns() {
643
+ local needed=50
644
+ local per_page=50
645
+
646
+ # Single request
647
+ cakemail campaigns list \
648
+ --per-page "$per_page" \
649
+ --page 1 \
650
+ -f json
651
+ }
652
+
653
+ # vs. fetching all pages
654
+ fetch_all_campaigns() {
655
+ # Potentially hundreds of requests
656
+ local page=1
657
+ while true; do
658
+ local data=$(cakemail campaigns list --page $page -f json)
659
+ [ $(echo "$data" | jq '.data | length') -eq 0 ] && break
660
+ ((page++))
661
+ done
662
+ }
663
+ ```
664
+
665
+ ### Conditional Fetching
666
+
667
+ ```bash
668
+ #!/bin/bash
669
+
670
+ # Only fetch if data changed
671
+ conditional_fetch() {
672
+ local resource="$1"
673
+ local etag_file=".etag-${resource}"
674
+
675
+ # Get stored ETag
676
+ local stored_etag=""
677
+ [ -f "$etag_file" ] && stored_etag=$(cat "$etag_file")
678
+
679
+ # Fetch with If-None-Match
680
+ local response=$(curl -s -D - \
681
+ -H "If-None-Match: $stored_etag" \
682
+ "https://api.cakemail.com/$resource")
683
+
684
+ local status=$(echo "$response" | grep HTTP | awk '{print $2}')
685
+
686
+ if [ "$status" = "304" ]; then
687
+ echo "Not modified - using cache"
688
+ return 0
689
+ fi
690
+
691
+ # Extract and store new ETag
692
+ local new_etag=$(echo "$response" | grep -i etag | cut -d' ' -f2)
693
+ echo "$new_etag" > "$etag_file"
694
+
695
+ echo "Data updated"
696
+ }
697
+ ```
698
+
699
+ ## Best Practices
700
+
701
+ 1. **Cache Aggressively**
702
+ - Cache list data (rarely changes)
703
+ - Don't cache real-time metrics
704
+ - Set appropriate TTLs
705
+
706
+ 2. **Batch Operations**
707
+ - Combine related API calls
708
+ - Use bulk endpoints when available
709
+ - Process in chunks
710
+
711
+ 3. **Optimize Parallelism**
712
+ - Test different concurrency levels
713
+ - Don't exceed 5-10 concurrent requests
714
+ - Monitor for rate limiting
715
+
716
+ 4. **Filter Server-Side**
717
+ - Use API filters, not local filtering
718
+ - Request only needed fields
719
+ - Paginate intelligently
720
+
721
+ 5. **Monitor Performance**
722
+ - Profile slow operations
723
+ - Track API response times
724
+ - Log cache hit rates
725
+
726
+ 6. **Manage Resources**
727
+ - Clean old cache files
728
+ - Monitor memory usage
729
+ - Stream large datasets
730
+
731
+ 7. **Handle Rate Limits**
732
+ - Implement exponential backoff
733
+ - Use adaptive delays
734
+ - Respect API limits
735
+
736
+ 8. **Optimize Scripts**
737
+ - Avoid redundant API calls
738
+ - Reuse data where possible
739
+ - Use efficient data structures
740
+
741
+ ## Performance Checklist
742
+
743
+ - [ ] Implemented caching for frequently accessed data
744
+ - [ ] Using server-side filtering instead of local filtering
745
+ - [ ] Processing data in streams for large datasets
746
+ - [ ] Limited concurrent requests to 3-5
747
+ - [ ] Using JSON format for data processing
748
+ - [ ] Implemented retry logic with backoff
749
+ - [ ] Monitoring resource usage (memory, disk)
750
+ - [ ] Cleaning up temporary files and cache
751
+ - [ ] Using pagination appropriately
752
+ - [ ] Profiled slow operations
753
+
754
+ ## Benchmarking Results
755
+
756
+ Example performance improvements:
757
+
758
+ | Operation | Before | After | Improvement |
759
+ |-----------|--------|-------|-------------|
760
+ | List 10k contacts | 45s | 5s | 9x faster |
761
+ | Generate 100 reports | 180s | 25s | 7x faster |
762
+ | Import 50k contacts | 300s | 60s | 5x faster |
763
+ | Cache hit rate | 0% | 85% | N/A |
764
+
765
+ *Results vary based on network, system resources, and API conditions.*
766
+