create-flowmo 1.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/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # create-flowmo
2
+
3
+ Scaffold an OutSystems-Lite project with screens, data models, logic flows, and built-in agent skills for AI-assisted prototyping.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-flowmo
9
+ ```
10
+
11
+ Or equivalently:
12
+
13
+ ```bash
14
+ npm create flowmo
15
+ ```
16
+
17
+ You'll be prompted for a project name, target platform (O11 or ODC), and app type. The CLI then scaffolds a project with:
18
+
19
+ - `screens/` — `.visual.html` starter screen with OutSystems UI layout
20
+ - `data/` — Entity definitions in Mermaid ER diagrams
21
+ - `logic/` — Server action flowcharts in Mermaid
22
+ - `scripts/` — Custom scripts
23
+ - `theme/` — OutSystems UI CSS and custom theme
24
+ - `.agents/skills/` — Copilot skills for OutSystems UI, SQL, and logic authoring
25
+
26
+ ## After Scaffolding
27
+
28
+ ```bash
29
+ cd my-project
30
+ npm install
31
+ npm run dev
32
+ ```
33
+
34
+ The project uses [Vite](https://vitejs.dev/) for local development with hot reload.
35
+
36
+ ## The Workflow
37
+
38
+ `create-flowmo` is the starting point of the Flowmo ecosystem. Once scaffolded, the project is designed to work with two VS Code extensions:
39
+
40
+ - **[Visual Inspector](https://marketplace.visualstudio.com/items?itemName=flowmo.flowmo-visual-inspector)** — opens `.visual.html` screen prototypes in a live preview with a layer panel for inspecting the element hierarchy.
41
+ - **[Flowchart Editor](https://marketplace.visualstudio.com/items?itemName=flowmo.flowmo-flowchart-editor)** — opens `.flowchart.md` logic flows in a visual drag-and-drop editor with bidirectional Mermaid sync.
42
+
43
+ Install both at once with the **[Flowmo Extension Pack](https://marketplace.visualstudio.com/items?itemName=flowmo.flowmo-extension-pack)**.
44
+
45
+ The scaffolded project also includes Copilot skills that understand OutSystems UI patterns, SQL conventions, and server action structure — so AI-generated code stays compatible with the platform.
46
+
47
+ ## Links and Support
48
+
49
+ - Web: [flowmo.lol](https://flowmo.lol)
50
+ - Issues: [GitHub Issues](https://github.com/izambasiron/flowmo-lol-flowchart-editor/issues)
51
+ - Email: [support@flowmo.lol](mailto:support@flowmo.lol)
52
+ - Support model: best-effort, no response-time guarantee
53
+
54
+ ## License
55
+
56
+ MIT
package/index.js ADDED
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ import { intro, outro, text, select, spinner, note, isCancel } from '@clack/prompts';
3
+ import picocolors from 'picocolors';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import { fileURLToPath } from 'url';
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ async function init() {
11
+ console.log(picocolors.cyan(`
12
+ ___ ___ ___
13
+ /\\__\\ /\\ \\ /\\__\\
14
+ /:/ / /::\\ \\ /:/ /
15
+ /:/ / /:/\\:\\ \\ /:/ /
16
+ /:/ / /:/ \\:\\ \\ /:/ /
17
+ /:/__/ /:/__/ \\:\\__\\ /:/__/
18
+ \\:\\ \\ \\:\\ \\ /:/ / \\:\\ \\
19
+ \\:\\ \\ \\:\\ /:/ / \\:\\ \\
20
+ \\:\\ \\ \\:\\/:/ / \\:\\ \\
21
+ \\:\\__\\ \\::/ / \\:\\__\\
22
+ \\/__/ \\/__/ \\/__/
23
+ `));
24
+
25
+ intro(picocolors.bgCyan(picocolors.black(' Flowmo: Create ')));
26
+
27
+ // 1. Project Name Prompt
28
+ const projectName = await text({
29
+ message: 'What is your project name?',
30
+ placeholder: 'my-vibe-module',
31
+ validate: (value) => {
32
+ if (!value) return 'Please enter a name.';
33
+ if (!/^[a-zA-Z0-9_-]+$/.test(value)) return 'Name can only contain letters, numbers, hyphens, and underscores.';
34
+ },
35
+ });
36
+
37
+ if (isCancel(projectName)) {
38
+ outro('Cancelled.');
39
+ process.exit(0);
40
+ }
41
+
42
+ // 2. Target Environment Selection
43
+ const platform = await select({
44
+ message: 'Which OutSystems platform are you targeting?',
45
+ options: [
46
+ { value: 'ODC', label: 'OutSystems Developer Cloud (PostgreSQL)' },
47
+ { value: 'O11', label: 'OutSystems 11 (T-SQL/MSSQL)' },
48
+ ],
49
+ });
50
+
51
+ if (isCancel(platform)) {
52
+ outro('Cancelled.');
53
+ process.exit(0);
54
+ }
55
+
56
+ // 3. App Type Selection
57
+ const appType = await select({
58
+ message: 'What type of app are you building?',
59
+ options: [
60
+ { value: 'reactive', label: 'Reactive Web App' },
61
+ { value: 'mobile', label: 'Mobile App' },
62
+ ],
63
+ });
64
+
65
+ if (isCancel(appType)) {
66
+ outro('Cancelled.');
67
+ process.exit(0);
68
+ }
69
+
70
+ const s = spinner();
71
+ s.start('Scaffolding your OutSystems-Lite environment...');
72
+
73
+ const projectPath = path.join(process.cwd(), projectName);
74
+
75
+ try {
76
+ // Scaffold basic structure
77
+ await fs.ensureDir(path.join(projectPath, '.agents'));
78
+ await fs.ensureDir(path.join(projectPath, 'data/sql'));
79
+ await fs.ensureDir(path.join(projectPath, 'logic'));
80
+ await fs.ensureDir(path.join(projectPath, 'screens'));
81
+ await fs.ensureDir(path.join(projectPath, 'scripts'));
82
+ await fs.ensureDir(path.join(projectPath, 'theme'));
83
+
84
+ // Copy bundled Agent Skills into the new project
85
+ const bundledSkills = path.join(__dirname, 'skills');
86
+ await fs.copy(bundledSkills, path.join(projectPath, '.agents/skills'));
87
+
88
+ // Copy starter template files (CSS, starter screen, data, logic)
89
+ const templateDir = path.join(__dirname, 'template');
90
+ await fs.copy(path.join(templateDir, 'theme'), path.join(projectPath, 'theme'));
91
+ await fs.copy(path.join(templateDir, 'screens'), path.join(projectPath, 'screens'));
92
+ await fs.copy(path.join(templateDir, 'data'), path.join(projectPath, 'data'));
93
+ await fs.copy(path.join(templateDir, 'logic'), path.join(projectPath, 'logic'));
94
+
95
+ // Generate package.json for the scaffolded project
96
+ const pkg = {
97
+ name: projectName,
98
+ private: true,
99
+ type: 'module',
100
+ scripts: {
101
+ dev: 'vite',
102
+ build: 'vite build',
103
+ preview: 'vite preview',
104
+ },
105
+ devDependencies: {
106
+ vite: '^6.0.0',
107
+ },
108
+ };
109
+ await fs.writeJson(path.join(projectPath, 'package.json'), pkg, { spaces: 2 });
110
+
111
+ // Generate vite config for multi-page HTML
112
+ const viteConfig = `import { defineConfig } from 'vite';
113
+ import { resolve } from 'path';
114
+ import { readdirSync } from 'fs';
115
+ import { fileURLToPath } from 'url';
116
+
117
+ const __dirname = resolve(fileURLToPath(import.meta.url), '..');
118
+
119
+ // Auto-discover all .html files in screens/
120
+ const screens = readdirSync(resolve(__dirname, 'screens'))
121
+ .filter(f => f.endsWith('.html'))
122
+ .reduce((entries, file) => {
123
+ entries[file.replace('.html', '')] = resolve(__dirname, 'screens', file);
124
+ return entries;
125
+ }, {});
126
+
127
+ export default defineConfig({
128
+ root: '.',
129
+ build: {
130
+ rollupOptions: {
131
+ input: {
132
+ main: resolve(__dirname, 'index.html'),
133
+ ...screens,
134
+ },
135
+ },
136
+ },
137
+ });
138
+ `;
139
+ await fs.writeFile(path.join(projectPath, 'vite.config.js'), viteConfig);
140
+
141
+ // Create a root index.html entry point
142
+ const safeName = projectName.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
143
+ const indexHtml = `<!DOCTYPE html>
144
+ <html lang="en">
145
+ <head>
146
+ <meta charset="UTF-8" />
147
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
148
+ <title>${safeName}</title>
149
+ </head>
150
+ <body>
151
+ <h1>${safeName}</h1>
152
+ <p>Open any screen from the <code>screens/</code> folder:</p>
153
+ <ul id="screen-list"></ul>
154
+ </body>
155
+ </html>
156
+ `;
157
+ await fs.writeFile(path.join(projectPath, 'index.html'), indexHtml);
158
+
159
+ s.stop('Project scaffolded successfully!');
160
+
161
+ note(`For the full visual editing experience, install the Flowmo Extension Pack in VS Code.\n\nOptional — to preview locally:\ncd ${projectName}\nnpm install\nnpm run dev`, 'Next Steps');
162
+ outro(picocolors.cyan('Time to vibe code! ⚡'));
163
+
164
+ } catch (err) {
165
+ s.stop('Scaffolding failed.');
166
+ console.error(picocolors.red(err));
167
+ }
168
+ }
169
+
170
+ init();
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "create-flowmo",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold an OutSystems-Lite project with screens, data, logic, and built-in agent skills",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "Flowmo",
9
+ "email": "support@flowmo.lol"
10
+ },
11
+ "homepage": "https://flowmo.lol",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/izambasiron/flowmo-lol-flowchart-editor"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/izambasiron/flowmo-lol-flowchart-editor/issues"
18
+ },
19
+ "keywords": [
20
+ "create",
21
+ "scaffold",
22
+ "outsystems",
23
+ "flowmo",
24
+ "vibe-coding",
25
+ "low-code"
26
+ ],
27
+ "bin": {
28
+ "create-flowmo": "index.js"
29
+ },
30
+ "files": [
31
+ "index.js",
32
+ "skills",
33
+ "template"
34
+ ],
35
+ "scripts": {
36
+ "start": "node index.js"
37
+ },
38
+ "dependencies": {
39
+ "@clack/prompts": "^0.7.0",
40
+ "fs-extra": "^11.1.1",
41
+ "picocolors": "^1.0.0"
42
+ }
43
+ }
@@ -0,0 +1,316 @@
1
+ ---
2
+ name: outsystems-logic
3
+ description: >-
4
+ OutSystems business logic patterns for both O11 and ODC. Covers action types
5
+ (Client, Server, Service, Data), screen lifecycle events, data types,
6
+ expressions, exception handling, and integration with built-in functions and
7
+ libraries. Use when the developer is designing screen logic, writing
8
+ expressions, handling errors, or orchestrating data flows in OutSystems.
9
+ compatibility: Designed for OutSystems Service Studio (O11 and ODC).
10
+ metadata:
11
+ version: "1.0"
12
+ source: "OutSystems documentation and platform conventions"
13
+ ---
14
+
15
+ # OutSystems Logic Skill
16
+
17
+ ## Action Types
18
+
19
+ OutSystems has four types of actions. Choose the right one based on where the logic runs and what it accesses.
20
+
21
+ ### Client Action
22
+ - **Runs on**: Browser / mobile device
23
+ - **Use for**: UI interactions, local validation, navigation, screen state changes
24
+ - **Can access**: Local storage, client variables, JavaScript, UI widgets
25
+ - **Cannot access**: Server database directly, server-side APIs
26
+ - **Performance**: Instant (no server round-trip)
27
+
28
+ ```
29
+ ClientAction: ValidateForm
30
+ If Form.Username = ""
31
+ FeedbackMessage("Username is required", "error")
32
+ Return
33
+ End If
34
+ If Length(Form.Password) < 8
35
+ FeedbackMessage("Password must be at least 8 characters", "error")
36
+ Return
37
+ End If
38
+ // Proceed
39
+ ```
40
+
41
+ ### Server Action
42
+ - **Runs on**: Server
43
+ - **Use for**: Database operations, business rules, server-side validation, calling external APIs
44
+ - **Can access**: Database entities, site properties, server-side APIs, timers, BPT
45
+ - **Cannot access**: UI widgets, client variables, local storage
46
+ - **Performance**: Requires server round-trip
47
+
48
+ ```
49
+ ServerAction: CreateOrder
50
+ Input: OrderRecord (Order type)
51
+ Output: OrderId (Long Integer)
52
+
53
+ // Validate
54
+ If OrderRecord.Total <= 0
55
+ Raise Exception "InvalidOrder" with "Total must be positive"
56
+ End If
57
+
58
+ // Create
59
+ CreateOrder(OrderRecord)
60
+ OrderId = OrderRecord.Id
61
+
62
+ // Audit
63
+ CreateAuditLog(Action: "CreateOrder", EntityId: OrderId)
64
+ ```
65
+
66
+ ### Service Action
67
+ - **Runs on**: Server (exposed as internal API)
68
+ - **Use for**: Reusable logic shared across modules, module-to-module communication
69
+ - **Can access**: Same as Server Action
70
+ - **Key difference**: Runs in an independent transaction (separate from the caller)
71
+ - **O11**: Available in Service modules
72
+ - **ODC**: Available in all server modules (Services concept is default)
73
+
74
+ ### Data Action
75
+ - **Runs on**: Server, triggered automatically by screens
76
+ - **Use for**: Fetching data for screen consumption
77
+ - **Can access**: Database, Server Actions, aggregates
78
+ - **Key feature**: Runs asynchronously — screen renders immediately, data loads in background
79
+ - **Output**: Automatically available to screen widgets via data binding
80
+
81
+ ```
82
+ DataAction: GetDashboardData
83
+ Output: KPIs (KPIDashboard type)
84
+ Output: RecentOrders (Order List)
85
+
86
+ // Aggregate: GetKPIs
87
+ KPIs.TotalOrders = Count(Orders)
88
+ KPIs.Revenue = Sum(Orders.Total)
89
+
90
+ // Aggregate: GetRecentOrders (Max Records: 10, Sort: CreatedDate DESC)
91
+ RecentOrders = GetRecentOrders.List
92
+ ```
93
+
94
+ ## Screen Lifecycle Events (Reactive Web / Mobile)
95
+
96
+ Events fire in this order during screen load:
97
+
98
+ ```
99
+ 1. OnInitialize — First. Set default values, initialize variables. Runs BEFORE data fetch.
100
+ 2. [Data Actions] — Automatic. All Data Actions run in parallel after OnInitialize.
101
+ 3. OnReady — After first render + data load. DOM is available. Start JS interop here.
102
+ 4. OnRender — After every re-render (data change, variable change). Use sparingly.
103
+ 5. OnDestroy — Screen is being removed. Clean up timers, subscriptions.
104
+ 6. OnParametersChanged — Input parameters changed (e.g., navigated to same screen with different ID).
105
+ ```
106
+
107
+ ### Rules
108
+ - `OnInitialize`: No DOM access. No async calls. Fast synchronous setup only.
109
+ - `OnReady`: Safe to call JavaScript, initialize third-party widgets.
110
+ - `OnRender`: Fires frequently. Never put heavy logic or Server Action calls here.
111
+ - `OnDestroy`: Clean up any `setInterval`, event listeners, or subscriptions.
112
+ - `OnParametersChanged`: Re-fetch data if the screen's input parameter changed. Check `If OldParam <> NewParam`.
113
+
114
+ ## Data Types
115
+
116
+ ### Basic Types
117
+ | Type | Description | Default Value |
118
+ |------|-------------|---------------|
119
+ | `Text` | String, unlimited length | `""` |
120
+ | `Integer` | 32-bit signed integer | `0` |
121
+ | `Long Integer` | 64-bit signed integer | `0` |
122
+ | `Decimal` | Fixed-point decimal (28 digits) | `0` |
123
+ | `Boolean` | True/False | `False` |
124
+ | `Date` | Date only (no time) | `#1900-01-01#` |
125
+ | `Time` | Time only (no date) | `#00:00:00#` |
126
+ | `DateTime` | Date + time | `#1900-01-01 00:00:00#` |
127
+ | `Binary Data` | Byte array / blob | `""` (empty) |
128
+ | `Phone Number` | Text formatted as phone | `""` |
129
+ | `Email` | Text formatted as email | `""` |
130
+ | `Currency` | Alias for Decimal | `0` |
131
+
132
+ ### Compound Types
133
+ | Type | Description |
134
+ |------|-------------|
135
+ | `Record` | Single row of an entity or structure |
136
+ | `List` | Collection of records |
137
+ | `Structure` | Custom compound type (like a DTO) |
138
+ | `Entity` | Database-backed record type |
139
+ | `Static Entity` | Enum-like fixed set of records |
140
+
141
+ ### Identifier Types
142
+ - `EntityName Identifier` — Typed foreign key (e.g., `Customer Identifier` = the Id type of Customer entity)
143
+ - `NullIdentifier()` — Returns the null/empty value for any identifier type
144
+
145
+ ## Expressions
146
+
147
+ OutSystems expressions are used in widget values, conditions, assignments, and calculated attributes.
148
+
149
+ ### Operators
150
+ | Operator | Description |
151
+ |----------|-------------|
152
+ | `+`, `-`, `*`, `/` | Arithmetic |
153
+ | `=`, `<>` | Equality / inequality |
154
+ | `>`, `<`, `>=`, `<=` | Comparison |
155
+ | `and`, `or`, `not` | Logical |
156
+ | `+` | String concatenation (Text type) |
157
+ | `like` | Pattern matching (SQL-style `%` wildcards) |
158
+
159
+ ### Common Expression Patterns
160
+ ```
161
+ // Conditional (inline IF)
162
+ If(Order.Status = "Shipped", "✓ Delivered", "Pending")
163
+
164
+ // Null check
165
+ If(Customer.Name = "", "Unknown", Customer.Name)
166
+
167
+ // Date formatting
168
+ FormatDateTime(CurrDateTime(), "yyyy-MM-dd HH:mm")
169
+
170
+ // Number formatting
171
+ FormatDecimal(Price, 2, ",", ".")
172
+
173
+ // String operations
174
+ ToUpper(Name)
175
+ Trim(Input)
176
+ Length(Description)
177
+ Substr(Text, 0, 100) + If(Length(Text) > 100, "...", "")
178
+
179
+ // Type conversion
180
+ IntegerToText(Count)
181
+ TextToInteger(Input)
182
+ IntegerToBoolean(Value)
183
+
184
+ // List operations
185
+ ListLength(Orders)
186
+ ListSort(Orders, Order.CreatedDate, ascending: False)
187
+ ListFilter(Orders, Order.Total > 100)
188
+ ListAny(Orders, Order.Status = "Pending")
189
+ ListAll(Orders, Order.IsValid)
190
+ ListIndexOf(Orders, Order.Id = TargetId)
191
+ ```
192
+
193
+ ## Exception Handling
194
+
195
+ ### Exception Hierarchy
196
+ ```
197
+ AllExceptions
198
+ ├── User Exception — Business rule violations (custom)
199
+ ├── Database Exception — DB constraint violations, connection errors
200
+ ├── Security Exception — Authorization failures, CSRF
201
+ ├── Communication Exception — External API timeouts, HTTP errors
202
+ └── Abort Activity Change — BPT-specific (O11)
203
+ ```
204
+
205
+ ### Pattern: Try/Catch in Server Action
206
+ ```
207
+ ServerAction: ProcessPayment
208
+ Start
209
+ ├── Call: ValidatePayment
210
+ ├── Call: ChargeCard (external API)
211
+ ├── Call: UpdateOrderStatus
212
+ └── End
213
+
214
+ Exception Handler: CommunicationException
215
+ ├── LogError("Payment API failed: " + ExceptionMessage)
216
+ ├── Call: RetryOrQueue
217
+ └── Raise User Exception "Payment failed. Please try again."
218
+
219
+ Exception Handler: DatabaseException
220
+ ├── LogError("DB error in ProcessPayment: " + ExceptionMessage)
221
+ └── Raise User Exception "Unable to process. Contact support."
222
+
223
+ Exception Handler: AllExceptions
224
+ ├── LogError("Unexpected error: " + ExceptionMessage)
225
+ └── Raise User Exception "Something went wrong."
226
+ ```
227
+
228
+ ### Rules
229
+ - Catch **specific** exceptions before general ones.
230
+ - **Always log** the original exception message before raising a user-friendly one.
231
+ - In Client Actions, use `FeedbackMessage` pattern instead of raising exceptions.
232
+ - **Never swallow exceptions silently** (empty catch block).
233
+ - Database exceptions in a Server Action abort the transaction — handle accordingly.
234
+
235
+ ## Flowchart Format (.flowchart.md)
236
+
237
+ When documenting action logic as a flowchart, create a `.flowchart.md` file using Mermaid `flowchart TD` syntax. Use only four node shapes:
238
+
239
+ | Shape | Syntax | Use for |
240
+ |-------|--------|---------|
241
+ | Start | `([Start])` | Entry point of the action |
242
+ | End | `([End])` or `([End Success])` | Terminal node (label the outcome) |
243
+ | Process | `[Step name]` | Any operation — assignments, calls, queries |
244
+ | Decision | `{Condition?}` | Branching — if/else, loops, switches |
245
+
246
+ Loops and switches are represented as `{Decision}` nodes with labeled edges looping back.
247
+
248
+ ### Example: Server Action flowchart
249
+
250
+ ```mermaid
251
+ flowchart TD
252
+ A([Start]) --> B[Validate OrderRecord]
253
+ B --> C{Total > 0?}
254
+ C -->|No| D[Raise InvalidOrder Exception]
255
+ D --> E([End Failure])
256
+ C -->|Yes| F[CreateOrder]
257
+ F --> G[CreateAuditLog]
258
+ G --> H([End Success])
259
+ ```
260
+
261
+ ### Example: Loop pattern
262
+
263
+ ```mermaid
264
+ flowchart TD
265
+ A([Start]) --> B[Get OrderList]
266
+ B --> C{More items?}
267
+ C -->|No| D([End])
268
+ C -->|Yes| E[Process current item]
269
+ E --> F[Move to next item]
270
+ F --> C
271
+ ```
272
+
273
+ ### Example: Exception handling
274
+
275
+ ```mermaid
276
+ flowchart TD
277
+ A([Start]) --> B[ValidatePayment]
278
+ B --> C[ChargeCard]
279
+ C --> D{API Success?}
280
+ D -->|Yes| E[UpdateOrderStatus]
281
+ E --> F([End Success])
282
+ D -->|No| G[LogError]
283
+ G --> H[RetryOrQueue]
284
+ H --> I([End Failure])
285
+ ```
286
+
287
+ ## Reference Loading
288
+
289
+ For detailed reference material on built-in functions, system libraries, and system actions, load these files on demand:
290
+
291
+ - **Built-in functions** (10 categories, 200+ functions): Load [references/builtin-functions.md](references/builtin-functions.md) if the user mentions type conversion, date math, text manipulation, formatting, or math functions
292
+ - **Libraries** (12 modules with utility functions): Load [references/libraries.md](references/libraries.md) if the user mentions BinaryData, DateTime, HTTP, JSON, Sanitization, Security, Text, or URL utilities
293
+ - **System actions** (Authentication, User management): Load [references/system-actions.md](references/system-actions.md) if the user mentions login, logout, user creation, role management, session, or authentication
294
+
295
+ ## Validation
296
+
297
+ After designing action logic, verify:
298
+ 1. The correct action type is chosen (Client for UI, Server for DB/API, Data for screen data fetching)
299
+ 2. Server Actions are not called from OnRender — use Data Actions or event handlers instead
300
+ 3. Exception handlers catch specific exceptions before `AllExceptions`
301
+ 4. No empty catch blocks — every handler logs the original `ExceptionMessage`
302
+ 5. Data Actions do not depend on each other’s output (they run in parallel)
303
+ 6. Lists are duplicated with `ListDuplicate` before modification if the original must be preserved
304
+
305
+ If any check fails, fix the design before presenting the output.
306
+
307
+ ## Gotchas
308
+
309
+ 1. **Client vs Server**: Never call Server Actions directly from OnRender. Use Data Actions for data fetching and explicit button/event handlers for mutations.
310
+ 2. **Transaction scope**: Each Server Action runs in a single database transaction. If it fails partway, everything rolls back. Service Actions run in their own transaction.
311
+ 3. **Async Data Actions**: Data Actions run in parallel. Do NOT depend on one Data Action's output in another. If you need sequential fetches, chain them inside a single Data Action.
312
+ 4. **List by reference**: Lists are passed by reference in OutSystems. Modifying a list inside a called action modifies the original. Use `ListDuplicate` if you need a copy.
313
+ 5. **Static Entity access**: Use `Entities.StaticEntityName.RecordName` to reference a specific value. Never hard-code the Id integer.
314
+ 6. **Site Properties vs Client Variables**: Site Properties (O11) / Settings (ODC) are server-side configuration. Client Variables are per-session, client-side state. Don't confuse them.
315
+ 7. **Timer/BPT**: In O11, Timers and BPT processes run in their own transaction context. They have no screen context — no client variables, no session.
316
+ 8. **ODC differences**: ODC has no Timers or BPT. Use ODC's native scheduled jobs and event-driven architecture instead.