@dforge-core/metadata 0.0.14 → 0.0.16

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/src/actions.ts CHANGED
@@ -24,13 +24,41 @@ export interface ActionDef {
24
24
  executionMode: ActionExecutionMode;
25
25
  /** DSL script name (file under `logic/actions/<script>.dsl`). */
26
26
  script: string;
27
- /** Run inside a transaction (failures roll back). */
27
+ /**
28
+ * Run the whole selection in one transaction, so a failure on any record
29
+ * rolls back the records already processed. **Defaults to `true`** when
30
+ * omitted (`ActionDef.IsTransacted` in the installer is a non-nullable
31
+ * `bool` initialised to `true`), so omit it only when you want atomicity.
32
+ *
33
+ * `false` opens no transaction, and what follows a failure depends on
34
+ * {@link ActionDef.executionMode}: `single` / `each` report it and continue
35
+ * with the next record, so use that for independent per-record work that
36
+ * should process as many records as pass; `batch` is a single script
37
+ * invocation with no loop to continue, so the run simply ends with its
38
+ * earlier writes committed.
39
+ *
40
+ * On a run queued to the background the **rollback** guarantee is lost
41
+ * outright — the worker opens no transaction, so nothing is ever undone. What
42
+ * remains of the flag there depends on the mode: the worker's `single`/`each`
43
+ * loop still reads it for control flow (stop after the first failed record vs.
44
+ * continue), while its `batch` path never reads it at all — one invocation,
45
+ * which ends on failure under either value.
46
+ */
28
47
  isTransacted?: boolean;
29
48
  /** Display order. */
30
49
  orderNum?: number;
31
- /** Run in the background (newer `async`; some modules use `isAsync`). */
50
+ /**
51
+ * @deprecated Not read by the installer — use {@link ActionDef.isAsync}.
52
+ * The module manifest model binds `isAsync` only, so this key is silently
53
+ * ignored and the action installs as synchronous.
54
+ */
32
55
  async?: boolean;
33
- /** Alternate spelling of `async` used by some modules. Prefer `async`. */
56
+ /**
57
+ * Permit background execution. It does not force it: `action.execute`
58
+ * carries its own `async` argument and the server branches on that, so an
59
+ * action with parameters is offered to the user as both "Run" (inline) and
60
+ * "Run in Background". A parameterless `isAsync` action is queued directly.
61
+ */
34
62
  isAsync?: boolean;
35
63
  }
36
64
 
package/src/entity.ts CHANGED
@@ -32,6 +32,48 @@ export interface EntityReference {
32
32
  onUpdate?: ReferentialAction;
33
33
  }
34
34
 
35
+ /**
36
+ * Per-view overrides for one column (`views.<name>.columns.<column_cd>`). Every
37
+ * property is optional; an omitted one falls back to the entity-level value.
38
+ *
39
+ * An EMPTY value is not the same as an omitted one — the runtime merges view over
40
+ * entity with COALESCE, so `flags: ""` means "no flags in this view" (a column
41
+ * neither visible nor editable) and `params: {}` suppresses the entity-level
42
+ * params rather than inheriting them.
43
+ */
44
+ export interface EntityViewColumnDef {
45
+ /** Flag letters for this column in this view, e.g. "VO" to expose it read-only. */
46
+ flags?: string;
47
+ orderNum?: number;
48
+ isNullable?: boolean;
49
+ editMask?: string;
50
+ displayFmt?: string;
51
+ /**
52
+ * Formula override for this column in this view, same DSL as a field's
53
+ * `formula`. Only on a column the entity declares as `columnType: "F"` — on any
54
+ * other column that field is the SQL default, so an override would be inert.
55
+ */
56
+ formula?: string;
57
+ /** Lookup filter override for a reference column in this view. */
58
+ refFilter?: unknown;
59
+ /** Control-specific configuration override for this view. */
60
+ params?: Record<string, unknown>;
61
+ }
62
+
63
+ /** One entity view: the columns it exposes, plus optional per-view overrides. */
64
+ export interface EntityViewDef {
65
+ /**
66
+ * Column code → per-view overrides. Columns ABSENT from this map are hidden in
67
+ * a folder bound to the view — that is the mechanism, not an oversight. `{}`
68
+ * (or null) exposes a column with no overrides. Must include the entity's
69
+ * primary key: records are addressed by it (hide it by omitting `V` from its
70
+ * flags instead).
71
+ */
72
+ columns: Record<string, EntityViewColumnDef | null>;
73
+ /** Free-form view configuration, stored on entity_view.params. */
74
+ params?: Record<string, unknown>;
75
+ }
76
+
35
77
  /** Automatic document numbering config (entity `numberSequence`). */
