cmdx 1.1.1 → 1.5.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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/.DS_Store +0 -0
  3. data/.cursor/prompts/docs.md +4 -1
  4. data/.cursor/prompts/llms.md +20 -0
  5. data/.cursor/prompts/rspec.md +4 -1
  6. data/.cursor/prompts/yardoc.md +3 -2
  7. data/.cursor/rules/cursor-instructions.mdc +56 -1
  8. data/.irbrc +6 -0
  9. data/.rubocop.yml +29 -18
  10. data/.ruby-version +1 -1
  11. data/CHANGELOG.md +6 -128
  12. data/LLM.md +3317 -0
  13. data/README.md +68 -44
  14. data/docs/attributes/coercions.md +162 -0
  15. data/docs/attributes/defaults.md +90 -0
  16. data/docs/attributes/definitions.md +281 -0
  17. data/docs/attributes/naming.md +78 -0
  18. data/docs/attributes/validations.md +309 -0
  19. data/docs/basics/chain.md +56 -249
  20. data/docs/basics/context.md +56 -289
  21. data/docs/basics/execution.md +114 -0
  22. data/docs/basics/setup.md +37 -334
  23. data/docs/callbacks.md +89 -467
  24. data/docs/deprecation.md +91 -174
  25. data/docs/getting_started.md +212 -202
  26. data/docs/internationalization.md +11 -647
  27. data/docs/interruptions/exceptions.md +23 -198
  28. data/docs/interruptions/faults.md +71 -151
  29. data/docs/interruptions/halt.md +109 -186
  30. data/docs/logging.md +44 -256
  31. data/docs/middlewares.md +113 -426
  32. data/docs/outcomes/result.md +81 -228
  33. data/docs/outcomes/states.md +33 -221
  34. data/docs/outcomes/statuses.md +21 -311
  35. data/docs/tips_and_tricks.md +120 -70
  36. data/docs/workflows.md +99 -283
  37. data/lib/cmdx/.DS_Store +0 -0
  38. data/lib/cmdx/attribute.rb +229 -0
  39. data/lib/cmdx/attribute_registry.rb +94 -0
  40. data/lib/cmdx/attribute_value.rb +193 -0
  41. data/lib/cmdx/callback_registry.rb +69 -77
  42. data/lib/cmdx/chain.rb +56 -73
  43. data/lib/cmdx/coercion_registry.rb +52 -68
  44. data/lib/cmdx/coercions/array.rb +19 -18
  45. data/lib/cmdx/coercions/big_decimal.rb +20 -24
  46. data/lib/cmdx/coercions/boolean.rb +26 -25
  47. data/lib/cmdx/coercions/complex.rb +21 -22
  48. data/lib/cmdx/coercions/date.rb +25 -23
  49. data/lib/cmdx/coercions/date_time.rb +24 -25
  50. data/lib/cmdx/coercions/float.rb +25 -22
  51. data/lib/cmdx/coercions/hash.rb +31 -32
  52. data/lib/cmdx/coercions/integer.rb +30 -24
  53. data/lib/cmdx/coercions/rational.rb +29 -24
  54. data/lib/cmdx/coercions/string.rb +19 -22
  55. data/lib/cmdx/coercions/symbol.rb +37 -0
  56. data/lib/cmdx/coercions/time.rb +26 -25
  57. data/lib/cmdx/configuration.rb +49 -108
  58. data/lib/cmdx/context.rb +222 -44
  59. data/lib/cmdx/deprecator.rb +61 -0
  60. data/lib/cmdx/errors.rb +42 -252
  61. data/lib/cmdx/exceptions.rb +39 -0
  62. data/lib/cmdx/faults.rb +78 -39
  63. data/lib/cmdx/freezer.rb +51 -0
  64. data/lib/cmdx/identifier.rb +30 -0
  65. data/lib/cmdx/locale.rb +52 -0
  66. data/lib/cmdx/log_formatters/json.rb +21 -22
  67. data/lib/cmdx/log_formatters/key_value.rb +20 -22
  68. data/lib/cmdx/log_formatters/line.rb +15 -22
  69. data/lib/cmdx/log_formatters/logstash.rb +22 -23
  70. data/lib/cmdx/log_formatters/raw.rb +16 -22
  71. data/lib/cmdx/middleware_registry.rb +70 -74
  72. data/lib/cmdx/middlewares/correlate.rb +90 -54
  73. data/lib/cmdx/middlewares/runtime.rb +58 -0
  74. data/lib/cmdx/middlewares/timeout.rb +48 -68
  75. data/lib/cmdx/railtie.rb +12 -45
  76. data/lib/cmdx/result.rb +229 -314
  77. data/lib/cmdx/task.rb +194 -366
  78. data/lib/cmdx/utils/call.rb +49 -0
  79. data/lib/cmdx/utils/condition.rb +71 -0
  80. data/lib/cmdx/utils/format.rb +61 -0
  81. data/lib/cmdx/validator_registry.rb +63 -72
  82. data/lib/cmdx/validators/exclusion.rb +38 -67
  83. data/lib/cmdx/validators/format.rb +48 -49
  84. data/lib/cmdx/validators/inclusion.rb +43 -74
  85. data/lib/cmdx/validators/length.rb +91 -154
  86. data/lib/cmdx/validators/numeric.rb +87 -162
  87. data/lib/cmdx/validators/presence.rb +37 -50
  88. data/lib/cmdx/version.rb +1 -1
  89. data/lib/cmdx/worker.rb +178 -0
  90. data/lib/cmdx/workflow.rb +85 -81
  91. data/lib/cmdx.rb +19 -13
  92. data/lib/generators/cmdx/install_generator.rb +14 -13
  93. data/lib/generators/cmdx/task_generator.rb +25 -50
  94. data/lib/generators/cmdx/templates/install.rb +11 -46
  95. data/lib/generators/cmdx/templates/task.rb.tt +3 -2
  96. data/lib/locales/en.yml +18 -4
  97. data/src/cmdx-logo.png +0 -0
  98. metadata +32 -116
  99. data/docs/ai_prompts.md +0 -393
  100. data/docs/basics/call.md +0 -317
  101. data/docs/configuration.md +0 -344
  102. data/docs/parameters/coercions.md +0 -396
  103. data/docs/parameters/defaults.md +0 -335
  104. data/docs/parameters/definitions.md +0 -446
  105. data/docs/parameters/namespacing.md +0 -378
  106. data/docs/parameters/validations.md +0 -405
  107. data/docs/testing.md +0 -553
  108. data/lib/cmdx/callback.rb +0 -53
  109. data/lib/cmdx/chain_inspector.rb +0 -56
  110. data/lib/cmdx/chain_serializer.rb +0 -63
  111. data/lib/cmdx/coercion.rb +0 -57
  112. data/lib/cmdx/coercions/virtual.rb +0 -29
  113. data/lib/cmdx/core_ext/hash.rb +0 -83
  114. data/lib/cmdx/core_ext/module.rb +0 -98
  115. data/lib/cmdx/core_ext/object.rb +0 -125
  116. data/lib/cmdx/correlator.rb +0 -122
  117. data/lib/cmdx/error.rb +0 -60
  118. data/lib/cmdx/fault.rb +0 -140
  119. data/lib/cmdx/immutator.rb +0 -52
  120. data/lib/cmdx/lazy_struct.rb +0 -246
  121. data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
  122. data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
  123. data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
  124. data/lib/cmdx/logger.rb +0 -49
  125. data/lib/cmdx/logger_ansi.rb +0 -68
  126. data/lib/cmdx/logger_serializer.rb +0 -116
  127. data/lib/cmdx/middleware.rb +0 -70
  128. data/lib/cmdx/parameter.rb +0 -312
  129. data/lib/cmdx/parameter_evaluator.rb +0 -231
  130. data/lib/cmdx/parameter_inspector.rb +0 -66
  131. data/lib/cmdx/parameter_registry.rb +0 -106
  132. data/lib/cmdx/parameter_serializer.rb +0 -59
  133. data/lib/cmdx/result_ansi.rb +0 -71
  134. data/lib/cmdx/result_inspector.rb +0 -71
  135. data/lib/cmdx/result_logger.rb +0 -59
  136. data/lib/cmdx/result_serializer.rb +0 -104
  137. data/lib/cmdx/rspec/matchers.rb +0 -28
  138. data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
  139. data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
  140. data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
  141. data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
  142. data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
  143. data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
  144. data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
  145. data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
  146. data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
  147. data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
  148. data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
  149. data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
  150. data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
  151. data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
  152. data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
  153. data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
  154. data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
  155. data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
  156. data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
  157. data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
  158. data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
  159. data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
  160. data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
  161. data/lib/cmdx/task_deprecator.rb +0 -52
  162. data/lib/cmdx/task_processor.rb +0 -246
  163. data/lib/cmdx/task_serializer.rb +0 -57
  164. data/lib/cmdx/utils/ansi_color.rb +0 -73
  165. data/lib/cmdx/utils/log_timestamp.rb +0 -36
  166. data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
  167. data/lib/cmdx/utils/name_affix.rb +0 -52
  168. data/lib/cmdx/validator.rb +0 -57
  169. data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
  170. data/lib/generators/cmdx/workflow_generator.rb +0 -84
  171. data/lib/locales/ar.yml +0 -35
  172. data/lib/locales/cs.yml +0 -35
  173. data/lib/locales/da.yml +0 -35
  174. data/lib/locales/de.yml +0 -35
  175. data/lib/locales/el.yml +0 -35
  176. data/lib/locales/es.yml +0 -35
  177. data/lib/locales/fi.yml +0 -35
  178. data/lib/locales/fr.yml +0 -35
  179. data/lib/locales/he.yml +0 -35
  180. data/lib/locales/hi.yml +0 -35
  181. data/lib/locales/it.yml +0 -35
  182. data/lib/locales/ja.yml +0 -35
  183. data/lib/locales/ko.yml +0 -35
  184. data/lib/locales/nl.yml +0 -35
  185. data/lib/locales/no.yml +0 -35
  186. data/lib/locales/pl.yml +0 -35
  187. data/lib/locales/pt.yml +0 -35
  188. data/lib/locales/ru.yml +0 -35
  189. data/lib/locales/sv.yml +0 -35
  190. data/lib/locales/th.yml +0 -35
  191. data/lib/locales/tr.yml +0 -35
  192. data/lib/locales/vi.yml +0 -35
  193. data/lib/locales/zh.yml +0 -35
