cmdx 1.1.2 → 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.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.cursor/prompts/docs.md +4 -1
- data/.cursor/prompts/llms.md +20 -0
- data/.cursor/prompts/rspec.md +4 -1
- data/.cursor/prompts/yardoc.md +3 -2
- data/.cursor/rules/cursor-instructions.mdc +56 -1
- data/.irbrc +6 -0
- data/.rubocop.yml +29 -18
- data/CHANGELOG.md +5 -133
- data/LLM.md +3317 -0
- data/README.md +68 -44
- data/docs/attributes/coercions.md +162 -0
- data/docs/attributes/defaults.md +90 -0
- data/docs/attributes/definitions.md +281 -0
- data/docs/attributes/naming.md +78 -0
- data/docs/attributes/validations.md +309 -0
- data/docs/basics/chain.md +56 -249
- data/docs/basics/context.md +56 -289
- data/docs/basics/execution.md +114 -0
- data/docs/basics/setup.md +37 -334
- data/docs/callbacks.md +89 -467
- data/docs/deprecation.md +91 -174
- data/docs/getting_started.md +212 -202
- data/docs/internationalization.md +11 -647
- data/docs/interruptions/exceptions.md +23 -198
- data/docs/interruptions/faults.md +71 -151
- data/docs/interruptions/halt.md +109 -186
- data/docs/logging.md +44 -256
- data/docs/middlewares.md +113 -426
- data/docs/outcomes/result.md +81 -228
- data/docs/outcomes/states.md +33 -221
- data/docs/outcomes/statuses.md +21 -311
- data/docs/tips_and_tricks.md +120 -70
- data/docs/workflows.md +99 -283
- data/lib/cmdx/.DS_Store +0 -0
- data/lib/cmdx/attribute.rb +229 -0
- data/lib/cmdx/attribute_registry.rb +94 -0
- data/lib/cmdx/attribute_value.rb +193 -0
- data/lib/cmdx/callback_registry.rb +69 -77
- data/lib/cmdx/chain.rb +56 -73
- data/lib/cmdx/coercion_registry.rb +52 -68
- data/lib/cmdx/coercions/array.rb +19 -18
- data/lib/cmdx/coercions/big_decimal.rb +20 -24
- data/lib/cmdx/coercions/boolean.rb +26 -25
- data/lib/cmdx/coercions/complex.rb +21 -22
- data/lib/cmdx/coercions/date.rb +25 -23
- data/lib/cmdx/coercions/date_time.rb +24 -25
- data/lib/cmdx/coercions/float.rb +25 -22
- data/lib/cmdx/coercions/hash.rb +31 -32
- data/lib/cmdx/coercions/integer.rb +30 -24
- data/lib/cmdx/coercions/rational.rb +29 -24
- data/lib/cmdx/coercions/string.rb +19 -22
- data/lib/cmdx/coercions/symbol.rb +37 -0
- data/lib/cmdx/coercions/time.rb +26 -25
- data/lib/cmdx/configuration.rb +49 -108
- data/lib/cmdx/context.rb +222 -44
- data/lib/cmdx/deprecator.rb +61 -0
- data/lib/cmdx/errors.rb +42 -252
- data/lib/cmdx/exceptions.rb +39 -0
- data/lib/cmdx/faults.rb +78 -39
- data/lib/cmdx/freezer.rb +51 -0
- data/lib/cmdx/identifier.rb +30 -0
- data/lib/cmdx/locale.rb +52 -0
- data/lib/cmdx/log_formatters/json.rb +21 -22
- data/lib/cmdx/log_formatters/key_value.rb +20 -22
- data/lib/cmdx/log_formatters/line.rb +15 -22
- data/lib/cmdx/log_formatters/logstash.rb +22 -23
- data/lib/cmdx/log_formatters/raw.rb +16 -22
- data/lib/cmdx/middleware_registry.rb +70 -74
- data/lib/cmdx/middlewares/correlate.rb +90 -54
- data/lib/cmdx/middlewares/runtime.rb +58 -0
- data/lib/cmdx/middlewares/timeout.rb +48 -68
- data/lib/cmdx/railtie.rb +12 -45
- data/lib/cmdx/result.rb +229 -314
- data/lib/cmdx/task.rb +194 -366
- data/lib/cmdx/utils/call.rb +49 -0
- data/lib/cmdx/utils/condition.rb +71 -0
- data/lib/cmdx/utils/format.rb +61 -0
- data/lib/cmdx/validator_registry.rb +63 -72
- data/lib/cmdx/validators/exclusion.rb +38 -67
- data/lib/cmdx/validators/format.rb +48 -49
- data/lib/cmdx/validators/inclusion.rb +43 -74
- data/lib/cmdx/validators/length.rb +91 -154
- data/lib/cmdx/validators/numeric.rb +87 -162
- data/lib/cmdx/validators/presence.rb +37 -50
- data/lib/cmdx/version.rb +1 -1
- data/lib/cmdx/worker.rb +178 -0
- data/lib/cmdx/workflow.rb +85 -81
- data/lib/cmdx.rb +19 -13
- data/lib/generators/cmdx/install_generator.rb +14 -13
- data/lib/generators/cmdx/task_generator.rb +25 -50
- data/lib/generators/cmdx/templates/install.rb +11 -46
- data/lib/generators/cmdx/templates/task.rb.tt +3 -2
- data/lib/locales/en.yml +18 -4
- data/src/cmdx-logo.png +0 -0
- metadata +32 -116
- data/docs/ai_prompts.md +0 -393
- data/docs/basics/call.md +0 -317
- data/docs/configuration.md +0 -344
- data/docs/parameters/coercions.md +0 -396
- data/docs/parameters/defaults.md +0 -335
- data/docs/parameters/definitions.md +0 -446
- data/docs/parameters/namespacing.md +0 -378
- data/docs/parameters/validations.md +0 -405
- data/docs/testing.md +0 -553
- data/lib/cmdx/callback.rb +0 -53
- data/lib/cmdx/chain_inspector.rb +0 -56
- data/lib/cmdx/chain_serializer.rb +0 -63
- data/lib/cmdx/coercion.rb +0 -57
- data/lib/cmdx/coercions/virtual.rb +0 -29
- data/lib/cmdx/core_ext/hash.rb +0 -83
- data/lib/cmdx/core_ext/module.rb +0 -98
- data/lib/cmdx/core_ext/object.rb +0 -125
- data/lib/cmdx/correlator.rb +0 -122
- data/lib/cmdx/error.rb +0 -67
- data/lib/cmdx/fault.rb +0 -140
- data/lib/cmdx/immutator.rb +0 -52
- data/lib/cmdx/lazy_struct.rb +0 -246
- data/lib/cmdx/log_formatters/pretty_json.rb +0 -40
- data/lib/cmdx/log_formatters/pretty_key_value.rb +0 -38
- data/lib/cmdx/log_formatters/pretty_line.rb +0 -41
- data/lib/cmdx/logger.rb +0 -49
- data/lib/cmdx/logger_ansi.rb +0 -68
- data/lib/cmdx/logger_serializer.rb +0 -116
- data/lib/cmdx/middleware.rb +0 -70
- data/lib/cmdx/parameter.rb +0 -312
- data/lib/cmdx/parameter_evaluator.rb +0 -231
- data/lib/cmdx/parameter_inspector.rb +0 -66
- data/lib/cmdx/parameter_registry.rb +0 -106
- data/lib/cmdx/parameter_serializer.rb +0 -59
- data/lib/cmdx/result_ansi.rb +0 -71
- data/lib/cmdx/result_inspector.rb +0 -71
- data/lib/cmdx/result_logger.rb +0 -59
- data/lib/cmdx/result_serializer.rb +0 -104
- data/lib/cmdx/rspec/matchers.rb +0 -28
- data/lib/cmdx/rspec/result_matchers/be_executed.rb +0 -42
- data/lib/cmdx/rspec/result_matchers/be_failed_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_skipped_task.rb +0 -94
- data/lib/cmdx/rspec/result_matchers/be_state_matchers.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/be_status_matchers.rb +0 -57
- data/lib/cmdx/rspec/result_matchers/be_successful_task.rb +0 -87
- data/lib/cmdx/rspec/result_matchers/have_bad_outcome.rb +0 -51
- data/lib/cmdx/rspec/result_matchers/have_caused_failure.rb +0 -58
- data/lib/cmdx/rspec/result_matchers/have_chain_index.rb +0 -59
- data/lib/cmdx/rspec/result_matchers/have_context.rb +0 -86
- data/lib/cmdx/rspec/result_matchers/have_empty_metadata.rb +0 -54
- data/lib/cmdx/rspec/result_matchers/have_good_outcome.rb +0 -52
- data/lib/cmdx/rspec/result_matchers/have_metadata.rb +0 -114
- data/lib/cmdx/rspec/result_matchers/have_preserved_context.rb +0 -66
- data/lib/cmdx/rspec/result_matchers/have_received_thrown_failure.rb +0 -64
- data/lib/cmdx/rspec/result_matchers/have_runtime.rb +0 -78
- data/lib/cmdx/rspec/result_matchers/have_thrown_failure.rb +0 -76
- data/lib/cmdx/rspec/task_matchers/be_well_formed_task.rb +0 -62
- data/lib/cmdx/rspec/task_matchers/have_callback.rb +0 -85
- data/lib/cmdx/rspec/task_matchers/have_cmd_setting.rb +0 -68
- data/lib/cmdx/rspec/task_matchers/have_executed_callbacks.rb +0 -92
- data/lib/cmdx/rspec/task_matchers/have_middleware.rb +0 -46
- data/lib/cmdx/rspec/task_matchers/have_parameter.rb +0 -181
- data/lib/cmdx/task_deprecator.rb +0 -58
- data/lib/cmdx/task_processor.rb +0 -246
- data/lib/cmdx/task_serializer.rb +0 -57
- data/lib/cmdx/utils/ansi_color.rb +0 -73
- data/lib/cmdx/utils/log_timestamp.rb +0 -36
- data/lib/cmdx/utils/monotonic_runtime.rb +0 -34
- data/lib/cmdx/utils/name_affix.rb +0 -52
- data/lib/cmdx/validator.rb +0 -57
- data/lib/generators/cmdx/templates/workflow.rb.tt +0 -7
- data/lib/generators/cmdx/workflow_generator.rb +0 -84
- data/lib/locales/ar.yml +0 -35
- data/lib/locales/cs.yml +0 -35
- data/lib/locales/da.yml +0 -35
- data/lib/locales/de.yml +0 -35
- data/lib/locales/el.yml +0 -35
- data/lib/locales/es.yml +0 -35
- data/lib/locales/fi.yml +0 -35
- data/lib/locales/fr.yml +0 -35
- data/lib/locales/he.yml +0 -35
- data/lib/locales/hi.yml +0 -35
- data/lib/locales/it.yml +0 -35
- data/lib/locales/ja.yml +0 -35
- data/lib/locales/ko.yml +0 -35
- data/lib/locales/nl.yml +0 -35
- data/lib/locales/no.yml +0 -35
- data/lib/locales/pl.yml +0 -35
- data/lib/locales/pt.yml +0 -35
- data/lib/locales/ru.yml +0 -35
- data/lib/locales/sv.yml +0 -35
- data/lib/locales/th.yml +0 -35
- data/lib/locales/tr.yml +0 -35
- data/lib/locales/vi.yml +0 -35
- 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
|
-
- [
|
8
|
-
- [
|
9
|
-
- [
|
10
|
-
- [
|
11
|
-
- [
|
12
|
-
- [
|
13
|
-
- [
|
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
|
-
##
|
18
|
+
## Modes
|
16
19
|
|
17
|
-
|
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
|
-
|
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
|
-
>
|
25
|
+
> Use `:raise` mode carefully in production environments as it will break existing workflows immediately.
|
67
26
|
|
68
27
|
```ruby
|
69
|
-
class
|
70
|
-
|
28
|
+
class ProcessObsoleteAPI < CMDx::Task
|
29
|
+
settings(deprecated: :raise)
|
71
30
|
|
72
|
-
def
|
73
|
-
#
|
74
|
-
charge_customer(amount)
|
31
|
+
def work
|
32
|
+
# Will never execute...
|
75
33
|
end
|
76
34
|
end
|
77
35
|
|
78
|
-
|
79
|
-
|
80
|
-
# → raises CMDx::DeprecationError: "ProcessLegacyPaymentTask usage prohibited"
|
36
|
+
result = ProcessObsoleteAPI.execute
|
37
|
+
#=> raises CMDx::DeprecationError: "ProcessObsoleteAPI usage prohibited"
|
81
38
|
```
|
82
39
|
|
83
|
-
### Log
|
40
|
+
### Log
|
84
41
|
|
85
|
-
|
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
|
90
|
-
|
45
|
+
class ProcessLegacyFormat < CMDx::Task
|
46
|
+
settings(deprecated: :log)
|
47
|
+
|
48
|
+
# Same
|
49
|
+
settings(deprecated: true)
|
91
50
|
|
92
|
-
def
|
93
|
-
#
|
94
|
-
charge_customer(amount)
|
51
|
+
def work
|
52
|
+
# Executes but logs deprecation warning...
|
95
53
|
end
|
96
54
|
end
|
97
55
|
|
98
|
-
|
99
|
-
result
|
100
|
-
result.successful? # → true
|
56
|
+
result = ProcessLegacyFormat.execute
|
57
|
+
result.successful? #=> true
|
101
58
|
|
102
|
-
#
|
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
|
-
###
|
63
|
+
### Warn
|
107
64
|
|
108
|
-
|
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
|
113
|
-
|
68
|
+
class ProcessOldData < CMDx::Task
|
69
|
+
settings(deprecated: :warn)
|
114
70
|
|
115
|
-
def
|
116
|
-
#
|
117
|
-
charge_customer(amount)
|
71
|
+
def work
|
72
|
+
# Executes but emits Ruby warning...
|
118
73
|
end
|
119
74
|
end
|
120
75
|
|
121
|
-
|
122
|
-
result
|
123
|
-
# stderr: [ProcessObsoletePaymentTask] DEPRECATED: migrate to replacement or discontinue use
|
76
|
+
result = ProcessOldData.execute
|
77
|
+
result.successful? #=> true
|
124
78
|
|
125
|
-
|
79
|
+
# Ruby warning appears in stderr:
|
80
|
+
# [ProcessOldData] DEPRECATED: migrate to replacement or discontinue use
|
126
81
|
```
|
127
82
|
|
128
|
-
##
|
83
|
+
## Declarations
|
129
84
|
|
130
|
-
###
|
85
|
+
### Symbol or String
|
131
86
|
|
132
87
|
```ruby
|
133
|
-
class
|
134
|
-
#
|
135
|
-
|
136
|
-
|
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
|
-
###
|
97
|
+
### Boolean or Nil
|
146
98
|
|
147
99
|
```ruby
|
148
|
-
class
|
149
|
-
#
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
-
|
110
|
+
### Method
|
161
111
|
|
162
|
-
|
163
|
-
|
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
|
-
|
121
|
+
private
|
166
122
|
|
167
|
-
|
168
|
-
|
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
|
-
|
186
|
-
|
187
|
-
### Catching Deprecation Errors
|
129
|
+
### Proc or Lambda
|
188
130
|
|
189
131
|
```ruby
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
201
|
-
|
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
|
-
|
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
|
216
|
-
|
217
|
-
|
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:** [
|
245
|
-
- **Next:** [
|
161
|
+
- **Prev:** [Internationalization (i18n)](internationalization.md)
|
162
|
+
- **Next:** [Workflows](workflows.md)
|