@dforge-core/metadata 0.0.7 → 0.0.8

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 CHANGED
@@ -5,6 +5,17 @@ All notable changes to `@dforge-core/metadata` are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this package adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.8] — 2026-07-17
9
+
10
+ ### Fixed
11
+
12
+ - `deriveDbDatatype` now returns `jsonb` (not `bytea`) for the `file` and `image`
13
+ field types, matching the server's `DdlService.ResolveDbTypeFromFieldType`.
14
+ These types classify as `binary`, but dForge stores a JSON metadata reference
15
+ (`{ storagePath, fileName, … }`) — the bytes live in file storage. Previously
16
+ an omitted `dbDatatype` on a file/image column derived `bytea`, which broke
17
+ file attachments on save.
18
+
8
19
  ## [0.0.3] — 2026-06-14
9
20
 
10
21
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AASA,sEAAsE;AACtE,eAAO,MAAM,aAAa,KAAK,CAAC;AAKhC,MAAM,WAAW,aAAa;IAC7B,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,GAAG,IAAI,CAuBhG;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAItF;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1E"}
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AASA,sEAAsE;AACtE,eAAO,MAAM,aAAa,KAAK,CAAC;AAkBhC,MAAM,WAAW,aAAa;IAC7B,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,GAAG,IAAI,CA4BhG;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAItF;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1E"}
package/dist/derive.js CHANGED
@@ -9,6 +9,18 @@ import { getFieldType } from "./field-types";
9
9
  export const NUMERIC_TOTAL = 18;
10
10
  /** Field types whose natural storage is unbounded `text` rather than `varchar`. */
11
11
  const TEXT_BACKED = new Set(["textarea", "richtext", "code"]);
12
+ /**
13
+ * Field types the server pins to a fixed column type regardless of their base
14
+ * datatype — mirrors `DdlService.ResolveDbTypeFromFieldType`. `file`/`image`
15
+ * classify as `binary`, but dForge stores them as a JSON metadata reference
16
+ * (`{ storagePath, fileName, … }`), NOT inline `bytea`; the column is `jsonb`.
17
+ * Without this override the base path derives `bytea`, and the file-attach
18
+ * pipeline — which reads/writes that JSON — fails on save.
19
+ */
20
+ const DB_DATATYPE_OVERRIDE = {
21
+ file: "jsonb",
22
+ image: "jsonb",
23
+ };
12
24
  /**
13
25
  * Derive the PostgreSQL `dbDatatype` for a field type. Returns `null` for
14
26
  * field types backed by relationships/sets (lookup, grid) that have no physical
@@ -22,6 +34,11 @@ export function deriveDbDatatype(fieldTypeCd, options = {}) {
22
34
  // column — the FK lives on a separate hidden column.
23
35
  if (ft.columnType === "R" || ft.columnType === "S")
24
36
  return null;
37
+ // Server-pinned column types that don't follow the base-datatype path
38
+ // (file/image → jsonb, not bytea). Mirrors ResolveDbTypeFromFieldType.
39
+ const override = DB_DATATYPE_OVERRIDE[ft.cd];
40
+ if (override)
41
+ return override;
25
42
  const base = baseToDbDatatype(ft.baseDatatypeCd);
26
43
  if (base === null)
27
44
  return null; // other non-physical base (link/ref/set)
@@ -1 +1 @@
1
- {"version":3,"file":"derive.js","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,+EAA+E;AAE/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEhC,mFAAmF;AACnF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAS9D;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,UAAyB,EAAE;IAChF,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAE1B,wEAAwE;IACxE,qDAAqD;IACrD,IAAI,EAAE,CAAC,UAAU,KAAK,GAAG,IAAI,EAAE,CAAC,UAAU,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEhE,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IACjD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,yCAAyC;IAEzE,IAAI,EAAE,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,CAAC;QACpD,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,aAAa,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,CAAC;IAED,IAAI,EAAE,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACpC,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAAE,OAAO,MAAM,CAAC;QAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC;QAC3C,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,WAAmB;IAChD,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,EAAE,SAAS;QAAE,OAAO,SAAS,CAAC;IACrC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAC5B,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACrD,OAAO,YAAY,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,yEAAyE;AACzE,EAAE;AACF,2EAA2E;AAC3E,+EAA+E;AAE/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEhC,mFAAmF;AACnF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,oBAAoB,GAA2B;IACpD,IAAI,EAAE,OAAO;IACb,KAAK,EAAE,OAAO;CACd,CAAC;AASF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,UAAyB,EAAE;IAChF,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE;QAAE,OAAO,SAAS,CAAC;IAE1B,wEAAwE;IACxE,qDAAqD;IACrD,IAAI,EAAE,CAAC,UAAU,KAAK,GAAG,IAAI,EAAE,CAAC,UAAU,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAEhE,sEAAsE;IACtE,uEAAuE;IACvE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,IAAI,GAAG,gBAAgB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IACjD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,yCAAyC;IAEzE,IAAI,EAAE,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,CAAC;QACpD,OAAO,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,aAAa,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,CAAC;IAED,IAAI,EAAE,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACpC,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAAE,OAAO,MAAM,CAAC;QAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,MAAM,CAAC;QAC3C,OAAO,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,WAAmB;IAChD,MAAM,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,EAAE,SAAS;QAAE,OAAO,SAAS,CAAC;IACrC,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAC5B,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACrD,OAAO,YAAY,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC;AAClD,CAAC"}
@@ -14,7 +14,11 @@ export interface TriggerDef {
14
14
  action: string;
15
15
  /** Static parameters passed to the action, merged with the injected record id. */
