@dforge-core/metadata 0.0.1
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/dist/actions.d.ts +31 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +7 -0
- package/dist/actions.js.map +1 -0
- package/dist/aggregation.d.ts +13 -0
- package/dist/aggregation.d.ts.map +1 -0
- package/dist/aggregation.js +17 -0
- package/dist/aggregation.js.map +1 -0
- package/dist/base-datatypes.d.ts +5 -0
- package/dist/base-datatypes.d.ts.map +1 -0
- package/dist/base-datatypes.js +49 -0
- package/dist/base-datatypes.js.map +1 -0
- package/dist/column-types.d.ts +5 -0
- package/dist/column-types.d.ts.map +1 -0
- package/dist/column-types.js +18 -0
- package/dist/column-types.js.map +1 -0
- package/dist/data-views.d.ts +95 -0
- package/dist/data-views.d.ts.map +1 -0
- package/dist/data-views.js +17 -0
- package/dist/data-views.js.map +1 -0
- package/dist/derive.d.ts +19 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +51 -0
- package/dist/derive.js.map +1 -0
- package/dist/entity.d.ts +110 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +7 -0
- package/dist/entity.js.map +1 -0
- package/dist/field-types.d.ts +260 -0
- package/dist/field-types.d.ts.map +1 -0
- package/dist/field-types.js +55 -0
- package/dist/field-types.js.map +1 -0
- package/dist/filter.d.ts +26 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +7 -0
- package/dist/filter.js.map +1 -0
- package/dist/folders.d.ts +32 -0
- package/dist/folders.d.ts.map +1 -0
- package/dist/folders.js +6 -0
- package/dist/folders.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs.d.ts +32 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +3 -0
- package/dist/jobs.js.map +1 -0
- package/dist/manifest.d.ts +53 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +4 -0
- package/dist/manifest.js.map +1 -0
- package/dist/menus.d.ts +32 -0
- package/dist/menus.d.ts.map +1 -0
- package/dist/menus.js +3 -0
- package/dist/menus.js.map +1 -0
- package/dist/print-templates.d.ts +36 -0
- package/dist/print-templates.d.ts.map +1 -0
- package/dist/print-templates.js +4 -0
- package/dist/print-templates.js.map +1 -0
- package/dist/reports.d.ts +84 -0
- package/dist/reports.d.ts.map +1 -0
- package/dist/reports.js +24 -0
- package/dist/reports.js.map +1 -0
- package/dist/roles.d.ts +10 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +3 -0
- package/dist/roles.js.map +1 -0
- package/dist/seed-data.d.ts +8 -0
- package/dist/seed-data.d.ts.map +1 -0
- package/dist/seed-data.js +4 -0
- package/dist/seed-data.js.map +1 -0
- package/dist/settings.d.ts +26 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +3 -0
- package/dist/settings.js.map +1 -0
- package/dist/traits.d.ts +14 -0
- package/dist/traits.d.ts.map +1 -0
- package/dist/traits.js +121 -0
- package/dist/traits.js.map +1 -0
- package/dist/triggers.d.ts +24 -0
- package/dist/triggers.d.ts.map +1 -0
- package/dist/triggers.js +3 -0
- package/dist/triggers.js.map +1 -0
- package/dist/types.d.ts +121 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/webhooks.d.ts +31 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +3 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +54 -0
- package/src/actions.ts +38 -0
- package/src/aggregation.ts +21 -0
- package/src/base-datatypes.ts +53 -0
- package/src/column-types.ts +22 -0
- package/src/data-views.ts +139 -0
- package/src/derive.ts +63 -0
- package/src/entity.ts +124 -0
- package/src/field-types.ts +68 -0
- package/src/filter.ts +34 -0
- package/src/folders.ts +39 -0
- package/src/index.ts +109 -0
- package/src/jobs.ts +34 -0
- package/src/manifest.ts +54 -0
- package/src/menus.ts +36 -0
- package/src/print-templates.ts +41 -0
- package/src/reports.ts +111 -0
- package/src/roles.ts +12 -0
- package/src/seed-data.ts +10 -0
- package/src/settings.ts +29 -0
- package/src/traits.ts +127 -0
- package/src/triggers.ts +27 -0
- package/src/types.ts +177 -0
- package/src/webhooks.ts +35 -0
package/dist/types.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Shared types for the dForge authoring registries.
|
|
2
|
+
//
|
|
3
|
+
// These describe the *authoring* surface (what a module author / visual editor
|
|
4
|
+
// works with), mirrored from the platform metadata seed data. Keep them free of
|
|
5
|
+
// any runtime dependency so this package stays import-safe everywhere.
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,uEAAuE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { EntityEvent } from "./triggers";
|
|
2
|
+
/** Outbound payload shaping for a webhook subscription. */
|
|
3
|
+
export interface WebhookPayload {
|
|
4
|
+
/** Field codes to include. If omitted, all visible fields are included. */
|
|
5
|
+
include?: string[];
|
|
6
|
+
/** Include previous field values on update/status_change events. */
|
|
7
|
+
includeOld?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** A single webhook subscription. */
|
|
10
|
+
export interface WebhookSubscription {
|
|
11
|
+
/** Unique identifier within the module. */
|
|
12
|
+
code: string;
|
|
13
|
+
description: string;
|
|
14
|
+
/** Entity code that triggers this webhook. */
|
|
15
|
+
entity: string;
|
|
16
|
+
event: EntityEvent;
|
|
17
|
+
/** Formula evaluated against the record; webhook fires only if true. */
|
|
18
|
+
condition?: string;
|
|
19
|
+
/** Default endpoint URL (admin can override). */
|
|
20
|
+
endpointUrl?: string;
|
|
21
|
+
/** Secret code for the API key (from `dforge.secret`). */
|
|
22
|
+
secretCd?: string;
|
|
23
|
+
payload?: WebhookPayload;
|
|
24
|
+
/** Enabled (default true). */
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** A `webhooks.json` file. */
|
|
28
|
+
export interface WebhooksFile {
|
|
29
|
+
subscriptions: WebhookSubscription[];
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qCAAqC;AACrC,MAAM,WAAW,mBAAmB;IACnC,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,8BAA8B;AAC9B,MAAM,WAAW,YAAY;IAC5B,aAAa,EAAE,mBAAmB,EAAE,CAAC;CACrC"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,iFAAiF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dforge-core/metadata",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Canonical dForge authoring registries — field types, base datatypes, column types, traits, data-view kinds and chart types — with derivation helpers. Runtime-dependency-free; the single source of truth shared by editors, the web app and the CLI.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "dForge",
|
|
7
|
+
"homepage": "https://github.com/iash44/dForge-core/tree/main/clients/packages/metadata#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/iash44/dForge-core.git",
|
|
11
|
+
"directory": "clients/packages/metadata"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/iash44/dForge-core/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"dforge",
|
|
18
|
+
"metadata",
|
|
19
|
+
"field-type",
|
|
20
|
+
"trait",
|
|
21
|
+
"registry",
|
|
22
|
+
"low-code"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"source": "./src/index.ts",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"src"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"prepack": "pnpm build",
|
|
47
|
+
"package": "pnpm pack"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^25.2.1",
|
|
51
|
+
"typescript": "^6.0.2",
|
|
52
|
+
"vitest": "^4.1.8"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/actions.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Actions — authoring shape of `ui/actions.json`.
|
|
2
|
+
//
|
|
3
|
+
// There is no published JSON schema for actions; this mirrors the shape used
|
|
4
|
+
// across the core modules. Parameter declarations and logic live in the action
|
|
5
|
+
// DSL file (`logic/actions/<script>.dsl`), not here.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Execution mode. 'single' (default) and 'each' run the DSL per selected record;
|
|
9
|
+
* 'batch' runs once with all records exposed as `__records`.
|
|
10
|
+
*/
|
|
11
|
+
export type ActionExecutionMode = "single" | "each" | "batch";
|
|
12
|
+
|
|
13
|
+
/** A single action declaration (value in the `ui/actions.json` map). */
|
|
14
|
+
export interface ActionDef {
|
|
15
|
+
/** Human-readable description. */
|
|
16
|
+
description: string;
|
|
17
|
+
/** Button label. */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Bootstrap icon class (e.g. 'bi-telephone-check'). */
|
|
20
|
+
icon: string;
|
|
21
|
+
/** Entity code this action targets. */
|
|
22
|
+
entityCode: string;
|
|
23
|
+
/** Per-record vs. batch execution. */
|
|
24
|
+
executionMode: ActionExecutionMode;
|
|
25
|
+
/** DSL script name (file under `logic/actions/<script>.dsl`). */
|
|
26
|
+
script: string;
|
|
27
|
+
/** Run inside a transaction (failures roll back). */
|
|
28
|
+
isTransacted?: boolean;
|
|
29
|
+
/** Display order. */
|
|
30
|
+
orderNum?: number;
|
|
31
|
+
/** Run in the background (newer `async`; some modules use `isAsync`). */
|
|
32
|
+
async?: boolean;
|
|
33
|
+
/** Alternate spelling of `async` used by some modules. Prefer `async`. */
|
|
34
|
+
isAsync?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A `ui/actions.json` file: action code → definition. */
|
|
38
|
+
export type ActionsFile = Record<string, ActionDef>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Aggregation types — the kinds of aggregation a column/report value can use.
|
|
2
|
+
//
|
|
3
|
+
// Declared as a `const` object + derived union (not a TS `enum`) so the package
|
|
4
|
+
// stays enum-free like the rest of its registries, while callers keep the
|
|
5
|
+
// ergonomic `AggType.Sum` member access. `AggType.Sum === "sum"` at runtime, so
|
|
6
|
+
// the value is interchangeable with the plain string everywhere.
|
|
7
|
+
|
|
8
|
+
export const AggType = {
|
|
9
|
+
Sum: "sum",
|
|
10
|
+
Count: "count",
|
|
11
|
+
Avg: "avg",
|
|
12
|
+
Min: "min",
|
|
13
|
+
Max: "max",
|
|
14
|
+
Group: "group",
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
/** One of the aggregation codes ("sum" | "count" | …). */
|
|
18
|
+
export type AggType = (typeof AggType)[keyof typeof AggType];
|
|
19
|
+
|
|
20
|
+
/** All aggregation types as an array (useful for iteration). */
|
|
21
|
+
export const AGG_TYPE_LIST: readonly AggType[] = Object.values(AggType);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Base datatype registry — mirror of
|
|
2
|
+
// server/database/system-modules/metadata/seed-data/base_datatypes.json
|
|
3
|
+
//
|
|
4
|
+
// Plus the canonical mapping from an abstract base datatype to the default
|
|
5
|
+
// PostgreSQL column type the platform generates. The visual editor uses this so
|
|
6
|
+
// the author never has to type a raw SQL type.
|
|
7
|
+
|
|
8
|
+
import type { BaseDatatypeCd } from "./types";
|
|
9
|
+
|
|
10
|
+
export const baseDatatypes: readonly BaseDatatypeCd[] = [
|
|
11
|
+
"binary",
|
|
12
|
+
"bool",
|
|
13
|
+
"culture",
|
|
14
|
+
"cuid",
|
|
15
|
+
"date",
|
|
16
|
+
"guid",
|
|
17
|
+
"json",
|
|
18
|
+
"link",
|
|
19
|
+
"number",
|
|
20
|
+
"project",
|
|
21
|
+
"ref",
|
|
22
|
+
"set",
|
|
23
|
+
"string",
|
|
24
|
+
"time",
|
|
25
|
+
"timestamp",
|
|
26
|
+
"xml",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
// Default PostgreSQL type per base datatype. `null` entries are non-physical
|
|
30
|
+
// (relationship/virtual) base types that never produce a column on their own.
|
|
31
|
+
const BASE_TO_DB: Record<BaseDatatypeCd, string | null> = {
|
|
32
|
+
binary: "bytea",
|
|
33
|
+
bool: "bool",
|
|
34
|
+
culture: "varchar",
|
|
35
|
+
cuid: "cuid",
|
|
36
|
+
date: "date",
|
|
37
|
+
guid: "uuid",
|
|
38
|
+
json: "jsonb",
|
|
39
|
+
link: null,
|
|
40
|
+
number: "numeric",
|
|
41
|
+
project: "varchar",
|
|
42
|
+
ref: null,
|
|
43
|
+
set: null,
|
|
44
|
+
string: "varchar",
|
|
45
|
+
time: "time",
|
|
46
|
+
timestamp: "timestamptz",
|
|
47
|
+
xml: "xml",
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Default PostgreSQL type for a base datatype, or `null` if non-physical. */
|
|
51
|
+
export function baseToDbDatatype(base: string): string | null {
|
|
52
|
+
return (BASE_TO_DB as Record<string, string | null>)[base] ?? null;
|
|
53
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Column-type registry — the seven column kinds a field can have. Letters match
|
|
2
|
+
// the `column.column_type` values and entity.schema.json. The `name` /
|
|
3
|
+
// `description` here drive the friendly "what kind of field is this?" picker.
|
|
4
|
+
|
|
5
|
+
import type { ColumnTypeDef } from "./types";
|
|
6
|
+
|
|
7
|
+
export const columnTypes: readonly ColumnTypeDef[] = [
|
|
8
|
+
{ cd: "D", name: "Value", description: "A plain data column stored on this entity." },
|
|
9
|
+
{ cd: "R", name: "Reference", description: "An N:1 lookup to another entity (foreign key)." },
|
|
10
|
+
{ cd: "S", name: "Child list", description: "A 1:N collection of child records from another entity." },
|
|
11
|
+
{ cd: "F", name: "Formula", description: "A virtual value calculated from a formula expression." },
|
|
12
|
+
{ cd: "G", name: "Generated", description: "A physical column auto-maintained by the database (aggregate or expression)." },
|
|
13
|
+
{ cd: "A", name: "Accumulation register", description: "Posts this document into a balance entity (single direction)." },
|
|
14
|
+
{ cd: "L", name: "Ledger register", description: "Posts document lines into movements + balances (double entry)." },
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
const byCd = new Map<string, ColumnTypeDef>(columnTypes.map((ct) => [ct.cd, ct]));
|
|
18
|
+
|
|
19
|
+
/** Look up a column type by letter. */
|
|
20
|
+
export function getColumnType(cd: string): ColumnTypeDef | undefined {
|
|
21
|
+
return byCd.get(cd);
|
|
22
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Data-view registries + the authoring shape of a `ui/data_views.json` file.
|
|
2
|
+
//
|
|
3
|
+
// `dataViewKinds` is the friendly picker list; `DataViewDef` and friends mirror
|
|
4
|
+
// the structural `data_views.schema.json` so editors/MCP/CLI can build views.
|
|
5
|
+
|
|
6
|
+
import type { Filter } from "./filter";
|
|
7
|
+
import type { NamedKind } from "./types";
|
|
8
|
+
|
|
9
|
+
export type { NamedKind };
|
|
10
|
+
|
|
11
|
+
/** `viewType` values for `ui/data_views.json`. Source: data_views.schema.json. */
|
|
12
|
+
export type ViewType =
|
|
13
|
+
| "grid"
|
|
14
|
+
| "list"
|
|
15
|
+
| "kanban"
|
|
16
|
+
| "calendar"
|
|
17
|
+
| "gallery"
|
|
18
|
+
| "tree-grid"
|
|
19
|
+
| "diagram"
|
|
20
|
+
| "master-detail"
|
|
21
|
+
| "library";
|
|
22
|
+
|
|
23
|
+
/** Friendly picker list of the view kinds. */
|
|
24
|
+
export const dataViewKinds: readonly NamedKind[] = [
|
|
25
|
+
{ cd: "grid", name: "Grid" },
|
|
26
|
+
{ cd: "list", name: "List" },
|
|
27
|
+
{ cd: "kanban", name: "Kanban" },
|
|
28
|
+
{ cd: "calendar", name: "Calendar" },
|
|
29
|
+
{ cd: "gallery", name: "Gallery" },
|
|
30
|
+
{ cd: "tree-grid", name: "Tree Grid" },
|
|
31
|
+
{ cd: "diagram", name: "Diagram" },
|
|
32
|
+
{ cd: "master-detail", name: "Master / Detail" },
|
|
33
|
+
{ cd: "library", name: "Library" },
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
/** A column in a data source: shorthand string code or a configured object. */
|
|
37
|
+
export type ViewColumn =
|
|
38
|
+
| string
|
|
39
|
+
| {
|
|
40
|
+
/** Column code. */
|
|
41
|
+
column_cd: string;
|
|
42
|
+
/** Column width in pixels. */
|
|
43
|
+
width?: number;
|
|
44
|
+
/** Whether the column is visible (default true). */
|
|
45
|
+
visible?: boolean;
|
|
46
|
+
/** Pin column to an edge. */
|
|
47
|
+
pinned?: "left" | "right";
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** One data source within a view. */
|
|
51
|
+
export interface DataSource {
|
|
52
|
+
/** Entity code to query. */
|
|
53
|
+
entityCode: string;
|
|
54
|
+
/** Nesting level (0 = root, 1+ = detail). */
|
|
55
|
+
level?: number;
|
|
56
|
+
/** Human-readable label (used in master-detail tabs). */
|
|
57
|
+
label?: string;
|
|
58
|
+
/** Column configuration. */
|
|
59
|
+
columns?: ViewColumn[];
|
|
60
|
+
/** Set field on the parent entity linking to this detail level. */
|
|
61
|
+
parentSetField?: string;
|
|
62
|
+
/** Per-source filter; overrides the view-level filter for this source. */
|
|
63
|
+
filter?: Filter;
|
|
64
|
+
/** Per-source sort order (array of `{ c, d }` entries). */
|
|
65
|
+
order?: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface KanbanViewConfig {
|
|
69
|
+
/** Dropdown/flags/reference column to group cards by. */
|
|
70
|
+
groupByField: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface CalendarViewConfig {
|
|
74
|
+
/** Date/timestamp column used to place events. */
|
|
75
|
+
dateField: string;
|
|
76
|
+
/** Column shown as event title (defaults to the entity toString). */
|
|
77
|
+
titleField?: string;
|
|
78
|
+
/** Column whose value drives the event pill color. */
|
|
79
|
+
colorField?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface TreeGridViewConfig {
|
|
83
|
+
/** Self-referencing FK column (parent record). */
|
|
84
|
+
parentField: string;
|
|
85
|
+
/** Node label field (defaults to entity toString). */
|
|
86
|
+
labelField?: string;
|
|
87
|
+
/** Field containing a Bootstrap icon class per node. */
|
|
88
|
+
iconField?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ListLevelConfig {
|
|
92
|
+
/** Entity code for this child level. */
|
|
93
|
+
entityCode: string;
|
|
94
|
+
/** FK column in the child entity linking back to the parent. */
|
|
95
|
+
foreignKey: string;
|
|
96
|
+
/** Columns to display inline for each child row. */
|
|
97
|
+
columns?: string[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ListViewConfig {
|
|
101
|
+
titleField?: string;
|
|
102
|
+
subtitleField?: string;
|
|
103
|
+
/** Hierarchical child levels for expandable list rows. */
|
|
104
|
+
levels?: ListLevelConfig[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface DiagramViewConfig {
|
|
108
|
+
/** Column to group diagram nodes by. */
|
|
109
|
+
groupByField?: string;
|
|
110
|
+
/** Group values hidden by default. */
|
|
111
|
+
defaultHiddenGroups?: string[];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A single data view (value in the `ui/data_views.json` map). */
|
|
115
|
+
export interface DataViewDef {
|
|
116
|
+
viewType: ViewType;
|
|
117
|
+
/** Human-readable view name. */
|
|
118
|
+
label?: string;
|
|
119
|
+
/** Bootstrap icon class (e.g. 'bi-bounding-box'). */
|
|
120
|
+
icon?: string;
|
|
121
|
+
description?: string;
|
|
122
|
+
/** Data sources (at least one; master-detail needs two). */
|
|
123
|
+
dataSources: DataSource[];
|
|
124
|
+
/** Default filter applied to all sources unless one declares its own. */
|
|
125
|
+
filter?: Filter;
|
|
126
|
+
/** Default sort order. */
|
|
127
|
+
order?: unknown;
|
|
128
|
+
/** View-type-specific configuration. */
|
|
129
|
+
viewConfig?:
|
|
130
|
+
| KanbanViewConfig
|
|
131
|
+
| CalendarViewConfig
|
|
132
|
+
| TreeGridViewConfig
|
|
133
|
+
| ListViewConfig
|
|
134
|
+
| DiagramViewConfig
|
|
135
|
+
| Record<string, unknown>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** A `ui/data_views.json` file: data view code → definition. */
|
|
139
|
+
export type DataViewsFile = Record<string, DataViewDef>;
|
package/src/derive.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Derivation helpers — turn a friendly field-type choice into the physical
|
|
2
|
+
// metadata the platform needs, so the author never types a raw SQL type.
|
|
3
|
+
//
|
|
4
|
+
// Defaults follow how core modules actually author fields (e.g. currency →
|
|
5
|
+
// numeric(18,2), color → varchar(20)); callers may always override the result.
|
|
6
|
+
|
|
7
|
+
import { baseToDbDatatype } from "./base-datatypes";
|
|
8
|
+
import { getFieldType } from "./field-types";
|
|
9
|
+
|
|
10
|
+
/** Total digits used when building a `numeric(total, scale)` type. */
|
|
11
|
+
export const NUMERIC_TOTAL = 18;
|
|
12
|
+
|
|
13
|
+
/** Field types whose natural storage is unbounded `text` rather than `varchar`. */
|
|
14
|
+
const TEXT_BACKED = new Set(["textarea", "richtext", "code"]);
|
|
15
|
+
|
|
16
|
+
export interface DeriveOptions {
|
|
17
|
+
/** Author-specified max length; overrides the field type's default. */
|
|
18
|
+
maxLen?: number | null;
|
|
19
|
+
/** Author-specified numeric scale; overrides the field type's default. */
|
|
20
|
+
precision?: number | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Derive the PostgreSQL `dbDatatype` for a field type. Returns `null` for
|
|
25
|
+
* field types backed by relationships/sets (lookup, grid) that have no physical
|
|
26
|
+
* column of their own. Unknown field types fall back to `varchar`.
|
|
27
|
+
*/
|
|
28
|
+
export function deriveDbDatatype(fieldTypeCd: string, options: DeriveOptions = {}): string | null {
|
|
29
|
+
const ft = getFieldType(fieldTypeCd);
|
|
30
|
+
if (!ft) return "varchar";
|
|
31
|
+
|
|
32
|
+
// Relationship-backed types (lookup → R, grid → S) never own a physical
|
|
33
|
+
// column — the FK lives on a separate hidden column.
|
|
34
|
+
if (ft.columnType === "R" || ft.columnType === "S") return null;
|
|
35
|
+
|
|
36
|
+
const base = baseToDbDatatype(ft.baseDatatypeCd);
|
|
37
|
+
if (base === null) return null; // other non-physical base (link/ref/set)
|
|
38
|
+
|
|
39
|
+
if (ft.baseDatatypeCd === "number") {
|
|
40
|
+
const precision = options.precision ?? ft.precision;
|
|
41
|
+
return precision != null ? `numeric(${NUMERIC_TOTAL},${precision})` : "numeric";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (ft.baseDatatypeCd === "string") {
|
|
45
|
+
if (TEXT_BACKED.has(ft.cd)) return "text";
|
|
46
|
+
const maxLen = options.maxLen ?? ft.maxLen;
|
|
47
|
+
return maxLen != null ? `varchar(${maxLen})` : "varchar";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return base;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Default `params` to seed a new field of this type with (or `undefined`). */
|
|
54
|
+
export function defaultParams(fieldTypeCd: string): Record<string, unknown> | undefined {
|
|
55
|
+
const ft = getFieldType(fieldTypeCd);
|
|
56
|
+
if (!ft?.defParams) return undefined;
|
|
57
|
+
return { ...ft.defParams };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The base datatype code a field type maps to (for `baseDatatypeCd` on F columns). */
|
|
61
|
+
export function deriveBaseDatatype(fieldTypeCd: string): string | undefined {
|
|
62
|
+
return getFieldType(fieldTypeCd)?.baseDatatypeCd;
|
|
63
|
+
}
|
package/src/entity.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Entity definition — authoring shape of an `entities/*.json` file.
|
|
2
|
+
//
|
|
3
|
+
// Mirror of entity.schema.json. Describes a table/view mapping: its fields
|
|
4
|
+
// (see FieldDef in types.ts), traits, references, number sequence and the
|
|
5
|
+
// register configs for accumulation/ledger entities.
|
|
6
|
+
|
|
7
|
+
import type { FieldDef } from "./types";
|
|
8
|
+
|
|
9
|
+
/** Trait codes valid in an entity's `traits` array (entity.schema.json enum). */
|
|
10
|
+
export type TraitCd =
|
|
11
|
+
| "identity"
|
|
12
|
+
| "audit"
|
|
13
|
+
| "audit-full"
|
|
14
|
+
| "soft-delete"
|
|
15
|
+
| "sorting"
|
|
16
|
+
| "postable"
|
|
17
|
+
| "accumulation"
|
|
18
|
+
| "ledger"
|
|
19
|
+
| "period"
|
|
20
|
+
| "file-library";
|
|
21
|
+
|
|
22
|
+
/** Foreign-key reference (legacy `references` block — prefer `link` on a field). */
|
|
23
|
+
export interface EntityReference {
|
|
24
|
+
from: { field: string };
|
|
25
|
+
to: { entity: string; field: string };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Automatic document numbering config (entity `numberSequence`). */
|
|
29
|
+
export interface NumberSequenceDef {
|
|
30
|
+
/** Column code that receives the auto-generated number. */
|
|
31
|
+
column: string;
|
|
32
|
+
/** Module setting code to resolve the prefix from (folder-scoped). */
|
|
33
|
+
prefixSettingCd?: string;
|
|
34
|
+
/** Fallback prefix when no setting is configured. */
|
|
35
|
+
defaultPrefix: string;
|
|
36
|
+
/** Format pattern. Placeholders: {prefix}, {yyyy}, {yy}, {mm}, {dd}, {seq:N}. */
|
|
37
|
+
pattern?: string;
|
|
38
|
+
/** Counter reset period (default: year). */
|
|
39
|
+
resetPeriod?: "year" | "month" | "day" | "never";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Period-entity config (`period` block; requires the `period` trait). */
|
|
43
|
+
export interface PeriodConfig {
|
|
44
|
+
/** Format for the period key. Placeholders: {yyyy}, {yy}, {MM}, {dd}, {q}. */
|
|
45
|
+
keyFormat: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Dynamic sign determination based on a field value. */
|
|
49
|
+
export interface SignConfig {
|
|
50
|
+
/** Field code that determines direction. */
|
|
51
|
+
field: string;
|
|
52
|
+
/** Values that mean increase. */
|
|
53
|
+
positive: string[];
|
|
54
|
+
/** Values that mean decrease. */
|
|
55
|
+
negative: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Accumulation-registry config (`accumulation` block; requires the trait). */
|
|
59
|
+
export interface AccumulationConfig {
|
|
60
|
+
/** Balance entity code (e.g. 'stock'). */
|
|
61
|
+
balanceEntity: string;
|
|
62
|
+
/** Balance lookup dimensions: field-name array or balance→document field map. */
|
|
63
|
+
dimensions: string[] | Record<string, string>;
|
|
64
|
+
/** Document field → balance field resource mappings. */
|
|
65
|
+
resources: Record<string, { balanceField: string }>;
|
|
66
|
+
/** Fixed posting direction for all postings. */
|
|
67
|
+
direction?: "increase" | "decrease";
|
|
68
|
+
/** Dynamic sign (alternative to `direction`). */
|
|
69
|
+
sign?: SignConfig;
|
|
70
|
+
autoCreateBalance?: boolean;
|
|
71
|
+
allowNegative?: boolean;
|
|
72
|
+
/** Document fields locked once posted. */
|
|
73
|
+
lockedFields?: string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** One registry target within an L-column's `registries` array. */
|
|
77
|
+
export interface LedgerRegistryEntry {
|
|
78
|
+
/** Movement entity code (e.g. 'posting_record'). */
|
|
79
|
+
movementEntity: string;
|
|
80
|
+
/** Line field → movement field mapping. */
|
|
81
|
+
movementMapping: Record<string, string>;
|
|
82
|
+
/** Balance entity code (e.g. 'balance_register'). */
|
|
83
|
+
balanceEntity: string;
|
|
84
|
+
balanceMapping: {
|
|
85
|
+
/** Balance row dimension columns. */
|
|
86
|
+
dimensions: string[];
|
|
87
|
+
/** Balance column for debit turnover. */
|
|
88
|
+
debitResource: string;
|
|
89
|
+
/** Balance column for credit turnover. */
|
|
90
|
+
creditResource: string;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A single entity definition (`entities/<code>.json`). */
|
|
95
|
+
export interface EntityDef {
|
|
96
|
+
/** Qualified entity code to extend (e.g. 'fin.invoice') — turns this into an extension. */
|
|
97
|
+
extends?: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
/** Database schema name override (defaults to module schema). */
|
|
100
|
+
schema?: string;
|
|
101
|
+
/** Database table/view name (defaults to entity code). */
|
|
102
|
+
dbObject?: string;
|
|
103
|
+
/** When true, maps to a SQL view (read-only, no DDL). */
|
|
104
|
+
isView?: boolean;
|
|
105
|
+
/** SELECT statement for view-backed entities (`isView=true`). */
|
|
106
|
+
viewSql?: string;
|
|
107
|
+
/** Display pattern using column placeholders, e.g. "{first_name} {last_name}". */
|
|
108
|
+
toString?: string;
|
|
109
|
+
/** Traits expanded at install time. */
|
|
110
|
+
traits?: TraitCd[];
|
|
111
|
+
/** Column definitions keyed by column code. */
|
|
112
|
+
fields: Record<string, FieldDef>;
|
|
113
|
+
/** Database constraints (unique, check, …); free-form per the schema. */
|
|
114
|
+
constraints?: Record<string, unknown>;
|
|
115
|
+
/** FK references (legacy format; prefer `link` on a field). */
|
|
116
|
+
references?: Record<string, EntityReference>;
|
|
117
|
+
/** Database index definitions; free-form per the schema. */
|
|
118
|
+
indexes?: Record<string, unknown>;
|
|
119
|
+
numberSequence?: NumberSequenceDef;
|
|
120
|
+
period?: PeriodConfig;
|
|
121
|
+
accumulation?: AccumulationConfig;
|
|
122
|
+
/** Entity-level platform behavior flags. */
|
|
123
|
+
params?: { restricted?: boolean } & Record<string, unknown>;
|
|
124
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Field-type registry — mirror of
|
|
2
|
+
// server/database/system-modules/metadata/seed-data/field_types.json
|
|
3
|
+
//
|
|
4
|
+
// This is the authoring source of truth: an author picks a `fieldTypeCd` and the
|
|
5
|
+
// editor derives `baseDatatypeCd` + `dbDatatype` from the entry here. Kept in
|
|
6
|
+
// sync with the server seed by the parity test in test/registries.test.ts —
|
|
7
|
+
// if that test fails after a seed change, regenerate this list.
|
|
8
|
+
|
|
9
|
+
import type { FieldTypeCd, FieldTypeDef } from "./types";
|
|
10
|
+
|
|
11
|
+
// `satisfies` (not `: readonly FieldTypeDef[]`) so the literal `cd` values are
|
|
12
|
+
// preserved in the type — that powers the compile-time coverage guard below.
|
|
13
|
+
export const fieldTypes = [
|
|
14
|
+
{ cd: "text", name: "Text", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
15
|
+
{ cd: "email", name: "Email", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
16
|
+
{ cd: "phone", name: "Phone", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
17
|
+
{ cd: "url", name: "URL", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
18
|
+
{ cd: "textarea", name: "Multiline Text", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
19
|
+
{ cd: "number", name: "Number", columnType: "D", baseDatatypeCd: "number", supportedAgg: "SCAM", maxLen: null, precision: null, useStringInFilter: false, defParams: { min: 0, max: 10000 } },
|
|
20
|
+
{ cd: "currency", name: "Currency", columnType: "D", baseDatatypeCd: "number", supportedAgg: "SCAM", maxLen: null, precision: 2, useStringInFilter: false, defParams: { min: 0, max: 1000000 } },
|
|
21
|
+
{ cd: "percent", name: "Percent", columnType: "D", baseDatatypeCd: "number", supportedAgg: "CAM", maxLen: null, precision: 2, useStringInFilter: false, defParams: { min: 0, max: 100 } },
|
|
22
|
+
{ cd: "checkbox", name: "Checkbox", columnType: "D", baseDatatypeCd: "bool", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
23
|
+
{ cd: "date", name: "Date", columnType: "D", baseDatatypeCd: "date", supportedAgg: "CM", maxLen: null, precision: null, useStringInFilter: false },
|
|
24
|
+
{ cd: "datetime", name: "Date & Time", columnType: "D", baseDatatypeCd: "timestamp", supportedAgg: "CM", maxLen: null, precision: null, useStringInFilter: false },
|
|
25
|
+
{ cd: "time", name: "Time", columnType: "D", baseDatatypeCd: "time", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },
|
|
26
|
+
{ cd: "dropdown", name: "Dropdown", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true },
|
|
27
|
+
{ cd: "flags", name: "Flags", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true, defParams: { style: "buttons" } },
|
|
28
|
+
{ cd: "json", name: "JSON Editor", columnType: "D", baseDatatypeCd: "json", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },
|
|
29
|
+
{ cd: "hidden", name: "Hidden", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },
|
|
30
|
+
{ cd: "color", name: "Color Picker", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: 20, precision: null, useStringInFilter: false },
|
|
31
|
+
{ cd: "image", name: "Image", columnType: "D", baseDatatypeCd: "binary", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },
|
|
32
|
+
{ cd: "file", name: "File", columnType: "D", baseDatatypeCd: "binary", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: false },
|
|
33
|
+
{ cd: "richtext", name: "Rich Text Editor", columnType: "D", baseDatatypeCd: "string", supportedAgg: null, maxLen: null, precision: null, useStringInFilter: true },
|
|
34
|
+
{ cd: "lookup", name: "Lookup", columnType: "R", baseDatatypeCd: "guid", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true },
|
|
35
|
+
{ cd: "user", name: "User Picker", columnType: "D", baseDatatypeCd: "cuid", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
36
|
+
{ cd: "grid", name: "Detail Grid", columnType: "S", baseDatatypeCd: "set", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
37
|
+
{ cd: "tags", name: "Tags", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: true },
|
|
38
|
+
{ cd: "entitylink", name: "Entity Link", columnType: "D", baseDatatypeCd: "json", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
39
|
+
{ cd: "code", name: "Code Editor", columnType: "D", baseDatatypeCd: "string", supportedAgg: "C", maxLen: null, precision: null, useStringInFilter: false },
|
|
40
|
+
] as const satisfies readonly FieldTypeDef[];
|
|
41
|
+
|
|
42
|
+
// Compile-time coverage guard: every `FieldTypeCd` must have a row above, and
|
|
43
|
+
// every row's `cd` must be a `FieldTypeCd`. If the union and the array drift
|
|
44
|
+
// apart, one of these two assignments fails to compile.
|
|
45
|
+
type _MissingFromArray = Exclude<FieldTypeCd, (typeof fieldTypes)[number]["cd"]>;
|
|
46
|
+
type _MissingFromUnion = Exclude<(typeof fieldTypes)[number]["cd"], FieldTypeCd>;
|
|
47
|
+
const _coverageOk: [_MissingFromArray, _MissingFromUnion] = [undefined as never, undefined as never];
|
|
48
|
+
void _coverageOk;
|
|
49
|
+
|
|
50
|
+
const byCd = new Map<string, FieldTypeDef>(fieldTypes.map((ft) => [ft.cd, ft]));
|
|
51
|
+
|
|
52
|
+
/** Set of all known field type codes, for membership checks. */
|
|
53
|
+
export const fieldTypeCds: ReadonlySet<string> = new Set(fieldTypes.map((ft) => ft.cd));
|
|
54
|
+
|
|
55
|
+
/** Type guard: is `cd` one of the known field type codes? */
|
|
56
|
+
export function isFieldTypeCd(cd: string | undefined): cd is FieldTypeCd {
|
|
57
|
+
return cd != null && fieldTypeCds.has(cd);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Look up a field type by code, or `undefined` if unknown. */
|
|
61
|
+
export function getFieldType(cd: string): FieldTypeDef | undefined {
|
|
62
|
+
return byCd.get(cd);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Field types whose default column kind is `columnType` (e.g. "D" value types). */
|
|
66
|
+
export function fieldTypesByColumnType(columnType: string): readonly FieldTypeDef[] {
|
|
67
|
+
return fieldTypes.filter((ft) => ft.columnType === columnType);
|
|
68
|
+
}
|