data/docs/deprecation.md CHANGED
@@ -4,242 +4,159 @@ Task deprecation provides a systematic approach to managing legacy tasks in CMDx
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [TLDR](#tldr)
8
- - [Deprecation Fundamentals](#deprecation-fundamentals)
9
- - [Deprecation Modes](#deprecation-modes)
10
- - [Configuration Examples](#configuration-examples)
11
- - [Migration Strategies](#migration-strategies)
12
- - [Error Handling](#error-handling)
13
- - [Best Practices](#best-practices)
7
+ - [Modes](#modes)
8
+ - [Raise](#raise)
9
+ - [Log](#log)
10
+ - [Warn](#warn)
11
+ - [Declarations](#declarations)
12
+ - [Symbol or String](#symbol-or-string)
13
+ - [Boolean or Nil](#boolean-or-nil)
14
+ - [Method](#method)
15
+ - [Proc or Lambda](#proc-or-lambda)
16
+ - [Class or Module](#class-or-module)
14
17
 
15
- ## TLDR
18
+ ## Modes
16
19
 
17
- ```ruby
18
- # Prevent task execution completely
19
- class LegacyTask < CMDx::Task
20
- cmd_setting!(deprecated: :error)
21
- end
22
-
23
- # Log deprecation warnings
24
- class OldTask < CMDx::Task
25
- cmd_setting!(deprecated: :log)
26
- end
27
-
28
- # Issue Ruby warnings
29
- class ObsoleteTask < CMDx::Task
30
- cmd_setting!(deprecated: :warning)
31
- end
32
-
33
- # Usage triggers appropriate deprecation handling
34
- LegacyTask.call # → raises DeprecationError
35
- OldTask.call # → logs warning via task.logger
36
- ObsoleteTask.call # → issues Ruby warning
37
- ```
20
+ ### Raise
38
21
 
39
- ## Deprecation Fundamentals
40
-
41
- > [!NOTE]
42
- > Task deprecation is configured using the `cmd_setting!` declaration and processed automatically by CMDx before task execution. The deprecation system integrates seamlessly with existing logging and error handling infrastructure.
43
-
44
- ### How It Works
45
-
46
- 1. **Configuration**: Tasks declare deprecation mode using `cmd_setting!(deprecated: mode)`
47
- 2. **Processing**: CMDx automatically calls `TaskDeprecator.call(task)` during task lifecycle
48
- 3. **Action**: Appropriate deprecation handling occurs based on configured mode
49
- 4. **Execution**: Task proceeds normally (unless `:error` mode prevents it)
50
-
51
- ### Available Modes
52
-
53
- | Mode | Behavior | Use Case |
54
- |------|----------|----------|
55
- | `:error` | Raises `DeprecationError` | Hard deprecation, prevent execution |
56
- | `:log` | Logs warning via `task.logger.warn` | Soft deprecation, track usage |
57
- | `:warning` | Issues Ruby warning | Development alerts |
58
- | `true` | Same as `:log` | Legacy boolean support |
59
- | `nil/false` | No deprecation handling | Default behavior |
60
-
61
- ## Deprecation Modes
62
-
63
- ### Error Mode (Hard Deprecation)
22
+ `:raise` mode prevents task execution entirely. Use this for tasks that should no longer be used under any circumstances.
64
23
 
65
24
  > [!WARNING]
66
- > Error mode prevents task execution entirely. Use this for tasks that should no longer be used under any circumstances.
25
+ > Use `:raise` mode carefully in production environments as it will break existing workflows immediately.
67
26
 
68
27
  ```ruby
69
- class ProcessLegacyPaymentTask < CMDx::Task
70
- cmd_setting!(deprecated: :error)
28
+ class ProcessObsoleteAPI < CMDx::Task
29
+ settings(deprecated: :raise)
71
30
 
72
- def call
73
- # This code will never execute
74
- charge_customer(amount)
31
+ def work
32
+ # Will never execute...
75
33
  end
76
34
  end
77
35
 
78
- # Attempting to use deprecated task
79
- result = ProcessLegacyPaymentTask.call(amount: 100)
80
- # → raises CMDx::DeprecationError: "ProcessLegacyPaymentTask usage prohibited"
36
+ result = ProcessObsoleteAPI.execute
37
+ #=> raises CMDx::DeprecationError: "ProcessObsoleteAPI usage prohibited"
81
38
  ```
82
39
 
83
- ### Log Mode (Soft Deprecation)
40
+ ### Log
84
41
 
85
- > [!TIP]
86
- > Log mode allows continued usage while tracking deprecation warnings. Perfect for gradual migration scenarios where immediate replacement isn't feasible.
42
+ `:log` mode allows continued usage while tracking deprecation warnings. Perfect for gradual migration scenarios where immediate replacement isn't feasible.
87
43
 
88
44
  ```ruby
89
- class ProcessOldPaymentTask < CMDx::Task
90
- cmd_setting!(deprecated: :log)
45
+ class ProcessLegacyFormat < CMDx::Task
46
+ settings(deprecated: :log)
47
+
48
+ # Same
49
+ settings(deprecated: true)
91
50
 
92
- def call
93
- # Task executes normally but logs deprecation warning
94
- charge_customer(amount)
51
+ def work
52
+ # Executes but logs deprecation warning...
95
53
  end
96
54
  end
97
55
 
98
- # Task executes with logged warning
99
- result = ProcessOldPaymentTask.call(amount: 100)
100
- result.successful? # → true
56
+ result = ProcessLegacyFormat.execute
57
+ result.successful? #=> true
101
58
 
102
- # Check logs for deprecation warning:
103
- # WARN -- : DEPRECATED: migrate to replacement or discontinue use
59
+ # Deprecation warning appears in logs:
60
+ # WARN -- : DEPRECATED: ProcessLegacyFormat - migrate to replacement or discontinue use
104
61
  ```
105
62
 
106
- ### Warning Mode (Development Alerts)
63
+ ### Warn
107
64
 
108
- > [!NOTE]
109
- > Warning mode issues Ruby warnings visible in development and testing environments. Useful for alerting developers without affecting production logging.
65
+ `:warn` mode issues Ruby warnings visible in development and testing environments. Useful for alerting developers without affecting production logging.
110
66
 
111
67
  ```ruby
112
- class ProcessObsoletePaymentTask < CMDx::Task
113
- cmd_setting!(deprecated: :warning)
68
+ class ProcessOldData < CMDx::Task
69
+ settings(deprecated: :warn)
114
70
 
115
- def call
116
- # Task executes with Ruby warning
117
- charge_customer(amount)
71
+ def work
72
+ # Executes but emits Ruby warning...
118
73
  end
119
74
  end
120
75
 
121
- # Task executes with Ruby warning
122
- result = ProcessObsoletePaymentTask.call(amount: 100)
123
- # stderr: [ProcessObsoletePaymentTask] DEPRECATED: migrate to replacement or discontinue use
76
+ result = ProcessOldData.execute
77
+ result.successful? #=> true
124
78
 
125
- result.successful? # true
79
+ # Ruby warning appears in stderr:
80
+ # [ProcessOldData] DEPRECATED: migrate to replacement or discontinue use
126
81
  ```
127
82
 
128
- ## Configuration Examples
83
+ ## Declarations
129
84
 
130
- ### Environment-Specific Deprecation
85
+ ### Symbol or String
131
86
 
132
87
  ```ruby
133
- class ExperimentalFeatureTask < CMDx::Task
134
- # Different deprecation behavior per environment
135
- cmd_setting!(
136
- deprecated: Rails.env.production? ? :error : :warning
137
- )
138
-
139
- def call
140
- enable_experimental_feature
141
- end
88
+ class OutdatedConnector < CMDx::Task
89
+ # Symbol
90
+ settings(deprecated: :raise)
91
+
92
+ # String
93
+ settings(deprecated: "warn")
142
94
  end
143
95
  ```
144
96
 
145
- ### Conditional Deprecation
97
+ ### Boolean or Nil
146
98
 
147
99
  ```ruby
148
- class LegacyIntegrationTask < CMDx::Task
149
- # Deprecate only for specific conditions
150
- cmd_setting!(
151
- deprecated: -> { ENV['NEW_API_ENABLED'] == 'true' ? :log : nil }
152
- )
153
-
154
- def call
155
- call_legacy_api
156
- end
100
+ class OutdatedConnector < CMDx::Task
101
+ # Deprecates with default :log mode
102
+ settings(deprecated: true)
103
+
104
+ # Skips deprecation
105
+ settings(deprecated: false)
106
+ settings(deprecated: nil)
157
107
  end
158
108
  ```
159
109
 
160
- ## Migration Strategies
110
+ ### Method
161
111
 
162
- > [!IMPORTANT]
163
- > When deprecating tasks, always provide clear migration paths and replacement implementations to minimize disruption.
112
+ ```ruby
113
+ class OutdatedConnector < CMDx::Task
114
+ # Symbol
115
+ settings(deprecated: :deprecated?)
116
+
117
+ def work
118
+ # Your logic here...
119
+ end
164
120
 
165
- ### Graceful Fallback
121
+ private
166
122
 
167
- ```ruby
168
- class NotificationTask < CMDx::Task
169
- cmd_setting!(deprecated: :log)
170
-
171
- def call
172
- # Provide fallback while encouraging migration
173
- logger.warn "Consider migrating to NotificationServiceV2"
174
-
175
- # Delegate to new service but maintain compatibility
176
- NotificationServiceV2.send_notification(
177
- recipient: recipient,
178
- message: message,
179
- delivery_method: :legacy
180
- )
123
+ def deprecated?
124
+ Time.now.year > 2024 ? :raise : false
181
125
  end
182
126
  end
183
127
  ```
184
128
 
185
- ## Error Handling
186
-
187
- ### Catching Deprecation Errors
129
+ ### Proc or Lambda
188
130
 
189
131
  ```ruby
190
- begin
191
- result = LegacyTask.call(params)
192
- rescue CMDx::DeprecationError => e
193
- # Handle deprecation gracefully
194
- Rails.logger.error "Attempted to use deprecated task: #{e.message}"
195
-
196
- # Use replacement task instead
197
- result = ReplacementTask.call(params)
198
- end
132
+ class OutdatedConnector < CMDx::Task
133
+ # Proc
134
+ settings(deprecated: proc { Rails.env.development? ? :raise : :log })
199
135
 
200
- if result.successful?
201
- # Process successful result
202
- else
203
- # Handle task failure
136
+ # Lambda
137
+ settings(deprecated: -> { Current.tenant.legacy_mode? ? :warn : :raise })
204
138
  end
205
139
  ```
206
140
 
207
- ## Best Practices
208
-
209
- ### Documentation and Communication
210
-
211
- > [!TIP]
212
- > Always document deprecation reasons, timelines, and migration paths. Clear communication prevents confusion and reduces support burden.
141
+ ### Class or Module
213
142
 
214
143
  ```ruby
215
- class LegacyReportTask < CMDx::Task
216
- # Document deprecation clearly
217
- cmd_setting!(deprecated: :log)
218
-
219
- # Class-level documentation
220
- # @deprecated Use ReportGeneratorV2Task instead
221
- # @see ReportGeneratorV2Task
222
- # @note This task will be removed in v2.0.0
223
- # @since 1.5.0 marked as deprecated
224
-
225
- def call
226
- # Add inline documentation
227
- logger.warn <<~DEPRECATION
228
- LegacyReportTask is deprecated and will be removed in v2.0.0.
229
- Please migrate to ReportGeneratorV2Task which provides:
230
- - Better performance
231
- - Enhanced error handling
232
- - More flexible output formats
233
-
234
- Migration guide: https://docs.example.com/migration/reports
235
- DEPRECATION
236
-
237
- generate_legacy_report
144
+ class OutdatedTaskDeprecator
145
+ def call(task)
146
+ task.class.name.include?("Outdated")
238
147
  end
239
148
  end
149
+
150
+ class OutdatedConnector < CMDx::Task
151
+ # Class or Module
152
+ settings(deprecated: OutdatedTaskDeprecator)
153
+
154
+ # Instance
155
+ settings(deprecated: OutdatedTaskDeprecator.new)
156
+ end
240
157
  ```
241
158
 
242
159
  ---
243
160
 
244
- - **Prev:** [Testing](testing.md)
245
- - **Next:** [AI Prompts](ai_prompts.md)
161
+ - **Prev:** [Internationalization (i18n)](internationalization.md)
162
+ - **Next:** [Workflows](workflows.md)