@pattern-stack/codegen 0.20.2 → 0.22.0
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 +48 -0
- package/README.md +5 -1
- package/dist/{chunk-BKP72EDW.js → chunk-6DQEIXYU.js} +10 -10
- package/dist/{chunk-KK5A7B2T.js → chunk-6ECCJVYW.js} +136 -20
- package/dist/chunk-6ECCJVYW.js.map +1 -0
- package/dist/{chunk-QMN3LQR3.js → chunk-FNHNSFIJ.js} +9 -9
- package/dist/{chunk-JGGZUP64.js → chunk-NXHL5YII.js} +7 -7
- package/dist/{chunk-D44QFQJZ.js → chunk-QXVCRA23.js} +2 -2
- package/dist/{chunk-MVKW2BCR.js → chunk-YULGWXCY.js} +4 -4
- package/dist/runtime/base-classes/index.js +20 -20
- package/dist/runtime/shared/openapi/index.js +3 -3
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/index.js +15 -15
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +10 -10
- package/dist/runtime/subsystems/bridge/index.js +18 -18
- package/dist/runtime/subsystems/cache/cache.module.js +1 -1
- package/dist/runtime/subsystems/cache/index.js +3 -3
- package/dist/runtime/subsystems/index.js +66 -66
- package/dist/runtime/subsystems/jobs/index.js +11 -11
- package/dist/runtime/subsystems/jobs/job-worker.module.js +5 -5
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +4 -4
- package/dist/src/cli/index.js +94 -80
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +111 -1
- package/dist/src/index.js +3 -1
- package/package.json +1 -1
- package/dist/chunk-KK5A7B2T.js.map +0 -1
- /package/dist/{chunk-BKP72EDW.js.map → chunk-6DQEIXYU.js.map} +0 -0
- /package/dist/{chunk-QMN3LQR3.js.map → chunk-FNHNSFIJ.js.map} +0 -0
- /package/dist/{chunk-JGGZUP64.js.map → chunk-NXHL5YII.js.map} +0 -0
- /package/dist/{chunk-D44QFQJZ.js.map → chunk-QXVCRA23.js.map} +0 -0
- /package/dist/{chunk-MVKW2BCR.js.map → chunk-YULGWXCY.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.21.0] — 2026-06-06
|
|
8
|
+
|
|
9
|
+
**FieldMeta enrichment (ADR-040, Phase A of type-aware rendering).** The
|
|
10
|
+
frontend emitter's per-entity field metadata now carries enough vocabulary for
|
|
11
|
+
consumers to build metadata-driven rendering (the swe-brain renderer kit
|
|
12
|
+
consumes this in its Phase B). All additive.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Full `ui_*` hint passthrough.** `ui_group`, `ui_visible`, `ui_placeholder`,
|
|
17
|
+
`ui_help`, and `ui_format` were accepted by the schema but dropped before
|
|
18
|
+
emission. They now survive parser → derivation → emitted `FieldMeta`
|
|
19
|
+
(`group` / `visible` / `placeholder` / `help` / `format`). `format` was
|
|
20
|
+
previously only ever the hardcoded timestamp `{ dateFormat: 'relative' }`;
|
|
21
|
+
authored `ui_format` now passes through.
|
|
22
|
+
- **Key-field curation (qField parity).** New YAML keys `ui_key_field` +
|
|
23
|
+
`ui_key_field_order` surface as `isKeyField` / `keyFieldOrder` on the row
|
|
24
|
+
(the qField / EAV `field_definitions` names — one vocabulary, multiple
|
|
25
|
+
homes), and `<camel>Metadata` gains `keyFields`: the ordered curated
|
|
26
|
+
field-name list (sorted by `keyFieldOrder`, declaration order for ties) that
|
|
27
|
+
drives card/preview field selection.
|
|
28
|
+
- **Family/behavior common-field bundles** (timestamps precedent): the
|
|
29
|
+
`soft_delete` behavior now contributes a `deletedAt` row
|
|
30
|
+
(datetime / tertiary / relative); entities whose declared fields carry the
|
|
31
|
+
synced/integrated shape (both `external_id` AND `provider`) get
|
|
32
|
+
`group: 'external_sync'` defaulted onto those fields (plus
|
|
33
|
+
`provider_metadata` when present) — a derivation default only, authored
|
|
34
|
+
`ui_group` always wins. Behavior-contributed columns (not in the parsed
|
|
35
|
+
field map) still get no rows: the emitter never emits a row for a column it
|
|
36
|
+
cannot see.
|
|
37
|
+
- **EAV `data_type` → `FieldType` contract.** `EAV_DATA_TYPE_TO_FIELD_TYPE`
|
|
38
|
+
(`string→text, integer/decimal→number, boolean→boolean, date→date,
|
|
39
|
+
datetime→datetime, json→json, reference→reference, picklist→enum,
|
|
40
|
+
multipicklist→enum`; multi-select rendering is consumer-side) is exported
|
|
41
|
+
from the package root AND emitted into every generated
|
|
42
|
+
`fields/field-meta.ts`, rendered from the same source object so the copies
|
|
43
|
+
cannot drift. See ADR-040 for the convergence story
|
|
44
|
+
(qField/CatalogField ↔ codegen FieldMeta ↔ EAV `field_definitions`).
|
|
45
|
+
|
|
46
|
+
### Tests
|
|
47
|
+
|
|
48
|
+
- emit-fields suite: hint passthrough (incl. author-override-beats-default and
|
|
49
|
+
string escaping), key-field curation + `keyFields` ordering, soft_delete /
|
|
50
|
+
external-sync bundles, EAV contract completeness vs the emitted copy.
|
|
51
|
+
- Frontend golden fixtures now exercise the new surface; the snapshot locks
|
|
52
|
+
curation ordering (`first_name` before `email` despite declaration order),
|
|
53
|
+
the `deletedAt` bundle, and the `external_sync` group default.
|
|
54
|
+
|
|
7
55
|
## [0.20.2] — 2026-06-06
|
|
8
56
|
|
|
9
57
|
Two consumer-found fixes (dogfooding swe-brain on 0.20.1).
|
package/README.md
CHANGED
|
@@ -209,11 +209,15 @@ frontend:
|
|
|
209
209
|
columnMapperNeedsCall: true # call the mapper (fn()) vs reference (fn)
|
|
210
210
|
apiBaseUrlImport: null # when set, import API_BASE_URL from it as baseURL
|
|
211
211
|
apiUrl: /api # REST base path when no apiBaseUrlImport
|
|
212
|
+
fields:
|
|
213
|
+
textareaThreshold: 500 # string→textarea cutoff (strict >); null DISABLES
|
|
212
214
|
```
|
|
213
215
|
|
|
214
216
|
`null`-disables convention: an **absent** `auth.function` defaults to
|
|
215
217
|
`getAuthorizationHeader`; an **explicit `null`** disables it entirely (no header
|
|
216
|
-
lines emitted). Likewise `sync.columnMapper: null` omits the Electric mapper
|
|
218
|
+
lines emitted). Likewise `sync.columnMapper: null` omits the Electric mapper, and
|
|
219
|
+
`fields.textareaThreshold: null` disables the string→textarea heuristic entirely
|
|
220
|
+
(bounded strings always render as `text` unless the author sets `ui_type: textarea`).
|
|
217
221
|
|
|
218
222
|
### Per-entity sync mode (`entity.sync`)
|
|
219
223
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MemoryJobOrchestrator
|
|
3
|
+
} from "./chunk-VQOAATIG.js";
|
|
1
4
|
import {
|
|
2
5
|
DrizzleJobRunService
|
|
3
6
|
} from "./chunk-3VEVGL74.js";
|
|
@@ -7,6 +10,12 @@ import {
|
|
|
7
10
|
import {
|
|
8
11
|
DrizzleJobStepService
|
|
9
12
|
} from "./chunk-DV4RV2DC.js";
|
|
13
|
+
import {
|
|
14
|
+
MemoryJobStepService
|
|
15
|
+
} from "./chunk-PNZSGAB2.js";
|
|
16
|
+
import {
|
|
17
|
+
MemoryJobStore
|
|
18
|
+
} from "./chunk-SNQ3TOWP.js";
|
|
10
19
|
import {
|
|
11
20
|
BULLMQ_CONNECTION,
|
|
12
21
|
BULLMQ_RESOLVED_CONFIG,
|
|
@@ -15,15 +24,6 @@ import {
|
|
|
15
24
|
import {
|
|
16
25
|
DrizzleJobOrchestrator
|
|
17
26
|
} from "./chunk-E6PLM6QG.js";
|
|
18
|
-
import {
|
|
19
|
-
MemoryJobOrchestrator
|
|
20
|
-
} from "./chunk-VQOAATIG.js";
|
|
21
|
-
import {
|
|
22
|
-
MemoryJobStepService
|
|
23
|
-
} from "./chunk-PNZSGAB2.js";
|
|
24
|
-
import {
|
|
25
|
-
MemoryJobStore
|
|
26
|
-
} from "./chunk-SNQ3TOWP.js";
|
|
27
27
|
import {
|
|
28
28
|
JOBS_LISTEN_NOTIFY,
|
|
29
29
|
JOBS_MULTI_TENANT,
|
|
@@ -114,4 +114,4 @@ JobsDomainModule = __decorateClass([
|
|
|
114
114
|
export {
|
|
115
115
|
JobsDomainModule
|
|
116
116
|
};
|
|
117
|
-
//# sourceMappingURL=chunk-
|
|
117
|
+
//# sourceMappingURL=chunk-6DQEIXYU.js.map
|
|
@@ -120,7 +120,12 @@ var UiMetadataSchema = z.object({
|
|
|
120
120
|
ui_visible: z.boolean().optional(),
|
|
121
121
|
ui_placeholder: z.string().optional(),
|
|
122
122
|
ui_help: z.string().optional(),
|
|
123
|
-
ui_format: z.record(z.unknown()).optional()
|
|
123
|
+
ui_format: z.record(z.unknown()).optional(),
|
|
124
|
+
// Key-field curation (ADR-040): curated/displayed fields that drive card &
|
|
125
|
+
// preview surfaces. Names mirror qField / EAV `field_definitions`
|
|
126
|
+
// (`isKeyField` / `keyFieldOrder`) — one metadata vocabulary, multiple homes.
|
|
127
|
+
ui_key_field: z.boolean().optional(),
|
|
128
|
+
ui_key_field_order: z.number().optional()
|
|
124
129
|
});
|
|
125
130
|
var BaseFieldSchema = z.object({
|
|
126
131
|
type: FieldTypeSchema,
|
|
@@ -1371,6 +1376,22 @@ function detectYamlType(filePath) {
|
|
|
1371
1376
|
}
|
|
1372
1377
|
|
|
1373
1378
|
// src/parser/load-entities.ts
|
|
1379
|
+
function parseUiMetadata(fieldDef) {
|
|
1380
|
+
return {
|
|
1381
|
+
label: fieldDef.ui_label,
|
|
1382
|
+
type: fieldDef.ui_type,
|
|
1383
|
+
importance: fieldDef.ui_importance,
|
|
1384
|
+
group: fieldDef.ui_group,
|
|
1385
|
+
sortable: fieldDef.ui_sortable,
|
|
1386
|
+
filterable: fieldDef.ui_filterable,
|
|
1387
|
+
visible: fieldDef.ui_visible,
|
|
1388
|
+
placeholder: fieldDef.ui_placeholder,
|
|
1389
|
+
help: fieldDef.ui_help,
|
|
1390
|
+
format: fieldDef.ui_format,
|
|
1391
|
+
keyField: fieldDef.ui_key_field,
|
|
1392
|
+
keyFieldOrder: fieldDef.ui_key_field_order
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1374
1395
|
function transformToEntity(result) {
|
|
1375
1396
|
const { definition, filePath } = result;
|
|
1376
1397
|
const queries = definition.queries?.filter((q) => "by" in q).map((q) => ({
|
|
@@ -1413,15 +1434,7 @@ function transformToEntity(result) {
|
|
|
1413
1434
|
min: fieldDef.min,
|
|
1414
1435
|
max: fieldDef.max
|
|
1415
1436
|
},
|
|
1416
|
-
ui:
|
|
1417
|
-
label: fieldDef.ui_label,
|
|
1418
|
-
type: fieldDef.ui_type,
|
|
1419
|
-
importance: fieldDef.ui_importance,
|
|
1420
|
-
group: fieldDef.ui_group,
|
|
1421
|
-
sortable: fieldDef.ui_sortable,
|
|
1422
|
-
filterable: fieldDef.ui_filterable,
|
|
1423
|
-
visible: fieldDef.ui_visible
|
|
1424
|
-
}
|
|
1437
|
+
ui: parseUiMetadata(fieldDef)
|
|
1425
1438
|
};
|
|
1426
1439
|
entity.fields.set(name, field);
|
|
1427
1440
|
}
|
|
@@ -1614,15 +1627,7 @@ function transformToRelationshipDefinition(result) {
|
|
|
1614
1627
|
min: fieldDef.min,
|
|
1615
1628
|
max: fieldDef.max
|
|
1616
1629
|
},
|
|
1617
|
-
ui:
|
|
1618
|
-
label: fieldDef.ui_label,
|
|
1619
|
-
type: fieldDef.ui_type,
|
|
1620
|
-
importance: fieldDef.ui_importance,
|
|
1621
|
-
group: fieldDef.ui_group,
|
|
1622
|
-
sortable: fieldDef.ui_sortable,
|
|
1623
|
-
filterable: fieldDef.ui_filterable,
|
|
1624
|
-
visible: fieldDef.ui_visible
|
|
1625
|
-
}
|
|
1630
|
+
ui: parseUiMetadata(fieldDef)
|
|
1626
1631
|
};
|
|
1627
1632
|
fields.set(name, field);
|
|
1628
1633
|
}
|
|
@@ -4066,6 +4071,111 @@ registerLibraryPattern(KnowledgePattern);
|
|
|
4066
4071
|
registerLibraryPattern(MetadataPattern);
|
|
4067
4072
|
registerLibraryPattern(JunctionPattern);
|
|
4068
4073
|
|
|
4074
|
+
// src/emitters/frontend/field-meta.ts
|
|
4075
|
+
var DEFAULT_TEXTAREA_THRESHOLD = 500;
|
|
4076
|
+
var CAMEL = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
4077
|
+
function formatLabel(fieldName) {
|
|
4078
|
+
return fieldName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4079
|
+
}
|
|
4080
|
+
function inferUiType(field, opts = {}) {
|
|
4081
|
+
if (field.ui.type) return field.ui.type;
|
|
4082
|
+
if (Array.isArray(field.choices) && field.choices.length > 0) return "enum";
|
|
4083
|
+
if (field.foreignKey) return "reference";
|
|
4084
|
+
const nameLower = field.name.toLowerCase();
|
|
4085
|
+
if (nameLower.includes("email")) return "email";
|
|
4086
|
+
if (nameLower.includes("url") || nameLower.includes("website")) return "url";
|
|
4087
|
+
if (nameLower.includes("password")) return "password";
|
|
4088
|
+
if (nameLower.includes("price") || nameLower.includes("amount") || nameLower.includes("cost") || nameLower.includes("value") || nameLower.includes("revenue")) {
|
|
4089
|
+
return "money";
|
|
4090
|
+
}
|
|
4091
|
+
if (nameLower.includes("percent") || nameLower.includes("rate")) {
|
|
4092
|
+
return "percentage";
|
|
4093
|
+
}
|
|
4094
|
+
const threshold = opts.textareaThreshold === void 0 ? DEFAULT_TEXTAREA_THRESHOLD : opts.textareaThreshold;
|
|
4095
|
+
switch (field.type) {
|
|
4096
|
+
case "string":
|
|
4097
|
+
return threshold !== null && field.constraints.maxLength && field.constraints.maxLength > threshold ? "textarea" : "text";
|
|
4098
|
+
case "integer":
|
|
4099
|
+
case "decimal":
|
|
4100
|
+
return "number";
|
|
4101
|
+
case "boolean":
|
|
4102
|
+
return "boolean";
|
|
4103
|
+
case "uuid":
|
|
4104
|
+
return "text";
|
|
4105
|
+
case "date":
|
|
4106
|
+
return "date";
|
|
4107
|
+
case "datetime":
|
|
4108
|
+
return "datetime";
|
|
4109
|
+
case "json":
|
|
4110
|
+
return "json";
|
|
4111
|
+
default:
|
|
4112
|
+
return "text";
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
function inferUiImportance(field) {
|
|
4116
|
+
if (field.ui.importance) return field.ui.importance;
|
|
4117
|
+
const nameLower = field.name.toLowerCase();
|
|
4118
|
+
if (["id", "created_at", "updated_at", "deleted_at"].includes(nameLower)) {
|
|
4119
|
+
return "tertiary";
|
|
4120
|
+
}
|
|
4121
|
+
if (field.foreignKey && nameLower.endsWith("_id")) return "secondary";
|
|
4122
|
+
if (field.required) return "primary";
|
|
4123
|
+
if (nameLower.includes("name") || nameLower.includes("title")) return "primary";
|
|
4124
|
+
return "secondary";
|
|
4125
|
+
}
|
|
4126
|
+
function isEntityRefField(field) {
|
|
4127
|
+
if (field.type === "entity_ref") return true;
|
|
4128
|
+
return field.name.endsWith("_entity_type") || field.name.endsWith("_entity_id");
|
|
4129
|
+
}
|
|
4130
|
+
function deriveFieldMeta(field, defaults = {}, opts = {}) {
|
|
4131
|
+
const hasChoices = Array.isArray(field.choices) && field.choices.length > 0;
|
|
4132
|
+
const meta = {
|
|
4133
|
+
field: CAMEL(field.name),
|
|
4134
|
+
label: field.ui.label ?? formatLabel(field.name),
|
|
4135
|
+
type: inferUiType(field, opts),
|
|
4136
|
+
importance: inferUiImportance(field),
|
|
4137
|
+
sortable: field.ui.sortable ?? false,
|
|
4138
|
+
filterable: field.ui.filterable ?? false
|
|
4139
|
+
};
|
|
4140
|
+
const group = field.ui.group ?? defaults.group;
|
|
4141
|
+
if (group !== void 0) meta.group = group;
|
|
4142
|
+
if (field.ui.visible !== void 0) meta.visible = field.ui.visible;
|
|
4143
|
+
if (field.ui.placeholder !== void 0) meta.placeholder = field.ui.placeholder;
|
|
4144
|
+
if (field.ui.help !== void 0) meta.help = field.ui.help;
|
|
4145
|
+
if (field.ui.format !== void 0) meta.format = field.ui.format;
|
|
4146
|
+
if (hasChoices) meta.choices = field.choices;
|
|
4147
|
+
if (field.foreignKey) meta.reference = field.foreignKey.table;
|
|
4148
|
+
if (field.ui.keyField) {
|
|
4149
|
+
meta.isKeyField = true;
|
|
4150
|
+
if (field.ui.keyFieldOrder !== void 0) {
|
|
4151
|
+
meta.keyFieldOrder = field.ui.keyFieldOrder;
|
|
4152
|
+
}
|
|
4153
|
+
}
|
|
4154
|
+
return meta;
|
|
4155
|
+
}
|
|
4156
|
+
var EXTERNAL_SYNC_GROUP = "external_sync";
|
|
4157
|
+
var EXTERNAL_SYNC_FIELDS = /* @__PURE__ */ new Set([
|
|
4158
|
+
"external_id",
|
|
4159
|
+
"provider",
|
|
4160
|
+
"provider_metadata"
|
|
4161
|
+
]);
|
|
4162
|
+
function hasExternalSyncShape(fieldNames) {
|
|
4163
|
+
const names = new Set(fieldNames);
|
|
4164
|
+
return names.has("external_id") && names.has("provider");
|
|
4165
|
+
}
|
|
4166
|
+
var EAV_DATA_TYPE_TO_FIELD_TYPE = {
|
|
4167
|
+
string: "text",
|
|
4168
|
+
integer: "number",
|
|
4169
|
+
decimal: "number",
|
|
4170
|
+
boolean: "boolean",
|
|
4171
|
+
date: "date",
|
|
4172
|
+
datetime: "datetime",
|
|
4173
|
+
json: "json",
|
|
4174
|
+
reference: "reference",
|
|
4175
|
+
picklist: "enum",
|
|
4176
|
+
multipicklist: "enum"
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4069
4179
|
// src/index.ts
|
|
4070
4180
|
async function analyzeDomain(entitiesDir, relationshipsOrOptions) {
|
|
4071
4181
|
const opts = typeof relationshipsOrOptions === "string" ? { relationshipsDir: relationshipsOrOptions } : relationshipsOrOptions ?? {};
|
|
@@ -4186,7 +4296,13 @@ export {
|
|
|
4186
4296
|
KnowledgePattern,
|
|
4187
4297
|
MetadataPattern,
|
|
4188
4298
|
IntegratedPattern,
|
|
4299
|
+
isEntityRefField,
|
|
4300
|
+
deriveFieldMeta,
|
|
4301
|
+
EXTERNAL_SYNC_GROUP,
|
|
4302
|
+
EXTERNAL_SYNC_FIELDS,
|
|
4303
|
+
hasExternalSyncShape,
|
|
4304
|
+
EAV_DATA_TYPE_TO_FIELD_TYPE,
|
|
4189
4305
|
analyzeDomain,
|
|
4190
4306
|
validateEntities
|
|
4191
4307
|
};
|
|
4192
|
-
//# sourceMappingURL=chunk-
|
|
4308
|
+
//# sourceMappingURL=chunk-6ECCJVYW.js.map
|