36
78
  export interface NumberSequenceDef {
37
79
  /** Column code that receives the auto-generated number. */
@@ -128,4 +170,15 @@ export interface EntityDef {
128
170
  accumulation?: AccumulationConfig;
129
171
  /** Entity-level platform behavior flags. */
130
172
  params?: { restricted?: boolean } & Record<string, unknown>;
173
+ /**
174
+ * Entity views keyed by view name — the platform's column-level security.
175
+ * Unrelated to `isView`/`viewSql` above (a SQL-view-backed entity) and to
176
+ * `ui/data_views.json` (grids, kanban, calendars), both of which the word
177
+ * "view" also names in this platform. A
178
+ * folder binds one view per entity through `ui/folders.json`
179
+ * (`entities.<code>.viewName`); users working in that folder see ONLY the
180
+ * columns the view lists. An entity without views (or a folder naming the
181
+ * conventional `"default"`, which declares nothing) shows the full column set.
182
+ */
183
+ views?: Record<string, EntityViewDef>;
131
184
  }
package/src/folders.ts CHANGED
@@ -7,7 +7,16 @@ import type { Filter } from "./filter";
7
7
 
8
8
  /** Per-entity binding within a folder. */
9
9
  export interface FolderEntityBinding {
10
- /** Data view code to use in this folder (commonly 'default'). */
10
+ /**
11
+ * Entity view to bind for this entity in this folder — a key under the entity
12
+ * definition's `views` (see `EntityViewDef`), NOT a data view code from
13
+ * `ui/data_views.json`. Users in this folder then see only the columns that
14
+ * view lists.
15
+ *
16
+ * The conventional `"default"` declares nothing and means "no view" (the full
17
+ * column set); any other name the entity does not declare fails the install,
18
+ * since falling back would grant more access than naming a view asks for.
19
+ */
11
20
  viewName?: string;
12
21
  /** Enable the quick-add (+) button for this entity here. */
13
22
  quickAdd?: boolean;
package/src/index.ts CHANGED
@@ -57,6 +57,8 @@ export { FILTER_GROUP_OPERATORS, FILTER_OPERATORS } from "./filter";
57
57
  export type {
58
58
  EntityDef,
59
59
  EntityReference,
60
+ EntityViewDef,
61
+ EntityViewColumnDef,
60
62
  NumberSequenceDef,
61
63
  PeriodConfig,
62
64
  AccumulationConfig,
@@ -88,6 +90,7 @@ export {
88
90
  chartTypes,
89
91
  type ReportsFile,
90
92
  type ReportDef,
93
+ type ReportEntityAttachment,
91
94
  type ReportLayout,
92
95
  type ReportPanel,
93
96
  type Dataset,
@@ -98,7 +101,7 @@ export {
98
101
  type VizType,
99
102
  } from "./reports";
100
103
 
101
- export type { ManifestDef, ManifestAuthor, ModuleDependency } from "./manifest";
104
+ export type { ManifestDef, ManifestAuthor, ModuleDependency, ModuleFeature } from "./manifest";
102
105
  export type { DepsFile, DepEntity, DepColumn, DepColumnType, DepColumnMode, DepProvenance, DepProvenanceKind } from "./deps";
103
106
  export { DEP_COLUMN_TYPES } from "./deps";
104
107
  export type { DomainsFile, DomainDef } from "./domains";
package/src/manifest.ts CHANGED
@@ -10,6 +10,13 @@ export interface ManifestAuthor {
10
10
  /** A dependency value: a semver range, or a partial-dep object. */
11
11
  export type ModuleDependency = string | { version: string; entities?: string[] };
12
12
 
13
+ /**
14
+ * A platform behaviour a package can opt into via `manifest.features`.
15
+ * Mirrors `dForge.Core.ModuleFeatures` and the `features` enum in
16
+ * manifest.schema.json — all three are edited together.
17
+ */
18
+ export type ModuleFeature = "report-security";
19
+
13
20
  /** A module package manifest (`manifest.json`). */
14
21
  export interface ManifestDef {
15
22
  /** Package format version (currently 1). */
@@ -47,6 +54,20 @@ export interface ManifestDef {
47
54
  icon?: string;
48
55
  /** Non-English locales the package promises to translate. */
49
56
  supportedLocales?: string[];
57
+ /**
58
+ * Opt-in platform behaviours this package was authored for. Absent or empty
59
+ * = the pre-feature behaviour for all of them, so a module written before a
60
+ * feature existed keeps working until its author declares it and republishes.
61
+ *
62
+ * Unknown names are rejected at package load, never ignored — a misspelled
63
+ * feature would silently leave the old behaviour in place while the manifest
64
+ * claims otherwise.
65
+ *
66
+ * - `report-security` — this module's reports require 'E' on their
67
+ * `sec_object`. Declare the matching `"report:<code>": "E"` grants in
68
+ * `security/roles.json` first, or the reports go dark for every role.
69
+ */
70
+ features?: ModuleFeature[];
50
71
  /** Initial creation date (YYYY-MM-DD). Informational. */
51
72
  created?: string;
52
73
  /** Last manifest edit date (YYYY-MM-DD). Informational. */
package/src/reports.ts CHANGED
@@ -35,25 +35,46 @@ export interface ParamDefBase {
35
35
  label?: string;
36
36
  /** Display order in the parameter form (falls back to declaration order). */
37
37
  orderNum?: number;
38
- /** Whether the user must provide a value. */
38
+ /**
39
+ * Whether the user must provide a value. Stored inverted as `param.is_nullable`.
40
+ * There is no `isRequired` spelling — the installer reads this key only.
41
+ */
39
42
  required?: boolean;
40
- /** Alternate spelling of `required` used by some modules. Prefer `required`. */
41
- isRequired?: boolean;
42
43
  /** Default value applied when the user doesn't provide one. */
43
44
  default?: unknown;
44
- /** Entity link for lookup-type params. */
45
- link?: { entity: string; otherKey?: string };
46
45
  /**
47
- * Field-type-specific params — most importantly `options` for a dropdown.
46
+ * Field-type-specific params, nested under this key — there is no top-level
47
+ * `link`. A lookup param needs `{ link: { entity, otherKey? } }`.
48
+ * Most importantly `options` for a dropdown.
48
49
  * Options accept the same rich `{value,label,icon,color}` form entity columns
49
50
  * use; a bare string list renders the raw codes in every locale, so label them.
50
51
  * Omitted on a domain-backed param: the domain owns the list.
51
52
  */
52
53
  params?: Record<string, unknown>;
54
+ /**
55
+ * Declares a REFERENCE parameter: the submitted value is the whole picked
56
+ * record rather than its bare key. The JSON counterpart of the action DSL's
57
+ * `paramCd: ref <entityCd>`, stored in the same `param.column_type`.
58
+ *
59
+ * Optional, and the choice is not cosmetic. Omitting it (a plain `lookup`)
60
+ * submits the key, which is what a filter comparing against a reference
61
+ * column needs — the server destructures a key object across every key
62
+ * column and ignores non-key fields. Declare `"R"` when the report wants the
63
+ * record itself, not just its identity.
64
+ *
65
+ * Either way the picker is configured by `params.link` (`{ entity, otherKey? }`);
66
+ * `otherKey` is filled from the target's primary key when omitted, comma-joined
67
+ * and paired POSITIONALLY with the key columns.
68
+ */
69
+ columnType?: "R";
53
70
  }
54
71
 
55
72
  /**
56
- * A report parameter definition (report- or dataset-level).
73
+ * A report parameter definition. Declared in the report-level `parameters` block,
74
+ * or as shorthand under the `params` of the dataset that consumes it — the
75
+ * installer merges both into the report's single param set, so either way the
76
+ * parameter serves the whole report. Report level wins on a code collision, and
77
+ * is the only sensible home for a parameter several datasets use.
57
78
  *
58
79
  * A parameter takes its control from an explicit `fieldTypeCd` **or** from a
59
80
  * column `domain` — never both. Declaring both is the one rejected combination
@@ -111,10 +132,42 @@ export interface Dataset {
111
132
  procedureName?: string;
112
133
  /** Optional column metadata overrides: field code → override. */
113
134
  columnsDef?: Record<string, ColumnDef>;
114
- /** Dataset-level parameters (alternative to report-level `parameters`). */
135
+ /**
136
+ * Parameters declared on this dataset — shorthand for the report-level
137
+ * `parameters` block, convenient when exactly one dataset uses the parameter.
138
+ * The installer merges both into the report's one param set either way.
139
+ */
115
140
  params?: Record<string, ParamDef>;
116
141
  }
117
142
 
143
+ /**
144
+ * One (entity, report) **record-report attachment**: the report becomes openable
145
+ * from that entity's record, with the record's values feeding its parameters.
146
+ * Each entry becomes one `dForge.entity_report` row.
147
+ *
148
+ * The binding is per-attachment, not per-report — the same report can attach to
149
+ * several entities with different parameter mappings, which is why the mapping
150
+ * lives here rather than on the report.
151
+ */
152
+ export interface ReportEntityAttachment {
153
+ /**
154
+ * Entity the report attaches to, qualified `module.entity` for anything
155
+ * outside the declaring module. That module must be a declared dependency.
156
+ */
157
+ entityCd: string;
158
+ /**
159
+ * Parameter mapping: report param code → **source column on the entity**.
160
+ * (Not a param declaration — those live in the report's `parameters` block or a
161
+ * dataset's `params`.) Resolved server-side
162
+ * from the record's PK. Source columns are limited to the PK, reference (`R`)
163
+ * columns, and bounded scalars; source and target must be type-compatible.
164
+ * Params left unmapped keep their normal behaviour (default, or prompt).
165
+ */
166
+ params?: Record<string, string>;
167
+ /** Sort position in the record's combined action + report menu. */
168
+ orderNum?: number;
169
+ }
170
+
118
171
  /** A single visualization panel within the report layout. */
119
172
  export interface ReportPanel {
120
173
  vizType: VizType;
@@ -136,8 +189,21 @@ export interface ReportDef {
136
189
  layout: ReportLayout;
137
190
  /** Dataset code → dataset definition. */
138
191
  datasets: Record<string, Dataset>;
139
- /** Report-level parameters (alternative to per-dataset `params`). */
192
+ /** Auto-refresh interval in seconds. Omit (or 0) for no auto-refresh. */
193
+ reloadInterval?: number;
194
+ /**
195
+ * Report-level parameter declarations — the canonical home for a parameter, and
196
+ * the only one that fits a parameter several datasets use. Merged with each
197
+ * dataset's `params` into the report's single param set; this block wins on a
198
+ * code collision.
199
+ */
140
200
  parameters?: Record<string, ParamDef>;
201
+ /**
202
+ * Record-report attachments: entities this report can be opened from, with the
203
+ * mapping from report parameter to source column on each. Omit for a report
204
+ * that is only reached from a menu.
205
+ */
206
+ entities?: ReportEntityAttachment[];
141
207
  }
142
208
 
143
209
  /** A `ui/reports.json` file: report code → definition. */