Skip to main content

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 / filePurposeRequired?
package.jsonIdentity (comindApp), dependencies, #typings import aliasYes
typings.tsApp-specific TypeScript types via makeEntity(fields)Yes
fields/index.tsField definitions (AppField[])Yes
fields/calc-fields/Calculated/virtual fields (bundled separately for browser)No
actions/index.tsAction definitions (AppAction[])Yes
actions/logic/index.tsServer-side action code (ActionLogic)No
actions/logic/preconditions.tsPrecondition checks (PreconditionLogic)No
views/layouts/default.tsxForm layout (JSX)Yes
views/list-views.tsGrid view definitions (AppListView[])No
views/logic/index.tsUI-side hooks (ViewLogic)No
settings/index.tsBacklinks, mutations, config (AppSettings)No
settings/secrets.tsSensitive config (COMIND_SECRET_* vars) - import only from actions/logic/No
settings/app-mutator.tsSchema post-processing at runtimeNo

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 UI
  • icon - the icon identifier rendered next to the app name
  • inheritFrom - 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.