Skip to content

Commit 2f89f47

Browse files
TASK-1884601: adding AGENTS.md
1 parent 9582915 commit 2f89f47

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed

AGENTS.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# AGENTS.md — Constellation Mobile SDK
2+
3+
## Project Overview
4+
5+
Kotlin Multiplatform (KMP) SDK for embedding Pega Constellation forms into mobile apps.
6+
Targets Android, iOS, JVM/Desktop. Group: `com.pega.constellation.sdk.kmp`.
7+
8+
### Module Layout
9+
10+
| Module | Purpose |
11+
|---|---|
12+
| `core` | Domain models, interfaces, component system (publishable) |
13+
| `engine-webview` | WebView-based engine with platform implementations (publishable) |
14+
| `engine-mock` | Mock engine for testing |
15+
| `ui-components-cmp` | Compose Multiplatform UI widgets (publishable) |
16+
| `ui-renderer-cmp` | Renderers bridging core components to UI (publishable) |
17+
| `test` | Integration test infrastructure and mocks |
18+
| `samples/` | Sample Android, desktop, and iOS apps |
19+
| `scripts/` | JavaScript bridge layer (pure ES modules, no bundler) |
20+
21+
## Build Commands
22+
23+
```bash
24+
# Full build
25+
./gradlew clean build
26+
27+
# Build + publish to local Maven
28+
./gradlew clean publishToMavenLocal
29+
30+
# Build a single module
31+
./gradlew :core:build
32+
./gradlew :engine-webview:build
33+
```
34+
35+
## Test Commands
36+
37+
Tests require an Android emulator/device or iOS simulator. Download CDN fixtures first:
38+
39+
```bash
40+
cd test/src/commonMain/composeResources/files/responses && ./download_js_files.sh
41+
```
42+
43+
### Android
44+
45+
```bash
46+
# All SDK instrumented tests
47+
./gradlew :test:connectedAndroidTest
48+
49+
# All sample app UI tests
50+
./gradlew :samples:android-cmp-app:connectedAndroidTest
51+
52+
# Single test class
53+
./gradlew :test:connectedAndroidTest \
54+
-Pandroid.testInstrumentationRunnerArguments.class=com.pega.constellation.sdk.kmp.test.ConstellationSdkTest
55+
56+
# Single test method
57+
./gradlew :test:connectedAndroidTest \
58+
-Pandroid.testInstrumentationRunnerArguments.class=com.pega.constellation.sdk.kmp.test.ConstellationSdkTest#test_initialization
59+
```
60+
61+
### iOS
62+
63+
```bash
64+
xcodebuild -project samples/swiftui-components-app/UITest/UITest.xcodeproj \
65+
-scheme UITest -destination "platform=iOS Simulator,name=<sim>" test
66+
67+
# Single test
68+
xcodebuild ... -only-testing:"UITest/TestCaseProcessing/testCaseProcessing" test
69+
```
70+
71+
---
72+
73+
## Kotlin Code Style
74+
75+
### Formatting & Imports
76+
77+
- 4-space indentation (no tabs); `kotlin.code.style=official` in `gradle.properties`
78+
- One primary class/interface per file; file name matches the class name
79+
- No explicit `public` modifier — rely on Kotlin's default public visibility
80+
- Imports: alphabetically ordered, no blank-line separators, no wildcard imports
81+
- Exception: iOS platform APIs like `platform.WebKit.*`
82+
- Order: android/androidx → project (`com.pega.*`) → kotlin → kotlinx → third-party → java
83+
84+
### Naming & Visibility
85+
86+
- Classes/Interfaces: PascalCase (`FlowContainerComponent`)
87+
- Functions: camelCase; Composables: PascalCase; factory funcs: `for`/`create` prefix
88+
- Variables: camelCase; constants: SCREAMING_SNAKE_CASE in `companion object`
89+
- Enums: SCREAMING_SNAKE_CASE; value classes: PascalCase (`ComponentId`)
90+
- Test methods: `test_` prefix + snake_case (`test_initialization()`)
91+
- `internal` for impl classes; `private set` on mutable Compose state; no explicit `public`
92+
93+
### Error Handling
94+
95+
- **Sealed classes** for domain state: `State.Ready`, `State.Error`, `EngineEvent.Error`
96+
- **`EngineError` interface** with concrete types: `JsError`, `InternalError`
97+
- **`runCatching`/`onFailure`** for defensive handling around component updates
98+
- **Null safety with early returns**: `val x = y as? Type ?: return`
99+
- **`Log` object** (`Log.i`, `Log.w`, `Log.e`) for non-critical failures — never swallow silently
100+
101+
### Patterns
102+
103+
- `StateFlow`/`MutableStateFlow` for observable state; `SharedFlow` for events
104+
- `CoroutineScope(Dispatchers.Main + SupervisorJob())` for lifecycle-scoped coroutines
105+
- `mutableStateOf` (Compose) for component property state, not StateFlow
106+
- `fun interface` for SAM types: `EngineEventHandler`, `ComponentProducer`
107+
- `companion object` with `private const val TAG` and factory methods
108+
- `@JvmInline value class` for type-safe identifiers; `lateinit var` for deferred init
109+
- KDoc on public API interfaces/classes with `@param`/`@property` tags; minimal docs on internals
110+
111+
---
112+
113+
## JavaScript Code Style (scripts/)
114+
115+
The `scripts/` directory contains a pure ES module JavaScript codebase (no bundler, no npm).
116+
This is a bridge layer loaded into a WebView at runtime.
117+
118+
### Formatting
119+
120+
- 4-space indentation (`.prettierrc`: `tabWidth: 4, useTabs: false`)
121+
- Double quotes for strings; semicolons at end of statements
122+
- Max line length: 120 (`.editorconfig`)
123+
- Relative imports with explicit `.js` extension: `import { BaseComponent } from "../../base.component.js";`
124+
- No bare module specifiers, no npm packages; one import per line
125+
126+
### File & Class Naming
127+
128+
- Files: `kebab-case.component.js` (e.g., `text-input.component.js`)
129+
- Classes: PascalCase matching file name (e.g., `TextInputComponent`)
130+
- Component type string: PascalCase without "Component" suffix (`this.type = "TextInput"`)
131+
132+
### Component Lifecycle
133+
134+
All components extend `BaseComponent`. Required methods in order:
135+
136+
1. **`constructor(componentsManager, pConn)`** — call `super(componentsManager, pConn)`, set `this.type`
137+
2. **`init()`** — register via `this.jsComponentPConnect.registerAndSubscribeComponent(this, this.checkAndUpdate)`, call `this.componentsManager.onComponentAdded(this)`, then `this.checkAndUpdate()`
138+
3. **`destroy()`** — call `super.destroy()`, unsubscribe, call `this.componentsManager.onComponentRemoved(this)`
139+
4. **`update(pConn, ...)`** — guard with equality check, reassign, call `this.checkAndUpdate()`
140+
5. **`checkAndUpdate()`** — call `this.jsComponentPConnect.shouldComponentUpdate(this)`, if true call `this.#updateSelf()`
141+
6. **`#updateSelf()`** — resolve props from `this.pConn`, build state, call `this.#sendPropsUpdate()`
142+
7. **`#sendPropsUpdate()`** — set `this.props = { ... }`, call `this.componentsManager.onComponentPropsUpdate(this)`
143+
144+
### Key Conventions
145+
146+
- **Private methods** use `#` prefix: `#updateSelf()`, `#sendPropsUpdate()`
147+
- **No TypeScript** in new code — the `.ts` files are legacy Angular originals kept as reference
148+
- Component registration: `scripts/dxcomponents/mappings/sdk-pega-component-map.js`
149+
- TAG-based logging: `const TAG = "ClassName";` with `Utils.log(TAG, ...)` or `console.warn`
150+
- Guard clauses with early return for null/undefined checks
151+
- `try/catch` only at system boundaries (bridge calls, JSON parsing)
152+
- String interpolation with template literals: `` `@P .${context}` ``
153+
154+
### TS → JS Migration Rules
155+
156+
When migrating `.ts` (Angular) components to `.js`:
157+
1. Remove all Angular imports, decorators (`@Component`, `@Input`), and TypeScript types
158+
2. Remove interfaces and type annotations
159+
3. Remove all Angular lifecycle interfaces
160+
4. For simple fields components extend `FieldBaseComponent`" if possible and reasonable.
161+
5. For Container components extend `ContainerBaseComponent` if possible and reasonable.
162+
6. For rest of components extend `BaseComponent`
163+
7. `ngOnInit``init()` with `componentsManager.onComponentAdded(this)`
164+
8. `ngOnDestroy``destroy()` with `super.destroy()` and `componentsManager.onComponentRemoved(this)`
165+
9. Merge `onStateChange` into `checkAndUpdate()`
166+
10. `updateSelf``#updateSelf()` (private); add `#sendPropsUpdate()` at end
167+
11. Add `update(pConn, ...)` method for external re-rendering
168+
12. Replace `this.pConn$``this.pConn`; remove all `as any` casts
169+
13. Normalize quotes to double quotes
170+
14. If there is some external dependency try to look in additional provided ts files and copy its content to migrated js. If nothing is found try to find existing equivalent in js files or create mocked implementation.

0 commit comments

Comments
 (0)