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 +56 -0
- package/index.js +170 -0
- package/package.json +43 -0
- package/skills/outsystems-logic/SKILL.md +316 -0
- package/skills/outsystems-logic/references/builtin-functions.md +144 -0
- package/skills/outsystems-logic/references/libraries.md +169 -0
- package/skills/outsystems-logic/references/system-actions.md +221 -0
- package/skills/outsystems-sql/SKILL.md +272 -0
- package/skills/outsystems-ui/SKILL.md +237 -0
- package/skills/outsystems-ui/assets/layout-template.html +43 -0
- package/skills/outsystems-ui/references/colors.md +236 -0
- package/skills/outsystems-ui/references/patterns-adaptive.md +178 -0
- package/skills/outsystems-ui/references/patterns-content.md +242 -0
- package/skills/outsystems-ui/references/patterns-interaction.md +288 -0
- package/skills/outsystems-ui/references/patterns-navigation.md +180 -0
- package/skills/outsystems-ui/references/patterns-numbers.md +94 -0
- package/skills/outsystems-ui/references/patterns-utilities.md +124 -0
- package/skills/outsystems-ui/references/screen-templates.md +390 -0
- package/skills/outsystems-ui/references/widgets.md +270 -0
- package/template/data/User.md +11 -0
- package/template/logic/GetUser.flowchart.md +7 -0
- package/template/screens/home.visual.html +36 -0
- package/template/theme/outsystems-ui.css +20139 -0
- package/template/theme/theme.css +19 -0
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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
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.
|