16
16
  params?: Record<string, unknown>;
17
- /** Run the action in the background after the triggering tx commits (default false). */
17
+ /**
18
+ * true: run in the background after the triggering tx commits (failures logged, not surfaced).
19
+ * false (default): run synchronously in the same tx — a failure rolls the mutation back and
20
+ * returns the error to the caller. Keep async:false actions fast.
21
+ */
18
22
  async?: boolean;
19
23
  }
20
24
  /** A `logic/triggers.json` file. */
@@ -1 +1 @@
1
- {"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"AAEA,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,KAAK,CAAC;AAEnF,oCAAoC;AACpC,MAAM,WAAW,UAAU;IAC1B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,wFAAwF;IACxF,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,UAAU,EAAE,CAAC;CACvB"}
1
+ {"version":3,"file":"triggers.d.ts","sourceRoot":"","sources":["../src/triggers.ts"],"names":[],"mappings":"AAEA,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,eAAe,GAAG,KAAK,CAAC;AAEnF,oCAAoC;AACpC,MAAM,WAAW,UAAU;IAC1B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,UAAU,EAAE,CAAC;CACvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dforge-core/metadata",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
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
5
  "license": "MIT",
6
6
  "author": "dForge",
@@ -50,7 +50,7 @@
50
50
  "async": {
51
51
  "type": "boolean",
52
52
  "default": false,
53
- "description": "When true, the action runs in the background — the triggering transaction commits before the action starts. Recommended for slow actions (emails, external API calls). When false, the action runs in the same transaction; failures roll back the original DB change."
53
+ "description": "When true, the action runs in the background — the triggering transaction commits before the action starts. Recommended for slow actions (emails, external API calls); background failures are logged, not surfaced to the caller. When false, the action runs synchronously inside the same transaction as the triggering insert/update/delete: if the action fails (e.g. calls error()), the whole mutation rolls back and the error is returned to the caller. Keep async:false actions fast — they run on the request's transaction."
54
54
  }
55
55
  },
56
56
  "additionalProperties": false
package/src/derive.ts CHANGED
@@ -13,6 +13,19 @@ export const NUMERIC_TOTAL = 18;
13
13
  /** Field types whose natural storage is unbounded `text` rather than `varchar`. */
14
14
  const TEXT_BACKED = new Set(["textarea", "richtext", "code"]);
15
15
 
16
+ /**
17
+ * Field types the server pins to a fixed column type regardless of their base
18
+ * datatype — mirrors `DdlService.ResolveDbTypeFromFieldType`. `file`/`image`
19
+ * classify as `binary`, but dForge stores them as a JSON metadata reference
20
+ * (`{ storagePath, fileName, … }`), NOT inline `bytea`; the column is `jsonb`.
21
+ * Without this override the base path derives `bytea`, and the file-attach
22
+ * pipeline — which reads/writes that JSON — fails on save.
23
+ */
24
+ const DB_DATATYPE_OVERRIDE: Record<string, string> = {
25
+ file: "jsonb",
26
+ image: "jsonb",
27
+ };
28
+
16
29
  export interface DeriveOptions {
17
30
  /** Author-specified max length; overrides the field type's default. */
18
31
  maxLen?: number | null;
@@ -33,6 +46,11 @@ export function deriveDbDatatype(fieldTypeCd: string, options: DeriveOptions = {
33
46
  // column — the FK lives on a separate hidden column.
34
47
  if (ft.columnType === "R" || ft.columnType === "S") return null;
35
48
 
49
+ // Server-pinned column types that don't follow the base-datatype path
50
+ // (file/image → jsonb, not bytea). Mirrors ResolveDbTypeFromFieldType.
51
+ const override = DB_DATATYPE_OVERRIDE[ft.cd];
52
+ if (override) return override;
53
+
36
54
  const base = baseToDbDatatype(ft.baseDatatypeCd);
37
55
  if (base === null) return null; // other non-physical base (link/ref/set)
38
56
 
package/src/triggers.ts CHANGED
@@ -17,7 +17,11 @@ export interface TriggerDef {
17
17
  action: string;
18
18
  /** Static parameters passed to the action, merged with the injected record id. */
19
19
  params?: Record<string, unknown>;
20
- /** Run the action in the background after the triggering tx commits (default false). */
20
+ /**
21
+ * true: run in the background after the triggering tx commits (failures logged, not surfaced).
22
+ * false (default): run synchronously in the same tx — a failure rolls the mutation back and
23
+ * returns the error to the caller. Keep async:false actions fast.
24
+ */
21
25
  async?: boolean;
22
26
  }
23
27