Package structure
Every v2 app follows the same directory convention. This page walks through each directory and file, shows minimal and standard examples, and explains the role of package.json.
Directory reference
| Directory / file | Purpose | Required? |
|---|---|---|
package.json | Identity (comindApp), dependencies, #typings import alias | Yes |
typings.ts | App-specific TypeScript types via makeEntity(fields) | Yes |
fields/index.ts | Field definitions (AppField[]) | Yes |
fields/calc-fields/ | Calculated/virtual fields (bundled separately for browser) | No |
actions/index.ts | Action definitions (AppAction[]) | Yes |
actions/logic/index.ts | Server-side action code (ActionLogic) | No |
actions/logic/preconditions.ts | Precondition checks (PreconditionLogic) | No |
views/layouts/default.tsx | Form layout (JSX) | Yes |
views/list-views.ts | Grid view definitions (AppListView[]) | No |
views/logic/index.ts | UI-side hooks (ViewLogic) | No |
settings/index.ts | Backlinks, mutations, config (AppSettings) | No |
settings/secrets.ts | Sensitive config (COMIND_SECRET_* vars) - import only from actions/logic/ | No |
settings/app-mutator.ts | Schema post-processing at runtime | No |
The four required entry points are fields, actions, form layouts, and typings.ts. Everything else is optional and added as needed.
Minimal app example
The simplest possible app - app-table-row - contains only the required files:
app-table-row/
├── package.json
├── typings.ts
├── fields/index.ts
├── actions/index.ts
└── views/layouts/
├── index.ts
└── default.tsx
Start here when scaffolding a new app. Add optional directories only when you need server-side logic, calculated fields, or custom settings.
Standard app example
A production app like app-task uses most of the available directories:
app-task/
├── package.json
├── typings.ts
├── fields/
│ ├── index.ts # Re-exports all field modules as AppField[]
│ ├── fields-core.ts # Core fields (title, description, state)
│ ├── fields-dates.ts # Date fields
│ ├── fields-people.ts # People/assignee fields
│ ├── ...
│ └── calc-fields/index.ts # Calculated fields (bundled for browser)
├── actions/
│ ├── index.ts # AppAction[] definitions
│ └── logic/
│ ├── index.ts # ActionLogic (server-side code)
│ └── preconditions.ts # PreconditionLogic
├── views/
│ ├── layouts/
│ │ ├── index.ts # AppLayout map
│ │ └── default.tsx # Main form layout (JSX)
│ └── logic/index.ts # ViewLogic (UI-side hooks)
└── settings/
├── index.ts # AppSettings (backlinks, mutations)
├── app-mutator.ts # Schema post-processing
└── secrets.ts # Sensitive config
Notice how fields/index.ts re-exports from smaller modules (fields-core.ts, fields-dates.ts, etc.). This keeps individual files short while the engine still receives a single AppField[] array. The same pattern applies to actions - actions/index.ts exports one AppAction[], but the definitions can be split across multiple files.
The calc-fields/ directory holds calculated fields that run in the browser. The build system bundles them separately so they can execute on the client without pulling in server-only code.
For details on the settings model - including backlinks, mutations, and secrets - see the dedicated page.
The package.json file
Every app's package.json includes a comindApp block that the engine reads at startup:
{
"name": "@comind/app-task",
"version": "0.5.161101",
"comindApp": {
"icon": "check",
"publishingAlias": "TASK",
"titleSingular": "Task",
"titlePlural": "Tasks"
},
"dependencies": {
"@comind/api": "*",
"@comind/plugin-color": "*",
"@comind/plugin-notification": "*"
},
"imports": {
"#typings": "./typings.ts"
}
}
Key fields inside comindApp:
publishingAlias- the short code used in URLs and API calls (e.g.TASK,TICKET)titleSingular/titlePlural- display names shown in the UIicon- the icon identifier rendered next to the app nameinheritFrom- points to a parent app when building a child app (see below)
The imports field defines the #typings alias so that app code can import types with import { entity } from '#typings' instead of relative paths. This alias is required.
Dependencies follow the standard npm format. List @comind/api for access to the core API, and add any plugins the app needs.
Child apps and inheritFrom
A child app extends a parent by adding inheritFrom to comindApp and listing the parent as a dependency:
{
"name": "@customer/app-custom-task",
"version": "0.5.241101",
"comindApp": {
"icon": "check",
"publishingAlias": "TASK",
"titleSingular": "Task",
"titlePlural": "Tasks",
"inheritFrom": "@comind/app-task"
},
"dependencies": {
"@comind/api": "*",
"@comind/app-task": "*"
},
"imports": {
"#typings": "./typings.ts"
}
}
The child inherits all fields, actions, layouts, and settings from the parent. It can then override or extend any of them. This is the primary mechanism for customer-specific customization. See Plugins and inheritFrom for the full inheritance model.