@dforge-core/dforge-mcp 0.1.3 → 0.1.4
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/CHANGELOG.md +7 -0
- package/dist/server.js +5 -4
- package/package.json +2 -2
- package/resources/schemas/data-views.schema.json +175 -4
- package/resources/schemas/deps.schema.json +84 -0
- package/resources/schemas/entity.schema.json +35 -5
- package/resources/schemas/manifest.schema.json +4 -2
- package/resources/schemas/reports.schema.json +30 -3
- package/resources/schemas/traits.schema.json +32 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ the release workflow, so committed `package.json` versions are placeholders.
|
|
|
6
6
|
|
|
7
7
|
## 0.1.3
|
|
8
8
|
|
|
9
|
+
### Changed
|
|
10
|
+
- Bumped the `@dforge-core/metadata` dependency to `^0.0.4` so the publish-time
|
|
11
|
+
schema vendoring (`prepublishOnly` → `vendor-schemas`) picks up the refreshed
|
|
12
|
+
`reports.schema.json` (`vizType: "kpi"` + KPI formula / cross-source config).
|
|
13
|
+
The vendored copy under `resources/schemas/` is regenerated on publish — it is
|
|
14
|
+
not hand-edited.
|
|
15
|
+
|
|
9
16
|
### Added
|
|
10
17
|
- **Cross-source report metrics & charts (schema).** `reports.schema.json` now
|
|
11
18
|
lists `kpi` as a `vizType` (was `metric`) and documents the two config shapes
|
package/dist/server.js
CHANGED
|
@@ -31155,6 +31155,7 @@ var fieldTypes = [
|
|
|
31155
31155
|
{ cd: "lookup", name: "Lookup", columnType: "R", baseDatatypeCd: "guid", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true },
|
|
31156
31156
|
{ cd: "user", name: "User Picker", columnType: "D", baseDatatypeCd: "cuid", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
31157
31157
|
{ cd: "grid", name: "Detail Grid", columnType: "S", baseDatatypeCd: "set", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
31158
|
+
{ cd: "list", name: "Detail List", columnType: "S", baseDatatypeCd: "set", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
31158
31159
|
{ cd: "tags", name: "Tags", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true },
|
|
31159
31160
|
{ cd: "entitylink", name: "Entity Link", columnType: "D", baseDatatypeCd: "json", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
31160
31161
|
{ cd: "code", name: "Code Editor", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false }
|
|
@@ -31219,8 +31220,8 @@ var traits = [
|
|
|
31219
31220
|
cd: "audit",
|
|
31220
31221
|
description: "Creation and update timestamps",
|
|
31221
31222
|
fields: {
|
|
31222
|
-
created_date: { dbDatatype: "timestamptz", fieldTypeCd: "datetime", flags: "I", isNullable: false,
|
|
31223
|
-
last_updated: { dbDatatype: "timestamptz", fieldTypeCd: "datetime", flags: "I", isNullable: false,
|
|
31223
|
+
created_date: { dbDatatype: "timestamptz", fieldTypeCd: "datetime", flags: "I", isNullable: false, orderNum: 9901, description: "Created Date", params: { serverDefault: { value: "now()", on: "insert" } } },
|
|
31224
|
+
last_updated: { dbDatatype: "timestamptz", fieldTypeCd: "datetime", flags: "I", isNullable: false, orderNum: 9902, description: "Last Updated", params: { serverDefault: { value: "now()", on: "save" } } }
|
|
31224
31225
|
}
|
|
31225
31226
|
},
|
|
31226
31227
|
{
|
|
@@ -31228,9 +31229,9 @@ var traits = [
|
|
|
31228
31229
|
description: "Full audit with user tracking",
|
|
31229
31230
|
includes: ["audit"],
|
|
31230
31231
|
fields: {
|
|
31231
|
-
created_by: { dbDatatype: "cuid", flags: "I", isNullable: false, orderNum: 9903, description: "Created By User ID" },
|
|
31232
|
+
created_by: { dbDatatype: "cuid", flags: "I", isNullable: false, orderNum: 9903, description: "Created By User ID", params: { serverDefault: { value: "currentUser()", on: "insert" } } },
|
|
31232
31233
|
created_by_user: { columnType: "R", fieldTypeCd: "lookup", flags: "I", orderNum: 9904, description: "Created By", link: { entity: "user", thisKey: "created_by", otherKey: "user_id" } },
|
|
31233
|
-
last_updated_by: { dbDatatype: "cuid", flags: "I", isNullable: false, orderNum: 9905, description: "Last Updated By User ID" },
|
|
31234
|
+
last_updated_by: { dbDatatype: "cuid", flags: "I", isNullable: false, orderNum: 9905, description: "Last Updated By User ID", params: { serverDefault: { value: "currentUser()", on: "save" } } },
|
|
31234
31235
|
last_updated_by_user: { columnType: "R", fieldTypeCd: "lookup", flags: "I", orderNum: 9906, description: "Last Updated By", link: { entity: "user", thisKey: "last_updated_by", otherKey: "user_id" } }
|
|
31235
31236
|
},
|
|
31236
31237
|
references: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dforge-core/dforge-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "MCP server for dForge module authoring. Exposes scaffold/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/iash44/dForge-core",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@dforge-core/dforge-cli": "^0.2.2",
|
|
35
|
-
"@dforge-core/metadata": "^0.0.
|
|
35
|
+
"@dforge-core/metadata": "^0.0.4",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"zod": "^4.4.3"
|
|
38
38
|
},
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
"$defs": {
|
|
11
11
|
"dataView": {
|
|
12
12
|
"type": "object",
|
|
13
|
-
"required": ["
|
|
13
|
+
"required": ["dataSources"],
|
|
14
14
|
"properties": {
|
|
15
15
|
"viewType": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"enum": ["grid", "list", "kanban", "calendar", "gallery", "tree-grid", "diagram", "master-detail", "library"],
|
|
18
|
-
"
|
|
17
|
+
"enum": ["grid", "list", "kanban", "calendar", "gallery", "tree-grid", "diagram", "master-detail", "library", "matrix"],
|
|
18
|
+
"default": "grid",
|
|
19
|
+
"description": "View display type. Optional — omitted or unknown values fall back to 'grid' at runtime."
|
|
19
20
|
},
|
|
20
21
|
"label": {
|
|
21
22
|
"type": "string",
|
|
@@ -56,7 +57,8 @@
|
|
|
56
57
|
{ "$ref": "#/$defs/rules/tree-grid" },
|
|
57
58
|
{ "$ref": "#/$defs/rules/list" },
|
|
58
59
|
{ "$ref": "#/$defs/rules/diagram" },
|
|
59
|
-
{ "$ref": "#/$defs/rules/master-detail" }
|
|
60
|
+
{ "$ref": "#/$defs/rules/master-detail" },
|
|
61
|
+
{ "$ref": "#/$defs/rules/matrix" }
|
|
60
62
|
]
|
|
61
63
|
},
|
|
62
64
|
|
|
@@ -126,6 +128,18 @@
|
|
|
126
128
|
"dataSources": { "minItems": 2 }
|
|
127
129
|
}
|
|
128
130
|
}
|
|
131
|
+
},
|
|
132
|
+
"matrix": {
|
|
133
|
+
"if": {
|
|
134
|
+
"properties": { "viewType": { "const": "matrix" } },
|
|
135
|
+
"required": ["viewType"]
|
|
136
|
+
},
|
|
137
|
+
"then": {
|
|
138
|
+
"properties": {
|
|
139
|
+
"viewConfig": { "$ref": "#/$defs/matrixViewConfig" }
|
|
140
|
+
},
|
|
141
|
+
"required": ["viewConfig"]
|
|
142
|
+
}
|
|
129
143
|
}
|
|
130
144
|
},
|
|
131
145
|
|
|
@@ -233,6 +247,163 @@
|
|
|
233
247
|
"additionalProperties": false
|
|
234
248
|
},
|
|
235
249
|
|
|
250
|
+
"matrixViewConfig": {
|
|
251
|
+
"type": "object",
|
|
252
|
+
"description": "Matrix (pivot) view: rowAxis × colAxis grid with a value-source cell entity holding one record per (rowKey, colKey).",
|
|
253
|
+
"required": ["rowAxis", "colAxis", "cell"],
|
|
254
|
+
"properties": {
|
|
255
|
+
"rowAxis": {
|
|
256
|
+
"description": "Row axis values: an entity's records ('dataset') or a column's dropdown options ('dropdown').",
|
|
257
|
+
"oneOf": [
|
|
258
|
+
{ "$ref": "#/$defs/matrixDatasetAxis" },
|
|
259
|
+
{ "$ref": "#/$defs/matrixDropdownAxis" }
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
"colAxis": {
|
|
263
|
+
"description": "Column axis values: 'dataset', 'dropdown', or a generated 'date' window.",
|
|
264
|
+
"oneOf": [
|
|
265
|
+
{ "$ref": "#/$defs/matrixDatasetAxis" },
|
|
266
|
+
{ "$ref": "#/$defs/matrixDropdownAxis" },
|
|
267
|
+
{ "$ref": "#/$defs/matrixDateAxis" }
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
"cell": { "$ref": "#/$defs/matrixCellConfig" }
|
|
271
|
+
},
|
|
272
|
+
"additionalProperties": false
|
|
273
|
+
},
|
|
274
|
+
"matrixDatasetAxis": {
|
|
275
|
+
"type": "object",
|
|
276
|
+
"description": "Dataset axis — axis values are the records of an entity.",
|
|
277
|
+
"required": ["kind", "entity", "labelField"],
|
|
278
|
+
"properties": {
|
|
279
|
+
"kind": { "const": "dataset" },
|
|
280
|
+
"entity": {
|
|
281
|
+
"type": "string",
|
|
282
|
+
"description": "Axis-source entity code (e.g. 'account', 'accounting_period')"
|
|
283
|
+
},
|
|
284
|
+
"labelField": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"description": "Column shown as the axis header label"
|
|
287
|
+
},
|
|
288
|
+
"keyField": {
|
|
289
|
+
"type": "string",
|
|
290
|
+
"description": "Column whose value is the match key. Defaults to the axis entity's PK."
|
|
291
|
+
},
|
|
292
|
+
"lockedField": {
|
|
293
|
+
"type": "string",
|
|
294
|
+
"description": "Boolean column; when true, that axis value's cells are read-only even in edit mode (e.g. a period's 'closed' flag)"
|
|
295
|
+
},
|
|
296
|
+
"filter": {
|
|
297
|
+
"$ref": "#/$defs/filter",
|
|
298
|
+
"description": "Optional filter scoping which axis records load (e.g. row axis scoped to 'statement == IncomeStatement')"
|
|
299
|
+
},
|
|
300
|
+
"hideEmpty": {
|
|
301
|
+
"type": "boolean",
|
|
302
|
+
"description": "COLUMN axis only — hide axis values that have no cell record in any row (e.g. periods with no posted activity), so a P&L doesn't trail off into empty months. Data-driven and reactive; ignored if it would hide every column."
|
|
303
|
+
},
|
|
304
|
+
"rangeControl": {
|
|
305
|
+
"type": "boolean",
|
|
306
|
+
"description": "COLUMN axis only — show a From/To range control above the matrix so the user can narrow the visible columns to a span (e.g. a quarter or a single year of periods). The chosen span persists in the view options and composes with hideEmpty."
|
|
307
|
+
},
|
|
308
|
+
"sort": {
|
|
309
|
+
"type": "array",
|
|
310
|
+
"items": { "$ref": "#/$defs/matrixAxisSort" },
|
|
311
|
+
"description": "Sort applied to the axis records"
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
"additionalProperties": false
|
|
315
|
+
},
|
|
316
|
+
"matrixDropdownAxis": {
|
|
317
|
+
"type": "object",
|
|
318
|
+
"description": "Dropdown axis — axis values are the options of a dropdown/flags column, read from its params.options.",
|
|
319
|
+
"required": ["kind", "column"],
|
|
320
|
+
"properties": {
|
|
321
|
+
"kind": { "const": "dropdown" },
|
|
322
|
+
"column": {
|
|
323
|
+
"type": "string",
|
|
324
|
+
"description": "'entity.column' reference to a dropdown/flags column whose options are the axis"
|
|
325
|
+
},
|
|
326
|
+
"values": {
|
|
327
|
+
"type": "array",
|
|
328
|
+
"items": { "type": "string" },
|
|
329
|
+
"description": "Optional subset + order of option values to render as columns (the option 'value' codes), the way kanban lanes are chosen. Omit to show every option in field order; values not matching an option are ignored."
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
"additionalProperties": false
|
|
333
|
+
},
|
|
334
|
+
"matrixDateAxis": {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"description": "Date axis — axis values generated from a date window rather than entity records. Column axis only.",
|
|
337
|
+
"required": ["kind", "grain", "window"],
|
|
338
|
+
"properties": {
|
|
339
|
+
"kind": { "const": "date" },
|
|
340
|
+
"grain": {
|
|
341
|
+
"const": "day",
|
|
342
|
+
"description": "Column granularity. v1: 'day'."
|
|
343
|
+
},
|
|
344
|
+
"window": {
|
|
345
|
+
"const": "week",
|
|
346
|
+
"description": "How many columns are shown. v1: 'week' (7 days, Monday-start)."
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"additionalProperties": false
|
|
350
|
+
},
|
|
351
|
+
"matrixAxisSort": {
|
|
352
|
+
"type": "object",
|
|
353
|
+
"required": ["col"],
|
|
354
|
+
"properties": {
|
|
355
|
+
"col": { "type": "string", "description": "Axis-entity column to sort by" },
|
|
356
|
+
"dir": { "type": "string", "enum": ["asc", "desc"], "description": "Sort direction (defaults to 'asc')" }
|
|
357
|
+
},
|
|
358
|
+
"additionalProperties": false
|
|
359
|
+
},
|
|
360
|
+
"matrixCellConfig": {
|
|
361
|
+
"type": "object",
|
|
362
|
+
"description": "The value-source cell of a matrix — one record per (rowKey, colKey).",
|
|
363
|
+
"required": ["entity", "rowKey", "colKey", "fields"],
|
|
364
|
+
"properties": {
|
|
365
|
+
"entity": {
|
|
366
|
+
"type": "string",
|
|
367
|
+
"description": "Cell entity code (the view's primary data source)"
|
|
368
|
+
},
|
|
369
|
+
"rowKey": {
|
|
370
|
+
"type": "string",
|
|
371
|
+
"description": "Cell column matching the row axis key"
|
|
372
|
+
},
|
|
373
|
+
"colKey": {
|
|
374
|
+
"type": "string",
|
|
375
|
+
"description": "Cell column matching the column axis key"
|
|
376
|
+
},
|
|
377
|
+
"fields": {
|
|
378
|
+
"type": "array",
|
|
379
|
+
"items": { "type": "string" },
|
|
380
|
+
"description": "Cell columns rendered inside each cell, in order"
|
|
381
|
+
},
|
|
382
|
+
"cardinality": {
|
|
383
|
+
"const": "one",
|
|
384
|
+
"description": "v1 supports a single record per cell"
|
|
385
|
+
},
|
|
386
|
+
"editable": {
|
|
387
|
+
"type": "boolean",
|
|
388
|
+
"description": "Whether cells edit in place"
|
|
389
|
+
},
|
|
390
|
+
"drill": {
|
|
391
|
+
"type": "boolean",
|
|
392
|
+
"description": "When true, read-only cell values become clickable and open the cell's record (where its child sets are shown)"
|
|
393
|
+
},
|
|
394
|
+
"seedFromRow": {
|
|
395
|
+
"type": "object",
|
|
396
|
+
"additionalProperties": { "type": "string" },
|
|
397
|
+
"description": "On empty-cell insert, copy values from the row axis record into the new cell record: { cellField: rowAxisField }"
|
|
398
|
+
},
|
|
399
|
+
"seedCurrentUser": {
|
|
400
|
+
"type": "array",
|
|
401
|
+
"items": { "type": "string" },
|
|
402
|
+
"description": "On empty-cell insert, set these cell fields to the current user id"
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
"additionalProperties": false
|
|
406
|
+
},
|
|
236
407
|
"viewColumn": {
|
|
237
408
|
"oneOf": [
|
|
238
409
|
{
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://dforge.dev/schemas/deps.schema.json",
|
|
4
|
+
"title": "dForge Module Dependency Contract",
|
|
5
|
+
"description": "Declares everything a consumer module consumes from one dependency: the entities, columns (with base datatype), and the provenance of each usage. One file per dependency at modules/<consumer>/deps/<provider>.json. The dependency is the source of truth; this file is the consumer's import list. See docs/modules/dependency-contracts.md.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["module", "version", "entities"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"module": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Code of the dependency (provider) module this contract is against (e.g. 'parties'). Matches a key in the consumer manifest's 'dependencies'."
|
|
13
|
+
},
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"minLength": 1,
|
|
17
|
+
"description": "Semver range the consumer requires of the provider (e.g. '>=0.1.0'). Should agree with the consumer manifest's dependency range for this module."
|
|
18
|
+
},
|
|
19
|
+
"entities": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"description": "Consumed provider entities, keyed by entity code (e.g. 'party').",
|
|
22
|
+
"minProperties": 1,
|
|
23
|
+
"additionalProperties": { "$ref": "#/definitions/entityContract" }
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"definitions": {
|
|
27
|
+
"entityContract": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"required": ["pk", "use"],
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"description": "What the consumer needs from one provider entity.",
|
|
32
|
+
"properties": {
|
|
33
|
+
"pk": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "The provider entity's primary-key column — the structural anchor every reference into the entity joins on. Note: the label/display column is NOT declared (display is the provider's concern, resolved from its toString)."
|
|
36
|
+
},
|
|
37
|
+
"use": {
|
|
38
|
+
"$ref": "#/definitions/provenanceList",
|
|
39
|
+
"description": "Entity-level provenance: why this entity is consumed (a ref targets it, the consumer extends it, a role grants on it, a view/report/print/action touches it)."
|
|
40
|
+
},
|
|
41
|
+
"columns": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"description": "Provider columns the consumer actually reads, writes, or joins on. Omit entirely for extension-only targets that touch no existing provider column.",
|
|
44
|
+
"additionalProperties": { "$ref": "#/definitions/columnContract" }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"columnContract": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["type", "use"],
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"description": "A single consumed provider column.",
|
|
53
|
+
"properties": {
|
|
54
|
+
"type": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": ["int2", "int4", "int8", "smallint", "integer", "bigint", "numeric", "decimal", "float4", "float8", "real", "money", "varchar", "text", "char", "bpchar", "bool", "boolean", "date", "time", "timetz", "timestamp", "timestamptz", "uuid", "json", "jsonb", "bytea"],
|
|
57
|
+
"description": "The provider column's STORAGE CLASS — its physical Postgres type with any length/scale stripped (int8, varchar, numeric, bool, date, timestamptz, jsonb, ...). Mirrors DdlService.ResolveDbType: dForge's 'cuid' Snowflake PK normalizes to int8 (use 'int8', not 'cuid'); varchar(n) -> varchar; fieldTypeCd 'currency'/'percent' -> numeric; 'number' -> int4. Pin the storage class (not a coarse base type) so an FK/lookup is meaningful: an int8 key is satisfiable by int8, not by any 'number'. The satisfaction check compares via EntityValidator.AreTypesCompatible (varchar==text, int8==bigint). This enum is a closed allowlist to catch typos at author time; extend it (and DependencyContractValidator.ValidColumnTypes, kept in lockstep) if a provider column legitimately uses a type not listed."
|
|
58
|
+
},
|
|
59
|
+
"use": {
|
|
60
|
+
"$ref": "#/definitions/provenanceList",
|
|
61
|
+
"description": "Per-column provenance: which symbols read/write/join on this column."
|
|
62
|
+
},
|
|
63
|
+
"mode": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"enum": ["read", "write"],
|
|
66
|
+
"default": "read",
|
|
67
|
+
"description": "Whether the consumer reads or writes this column. 'write' (e.g. an action that inserts/updates the provider row) additionally implies an insert/update right and a settable column. Default 'read'; omit for reads."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"provenanceList": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"minItems": 1,
|
|
74
|
+
"uniqueItems": true,
|
|
75
|
+
"description": "One or more usage tokens. Each names the consuming symbol so the completeness lint can reconcile declared usage against actual usage.",
|
|
76
|
+
"items": { "$ref": "#/definitions/provenance" }
|
|
77
|
+
},
|
|
78
|
+
"provenance": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"pattern": "^(extends:[^.]+\\.[^.]+|(ref|formula|action|view|report|print|role):.+)$",
|
|
81
|
+
"description": "A usage token of the form '<kind>:<symbol>'. Kinds: ref:<entity.column> (a link column joins to the dependency; the entity is qualified <module>.<entity> when it's an extension host), extends:<module.entity> (the consumer extends the provider entity — MUST be fully qualified, e.g. extends:parties.party, never bare extends:party), formula:<entity.column> (a formula nav path reads it), action:<action_cd> (DSL reads/writes it, incl. raw query() SQL), view:<view_cd> (a data view's dataSource targets it), report:<report_cd> (a report queries it), print:<template_cd> (a print template navigates into it), role:<role_cd> (a security role grants rights on the entity). Note: 'display' is intentionally NOT a kind — the label is the provider's concern."
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -4,12 +4,15 @@
|
|
|
4
4
|
"title": "dForge Entity Definition",
|
|
5
5
|
"description": "Defines an entity (table/view mapping) for a dForge module",
|
|
6
6
|
"type": "object",
|
|
7
|
-
"
|
|
7
|
+
"anyOf": [
|
|
8
|
+
{ "required": ["fields"] },
|
|
9
|
+
{ "required": ["traits"] }
|
|
10
|
+
],
|
|
8
11
|
"properties": {
|
|
9
12
|
"extends": {
|
|
10
13
|
"type": "string",
|
|
11
|
-
"pattern": "^[a-z][a-z0-9_]*\\.[a-z][a-z0-9_]*$",
|
|
12
|
-
"description": "Qualified entity code of the target entity to extend (e.g. 'fin.invoice'). When set, this entity definition adds columns to an existing entity owned by another module instead of creating a new entity. Physical columns are stored in a 1:1 extension table."
|
|
14
|
+
"pattern": "^[a-z][a-z0-9_-]*\\.[a-z][a-z0-9_]*$",
|
|
15
|
+
"description": "Qualified entity code of the target entity to extend, as '<module>.<entity>' (e.g. 'fin.invoice' or 'cdy-tax.tax_invoice_issued_line'). The module part may contain hyphens (module codes are hyphenated, e.g. 'crm-fin'); the entity part is snake_case. When set, this entity definition adds columns to an existing entity owned by another module instead of creating a new entity. Physical columns are stored in a 1:1 extension table."
|
|
13
16
|
},
|
|
14
17
|
"description": {
|
|
15
18
|
"type": "string",
|
|
@@ -180,10 +183,10 @@
|
|
|
180
183
|
{ "type": "string" },
|
|
181
184
|
{
|
|
182
185
|
"type": "object",
|
|
183
|
-
"required": ["value"
|
|
186
|
+
"required": ["value"],
|
|
184
187
|
"properties": {
|
|
185
188
|
"value": { "type": "string" },
|
|
186
|
-
"label": { "type": "string" },
|
|
189
|
+
"label": { "type": "string", "description": "Display text. Optional — the runtime falls back to `value` when omitted (column.ts: label = o.label ?? value)." },
|
|
187
190
|
"icon": { "type": "string" },
|
|
188
191
|
"color": { "type": "string" }
|
|
189
192
|
},
|
|
@@ -192,6 +195,9 @@
|
|
|
192
195
|
]
|
|
193
196
|
},
|
|
194
197
|
"description": "Options for dropdown/flags field types"
|
|
198
|
+
},
|
|
199
|
+
"serverDefault": {
|
|
200
|
+
"$ref": "#/$defs/serverDefault"
|
|
195
201
|
}
|
|
196
202
|
}
|
|
197
203
|
},
|
|
@@ -309,6 +315,30 @@
|
|
|
309
315
|
},
|
|
310
316
|
"additionalProperties": false
|
|
311
317
|
},
|
|
318
|
+
"serverDefault": {
|
|
319
|
+
"type": "object",
|
|
320
|
+
"description": "Platform-computed value filled server-side on write (used by the audit / audit-full traits). Resolved by the write pipeline (data.insert/update and DSL insert/update) — not a formula.",
|
|
321
|
+
"required": ["value"],
|
|
322
|
+
"properties": {
|
|
323
|
+
"value": {
|
|
324
|
+
"type": "string",
|
|
325
|
+
"enum": ["now()", "currentUser()"],
|
|
326
|
+
"description": "Whitelisted server-context expression: now() → transaction clock, currentUser() → caller's user id."
|
|
327
|
+
},
|
|
328
|
+
"on": {
|
|
329
|
+
"type": "string",
|
|
330
|
+
"enum": ["insert", "update", "save"],
|
|
331
|
+
"default": "insert",
|
|
332
|
+
"description": "Phase(s) in which the value is applied. 'save' = both insert and update."
|
|
333
|
+
},
|
|
334
|
+
"ifEmpty": {
|
|
335
|
+
"type": "boolean",
|
|
336
|
+
"default": false,
|
|
337
|
+
"description": "When true, only fill if the caller did not provide a value; when false (default), always override."
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
"additionalProperties": false
|
|
341
|
+
},
|
|
312
342
|
"numberSequence": {
|
|
313
343
|
"type": "object",
|
|
314
344
|
"description": "Automatic document numbering. Auto-generates a formatted number on INSERT when the target column is empty.",
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"code": {
|
|
20
20
|
"type": "string",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
21
|
+
"minLength": 2,
|
|
22
|
+
"maxLength": 64,
|
|
23
|
+
"pattern": "^[a-z][a-z0-9]*([._-][a-z0-9]+)*$",
|
|
24
|
+
"description": "Module code — becomes the schema name and module_cd in DB, and the public marketplace slug. Lowercase letters and digits, with single '.', '-', or '_' separators (no consecutive, leading, or trailing separator); 2–64 chars, must start with a letter and end alphanumeric. Mirrors the marketplace publish validator (ModulePublishService.ModuleCodePattern)."
|
|
23
25
|
},
|
|
24
26
|
"kind": {
|
|
25
27
|
"type": "string",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"properties": {
|
|
52
52
|
"vizType": {
|
|
53
53
|
"type": "string",
|
|
54
|
-
"enum": ["table", "chart", "
|
|
55
|
-
"description": "Visualization type"
|
|
54
|
+
"enum": ["table", "chart", "kpi", "pivot", "tree", "markdown"],
|
|
55
|
+
"description": "Visualization type. (Older packages may use `metric` for `kpi`.)"
|
|
56
56
|
},
|
|
57
57
|
"datasetCd": {
|
|
58
58
|
"type": "string",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"title": { "type": "string" },
|
|
62
62
|
"config": {
|
|
63
63
|
"type": "object",
|
|
64
|
-
"description": "Viz-type-specific configuration
|
|
64
|
+
"description": "Viz-type-specific configuration.\n- chart: { chartType ('bar'|'horizontalBar'|'stackedBar'|'combo'|'line'|'area'|'pie'|'doughnut'|'scatter'|'bubble'|'funnel'|'heatmap'), categoryCol, valueCol, agg, seriesCol?, sizeCol?, chartSize? ('sm'|'m'|'l'|'xl'), clickAction?, showTrend?, series? }. `series` (a single object OR an array) declares CROSS-SOURCE overlay series on bar/horizontalBar/line/area, each { source?, categoryCol, valueCol, agg, label? }; `source` names a sibling dataset code (omit = the panel's own dataset). Overlays share one category axis (union of category values; bar gaps fill 0, line/area gap with null).\n- kpi: { metrics: [ metric, ... ] }. Each metric is EITHER an aggregation metric { column, agg ('sum'|'avg'|'min'|'max'|'count'), display?, label?, target?, min?, max?, icon?, sparklineDimension? } OR a FORMULA metric { formula (e.g. '[won] / [all] * 100'), inputs: [ { alias, column, agg, source? } ], format?: { style?: 'number'|'percent', decimals? }, display?, label? }. A formula input's `source` names a sibling dataset code for CROSS-DATASET metrics (omit = the panel's own dataset). Auto format (omit `format.style`) inherits the first input column's own formatter (currency/grouping).\n- table: { groupRules, aggregations, colorRules }."
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"additionalProperties": false
|
|
@@ -121,11 +121,38 @@
|
|
|
121
121
|
"sort": {
|
|
122
122
|
"type": "array",
|
|
123
123
|
"items": { "$ref": "#/$defs/sortClause" }
|
|
124
|
+
},
|
|
125
|
+
"rollup": {
|
|
126
|
+
"$ref": "#/$defs/rollup"
|
|
124
127
|
}
|
|
125
128
|
},
|
|
126
129
|
"additionalProperties": false
|
|
127
130
|
},
|
|
128
131
|
|
|
132
|
+
"rollup": {
|
|
133
|
+
"type": "object",
|
|
134
|
+
"description": "Hierarchy rollup: roll the flat leaf result up a self-referencing tree so parent rows carry the SUM of their descendants. Emits one row per tree node (leaf + ancestor), plus '_level' (depth from root) and '_isLeaf'. Measures must be additively-rollable (physical additive columns or LINEAR formulas of them, e.g. closing = opening + turnover); non-linear measures (ratios, products) are not valid and need a computed-row feature instead.",
|
|
135
|
+
"required": ["tree", "treeKey", "parentField", "leafKey", "measures", "labels"],
|
|
136
|
+
"properties": {
|
|
137
|
+
"tree": { "type": "string", "description": "Entity code that owns the hierarchy (e.g. 'account')." },
|
|
138
|
+
"treeKey": { "type": "string", "description": "Primary-key column of the tree entity (e.g. 'account_id')." },
|
|
139
|
+
"parentField": { "type": "string", "description": "Self-referencing parent FK on the tree entity; roots have it NULL (e.g. 'parent_account_id')." },
|
|
140
|
+
"leafKey": { "type": "string", "description": "Column on the query entity identifying the tree node a row belongs to (e.g. 'account_id' on balance_register)." },
|
|
141
|
+
"measures": {
|
|
142
|
+
"type": "array",
|
|
143
|
+
"items": { "type": "string" },
|
|
144
|
+
"description": "Value column codes (on the query entity) to SUM into ancestors. Must be additively-rollable."
|
|
145
|
+
},
|
|
146
|
+
"labels": {
|
|
147
|
+
"type": "array",
|
|
148
|
+
"items": { "type": "string" },
|
|
149
|
+
"description": "Node-describing columns, expressed as the report column keys ('account.account_code'). Sourced from the tree entity's physical column (the segment after the dot); emitted under the same key so the report layout is unchanged."
|
|
150
|
+
},
|
|
151
|
+
"order": { "type": "string", "description": "Tree-entity column code for pre-order sort (e.g. 'account_code'), resolved through metadata to its physical column. Assumes hierarchy-sortable codes; defaults to treeKey." }
|
|
152
|
+
},
|
|
153
|
+
"additionalProperties": false
|
|
154
|
+
},
|
|
155
|
+
|
|
129
156
|
"sortClause": {
|
|
130
157
|
"type": "object",
|
|
131
158
|
"required": ["c"],
|
|
@@ -66,6 +66,38 @@
|
|
|
66
66
|
},
|
|
67
67
|
"required": ["entity", "thisKey", "otherKey"],
|
|
68
68
|
"additionalProperties": false
|
|
69
|
+
},
|
|
70
|
+
"params": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"description": "Field-type-specific configuration; same shape as entity field params (e.g. serverDefault).",
|
|
73
|
+
"properties": {
|
|
74
|
+
"serverDefault": { "$ref": "#/$defs/serverDefault" }
|
|
75
|
+
},
|
|
76
|
+
"additionalProperties": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"additionalProperties": false
|
|
80
|
+
},
|
|
81
|
+
"serverDefault": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"description": "Platform-computed value filled server-side on write (used by the audit / audit-full traits). Resolved by the write pipeline — not a formula.",
|
|
84
|
+
"required": ["value"],
|
|
85
|
+
"properties": {
|
|
86
|
+
"value": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"enum": ["now()", "currentUser()"],
|
|
89
|
+
"description": "Whitelisted server-context expression: now() → transaction clock, currentUser() → caller's user id."
|
|
90
|
+
},
|
|
91
|
+
"on": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"enum": ["insert", "update", "save"],
|
|
94
|
+
"default": "insert",
|
|
95
|
+
"description": "Phase(s) in which the value is applied. 'save' = both insert and update."
|
|
96
|
+
},
|
|
97
|
+
"ifEmpty": {
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"default": false,
|
|
100
|
+
"description": "When true, only fill if the caller did not provide a value; when false (default), always override."
|
|
69
101
|
}
|
|
70
102
|
},
|
|
71
103
|
"additionalProperties": false
|