@ghentcdh/crouton-api 0.0.1-alpha.37 → 0.0.1-alpha.39

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/index.cjs CHANGED
@@ -52,157 +52,6 @@ module.exports = __toCommonJS(index_exports);
52
52
  var getImportMetaUrl = /* @__PURE__ */ __name(() => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href, "getImportMetaUrl");
53
53
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
54
54
 
55
- // src/lib/crud/builder/column-predicates.ts
56
- var isBoolean = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "boolean" || col.columnType === "boolean", "isBoolean");
57
- var isRelation = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "relation", "isRelation");
58
- var isAutocomplete = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "autocomplete", "isAutocomplete");
59
- var isRecordCell = /* @__PURE__ */ __name((col) => isRelation(col) || isAutocomplete(col), "isRecordCell");
60
- var isDateRange = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "date-range", "isDateRange");
61
-
62
- // src/lib/crud/builder/column.utils.ts
63
- var colPosition = /* @__PURE__ */ __name((col, i) => col.fieldInput?.position ?? i, "colPosition");
64
- var sortByPosition = /* @__PURE__ */ __name((cols) => cols.map((col, i) => ({
65
- col,
66
- i
67
- })).sort((a, b) => colPosition(a.col, a.i) - colPosition(b.col, b.i)).map(({ col }) => col), "sortByPosition");
68
- var columnForContext = /* @__PURE__ */ __name((col, ctx) => {
69
- if (ctx === "view") return {
70
- ...col,
71
- fieldInput: col.fieldView ?? col.fieldInput
72
- };
73
- if (ctx === "table") return {
74
- ...col,
75
- fieldInput: col.fieldTable ?? col.fieldInput
76
- };
77
- return col;
78
- }, "columnForContext");
79
- var toViewColumn = /* @__PURE__ */ __name((col) => ({
80
- id: col.id,
81
- ...col.label && {
82
- label: col.label
83
- },
84
- ...col.sortable != null && {
85
- sortable: col.sortable
86
- },
87
- ...col.searchable != null && {
88
- searchable: col.searchable
89
- },
90
- ...col.fieldInput && {
91
- fieldInput: col.fieldInput
92
- }
93
- }), "toViewColumn");
94
-
95
- // src/lib/crud/builder/sort.helpers.ts
96
- var deriveSortId = /* @__PURE__ */ __name((col) => {
97
- if (col.sortable === false) return null;
98
- if (col.sortId) return col.sortId;
99
- const base = col.column ?? col.id;
100
- const fi = col.fieldInput;
101
- const opts = fi?.options ?? {};
102
- const isRelation2 = fi?.format === "relation" || !!fi?.relationType || fi?.type === "autocomplete" || typeof opts.resource === "string";
103
- if (isRelation2) {
104
- const key = (typeof col.displayKey === "string" ? col.displayKey : void 0) ?? (typeof opts.displayKey === "string" ? opts.displayKey : void 0) ?? (typeof opts.labelKey === "string" ? opts.labelKey : void 0);
105
- if (!key) return null;
106
- const path2 = key.includes(".") ? key : `${base}.${key}`;
107
- return path2.endsWith(".label") ? path2.slice(0, -".label".length) : path2;
108
- }
109
- if (!col.displayKey) return base;
110
- const path = `${base}.${col.displayKey}`;
111
- const isValueLabel = !!col.enum || opts.emitObject === true;
112
- if (isValueLabel && path.endsWith(".label")) return path.slice(0, -".label".length);
113
- return path;
114
- }, "deriveSortId");
115
- var resolveDefaultSort = /* @__PURE__ */ __name((tableCols, allColumns) => {
116
- const isSortable = /* @__PURE__ */ __name((c) => c.sortable !== false, "isSortable");
117
- const sortCol = tableCols.find((c) => c.defaultSort && isSortable(c)) ?? tableCols.find(isSortable) ?? allColumns?.find((c) => c.idField);
118
- return sortCol ? deriveSortId(sortCol) ?? sortCol.id : void 0;
119
- }, "resolveDefaultSort");
120
-
121
- // src/lib/crud/builder/schema-transforms.ts
122
- var allowAdditionalProperties = /* @__PURE__ */ __name((schema) => {
123
- if (schema["type"] === "object") {
124
- schema["additionalProperties"] = true;
125
- const props = schema["properties"];
126
- if (props) {
127
- for (const value of Object.values(props)) {
128
- allowAdditionalProperties(value);
129
- }
130
- }
131
- }
132
- if (schema["type"] === "array" && schema["items"]) {
133
- allowAdditionalProperties(schema["items"]);
134
- }
135
- }, "allowAdditionalProperties");
136
- var isNullableProperty = /* @__PURE__ */ __name((prop) => {
137
- const anyOf = prop?.["anyOf"];
138
- return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
139
- }, "isNullableProperty");
140
- var dropNullableFromRequired = /* @__PURE__ */ __name((schema) => {
141
- if (schema["type"] !== "object") return;
142
- const props = schema["properties"];
143
- if (!props) return;
144
- const required = schema["required"];
145
- if (Array.isArray(required)) {
146
- schema["required"] = required.filter((key) => !isNullableProperty(props[key]));
147
- }
148
- for (const value of Object.values(props)) {
149
- dropNullableFromRequired(value);
150
- }
151
- }, "dropNullableFromRequired");
152
- var enforceRequiredMinLength = /* @__PURE__ */ __name((schema) => {
153
- if (schema["type"] !== "object") return;
154
- const props = schema["properties"];
155
- if (!props) return;
156
- for (const prop of Object.values(props)) {
157
- if (prop?.["type"] === "string" && !("minLength" in prop)) {
158
- prop["minLength"] = 1;
159
- } else if (prop?.["type"] === "object") {
160
- enforceRequiredMinLength(prop);
161
- }
162
- }
163
- }, "enforceRequiredMinLength");
164
- var applySchemaTransforms = /* @__PURE__ */ __name((schema) => {
165
- allowAdditionalProperties(schema);
166
- dropNullableFromRequired(schema);
167
- enforceRequiredMinLength(schema);
168
- }, "applySchemaTransforms");
169
-
170
- // src/lib/crud/builder/schema.helpers.ts
171
- var pickByColumns = /* @__PURE__ */ __name((schema, columns, filter) => {
172
- if (!schema) return void 0;
173
- if (!columns?.length) return schema;
174
- const baseFilter = /* @__PURE__ */ __name((c) => !isRelation(c) && (filter ? filter(c) : true), "baseFilter");
175
- const schemaKeys = new Set(Object.keys(schema.shape));
176
- const filtered = columns.filter((c) => baseFilter(c) && schemaKeys.has(c.id));
177
- if (!filtered.length) return void 0;
178
- const mask = Object.fromEntries(filtered.map((c) => [
179
- c.id,
180
- true
181
- ]));
182
- return schema.pick(mask);
183
- }, "pickByColumns");
184
- var opWithSchema = /* @__PURE__ */ __name((enabled, schema) => {
185
- if (enabled === false) return void 0;
186
- return schema ? {
187
- schema
188
- } : true;
189
- }, "opWithSchema");
190
- var upsertOp = /* @__PURE__ */ __name((entry, schema) => {
191
- if (!entry) return void 0;
192
- if (entry === true) {
193
- throw new Error("`operations.upsert` must be an object with `upsertOn`, not `true`.");
194
- }
195
- if (typeof entry === "object") {
196
- return {
197
- upsertOn: entry.upsertOn,
198
- ...schema && {
199
- schema
200
- }
201
- };
202
- }
203
- return void 0;
204
- }, "upsertOp");
205
-
206
55
  // ../crouton-core/src/lib/request.model.ts
207
56
  var import_zod2 = require("zod");
208
57
 
@@ -334,10 +183,10 @@ var ElementBuilder = class extends Builder {
334
183
  return this;
335
184
  }
336
185
  /** Merge a patch into the typed options bag. */
337
- opt(patch2) {
186
+ opt(patch) {
338
187
  this.options = {
339
188
  ...this.options,
340
- ...patch2
189
+ ...patch
341
190
  };
342
191
  return this;
343
192
  }
@@ -835,8 +684,18 @@ var FieldInputSchema = import_zod9.z.object({
835
684
  relationType: RelationType.optional(),
836
685
  /** FK field on the child model pointing back to the parent, e.g. `"workId"`. Defaults to `${parentModel}Id`. */
837
686
  foreignKey: import_zod9.z.string().optional(),
687
+ /** Override the Prisma relation field name when it differs from the column id. */
688
+ relation: import_zod9.z.string().optional(),
838
689
  /** Override the display order in form views. Lower values come first. */
839
690
  position: import_zod9.z.number().optional(),
691
+ /**
692
+ * Value to pre-fill this field with on the create form. Injected into the
693
+ * generated form view's JSON Schema as `properties[id].default`, which
694
+ * `parseValue({})` (see `@ghentcdh/crouton-vue`'s `form-def.ts`) applies when
695
+ * opening a blank record. Not applicable to `format: "relation"` fields —
696
+ * relation columns are excluded from the picked data schema entirely.
697
+ */
698
+ defaultValue: import_zod9.z.unknown().optional(),
840
699
  options: import_zod9.z.union([
841
700
  RelationFieldInputOptionsSchema,
842
701
  import_zod9.z.unknown()
@@ -1284,7 +1143,122 @@ var CONFIG_FILES = [
1284
1143
  "crouton.js"
1285
1144
  ];
1286
1145
 
1287
- // src/lib/crud/builder/table-schema.builder.ts
1146
+ // ../crouton-core/src/lib/view/column-predicates.ts
1147
+ var isBoolean = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "boolean" || col.columnType === "boolean", "isBoolean");
1148
+ var isRelation = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "relation", "isRelation");
1149
+ var isAutocomplete = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "autocomplete", "isAutocomplete");
1150
+ var isRecordCell = /* @__PURE__ */ __name((col) => isRelation(col) || isAutocomplete(col), "isRecordCell");
1151
+ var isDateRange = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "date-range", "isDateRange");
1152
+
1153
+ // ../crouton-core/src/lib/view/column.utils.ts
1154
+ var colPosition = /* @__PURE__ */ __name((col, i) => col.fieldInput?.position ?? i, "colPosition");
1155
+ var sortByPosition = /* @__PURE__ */ __name((cols) => cols.map((col, i) => ({
1156
+ col,
1157
+ i
1158
+ })).sort((a, b) => colPosition(a.col, a.i) - colPosition(b.col, b.i)).map(({ col }) => col), "sortByPosition");
1159
+ var columnForContext = /* @__PURE__ */ __name((col, ctx) => {
1160
+ if (ctx === "view") return {
1161
+ ...col,
1162
+ fieldInput: col.fieldView ?? col.fieldInput
1163
+ };
1164
+ if (ctx === "table") return {
1165
+ ...col,
1166
+ fieldInput: col.fieldTable ?? col.fieldInput
1167
+ };
1168
+ return col;
1169
+ }, "columnForContext");
1170
+ var toViewColumn = /* @__PURE__ */ __name((col) => ({
1171
+ id: col.id,
1172
+ ...col.label && {
1173
+ label: col.label
1174
+ },
1175
+ ...col.sortable != null && {
1176
+ sortable: col.sortable
1177
+ },
1178
+ ...col.searchable != null && {
1179
+ searchable: col.searchable
1180
+ },
1181
+ ...col.fieldInput && {
1182
+ fieldInput: col.fieldInput
1183
+ }
1184
+ }), "toViewColumn");
1185
+
1186
+ // ../crouton-core/src/lib/view/sort.helpers.ts
1187
+ var deriveSortId = /* @__PURE__ */ __name((col) => {
1188
+ if (col.sortable === false) return null;
1189
+ if (col.sortId) return col.sortId;
1190
+ const base = col.column ?? col.id;
1191
+ const fi = col.fieldInput;
1192
+ const opts = fi?.options ?? {};
1193
+ const isRelation2 = fi?.format === "relation" || !!fi?.relationType || fi?.type === "autocomplete" || typeof opts.resource === "string";
1194
+ if (isRelation2) {
1195
+ const key = (typeof col.displayKey === "string" ? col.displayKey : void 0) ?? (typeof opts.displayKey === "string" ? opts.displayKey : void 0) ?? (typeof opts.labelKey === "string" ? opts.labelKey : void 0);
1196
+ if (!key) return null;
1197
+ const path2 = key.includes(".") ? key : `${base}.${key}`;
1198
+ return path2.endsWith(".label") ? path2.slice(0, -".label".length) : path2;
1199
+ }
1200
+ if (!col.displayKey) return base;
1201
+ const path = `${base}.${col.displayKey}`;
1202
+ const isValueLabel = !!col.enum || opts.emitObject === true;
1203
+ if (isValueLabel && path.endsWith(".label")) return path.slice(0, -".label".length);
1204
+ return path;
1205
+ }, "deriveSortId");
1206
+ var resolveDefaultSort = /* @__PURE__ */ __name((tableCols, allColumns) => {
1207
+ const isSortable = /* @__PURE__ */ __name((c) => c.sortable !== false, "isSortable");
1208
+ const sortCol = tableCols.find((c) => c.defaultSort && isSortable(c)) ?? tableCols.find(isSortable) ?? allColumns?.find((c) => c.idField);
1209
+ return sortCol ? deriveSortId(sortCol) ?? sortCol.id : void 0;
1210
+ }, "resolveDefaultSort");
1211
+
1212
+ // ../crouton-core/src/lib/view/schema-transforms.ts
1213
+ var allowAdditionalProperties = /* @__PURE__ */ __name((schema) => {
1214
+ if (schema["type"] === "object") {
1215
+ schema["additionalProperties"] = true;
1216
+ const props = schema["properties"];
1217
+ if (props) {
1218
+ for (const value of Object.values(props)) {
1219
+ allowAdditionalProperties(value);
1220
+ }
1221
+ }
1222
+ }
1223
+ if (schema["type"] === "array" && schema["items"]) {
1224
+ allowAdditionalProperties(schema["items"]);
1225
+ }
1226
+ }, "allowAdditionalProperties");
1227
+ var isNullableProperty = /* @__PURE__ */ __name((prop) => {
1228
+ const anyOf = prop?.["anyOf"];
1229
+ return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
1230
+ }, "isNullableProperty");
1231
+ var dropNullableFromRequired = /* @__PURE__ */ __name((schema) => {
1232
+ if (schema["type"] !== "object") return;
1233
+ const props = schema["properties"];
1234
+ if (!props) return;
1235
+ const required = schema["required"];
1236
+ if (Array.isArray(required)) {
1237
+ schema["required"] = required.filter((key) => !isNullableProperty(props[key]));
1238
+ }
1239
+ for (const value of Object.values(props)) {
1240
+ dropNullableFromRequired(value);
1241
+ }
1242
+ }, "dropNullableFromRequired");
1243
+ var enforceRequiredMinLength = /* @__PURE__ */ __name((schema) => {
1244
+ if (schema["type"] !== "object") return;
1245
+ const props = schema["properties"];
1246
+ if (!props) return;
1247
+ for (const prop of Object.values(props)) {
1248
+ if (prop?.["type"] === "string" && !("minLength" in prop)) {
1249
+ prop["minLength"] = 1;
1250
+ } else if (prop?.["type"] === "object") {
1251
+ enforceRequiredMinLength(prop);
1252
+ }
1253
+ }
1254
+ }, "enforceRequiredMinLength");
1255
+ var applySchemaTransforms = /* @__PURE__ */ __name((schema) => {
1256
+ allowAdditionalProperties(schema);
1257
+ dropNullableFromRequired(schema);
1258
+ enforceRequiredMinLength(schema);
1259
+ }, "applySchemaTransforms");
1260
+
1261
+ // ../crouton-core/src/lib/view/table-schema.builder.ts
1288
1262
  var SHARED_CELL_OPTION_KEYS = [
1289
1263
  "values",
1290
1264
  "storeValue",
@@ -1353,7 +1327,7 @@ var buildTableUiSchema = /* @__PURE__ */ __name((cols) => {
1353
1327
  return layout;
1354
1328
  }, "buildTableUiSchema");
1355
1329
 
1356
- // src/lib/crud/builder/form-schema.builder.ts
1330
+ // ../crouton-core/src/lib/view/form-schema.builder.ts
1357
1331
  var buildConditionSchema = /* @__PURE__ */ __name((when) => {
1358
1332
  if (when.notExists) return {
1359
1333
  not: {
@@ -1447,7 +1421,7 @@ var buildFormUiSchema = /* @__PURE__ */ __name((cols) => {
1447
1421
  return layout.build();
1448
1422
  }, "buildFormUiSchema");
1449
1423
 
1450
- // src/lib/crud/builder/calculated-columns.builder.ts
1424
+ // ../crouton-core/src/lib/view/calculated-columns.builder.ts
1451
1425
  var isVisibleInMode = /* @__PURE__ */ __name((c, mode) => mode === "table" ? !c.hiddenInTable : !c.hiddenInView, "isVisibleInMode");
1452
1426
  var buildCalculatedElement = /* @__PURE__ */ __name((c, mode) => mode === "table" ? {
1453
1427
  type: c.type === "boolean" ? "BooleanCell" : "TextCell",
@@ -1505,111 +1479,45 @@ var injectCalculatedColumnsIntoView = /* @__PURE__ */ __name((viewConfig, calcul
1505
1479
  const columns = [
1506
1480
  ...viewConfig.columns
1507
1481
  ];
1508
- for (const c of visible) {
1509
- const pos = calcPosition(c);
1510
- const entry = {
1511
- id: c.id,
1512
- label: c.label
1513
- };
1514
- if (pos !== void 0) {
1515
- columns.splice(pos, 0, entry);
1516
- } else {
1517
- columns.push(entry);
1518
- }
1519
- }
1520
- return {
1521
- ...viewConfig,
1522
- json_schema: jsonSchema,
1523
- ui_schema: uiSchema,
1524
- columns
1525
- };
1526
- }, "injectCalculatedColumnsIntoView");
1527
- var injectCalculatedColumns = /* @__PURE__ */ __name((tableView, calculated) => injectCalculatedColumnsIntoView(tableView, calculated, "table"), "injectCalculatedColumns");
1528
- var injectCalculatedColumnsToView = /* @__PURE__ */ __name((viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view"), "injectCalculatedColumnsToView");
1529
-
1530
- // src/lib/crud/builder/view.builder.ts
1531
- var import_zod18 = require("zod");
1532
-
1533
- // src/lib/crud/schema.utils.ts
1534
- var import_zod17 = require("zod");
1535
- var dateOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
1536
- if (zodSchema instanceof import_zod17.z.ZodDate) {
1537
- jsonSchema.type = "string";
1538
- jsonSchema.format = "date-time";
1539
- }
1540
- }, "dateOverride");
1541
- var jsonSchemaOpts = {
1542
- unrepresentable: "any",
1543
- override: dateOverride
1544
- };
1545
- function isZodSchema(schema) {
1546
- return schema instanceof import_zod17.ZodObject;
1547
- }
1548
- __name(isZodSchema, "isZodSchema");
1549
- var isNullableProperty2 = /* @__PURE__ */ __name((property) => {
1550
- const anyOf = property?.["anyOf"];
1551
- return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
1552
- }, "isNullableProperty");
1553
- var dropNullableFromRequired2 = /* @__PURE__ */ __name((jsonSchema) => {
1554
- const { properties, required } = jsonSchema;
1555
- if (!properties || !Array.isArray(required)) return;
1556
- jsonSchema.required = required.filter((key) => !isNullableProperty2(properties[key]));
1557
- }, "dropNullableFromRequired");
1558
- function toJsonSchema(schema) {
1559
- if (isZodSchema(schema)) {
1560
- const jsonSchema = (0, import_zod17.toJSONSchema)(schema, {
1561
- target: "openApi3",
1562
- ...jsonSchemaOpts
1563
- });
1564
- dropNullableFromRequired2(jsonSchema);
1565
- return jsonSchema;
1566
- }
1567
- return schema;
1568
- }
1569
- __name(toJsonSchema, "toJsonSchema");
1570
- var unwrap = /* @__PURE__ */ __name((schema) => {
1571
- let s = schema;
1572
- let t = s?._zod?.def?.type;
1573
- while (t === "optional" || t === "nullable" || t === "default" || t === "readonly") {
1574
- s = s._zod.def.innerType;
1575
- t = s?._zod?.def?.type;
1576
- }
1577
- return s;
1578
- }, "unwrap");
1579
- var typeOf = /* @__PURE__ */ __name((schema) => schema?._zod?.def?.type, "typeOf");
1580
- function toSelectFields(schema) {
1581
- if (!isZodSchema(schema)) {
1582
- return Object.keys(schema.properties).reduce((acc, key) => {
1583
- acc[key] = true;
1584
- return acc;
1585
- }, {});
1586
- }
1587
- const result = {};
1588
- for (const [key, value] of Object.entries(schema.shape)) {
1589
- const inner = unwrap(value);
1590
- const t = typeOf(inner);
1591
- if (t === "object") {
1592
- result[key] = {
1593
- select: toSelectFields(inner)
1594
- };
1595
- continue;
1596
- }
1597
- if (t === "array") {
1598
- const element = unwrap(inner._zod.def.element);
1599
- if (typeOf(element) === "object") {
1600
- result[key] = {
1601
- select: toSelectFields(element)
1602
- };
1603
- continue;
1604
- }
1482
+ for (const c of visible) {
1483
+ const pos = calcPosition(c);
1484
+ const entry = {
1485
+ id: c.id,
1486
+ label: c.label
1487
+ };
1488
+ if (pos !== void 0) {
1489
+ columns.splice(pos, 0, entry);
1490
+ } else {
1491
+ columns.push(entry);
1605
1492
  }
1606
- result[key] = true;
1607
1493
  }
1608
- return result;
1609
- }
1610
- __name(toSelectFields, "toSelectFields");
1494
+ return {
1495
+ ...viewConfig,
1496
+ json_schema: jsonSchema,
1497
+ ui_schema: uiSchema,
1498
+ columns
1499
+ };
1500
+ }, "injectCalculatedColumnsIntoView");
1501
+ var injectCalculatedColumns = /* @__PURE__ */ __name((tableView, calculated) => injectCalculatedColumnsIntoView(tableView, calculated, "table"), "injectCalculatedColumns");
1502
+ var injectCalculatedColumnsToView = /* @__PURE__ */ __name((viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view"), "injectCalculatedColumnsToView");
1503
+
1504
+ // ../crouton-core/src/lib/view/view.builder.ts
1505
+ var import_zod18 = require("zod");
1506
+
1507
+ // ../crouton-core/src/lib/view/json-schema.opts.ts
1508
+ var import_zod17 = require("zod");
1509
+ var dateOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
1510
+ if (zodSchema instanceof import_zod17.z.ZodDate) {
1511
+ jsonSchema.type = "string";
1512
+ jsonSchema.format = "date-time";
1513
+ }
1514
+ }, "dateOverride");
1515
+ var jsonSchemaOpts = {
1516
+ unrepresentable: "any",
1517
+ override: dateOverride
1518
+ };
1611
1519
 
1612
- // src/lib/crud/builder/view.builder.ts
1520
+ // ../crouton-core/src/lib/view/view.builder.ts
1613
1521
  var patchFilterProperties = /* @__PURE__ */ __name((jsonSchema, columns) => {
1614
1522
  if (!columns?.length) return;
1615
1523
  const properties = jsonSchema.properties;
@@ -1650,6 +1558,15 @@ var patchFilterProperties = /* @__PURE__ */ __name((jsonSchema, columns) => {
1650
1558
  }
1651
1559
  }
1652
1560
  }, "patchFilterProperties");
1561
+ var injectFieldDefaults = /* @__PURE__ */ __name((jsonSchema, columns) => {
1562
+ const properties = jsonSchema.properties;
1563
+ if (!properties) return;
1564
+ for (const col of columns) {
1565
+ if (col.fieldInput?.defaultValue === void 0) continue;
1566
+ const prop = properties[col.id];
1567
+ if (prop) prop.default = col.fieldInput.defaultValue;
1568
+ }
1569
+ }, "injectFieldDefaults");
1653
1570
  var buildView = /* @__PURE__ */ __name((schema, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
1654
1571
  if (!schema || !columns?.length) return void 0;
1655
1572
  const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
@@ -1668,6 +1585,7 @@ var buildView = /* @__PURE__ */ __name((schema, columns, visible, buildUiSchema,
1668
1585
  ...jsonSchemaOpts
1669
1586
  });
1670
1587
  applySchemaTransforms(jsonSchema);
1588
+ injectFieldDefaults(jsonSchema, visibleCols);
1671
1589
  if (schemaVisible) {
1672
1590
  const nonEditableIds = new Set(schemaCols.filter((c) => c.idField || c.createable === false && c.updateable === false).map((c) => c.id));
1673
1591
  const required = jsonSchema["required"];
@@ -1715,7 +1633,7 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
1715
1633
  const buildJsonSchema = /* @__PURE__ */ __name((cols) => {
1716
1634
  const properties = {};
1717
1635
  for (const c of cols) {
1718
- if (c.column) {
1636
+ if (c.column && c.column !== c.id) {
1719
1637
  if (!properties[c.column]) {
1720
1638
  properties[c.column] = {
1721
1639
  type: "object",
@@ -1745,7 +1663,10 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
1745
1663
  ...isObject ? {} : {
1746
1664
  type: c.columnType ?? "string"
1747
1665
  },
1748
- title: c.label ?? c.id
1666
+ title: c.label ?? c.id,
1667
+ ...c.fieldInput?.defaultValue !== void 0 ? {
1668
+ default: c.fieldInput.defaultValue
1669
+ } : {}
1749
1670
  };
1750
1671
  }
1751
1672
  }
@@ -1768,7 +1689,7 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
1768
1689
  elements: elements.map((el) => {
1769
1690
  const id = el.scope?.replace("#/properties/", "");
1770
1691
  const col = id ? colMap[id] : void 0;
1771
- if (!col?.column) return el;
1692
+ if (!col?.column || col.column === col.id) return el;
1772
1693
  const keyPath = (col.displayKey ?? col.id).split(".");
1773
1694
  const propPath = keyPath.map((k) => `properties/${k}`).join("/");
1774
1695
  return {
@@ -1804,26 +1725,42 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
1804
1725
  return Object.keys(views).length ? views : void 0;
1805
1726
  }, "buildViewsFromColumns");
1806
1727
 
1728
+ // ../crouton-core/src/lib/view/view.schema.ts
1729
+ var import_zod19 = require("zod");
1730
+ var ViewColumnConfigSchema = import_zod19.z.object({
1731
+ id: import_zod19.z.string(),
1732
+ label: import_zod19.z.string().optional(),
1733
+ sortable: import_zod19.z.boolean().optional(),
1734
+ searchable: import_zod19.z.boolean().optional(),
1735
+ fieldInput: FieldInputSchema.optional()
1736
+ });
1737
+ var ViewConfigSchema = import_zod19.z.object({
1738
+ json_schema: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.unknown()),
1739
+ ui_schema: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.unknown()),
1740
+ columns: import_zod19.z.array(ViewColumnConfigSchema),
1741
+ defaultSort: import_zod19.z.string().optional()
1742
+ });
1743
+
1807
1744
  // src/lib/crouton-api.module.ts
1808
- var import_common14 = require("@nestjs/common");
1745
+ var import_common19 = require("@nestjs/common");
1809
1746
  var import_core = require("@nestjs/core");
1810
1747
 
1811
1748
  // src/lib/crud/app-layout/app-layout.types.ts
1812
- var import_zod19 = require("zod");
1813
- var SidebarLeafSchema = import_zod19.z.object({
1814
- kind: import_zod19.z.literal("item").default("item"),
1815
- id: import_zod19.z.string(),
1816
- label: import_zod19.z.string(),
1817
- position: import_zod19.z.number().optional()
1749
+ var import_zod20 = require("zod");
1750
+ var SidebarLeafSchema = import_zod20.z.object({
1751
+ kind: import_zod20.z.literal("item").default("item"),
1752
+ id: import_zod20.z.string(),
1753
+ label: import_zod20.z.string(),
1754
+ position: import_zod20.z.number().optional()
1818
1755
  });
1819
- var SidebarGroupSchema2 = import_zod19.z.object({
1820
- kind: import_zod19.z.literal("group").default("group"),
1821
- id: import_zod19.z.string(),
1822
- label: import_zod19.z.string(),
1823
- position: import_zod19.z.number().optional(),
1824
- children: import_zod19.z.array(SidebarLeafSchema).default([])
1756
+ var SidebarGroupSchema2 = import_zod20.z.object({
1757
+ kind: import_zod20.z.literal("group").default("group"),
1758
+ id: import_zod20.z.string(),
1759
+ label: import_zod20.z.string(),
1760
+ position: import_zod20.z.number().optional(),
1761
+ children: import_zod20.z.array(SidebarLeafSchema).default([])
1825
1762
  });
1826
- var SidebarNodeSchema = import_zod19.z.discriminatedUnion("kind", [
1763
+ var SidebarNodeSchema = import_zod20.z.discriminatedUnion("kind", [
1827
1764
  SidebarLeafSchema,
1828
1765
  SidebarGroupSchema2
1829
1766
  ]);
@@ -2089,51 +2026,51 @@ CroutonValidationExceptionFilter = _ts_decorate3([
2089
2026
  ], CroutonValidationExceptionFilter);
2090
2027
 
2091
2028
  // src/lib/crud/crud-controller.factory.ts
2092
- var import_common11 = require("@nestjs/common");
2093
- var import_swagger6 = require("@nestjs/swagger");
2029
+ var import_common16 = require("@nestjs/common");
2030
+ var import_swagger11 = require("@nestjs/swagger");
2094
2031
 
2095
2032
  // src/lib/crud/action/action.types.ts
2096
- var import_zod20 = require("zod");
2097
- var ActionMetadataSchema = import_zod20.z.object({
2033
+ var import_zod21 = require("zod");
2034
+ var ActionMetadataSchema = import_zod21.z.object({
2098
2035
  /** URL segment used in the endpoint. */
2099
- id: import_zod20.z.string(),
2036
+ id: import_zod21.z.string(),
2100
2037
  /** Human-readable label shown as a button. */
2101
- label: import_zod20.z.string().optional(),
2038
+ label: import_zod21.z.string().optional(),
2102
2039
  /** MDI icon name, e.g. `"mdi:open-in-new"`. */
2103
- icon: import_zod20.z.string().optional(),
2040
+ icon: import_zod21.z.string().optional(),
2104
2041
  /** Tooltip text. Falls back to `label` when omitted. */
2105
- tooltip: import_zod20.z.string().optional(),
2042
+ tooltip: import_zod21.z.string().optional(),
2106
2043
  /** Per-row condition — button is hidden when false. */
2107
- condition: import_zod20.z.custom().optional()
2044
+ condition: import_zod21.z.custom().optional()
2108
2045
  });
2109
2046
  var ResourceLinkActionSchema = ActionMetadataSchema.extend({
2110
- type: import_zod20.z.literal("link"),
2047
+ type: import_zod21.z.literal("link"),
2111
2048
  /** URL to open. May contain `{id}` or `{env.VAR}` placeholders. */
2112
- href: import_zod20.z.string()
2049
+ href: import_zod21.z.string()
2113
2050
  });
2114
2051
  var ResourceRowProcedureActionSchema = ActionMetadataSchema.extend({
2115
- type: import_zod20.z.literal("procedure").optional(),
2052
+ type: import_zod21.z.literal("procedure").optional(),
2116
2053
  /** HTTP method for the endpoint. Defaults to `"post"`. */
2117
- method: import_zod20.z.string().optional(),
2054
+ method: import_zod21.z.string().optional(),
2118
2055
  /** Static data payload merged into the request body by the frontend. */
2119
- data: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.unknown()).optional(),
2056
+ data: import_zod21.z.record(import_zod21.z.string(), import_zod21.z.unknown()).optional(),
2120
2057
  /** Procedure called with `(prisma, recordId)`. */
2121
- procedure: import_zod20.z.custom((v) => typeof v === "function")
2058
+ procedure: import_zod21.z.custom((v) => typeof v === "function")
2122
2059
  });
2123
2060
  var ResourceTableProcedureActionSchema = ActionMetadataSchema.extend({
2124
- type: import_zod20.z.literal("procedure").optional(),
2061
+ type: import_zod21.z.literal("procedure").optional(),
2125
2062
  /** HTTP method for the endpoint. Defaults to `"post"`. */
2126
- method: import_zod20.z.string().optional(),
2063
+ method: import_zod21.z.string().optional(),
2127
2064
  /** Static data payload merged into the request body by the frontend. */
2128
- data: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.unknown()).optional(),
2065
+ data: import_zod21.z.record(import_zod21.z.string(), import_zod21.z.unknown()).optional(),
2129
2066
  /** Procedure called with `(prisma)` — no record id. */
2130
- procedure: import_zod20.z.custom((v) => typeof v === "function")
2067
+ procedure: import_zod21.z.custom((v) => typeof v === "function")
2131
2068
  });
2132
- var ResourceRowActionSchema = import_zod20.z.union([
2069
+ var ResourceRowActionSchema = import_zod21.z.union([
2133
2070
  ResourceRowProcedureActionSchema,
2134
2071
  ResourceLinkActionSchema
2135
2072
  ]);
2136
- var ResourceTableActionSchema = import_zod20.z.union([
2073
+ var ResourceTableActionSchema = import_zod21.z.union([
2137
2074
  ResourceTableProcedureActionSchema,
2138
2075
  ResourceLinkActionSchema
2139
2076
  ]);
@@ -2325,6 +2262,23 @@ var buildChildSortClause = /* @__PURE__ */ __name((sort, sortDir) => {
2325
2262
  [part]: acc
2326
2263
  }, {});
2327
2264
  }, "buildChildSortClause");
2265
+ var buildFindOneIncludes = /* @__PURE__ */ __name((subResources, configInclude) => {
2266
+ const autoSubs = subResources.filter((s) => s.includeInFindOne);
2267
+ const flatIncludes = autoSubs.length ? Object.fromEntries(autoSubs.map((s) => s.findOneOrderBy ? [
2268
+ s.relation,
2269
+ {
2270
+ orderBy: s.findOneOrderBy
2271
+ }
2272
+ ] : [
2273
+ s.relation,
2274
+ true
2275
+ ])) : void 0;
2276
+ if (!flatIncludes && !configInclude) return void 0;
2277
+ return {
2278
+ ...flatIncludes,
2279
+ ...configInclude
2280
+ };
2281
+ }, "buildFindOneIncludes");
2328
2282
 
2329
2283
  // src/lib/crud/read.repository.ts
2330
2284
  var parseFilterString = /* @__PURE__ */ __name((raw) => {
@@ -2587,7 +2541,6 @@ var ReadRepository = class {
2587
2541
  * @throws {NotFoundException} When no record exists for the given id.
2588
2542
  */
2589
2543
  async findOne(id) {
2590
- const formIncludes = (this.config.subResources ?? []).filter((s) => s.includeInFindOne).map((s) => s.relation);
2591
2544
  const projection = this.projection("findOne");
2592
2545
  const idField = this.config.idField ?? "id";
2593
2546
  const query = {
@@ -2596,15 +2549,7 @@ var ReadRepository = class {
2596
2549
  },
2597
2550
  ...projection
2598
2551
  };
2599
- const flatIncludes = formIncludes.length ? Object.fromEntries(formIncludes.map((r) => [
2600
- r,
2601
- true
2602
- ])) : void 0;
2603
- const configInclude = buildIncludeClause(this.config.include);
2604
- const mergedInclude = flatIncludes || configInclude ? {
2605
- ...flatIncludes,
2606
- ...configInclude
2607
- } : void 0;
2552
+ const mergedInclude = buildFindOneIncludes(this.config.subResources ?? [], buildIncludeClause(this.config.include));
2608
2553
  if (mergedInclude) {
2609
2554
  if (projection.select) {
2610
2555
  query.select = {
@@ -2708,6 +2653,75 @@ var ReadRepository = class {
2708
2653
  }
2709
2654
  };
2710
2655
 
2656
+ // src/lib/crud/schema.utils.ts
2657
+ var import_zod22 = require("zod");
2658
+ function isZodSchema(schema) {
2659
+ return schema instanceof import_zod22.ZodObject;
2660
+ }
2661
+ __name(isZodSchema, "isZodSchema");
2662
+ var isNullableProperty2 = /* @__PURE__ */ __name((property) => {
2663
+ const anyOf = property?.["anyOf"];
2664
+ return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
2665
+ }, "isNullableProperty");
2666
+ var dropNullableFromRequired2 = /* @__PURE__ */ __name((jsonSchema) => {
2667
+ const { properties, required } = jsonSchema;
2668
+ if (!properties || !Array.isArray(required)) return;
2669
+ jsonSchema.required = required.filter((key) => !isNullableProperty2(properties[key]));
2670
+ }, "dropNullableFromRequired");
2671
+ function toJsonSchema(schema) {
2672
+ if (isZodSchema(schema)) {
2673
+ const jsonSchema = (0, import_zod22.toJSONSchema)(schema, {
2674
+ target: "openApi3",
2675
+ ...jsonSchemaOpts
2676
+ });
2677
+ dropNullableFromRequired2(jsonSchema);
2678
+ return jsonSchema;
2679
+ }
2680
+ return schema;
2681
+ }
2682
+ __name(toJsonSchema, "toJsonSchema");
2683
+ var unwrap = /* @__PURE__ */ __name((schema) => {
2684
+ let s = schema;
2685
+ let t = s?._zod?.def?.type;
2686
+ while (t === "optional" || t === "nullable" || t === "default" || t === "readonly") {
2687
+ s = s._zod.def.innerType;
2688
+ t = s?._zod?.def?.type;
2689
+ }
2690
+ return s;
2691
+ }, "unwrap");
2692
+ var typeOf = /* @__PURE__ */ __name((schema) => schema?._zod?.def?.type, "typeOf");
2693
+ function toSelectFields(schema) {
2694
+ if (!isZodSchema(schema)) {
2695
+ return Object.keys(schema.properties).reduce((acc, key) => {
2696
+ acc[key] = true;
2697
+ return acc;
2698
+ }, {});
2699
+ }
2700
+ const result = {};
2701
+ for (const [key, value] of Object.entries(schema.shape)) {
2702
+ const inner = unwrap(value);
2703
+ const t = typeOf(inner);
2704
+ if (t === "object") {
2705
+ result[key] = {
2706
+ select: toSelectFields(inner)
2707
+ };
2708
+ continue;
2709
+ }
2710
+ if (t === "array") {
2711
+ const element = unwrap(inner._zod.def.element);
2712
+ if (typeOf(element) === "object") {
2713
+ result[key] = {
2714
+ select: toSelectFields(element)
2715
+ };
2716
+ continue;
2717
+ }
2718
+ }
2719
+ result[key] = true;
2720
+ }
2721
+ return result;
2722
+ }
2723
+ __name(toSelectFields, "toSelectFields");
2724
+
2711
2725
  // src/lib/crud/write.repository.ts
2712
2726
  var import_common5 = require("@nestjs/common");
2713
2727
 
@@ -3202,9 +3216,125 @@ var registerTableActionRoutes = /* @__PURE__ */ __name((ctx) => {
3202
3216
  }
3203
3217
  }, "registerTableActionRoutes");
3204
3218
 
3205
- // src/lib/crud/operations/register-crud.ts
3219
+ // src/lib/crud/operations/register-create.ts
3206
3220
  var import_common8 = require("@nestjs/common");
3207
3221
  var import_swagger3 = require("@nestjs/swagger");
3222
+ var defaultCreate = /* @__PURE__ */ __name((ctx) => {
3223
+ if (!isOperationEnabled(ctx.definition, "create")) return null;
3224
+ const { cls, config, createSchema, bodyDecorator } = ctx;
3225
+ const methodName = "create";
3226
+ return {
3227
+ route: "",
3228
+ methodName,
3229
+ name: config.name,
3230
+ createFn: /* @__PURE__ */ __name(function(body) {
3231
+ return this.repo.create(body);
3232
+ }, "createFn"),
3233
+ decorators: /* @__PURE__ */ __name(() => {
3234
+ bodyDecorator(createSchema, {
3235
+ coerceNullableUndefinedToNull: true
3236
+ })(cls.prototype, methodName, 0);
3237
+ }, "decorators")
3238
+ };
3239
+ }, "defaultCreate");
3240
+ var childCreate = /* @__PURE__ */ __name((sub) => (ctx) => {
3241
+ if (!isOperationEnabled(sub.operations, "create")) return null;
3242
+ const { cls } = ctx;
3243
+ const methodName = `createChild_${sub.childRoute}`;
3244
+ const createFn = /* @__PURE__ */ __name(async function(id, body) {
3245
+ return this.repo.createChild(id, sub, body);
3246
+ }, "createFn");
3247
+ const decorators = /* @__PURE__ */ __name(() => {
3248
+ (0, import_common8.Param)("id")(cls.prototype, methodName, 0);
3249
+ (0, import_common8.Body)()(cls.prototype, methodName, 1);
3250
+ }, "decorators");
3251
+ return {
3252
+ route: `:id/${sub.childRoute}`,
3253
+ methodName,
3254
+ name: sub.childRoute,
3255
+ createFn,
3256
+ decorators
3257
+ };
3258
+ }, "childCreate");
3259
+ var registerCreate = /* @__PURE__ */ __name((ctx, sub) => {
3260
+ const operationFn = sub ? childCreate(sub) : defaultCreate;
3261
+ const properties = operationFn(ctx);
3262
+ if (!properties) return;
3263
+ const { methodName, route, name } = properties;
3264
+ const { cls } = ctx;
3265
+ def(cls, methodName, properties.createFn);
3266
+ const d = desc(cls, methodName);
3267
+ (0, import_common8.Post)(route)(cls.prototype, methodName, d);
3268
+ (0, import_swagger3.ApiOperation)({
3269
+ summary: `Create a ${name}`
3270
+ })(cls.prototype, methodName, d);
3271
+ (0, import_swagger3.ApiResponse)({
3272
+ status: 201,
3273
+ description: `${name} created`
3274
+ })(cls.prototype, methodName, d);
3275
+ properties.decorators();
3276
+ }, "registerCreate");
3277
+
3278
+ // src/lib/crud/operations/register-delete.ts
3279
+ var import_common9 = require("@nestjs/common");
3280
+ var import_swagger4 = require("@nestjs/swagger");
3281
+ var defaultDelete = /* @__PURE__ */ __name((ctx) => {
3282
+ if (!isOperationEnabled(ctx.definition, "delete")) return null;
3283
+ const { config } = ctx;
3284
+ const methodName = "delete";
3285
+ const { name } = config;
3286
+ return {
3287
+ route: ":id",
3288
+ methodName,
3289
+ name,
3290
+ decorators: /* @__PURE__ */ __name(() => {
3291
+ }, "decorators"),
3292
+ deleteFn: /* @__PURE__ */ __name(function(id) {
3293
+ return this.repo.delete(id);
3294
+ }, "deleteFn")
3295
+ };
3296
+ }, "defaultDelete");
3297
+ var deleteChild = /* @__PURE__ */ __name((sub) => (ctx) => {
3298
+ if (!isOperationEnabled(sub.operations, "delete")) return null;
3299
+ const { cls } = ctx;
3300
+ const methodName = `deleteChild_${sub.childRoute}`;
3301
+ const deleteFn = /* @__PURE__ */ __name(async function(childId, parentId) {
3302
+ return this.repo.deleteChild(sub, childId, parentId);
3303
+ }, "deleteFn");
3304
+ const decorators = /* @__PURE__ */ __name(() => {
3305
+ (0, import_common9.Param)("childId")(cls.prototype, methodName, 0);
3306
+ }, "decorators");
3307
+ return {
3308
+ route: `:id/${sub.childRoute}/:childId`,
3309
+ methodName,
3310
+ name: sub.childRoute,
3311
+ deleteFn,
3312
+ decorators
3313
+ };
3314
+ }, "deleteChild");
3315
+ var registerDelete = /* @__PURE__ */ __name((ctx, sub) => {
3316
+ const operationFn = sub ? deleteChild(sub) : defaultDelete;
3317
+ const { cls } = ctx;
3318
+ const properties = operationFn(ctx);
3319
+ if (!properties) return;
3320
+ const { methodName, route, name } = properties;
3321
+ def(cls, methodName, properties.deleteFn);
3322
+ const d = desc(cls, methodName);
3323
+ (0, import_common9.Delete)(route)(cls.prototype, methodName, d);
3324
+ (0, import_common9.Param)("id")(cls.prototype, methodName, 0);
3325
+ (0, import_swagger4.ApiOperation)({
3326
+ summary: `Delete ${name} record`
3327
+ })(cls.prototype, methodName, d);
3328
+ (0, import_swagger4.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
3329
+ (0, import_swagger4.ApiResponse)({
3330
+ status: 200
3331
+ })(cls.prototype, methodName, d);
3332
+ properties.decorators();
3333
+ }, "registerDelete");
3334
+
3335
+ // src/lib/crud/operations/register-findall.ts
3336
+ var import_common10 = require("@nestjs/common");
3337
+ var import_swagger5 = require("@nestjs/swagger");
3208
3338
 
3209
3339
  // src/lib/crud/request.dto.ts
3210
3340
  var import_zod_nestjs = require("@anatine/zod-nestjs");
@@ -3285,8 +3415,8 @@ var ZodValidationPipe = class {
3285
3415
  }
3286
3416
  };
3287
3417
 
3288
- // src/lib/crud/operations/register-crud.ts
3289
- var findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
3418
+ // src/lib/crud/operations/register-findall.ts
3419
+ var _findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
3290
3420
  const effectiveParams = {
3291
3421
  ...params
3292
3422
  };
@@ -3313,41 +3443,74 @@ var findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
3313
3443
  filter: params.filter
3314
3444
  }
3315
3445
  };
3316
- }, "findAll");
3317
- var findOne = /* @__PURE__ */ __name(async (repo, id) => {
3318
- return repo.findOne(id);
3319
- }, "findOne");
3320
- var create = /* @__PURE__ */ __name(async (repo, body) => {
3321
- return repo.create(body);
3322
- }, "create");
3323
- var update = /* @__PURE__ */ __name(async (repo, id, body) => {
3324
- return repo.update(id, body);
3325
- }, "update");
3326
- var patch = /* @__PURE__ */ __name(async (repo, id, body) => {
3327
- return repo.patch(id, body);
3328
- }, "patch");
3329
- var upsert = /* @__PURE__ */ __name(async (repo, body) => {
3330
- return repo.upsert(body);
3331
- }, "upsert");
3332
- var del = /* @__PURE__ */ __name(async (repo, id) => {
3333
- return repo.delete(id);
3334
- }, "del");
3335
- var registerFindAll = /* @__PURE__ */ __name((ctx) => {
3446
+ }, "_findAll");
3447
+ var findAllByParent = /* @__PURE__ */ __name(async (repo, id, childRoute, params) => {
3448
+ const { data, count } = await repo.findAllByParent(id, childRoute, params);
3449
+ const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
3450
+ return {
3451
+ data,
3452
+ request: {
3453
+ count,
3454
+ page: params.page,
3455
+ pageSize: params.pageSize,
3456
+ totalPages,
3457
+ sort: params.sort,
3458
+ sortDir: params.sortDir,
3459
+ filter: params.filter
3460
+ }
3461
+ };
3462
+ }, "findAllByParent");
3463
+ var defaultFindAll = /* @__PURE__ */ __name((ctx) => {
3336
3464
  if (!isOperationEnabled(ctx.definition, "findAll")) return;
3337
- const { cls, config, listSchema } = ctx;
3338
- const { name } = config;
3465
+ const { config } = ctx;
3339
3466
  const lookupLabel = config.lookup?.label;
3340
- def(cls, "findAll", async function(params, q) {
3341
- return findAll(this.repo, params, q, lookupLabel);
3342
- });
3343
- const d = desc(cls, "findAll");
3344
- (0, import_common8.Get)()(cls.prototype, "findAll", d);
3345
- (0, import_common8.Query)(new ZodValidationPipe(RequestDtoNoOffset.zodSchema))(cls.prototype, "findAll", 0);
3346
- (0, import_common8.Query)("q")(cls.prototype, "findAll", 1);
3347
- (0, import_swagger3.ApiOperation)({
3467
+ const findAll = /* @__PURE__ */ __name(async function(params, q) {
3468
+ return _findAll(this.repo, params, q, lookupLabel);
3469
+ }, "findAll");
3470
+ return {
3471
+ route: "",
3472
+ name: config.name,
3473
+ methodName: "findAll",
3474
+ findAll,
3475
+ listSchema: ctx.listSchema,
3476
+ decorators: /* @__PURE__ */ __name(() => {
3477
+ }, "decorators")
3478
+ };
3479
+ }, "defaultFindAll");
3480
+ var childFindAll = /* @__PURE__ */ __name((sub) => (ctx) => {
3481
+ if (!isOperationEnabled(sub.operations, "findAll")) return;
3482
+ const { cls } = ctx;
3483
+ const methodName = `findAllBy_${sub.childRoute}`;
3484
+ const findAll = /* @__PURE__ */ __name(async function(params, q, id) {
3485
+ return findAllByParent(this.repo, id, sub.childRoute, params);
3486
+ }, "findAll");
3487
+ const decorators = /* @__PURE__ */ __name(() => {
3488
+ (0, import_common10.Param)("id")(cls.prototype, methodName, 2);
3489
+ }, "decorators");
3490
+ return {
3491
+ name: sub.childRoute,
3492
+ methodName,
3493
+ findAll,
3494
+ route: `:id/${sub.childRoute}`,
3495
+ decorators,
3496
+ listSchema: ctx.listSchema
3497
+ };
3498
+ }, "childFindAll");
3499
+ var registerFindAll = /* @__PURE__ */ __name((ctx, sub) => {
3500
+ const operationFn = sub ? childFindAll(sub) : defaultFindAll;
3501
+ const properties = operationFn(ctx);
3502
+ if (!properties) return;
3503
+ const { methodName, route, name, listSchema } = properties;
3504
+ const { cls } = ctx;
3505
+ def(cls, methodName, properties.findAll);
3506
+ const d = desc(cls, methodName);
3507
+ (0, import_common10.Get)(route)(cls.prototype, methodName, d);
3508
+ (0, import_common10.Query)(new ZodValidationPipe(RequestDtoNoOffset.zodSchema))(cls.prototype, methodName, 0);
3509
+ (0, import_common10.Query)("q")(cls.prototype, methodName, 1);
3510
+ (0, import_swagger5.ApiOperation)({
3348
3511
  summary: `List all ${name}s`
3349
- })(cls.prototype, "findAll", d);
3350
- (0, import_swagger3.ApiResponse)({
3512
+ })(cls.prototype, methodName, d);
3513
+ (0, import_swagger5.ApiResponse)({
3351
3514
  status: 200,
3352
3515
  description: `Array of ${name}`,
3353
3516
  ...listSchema && {
@@ -3356,179 +3519,161 @@ var registerFindAll = /* @__PURE__ */ __name((ctx) => {
3356
3519
  items: toJsonSchema(listSchema)
3357
3520
  }
3358
3521
  }
3359
- })(cls.prototype, "findAll", d);
3522
+ })(cls.prototype, methodName, d);
3523
+ properties.decorators();
3360
3524
  }, "registerFindAll");
3361
- var registerFindOne = /* @__PURE__ */ __name((ctx) => {
3362
- if (!isOperationEnabled(ctx.definition, "findOne")) return;
3363
- const { cls, config, oneSchema, idParamMeta } = ctx;
3364
- const { name } = config;
3365
- def(cls, "findOne", function(id) {
3366
- return findOne(this.repo, id);
3367
- });
3368
- const d = desc(cls, "findOne");
3369
- (0, import_common8.Get)(":id")(cls.prototype, "findOne", d);
3370
- (0, import_common8.Param)("id")(cls.prototype, "findOne", 0);
3371
- (0, import_swagger3.ApiOperation)({
3525
+
3526
+ // src/lib/crud/operations/register-findone.ts
3527
+ var import_common11 = require("@nestjs/common");
3528
+ var import_swagger6 = require("@nestjs/swagger");
3529
+ var defaultFindOne = /* @__PURE__ */ __name((ctx) => {
3530
+ if (!isOperationEnabled(ctx.definition, "findOne")) return null;
3531
+ const { cls, config } = ctx;
3532
+ const methodName = "findOne";
3533
+ return {
3534
+ route: ":id",
3535
+ methodName,
3536
+ name: config.name,
3537
+ findOneFn: /* @__PURE__ */ __name(function(id) {
3538
+ return this.repo.findOne(id);
3539
+ }, "findOneFn"),
3540
+ decorators: /* @__PURE__ */ __name(() => {
3541
+ (0, import_common11.Param)("id")(cls.prototype, methodName, 0);
3542
+ }, "decorators")
3543
+ };
3544
+ }, "defaultFindOne");
3545
+ var childFindOne = /* @__PURE__ */ __name((sub) => (ctx) => {
3546
+ if (!isOperationEnabled(sub.operations, "findOne")) return null;
3547
+ const { cls } = ctx;
3548
+ const methodName = `findOneChild_${sub.childRoute}`;
3549
+ const findOneFn = /* @__PURE__ */ __name(async function(parentId, childId) {
3550
+ return this.repo.findOneChild(sub, childId, parentId);
3551
+ }, "findOneFn");
3552
+ const decorators = /* @__PURE__ */ __name(() => {
3553
+ (0, import_common11.Param)("id")(cls.prototype, methodName, 0);
3554
+ (0, import_common11.Param)("childId")(cls.prototype, methodName, 1);
3555
+ }, "decorators");
3556
+ return {
3557
+ route: `:id/${sub.childRoute}/:childId`,
3558
+ methodName,
3559
+ name: sub.childRoute,
3560
+ findOneFn,
3561
+ decorators
3562
+ };
3563
+ }, "childFindOne");
3564
+ var registerFindOne = /* @__PURE__ */ __name((ctx, sub) => {
3565
+ const operationFn = sub ? childFindOne(sub) : defaultFindOne;
3566
+ const properties = operationFn(ctx);
3567
+ if (!properties) return;
3568
+ const { methodName, route, name } = properties;
3569
+ const { cls } = ctx;
3570
+ def(cls, methodName, properties.findOneFn);
3571
+ const d = desc(cls, methodName);
3572
+ (0, import_common11.Get)(route)(cls.prototype, methodName, d);
3573
+ (0, import_swagger6.ApiOperation)({
3372
3574
  summary: `Get one ${name} by id`
3373
- })(cls.prototype, "findOne", d);
3374
- (0, import_swagger3.ApiParam)(idParamMeta)(cls.prototype, "findOne", d);
3375
- (0, import_swagger3.ApiResponse)({
3376
- status: 200,
3377
- description: `The ${name}`,
3378
- ...oneSchema && {
3379
- schema: toJsonSchema(oneSchema)
3380
- }
3381
- })(cls.prototype, "findOne", d);
3382
- (0, import_swagger3.ApiNotFoundResponse)({
3383
- description: "Not found"
3384
- })(cls.prototype, "findOne", d);
3385
- }, "registerFindOne");
3386
- var registerCreate = /* @__PURE__ */ __name((ctx) => {
3387
- if (!isOperationEnabled(ctx.definition, "create")) return;
3388
- const { cls, config, createSchema, bodyDecorator } = ctx;
3389
- const { name } = config;
3390
- def(cls, "create", function(body) {
3391
- return create(this.repo, body);
3392
- });
3393
- const d = desc(cls, "create");
3394
- (0, import_common8.Post)()(cls.prototype, "create", d);
3395
- bodyDecorator(createSchema, {
3396
- coerceNullableUndefinedToNull: true
3397
- })(cls.prototype, "create", 0);
3398
- (0, import_swagger3.ApiOperation)({
3399
- summary: `Create a ${name}`
3400
- })(cls.prototype, "create", d);
3401
- if (createSchema) (0, import_swagger3.ApiBody)({
3402
- schema: toJsonSchema(createSchema)
3403
- })(cls.prototype, "create", d);
3404
- (0, import_swagger3.ApiResponse)({
3405
- status: 201,
3406
- description: `${name} created`
3407
- })(cls.prototype, "create", d);
3408
- }, "registerCreate");
3409
- var registerUpdate = /* @__PURE__ */ __name((ctx) => {
3410
- if (!isOperationEnabled(ctx.definition, "update")) return;
3411
- const { cls, config, updateSchema, idParamMeta, bodyDecorator } = ctx;
3412
- const { name } = config;
3413
- def(cls, "update", function(id, body) {
3414
- return update(this.repo, id, body);
3415
- });
3416
- const d = desc(cls, "update");
3417
- (0, import_common8.Put)(":id")(cls.prototype, "update", d);
3418
- (0, import_common8.Param)("id")(cls.prototype, "update", 0);
3419
- bodyDecorator(updateSchema)(cls.prototype, "update", 1);
3420
- (0, import_swagger3.ApiOperation)({
3421
- summary: `Replace a ${name}`
3422
- })(cls.prototype, "update", d);
3423
- (0, import_swagger3.ApiParam)(idParamMeta)(cls.prototype, "update", d);
3424
- if (updateSchema) (0, import_swagger3.ApiBody)({
3425
- schema: toJsonSchema(updateSchema)
3426
- })(cls.prototype, "update", d);
3427
- (0, import_swagger3.ApiResponse)({
3575
+ })(cls.prototype, methodName, d);
3576
+ (0, import_swagger6.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
3577
+ (0, import_swagger6.ApiResponse)({
3428
3578
  status: 200,
3429
- description: `${name} replaced`
3430
- })(cls.prototype, "update", d);
3431
- (0, import_swagger3.ApiNotFoundResponse)({
3579
+ description: `The ${name}`,
3580
+ ...ctx.oneSchema && {
3581
+ schema: toJsonSchema(ctx.oneSchema)
3582
+ }
3583
+ })(cls.prototype, methodName, d);
3584
+ (0, import_swagger6.ApiNotFoundResponse)({
3432
3585
  description: "Not found"
3433
- })(cls.prototype, "update", d);
3434
- }, "registerUpdate");
3435
- var registerPatch = /* @__PURE__ */ __name((ctx) => {
3436
- if (!isOperationEnabled(ctx.definition, "patch")) return;
3437
- const { cls, config, patchSchema, idParamMeta, bodyDecorator } = ctx;
3438
- const { name } = config;
3439
- def(cls, "patch", function(id, body) {
3440
- return patch(this.repo, id, body);
3441
- });
3442
- const d = desc(cls, "patch");
3443
- (0, import_common8.Patch)(":id")(cls.prototype, "patch", d);
3444
- (0, import_common8.Param)("id")(cls.prototype, "patch", 0);
3445
- bodyDecorator(patchSchema)(cls.prototype, "patch", 1);
3446
- (0, import_swagger3.ApiOperation)({
3586
+ })(cls.prototype, methodName, d);
3587
+ properties.decorators();
3588
+ }, "registerFindOne");
3589
+
3590
+ // src/lib/crud/operations/register-patch.ts
3591
+ var import_common12 = require("@nestjs/common");
3592
+ var import_swagger7 = require("@nestjs/swagger");
3593
+ var defaultPatch = /* @__PURE__ */ __name((ctx) => {
3594
+ if (!isOperationEnabled(ctx.definition, "patch")) return null;
3595
+ const { cls, config, patchSchema, bodyDecorator } = ctx;
3596
+ const methodName = "patch";
3597
+ return {
3598
+ route: ":id",
3599
+ methodName,
3600
+ name: config.name,
3601
+ patchFn: /* @__PURE__ */ __name(function(id, body) {
3602
+ return this.repo.patch(id, body);
3603
+ }, "patchFn"),
3604
+ decorators: /* @__PURE__ */ __name(() => {
3605
+ (0, import_common12.Param)("id")(cls.prototype, methodName, 0);
3606
+ bodyDecorator(patchSchema)(cls.prototype, methodName, 1);
3607
+ }, "decorators")
3608
+ };
3609
+ }, "defaultPatch");
3610
+ var childPatch = /* @__PURE__ */ __name((sub) => (ctx) => {
3611
+ if (!isOperationEnabled(sub.operations, "patch")) return null;
3612
+ const { cls } = ctx;
3613
+ const methodName = `patchChild_${sub.childRoute}`;
3614
+ const patchFn = /* @__PURE__ */ __name(async function(_id, childId, body) {
3615
+ return this.repo.updateChild(sub, childId, body);
3616
+ }, "patchFn");
3617
+ const decorators = /* @__PURE__ */ __name(() => {
3618
+ (0, import_common12.Param)("id")(cls.prototype, methodName, 0);
3619
+ (0, import_common12.Param)("childId")(cls.prototype, methodName, 1);
3620
+ (0, import_common12.Body)()(cls.prototype, methodName, 2);
3621
+ }, "decorators");
3622
+ return {
3623
+ route: `:id/${sub.childRoute}/:childId`,
3624
+ methodName,
3625
+ name: sub.childRoute,
3626
+ patchFn,
3627
+ decorators
3628
+ };
3629
+ }, "childPatch");
3630
+ var registerPatch = /* @__PURE__ */ __name((ctx, sub) => {
3631
+ const operationFn = sub ? childPatch(sub) : defaultPatch;
3632
+ const properties = operationFn(ctx);
3633
+ if (!properties) return;
3634
+ const { methodName, route, name } = properties;
3635
+ const { cls } = ctx;
3636
+ def(cls, methodName, properties.patchFn);
3637
+ const d = desc(cls, methodName);
3638
+ (0, import_common12.Patch)(route)(cls.prototype, methodName, d);
3639
+ (0, import_swagger7.ApiOperation)({
3447
3640
  summary: `Update a ${name}`
3448
- })(cls.prototype, "patch", d);
3449
- (0, import_swagger3.ApiParam)(idParamMeta)(cls.prototype, "patch", d);
3450
- if (patchSchema) (0, import_swagger3.ApiBody)({
3451
- schema: toJsonSchema(patchSchema)
3452
- })(cls.prototype, "patch", d);
3453
- (0, import_swagger3.ApiResponse)({
3641
+ })(cls.prototype, methodName, d);
3642
+ (0, import_swagger7.ApiResponse)({
3454
3643
  status: 200,
3455
3644
  description: `${name} updated`
3456
- })(cls.prototype, "patch", d);
3457
- (0, import_swagger3.ApiNotFoundResponse)({
3645
+ })(cls.prototype, methodName, d);
3646
+ (0, import_swagger7.ApiNotFoundResponse)({
3458
3647
  description: "Not found"
3459
- })(cls.prototype, "patch", d);
3648
+ })(cls.prototype, methodName, d);
3649
+ properties.decorators();
3460
3650
  }, "registerPatch");
3461
- var registerUpsert = /* @__PURE__ */ __name((ctx) => {
3462
- if (!isOperationEnabled(ctx.definition, "upsert")) return;
3463
- const { cls, config, upsertSchema, bodyDecorator } = ctx;
3464
- const { name } = config;
3465
- def(cls, "upsert", function(body) {
3466
- return upsert(this.repo, body);
3467
- });
3468
- const d = desc(cls, "upsert");
3469
- (0, import_common8.Put)()(cls.prototype, "upsert", d);
3470
- bodyDecorator(upsertSchema, {
3471
- coerceNullableUndefinedToNull: true
3472
- })(cls.prototype, "upsert", 0);
3473
- (0, import_swagger3.ApiOperation)({
3474
- summary: `Upsert a ${name}`
3475
- })(cls.prototype, "upsert", d);
3476
- if (upsertSchema) (0, import_swagger3.ApiBody)({
3477
- schema: toJsonSchema(upsertSchema)
3478
- })(cls.prototype, "upsert", d);
3479
- (0, import_swagger3.ApiResponse)({
3480
- status: 200,
3481
- description: `${name} upserted`
3482
- })(cls.prototype, "upsert", d);
3483
- }, "registerUpsert");
3484
- var registerDelete = /* @__PURE__ */ __name((ctx) => {
3485
- if (!isOperationEnabled(ctx.definition, "delete")) return;
3486
- const { cls, config, idParamMeta } = ctx;
3487
- const { name } = config;
3488
- def(cls, "delete", function(id) {
3489
- return del(this.repo, id);
3490
- });
3491
- const d = desc(cls, "delete");
3492
- (0, import_common8.Delete)(":id")(cls.prototype, "delete", d);
3493
- (0, import_common8.Param)("id")(cls.prototype, "delete", 0);
3494
- (0, import_swagger3.ApiOperation)({
3495
- summary: `Delete a ${name}`
3496
- })(cls.prototype, "delete", d);
3497
- (0, import_swagger3.ApiParam)(idParamMeta)(cls.prototype, "delete", d);
3498
- (0, import_swagger3.ApiResponse)({
3499
- status: 200,
3500
- description: `${name} deleted`
3501
- })(cls.prototype, "delete", d);
3502
- (0, import_swagger3.ApiNotFoundResponse)({
3503
- description: "Not found"
3504
- })(cls.prototype, "delete", d);
3505
- }, "registerDelete");
3506
3651
 
3507
3652
  // src/lib/crud/operations/register-schema-endpoints.ts
3508
- var import_common9 = require("@nestjs/common");
3509
- var import_swagger4 = require("@nestjs/swagger");
3653
+ var import_common13 = require("@nestjs/common");
3654
+ var import_swagger8 = require("@nestjs/swagger");
3510
3655
 
3511
3656
  // src/lib/crud/resource/PatchResourceJson.schema.ts
3512
- var import_zod21 = require("zod");
3513
- var FieldVariantPatchSchema = import_zod21.z.object({
3514
- type: import_zod21.z.string().nullable().optional(),
3515
- format: import_zod21.z.string().nullable().optional(),
3516
- resource: import_zod21.z.string().nullable().optional(),
3517
- position: import_zod21.z.number().nullable().optional(),
3518
- options: import_zod21.z.record(import_zod21.z.string(), import_zod21.z.unknown().nullable()).optional()
3657
+ var import_zod23 = require("zod");
3658
+ var FieldVariantPatchSchema = import_zod23.z.object({
3659
+ type: import_zod23.z.string().nullable().optional(),
3660
+ format: import_zod23.z.string().nullable().optional(),
3661
+ resource: import_zod23.z.string().nullable().optional(),
3662
+ position: import_zod23.z.number().nullable().optional(),
3663
+ options: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown().nullable()).optional()
3519
3664
  }).partial();
3520
- var PatchColumnSchema = import_zod21.z.object({
3521
- label: import_zod21.z.string().optional(),
3522
- column: import_zod21.z.string().optional(),
3523
- hiddenInTable: import_zod21.z.boolean().optional(),
3524
- hiddenInForm: import_zod21.z.boolean().optional(),
3525
- hiddenInView: import_zod21.z.boolean().optional(),
3665
+ var PatchColumnSchema = import_zod23.z.object({
3666
+ label: import_zod23.z.string().optional(),
3667
+ column: import_zod23.z.string().optional(),
3668
+ hiddenInTable: import_zod23.z.boolean().optional(),
3669
+ hiddenInForm: import_zod23.z.boolean().optional(),
3670
+ hiddenInView: import_zod23.z.boolean().optional(),
3526
3671
  fieldInput: FieldVariantPatchSchema.optional(),
3527
3672
  fieldView: FieldVariantPatchSchema.optional(),
3528
3673
  fieldTable: FieldVariantPatchSchema.optional()
3529
3674
  }).partial();
3530
- var PatchResourceJsonSchema = import_zod21.z.object({
3531
- columns: import_zod21.z.record(import_zod21.z.string(), PatchColumnSchema)
3675
+ var PatchResourceJsonSchema = import_zod23.z.object({
3676
+ columns: import_zod23.z.record(import_zod23.z.string(), PatchColumnSchema)
3532
3677
  });
3533
3678
 
3534
3679
  // src/lib/crud/resource/WriteResourceJson.ts
@@ -3542,8 +3687,8 @@ var FIELD_VARIANT_KEYS = [
3542
3687
  "fieldView",
3543
3688
  "fieldTable"
3544
3689
  ];
3545
- var mergeColumn = /* @__PURE__ */ __name((existing, patch2) => {
3546
- const { fieldInput, fieldView, fieldTable, ...rest } = patch2;
3690
+ var mergeColumn = /* @__PURE__ */ __name((existing, patch) => {
3691
+ const { fieldInput, fieldView, fieldTable, ...rest } = patch;
3547
3692
  const variantPatches = {
3548
3693
  fieldInput,
3549
3694
  fieldView,
@@ -3560,11 +3705,11 @@ var mergeColumn = /* @__PURE__ */ __name((existing, patch2) => {
3560
3705
  }
3561
3706
  return merged;
3562
3707
  }, "mergeColumn");
3563
- var applyColumnPatch = /* @__PURE__ */ __name((raw, patch2) => {
3708
+ var applyColumnPatch = /* @__PURE__ */ __name((raw, patch) => {
3564
3709
  const columns = raw["columns"];
3565
3710
  if (Array.isArray(columns)) {
3566
3711
  const updated = columns.map((col) => {
3567
- const columnPatch = patch2.columns[col["id"]];
3712
+ const columnPatch = patch.columns[col["id"]];
3568
3713
  return columnPatch ? mergeColumn(col, columnPatch) : col;
3569
3714
  });
3570
3715
  return {
@@ -3576,7 +3721,7 @@ var applyColumnPatch = /* @__PURE__ */ __name((raw, patch2) => {
3576
3721
  const updated = {
3577
3722
  ...columns
3578
3723
  };
3579
- for (const [id, columnPatch] of Object.entries(patch2.columns)) {
3724
+ for (const [id, columnPatch] of Object.entries(patch.columns)) {
3580
3725
  if (!(id in updated)) continue;
3581
3726
  updated[id] = mergeColumn(updated[id], columnPatch);
3582
3727
  }
@@ -3795,6 +3940,35 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
3795
3940
  tableActions: resolveActions(baseAction, config.tableActions)
3796
3941
  };
3797
3942
  }, "buildViewsPayload");
3943
+ var buildSubResourceViewsPayload = /* @__PURE__ */ __name((config, sub, baseUrl) => {
3944
+ if (!sub.views) return void 0;
3945
+ const { route } = config;
3946
+ const childUri = `${baseUrl}/${route}/{parent.id}/${sub.childRoute}`;
3947
+ return {
3948
+ id: `${route}/${sub.childRoute}`,
3949
+ name: sub.name ?? sub.childRoute,
3950
+ route: sub.childRoute,
3951
+ uri: childUri,
3952
+ title: sub.title ?? sub.childRoute,
3953
+ idField: sub.idField ?? "id",
3954
+ idType: sub.idType ?? "string",
3955
+ ...sub.modalSize && {
3956
+ modalSize: sub.modalSize
3957
+ },
3958
+ operations: buildSubResourceOperations(sub.operations, childUri, sub.idField ?? "id"),
3959
+ schemas: Object.fromEntries(Object.entries(sub.views).map(([key, v]) => [
3960
+ key,
3961
+ {
3962
+ data: v.json_schema,
3963
+ ui: v.ui_schema,
3964
+ ...v.defaultSort !== void 0 && {
3965
+ defaultSort: v.defaultSort
3966
+ }
3967
+ }
3968
+ ])),
3969
+ actions: resolveActions(`${baseUrl}/${sub.childRoute}`, sub.actions)
3970
+ };
3971
+ }, "buildSubResourceViewsPayload");
3798
3972
  var buildEditableColumnsPayload = /* @__PURE__ */ __name((config) => {
3799
3973
  const columns = config.columns ?? [];
3800
3974
  return {
@@ -3834,36 +4008,15 @@ var registerDefinitionEndpoint = /* @__PURE__ */ __name((ctx) => {
3834
4008
  return definitionPayload;
3835
4009
  });
3836
4010
  const d = desc(cls, "getDefinition");
3837
- (0, import_common9.Get)("definition")(cls.prototype, "getDefinition", d);
3838
- (0, import_swagger4.ApiOperation)({
4011
+ (0, import_common13.Get)("definition")(cls.prototype, "getDefinition", d);
4012
+ (0, import_swagger8.ApiOperation)({
3839
4013
  summary: `Get the resource definition for ${name}`
3840
4014
  })(cls.prototype, "getDefinition", d);
3841
- (0, import_swagger4.ApiResponse)({
4015
+ (0, import_swagger8.ApiResponse)({
3842
4016
  status: 200,
3843
4017
  description: `Definition (operations + schemas) for ${name}`
3844
4018
  })(cls.prototype, "getDefinition", d);
3845
4019
  }, "registerDefinitionEndpoint");
3846
- var registerSchemasEndpoint = /* @__PURE__ */ __name((ctx) => {
3847
- const { cls, config, baseUrl } = ctx;
3848
- const { route, name } = config;
3849
- const viewsPayload = buildViewsPayload(config, baseUrl);
3850
- def(cls, "getSchemas", async function() {
3851
- if (IS_DEV) {
3852
- const fresh = await this.configRegistry.getByRoute(route);
3853
- if (fresh) return buildViewsPayload(fresh, baseUrl) ?? viewsPayload;
3854
- }
3855
- return viewsPayload;
3856
- });
3857
- const d = desc(cls, "getSchemas");
3858
- (0, import_common9.Get)("schemas")(cls.prototype, "getSchemas", d);
3859
- (0, import_swagger4.ApiOperation)({
3860
- summary: `Get view schemas (table/form) for ${name}`
3861
- })(cls.prototype, "getSchemas", d);
3862
- (0, import_swagger4.ApiResponse)({
3863
- status: 200,
3864
- description: `View schemas for ${name}`
3865
- })(cls.prototype, "getSchemas", d);
3866
- }, "registerSchemasEndpoint");
3867
4020
  var registerResourceJsonEndpoint = /* @__PURE__ */ __name((ctx) => {
3868
4021
  const { cls, config, baseUrl } = ctx;
3869
4022
  const { route, name } = config;
@@ -3876,11 +4029,11 @@ var registerResourceJsonEndpoint = /* @__PURE__ */ __name((ctx) => {
3876
4029
  return resourceJsonPayload;
3877
4030
  });
3878
4031
  const d = desc(cls, "getResourceJson");
3879
- (0, import_common9.Get)("resource.json")(cls.prototype, "getResourceJson", d);
3880
- (0, import_swagger4.ApiOperation)({
4032
+ (0, import_common13.Get)("resource.json")(cls.prototype, "getResourceJson", d);
4033
+ (0, import_swagger8.ApiOperation)({
3881
4034
  summary: `Get resource descriptor for ${name}`
3882
4035
  })(cls.prototype, "getResourceJson", d);
3883
- (0, import_swagger4.ApiResponse)({
4036
+ (0, import_swagger8.ApiResponse)({
3884
4037
  status: 200,
3885
4038
  description: `Resource descriptor (operations + JSON Schema) for ${name}`
3886
4039
  })(cls.prototype, "getResourceJson", d);
@@ -3890,17 +4043,17 @@ var registerResourceColumnsEndpoint = /* @__PURE__ */ __name((ctx) => {
3890
4043
  const { route, name } = config;
3891
4044
  def(cls, "getResourceColumns", async function() {
3892
4045
  if (!IS_DEV) {
3893
- throw new import_common9.ForbiddenException("The resource schema editor is only available when the backend is running in local dev mode.");
4046
+ throw new import_common13.ForbiddenException("The resource schema editor is only available when the backend is running in local dev mode.");
3894
4047
  }
3895
4048
  const fresh = await this.configRegistry.getByRoute(route);
3896
4049
  return buildEditableColumnsPayload(fresh ?? config);
3897
4050
  });
3898
4051
  const d = desc(cls, "getResourceColumns");
3899
- (0, import_common9.Get)("resource-columns")(cls.prototype, "getResourceColumns", d);
3900
- (0, import_swagger4.ApiOperation)({
4052
+ (0, import_common13.Get)("resource-columns")(cls.prototype, "getResourceColumns", d);
4053
+ (0, import_swagger8.ApiOperation)({
3901
4054
  summary: `Dev-only: get the editable column list for ${name}`
3902
4055
  })(cls.prototype, "getResourceColumns", d);
3903
- (0, import_swagger4.ApiResponse)({
4056
+ (0, import_swagger8.ApiResponse)({
3904
4057
  status: 200,
3905
4058
  description: `Editable column list for ${name}`
3906
4059
  })(cls.prototype, "getResourceColumns", d);
@@ -3910,34 +4063,34 @@ var registerResourceJsonPatchEndpoint = /* @__PURE__ */ __name((ctx) => {
3910
4063
  const { route, name } = config;
3911
4064
  def(cls, "patchResourceJson", async function(body) {
3912
4065
  if (!IS_DEV) {
3913
- throw new import_common9.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
4066
+ throw new import_common13.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
3914
4067
  }
3915
4068
  await this.configRegistry.getByRoute(route);
3916
4069
  const dir = this.configRegistry.getResourceDir(route);
3917
4070
  if (!dir) {
3918
- throw new import_common9.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
4071
+ throw new import_common13.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
3919
4072
  }
3920
4073
  const jsonPath = (0, import_node_path5.join)(dir, "resource.json");
3921
4074
  const raw = readRawResourceJson(jsonPath);
3922
4075
  if (!raw) {
3923
- throw new import_common9.NotFoundException(`resource.json not found at ${jsonPath}`);
4076
+ throw new import_common13.NotFoundException(`resource.json not found at ${jsonPath}`);
3924
4077
  }
3925
4078
  const merged = applyColumnPatch(raw, body);
3926
4079
  const validated = validateResourceJson(merged);
3927
4080
  if (!validated.success) {
3928
- throw new import_common9.BadRequestException(validated.error.issues);
4081
+ throw new import_common13.BadRequestException(validated.error.issues);
3929
4082
  }
3930
4083
  writeRawResourceJson(jsonPath, merged);
3931
4084
  const fresh = await this.configRegistry.getByRoute(route);
3932
4085
  return buildEditableColumnsPayload(fresh ?? config);
3933
4086
  });
3934
4087
  const d = desc(cls, "patchResourceJson");
3935
- (0, import_common9.Patch)("resource.json")(cls.prototype, "patchResourceJson", d);
3936
- (0, import_common9.Body)(new ZodValidationPipe(PatchResourceJsonSchema))(cls.prototype, "patchResourceJson", 0);
3937
- (0, import_swagger4.ApiOperation)({
4088
+ (0, import_common13.Patch)("resource.json")(cls.prototype, "patchResourceJson", d);
4089
+ (0, import_common13.Body)(new ZodValidationPipe(PatchResourceJsonSchema))(cls.prototype, "patchResourceJson", 0);
4090
+ (0, import_swagger8.ApiOperation)({
3938
4091
  summary: `Dev-only: patch column layout in resource.json for ${name}`
3939
4092
  })(cls.prototype, "patchResourceJson", d);
3940
- (0, import_swagger4.ApiResponse)({
4093
+ (0, import_swagger8.ApiResponse)({
3941
4094
  status: 200,
3942
4095
  description: `Updated resource descriptor for ${name}`
3943
4096
  })(cls.prototype, "patchResourceJson", d);
@@ -3947,26 +4100,26 @@ var registerResourceJsonRawGetEndpoint = /* @__PURE__ */ __name((ctx) => {
3947
4100
  const { route, name } = config;
3948
4101
  def(cls, "getResourceJsonRaw", async function() {
3949
4102
  if (!IS_DEV) {
3950
- throw new import_common9.ForbiddenException("The resource JSON editor is only available when the backend is running in local dev mode.");
4103
+ throw new import_common13.ForbiddenException("The resource JSON editor is only available when the backend is running in local dev mode.");
3951
4104
  }
3952
4105
  await this.configRegistry.getByRoute(route);
3953
4106
  const dir = this.configRegistry.getResourceDir(route);
3954
4107
  if (!dir) {
3955
- throw new import_common9.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
4108
+ throw new import_common13.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
3956
4109
  }
3957
4110
  const jsonPath = (0, import_node_path5.join)(dir, "resource.json");
3958
4111
  const raw = readRawResourceJson(jsonPath);
3959
4112
  if (!raw) {
3960
- throw new import_common9.NotFoundException(`resource.json not found at ${jsonPath}`);
4113
+ throw new import_common13.NotFoundException(`resource.json not found at ${jsonPath}`);
3961
4114
  }
3962
4115
  return raw;
3963
4116
  });
3964
4117
  const d = desc(cls, "getResourceJsonRaw");
3965
- (0, import_common9.Get)("resource-json-raw")(cls.prototype, "getResourceJsonRaw", d);
3966
- (0, import_swagger4.ApiOperation)({
4118
+ (0, import_common13.Get)("resource-json-raw")(cls.prototype, "getResourceJsonRaw", d);
4119
+ (0, import_swagger8.ApiOperation)({
3967
4120
  summary: `Dev-only: get the raw resource.json for ${name}`
3968
4121
  })(cls.prototype, "getResourceJsonRaw", d);
3969
- (0, import_swagger4.ApiResponse)({
4122
+ (0, import_swagger8.ApiResponse)({
3970
4123
  status: 200,
3971
4124
  description: `Raw resource.json for ${name}`
3972
4125
  })(cls.prototype, "getResourceJsonRaw", d);
@@ -3976,243 +4129,179 @@ var registerResourceJsonRawPutEndpoint = /* @__PURE__ */ __name((ctx) => {
3976
4129
  const { route, name } = config;
3977
4130
  def(cls, "putResourceJsonRaw", async function(body) {
3978
4131
  if (!IS_DEV) {
3979
- throw new import_common9.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
4132
+ throw new import_common13.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
3980
4133
  }
3981
4134
  await this.configRegistry.getByRoute(route);
3982
4135
  const dir = this.configRegistry.getResourceDir(route);
3983
4136
  if (!dir) {
3984
- throw new import_common9.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
4137
+ throw new import_common13.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
3985
4138
  }
3986
4139
  const validated = validateResourceJson(body);
3987
4140
  if (!validated.success) {
3988
- throw new import_common9.BadRequestException(validated.error.issues);
4141
+ throw new import_common13.BadRequestException(validated.error.issues);
3989
4142
  }
3990
4143
  const jsonPath = (0, import_node_path5.join)(dir, "resource.json");
3991
4144
  writeRawResourceJson(jsonPath, body);
3992
4145
  return body;
3993
4146
  });
3994
4147
  const d = desc(cls, "putResourceJsonRaw");
3995
- (0, import_common9.Put)("resource-json-raw")(cls.prototype, "putResourceJsonRaw", d);
3996
- (0, import_common9.Body)()(cls.prototype, "putResourceJsonRaw", 0);
3997
- (0, import_swagger4.ApiOperation)({
4148
+ (0, import_common13.Put)("resource-json-raw")(cls.prototype, "putResourceJsonRaw", d);
4149
+ (0, import_common13.Body)()(cls.prototype, "putResourceJsonRaw", 0);
4150
+ (0, import_swagger8.ApiOperation)({
3998
4151
  summary: `Dev-only: replace the full resource.json for ${name}`
3999
4152
  })(cls.prototype, "putResourceJsonRaw", d);
4000
- (0, import_swagger4.ApiResponse)({
4153
+ (0, import_swagger8.ApiResponse)({
4001
4154
  status: 200,
4002
4155
  description: `Updated raw resource.json for ${name}`
4003
4156
  })(cls.prototype, "putResourceJsonRaw", d);
4004
4157
  }, "registerResourceJsonRawPutEndpoint");
4005
4158
 
4006
- // src/lib/crud/operations/register-sub-resources.ts
4007
- var import_common10 = require("@nestjs/common");
4008
- var import_swagger5 = require("@nestjs/swagger");
4009
- var findAllByParent = /* @__PURE__ */ __name(async (repo, id, childRoute, params) => {
4010
- const { data, count } = await repo.findAllByParent(id, childRoute, params);
4011
- const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
4159
+ // src/lib/crud/operations/register-schemas.ts
4160
+ var import_common14 = require("@nestjs/common");
4161
+ var import_swagger9 = require("@nestjs/swagger");
4162
+ var defaultSchemas = /* @__PURE__ */ __name((ctx) => {
4163
+ const { config, baseUrl } = ctx;
4164
+ const { route, name } = config;
4165
+ const viewsPayload = buildViewsPayload(config, baseUrl);
4012
4166
  return {
4013
- data,
4014
- request: {
4015
- count,
4016
- page: params.page,
4017
- pageSize: params.pageSize,
4018
- totalPages,
4019
- sort: params.sort,
4020
- sortDir: params.sortDir,
4021
- filter: params.filter
4022
- }
4023
- };
4024
- }, "findAllByParent");
4025
- var createChild = /* @__PURE__ */ __name(async (repo, id, sub, body) => {
4026
- return repo.createChild(id, sub, body);
4027
- }, "createChild");
4028
- var findOneChild = /* @__PURE__ */ __name(async (repo, sub, childId, parentId) => {
4029
- return repo.findOneChild(sub, childId, parentId);
4030
- }, "findOneChild");
4031
- var updateChild = /* @__PURE__ */ __name(async (repo, sub, childId, body) => {
4032
- return repo.updateChild(sub, childId, body);
4033
- }, "updateChild");
4034
- var deleteChild = /* @__PURE__ */ __name(async (repo, sub, childId, parentId) => {
4035
- return repo.deleteChild(sub, childId, parentId);
4036
- }, "deleteChild");
4037
- var registerSubResourceSchemas = /* @__PURE__ */ __name((ctx, sub) => {
4038
- if (!sub.views) return;
4039
- const { cls, config, baseUrl } = ctx;
4040
- const { route } = config;
4041
- const methodName = `getSchemas_${sub.childRoute}`;
4042
- const childUri = `${baseUrl}/${route}/{parent.id}/${sub.childRoute}`;
4043
- const schemasPayload = {
4044
- id: sub.name ?? sub.childRoute,
4045
- name: sub.name ?? sub.childRoute,
4046
- route: sub.childRoute,
4047
- uri: childUri,
4048
- title: sub.title ?? sub.childRoute,
4049
- idField: sub.idField ?? "id",
4050
- idType: sub.idType ?? "string",
4051
- ...sub.modalSize && {
4052
- modalSize: sub.modalSize
4053
- },
4054
- operations: buildSubResourceOperations(sub.operations, childUri, sub.idField ?? "id"),
4055
- schemas: Object.fromEntries(Object.entries(sub.views).map(([key, v]) => [
4056
- key,
4057
- {
4058
- data: v.json_schema,
4059
- ui: v.ui_schema,
4060
- ...v.defaultSort !== void 0 && {
4061
- defaultSort: v.defaultSort
4062
- }
4167
+ route: "schemas",
4168
+ methodName: "getSchemas",
4169
+ name,
4170
+ schemasFn: /* @__PURE__ */ __name(async function() {
4171
+ if (IS_DEV) {
4172
+ const fresh = await this.configRegistry.getByRoute(route);
4173
+ if (fresh) return buildViewsPayload(fresh, baseUrl) ?? viewsPayload;
4063
4174
  }
4064
- ])),
4065
- actions: resolveActions(`${baseUrl}/${sub.childRoute}`, sub.actions)
4175
+ return viewsPayload;
4176
+ }, "schemasFn"),
4177
+ decorators: /* @__PURE__ */ __name(() => {
4178
+ }, "decorators")
4066
4179
  };
4067
- def(cls, methodName, async function() {
4068
- return schemasPayload;
4069
- });
4070
- const ds = desc(cls, methodName);
4071
- (0, import_common10.Get)(`${sub.childRoute}/schemas`)(cls.prototype, methodName, ds);
4072
- (0, import_swagger5.ApiOperation)({
4073
- summary: `Get schemas for ${sub.childRoute}`
4074
- })(cls.prototype, methodName, ds);
4075
- (0, import_swagger5.ApiResponse)({
4076
- status: 200,
4077
- description: `View schemas for ${sub.childRoute}`
4078
- })(cls.prototype, methodName, ds);
4079
- }, "registerSubResourceSchemas");
4080
- var registerSubResourceFindAll = /* @__PURE__ */ __name((ctx, sub) => {
4081
- if (sub.operations?.findAll === false) return;
4082
- const { cls, config } = ctx;
4083
- const { name } = config;
4084
- const idParamMeta = ctx.idParamMeta;
4085
- const methodName = `findAllBy_${sub.childRoute}`;
4086
- def(cls, methodName, async function(id, params) {
4087
- return findAllByParent(this.repo, id, sub.childRoute, params);
4088
- });
4089
- const d = desc(cls, methodName);
4090
- (0, import_common10.Get)(`:id/${sub.childRoute}`)(cls.prototype, methodName, d);
4091
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4092
- (0, import_common10.Query)(new ZodValidationPipe(RequestDtoNoOffset.zodSchema))(cls.prototype, methodName, 1);
4093
- (0, import_swagger5.ApiOperation)({
4094
- summary: `List ${sub.childRoute} for a ${name}`
4095
- })(cls.prototype, methodName, d);
4096
- (0, import_swagger5.ApiParam)(idParamMeta)(cls.prototype, methodName, d);
4097
- (0, import_swagger5.ApiResponse)({
4098
- status: 200,
4099
- description: `${sub.childRoute} list`
4100
- })(cls.prototype, methodName, d);
4101
- }, "registerSubResourceFindAll");
4102
- var registerSubResourceCreate = /* @__PURE__ */ __name((ctx, sub) => {
4103
- if (!sub.operations?.create) return;
4104
- const { cls, config } = ctx;
4105
- const { name } = config;
4106
- const methodName = `createChild_${sub.childRoute}`;
4107
- def(cls, methodName, async function(id, body) {
4108
- return createChild(this.repo, id, sub, body);
4109
- });
4110
- const d = desc(cls, methodName);
4111
- (0, import_common10.Post)(`:id/${sub.childRoute}`)(cls.prototype, methodName, d);
4112
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4113
- (0, import_common10.Body)()(cls.prototype, methodName, 1);
4114
- (0, import_swagger5.ApiOperation)({
4115
- summary: `Create ${sub.childRoute} for a ${name}`
4116
- })(cls.prototype, methodName, d);
4117
- (0, import_swagger5.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
4118
- (0, import_swagger5.ApiResponse)({
4119
- status: 201,
4120
- description: `${sub.childRoute} created`
4121
- })(cls.prototype, methodName, d);
4122
- }, "registerSubResourceCreate");
4123
- var registerSubResourceFindOne = /* @__PURE__ */ __name((ctx, sub) => {
4124
- if (!sub.operations?.findOne) return;
4180
+ }, "defaultSchemas");
4181
+ var childSchemas = /* @__PURE__ */ __name((sub) => (ctx) => {
4182
+ if (!sub.views) return null;
4183
+ const { config, baseUrl } = ctx;
4184
+ const schemasPayload = buildSubResourceViewsPayload(config, sub, baseUrl);
4185
+ return {
4186
+ route: `${sub.childRoute}/schemas`,
4187
+ methodName: `getSchemas_${sub.childRoute}`,
4188
+ name: sub.childRoute,
4189
+ schemasFn: /* @__PURE__ */ __name(async function() {
4190
+ return schemasPayload;
4191
+ }, "schemasFn"),
4192
+ decorators: /* @__PURE__ */ __name(() => {
4193
+ }, "decorators")
4194
+ };
4195
+ }, "childSchemas");
4196
+ var registerSchemas = /* @__PURE__ */ __name((ctx, sub) => {
4197
+ const operationFn = sub ? childSchemas(sub) : defaultSchemas;
4198
+ const properties = operationFn(ctx);
4199
+ if (!properties) return;
4200
+ const { methodName, route, name } = properties;
4125
4201
  const { cls } = ctx;
4126
- const methodName = `findOneChild_${sub.childRoute}`;
4127
- def(cls, methodName, async function(parentId, childId) {
4128
- return findOneChild(this.repo, sub, childId, parentId);
4129
- });
4202
+ def(cls, methodName, properties.schemasFn);
4130
4203
  const d = desc(cls, methodName);
4131
- (0, import_common10.Get)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
4132
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4133
- (0, import_common10.Param)("childId")(cls.prototype, methodName, 1);
4134
- (0, import_swagger5.ApiOperation)({
4135
- summary: `Get a ${sub.childRoute} record`
4204
+ (0, import_common14.Get)(route)(cls.prototype, methodName, d);
4205
+ (0, import_swagger9.ApiOperation)({
4206
+ summary: `Get view schemas for ${name}`
4136
4207
  })(cls.prototype, methodName, d);
4137
- (0, import_swagger5.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
4138
- (0, import_swagger5.ApiResponse)({
4208
+ (0, import_swagger9.ApiResponse)({
4139
4209
  status: 200,
4140
- description: `${sub.childRoute} record`
4210
+ description: `View schemas for ${name}`
4141
4211
  })(cls.prototype, methodName, d);
4142
- }, "registerSubResourceFindOne");
4143
- var registerSubResourceUpdate = /* @__PURE__ */ __name((ctx, sub) => {
4144
- if (!sub.operations?.update) return;
4212
+ properties.decorators();
4213
+ }, "registerSchemas");
4214
+
4215
+ // src/lib/crud/operations/register-update.ts
4216
+ var import_common15 = require("@nestjs/common");
4217
+ var import_swagger10 = require("@nestjs/swagger");
4218
+ var defaultUpdate = /* @__PURE__ */ __name((ctx) => {
4219
+ if (!isOperationEnabled(ctx.definition, "update")) return null;
4220
+ const { cls, config, updateSchema, bodyDecorator } = ctx;
4221
+ const methodName = "update";
4222
+ return {
4223
+ route: ":id",
4224
+ methodName,
4225
+ name: config.name,
4226
+ updateFn: /* @__PURE__ */ __name(function(id, body) {
4227
+ return this.repo.update(id, body);
4228
+ }, "updateFn"),
4229
+ decorators: /* @__PURE__ */ __name(() => {
4230
+ (0, import_common15.Param)("id")(cls.prototype, methodName, 0);
4231
+ bodyDecorator(updateSchema)(cls.prototype, methodName, 1);
4232
+ }, "decorators")
4233
+ };
4234
+ }, "defaultUpdate");
4235
+ var childUpdate = /* @__PURE__ */ __name((sub) => (ctx) => {
4236
+ if (!isOperationEnabled(sub.operations, "update")) return null;
4145
4237
  const { cls } = ctx;
4146
4238
  const methodName = `updateChild_${sub.childRoute}`;
4147
- def(cls, methodName, async function(_id, childId, body) {
4148
- return updateChild(this.repo, sub, childId, body);
4149
- });
4150
- const d = desc(cls, methodName);
4151
- (0, import_common10.Put)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
4152
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4153
- (0, import_common10.Param)("childId")(cls.prototype, methodName, 1);
4154
- (0, import_common10.Body)()(cls.prototype, methodName, 2);
4155
- (0, import_swagger5.ApiOperation)({
4156
- summary: `Replace a ${sub.childRoute} record`
4157
- })(cls.prototype, methodName, d);
4158
- (0, import_swagger5.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
4159
- (0, import_swagger5.ApiResponse)({
4160
- status: 200,
4161
- description: `${sub.childRoute} replaced`
4162
- })(cls.prototype, methodName, d);
4163
- }, "registerSubResourceUpdate");
4164
- var registerSubResourcePatch = /* @__PURE__ */ __name((ctx, sub) => {
4165
- if (!sub.operations?.patch) return;
4239
+ const updateFn = /* @__PURE__ */ __name(async function(_id, childId, body) {
4240
+ return this.repo.updateChild(sub, childId, body);
4241
+ }, "updateFn");
4242
+ const decorators = /* @__PURE__ */ __name(() => {
4243
+ (0, import_common15.Param)("id")(cls.prototype, methodName, 0);
4244
+ (0, import_common15.Param)("childId")(cls.prototype, methodName, 1);
4245
+ (0, import_common15.Body)()(cls.prototype, methodName, 2);
4246
+ }, "decorators");
4247
+ return {
4248
+ route: `:id/${sub.childRoute}/:childId`,
4249
+ methodName,
4250
+ name: sub.childRoute,
4251
+ updateFn,
4252
+ decorators
4253
+ };
4254
+ }, "childUpdate");
4255
+ var registerUpdate = /* @__PURE__ */ __name((ctx, sub) => {
4256
+ const operationFn = sub ? childUpdate(sub) : defaultUpdate;
4257
+ const properties = operationFn(ctx);
4258
+ if (!properties) return;
4259
+ const { methodName, route, name } = properties;
4166
4260
  const { cls } = ctx;
4167
- const methodName = `patchChild_${sub.childRoute}`;
4168
- def(cls, methodName, async function(_id, childId, body) {
4169
- return updateChild(this.repo, sub, childId, body);
4170
- });
4261
+ def(cls, methodName, properties.updateFn);
4171
4262
  const d = desc(cls, methodName);
4172
- (0, import_common10.Patch)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
4173
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4174
- (0, import_common10.Param)("childId")(cls.prototype, methodName, 1);
4175
- (0, import_common10.Body)()(cls.prototype, methodName, 2);
4176
- (0, import_swagger5.ApiOperation)({
4177
- summary: `Update a ${sub.childRoute} record`
4263
+ (0, import_common15.Put)(route)(cls.prototype, methodName, d);
4264
+ (0, import_swagger10.ApiOperation)({
4265
+ summary: `Replace a ${name}`
4178
4266
  })(cls.prototype, methodName, d);
4179
- (0, import_swagger5.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
4180
- (0, import_swagger5.ApiResponse)({
4267
+ (0, import_swagger10.ApiResponse)({
4181
4268
  status: 200,
4182
- description: `${sub.childRoute} updated`
4183
- })(cls.prototype, methodName, d);
4184
- }, "registerSubResourcePatch");
4185
- var registerSubResourceDelete = /* @__PURE__ */ __name((ctx, sub) => {
4186
- if (!sub.operations?.delete) return;
4187
- const { cls } = ctx;
4188
- const methodName = `deleteChild_${sub.childRoute}`;
4189
- def(cls, methodName, async function(parentId, childId) {
4190
- return deleteChild(this.repo, sub, childId, parentId);
4191
- });
4192
- const d = desc(cls, methodName);
4193
- (0, import_common10.Delete)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
4194
- (0, import_common10.Param)("id")(cls.prototype, methodName, 0);
4195
- (0, import_common10.Param)("childId")(cls.prototype, methodName, 1);
4196
- (0, import_swagger5.ApiOperation)({
4197
- summary: `Delete ${sub.childRoute} record`
4269
+ description: `${name} replaced`
4198
4270
  })(cls.prototype, methodName, d);
4199
- (0, import_swagger5.ApiParam)(ctx.idParamMeta)(cls.prototype, methodName, d);
4200
- (0, import_swagger5.ApiResponse)({
4201
- status: 200,
4202
- description: `${sub.childRoute} deleted`
4271
+ (0, import_swagger10.ApiNotFoundResponse)({
4272
+ description: "Not found"
4203
4273
  })(cls.prototype, methodName, d);
4204
- }, "registerSubResourceDelete");
4274
+ properties.decorators();
4275
+ }, "registerUpdate");
4276
+
4277
+ // src/lib/crud/operations/register-endpoints.ts
4278
+ var registerEndpoints = /* @__PURE__ */ __name((ctx) => {
4279
+ registerSubResourceRoutes(ctx);
4280
+ registerFindAll(ctx);
4281
+ registerResourceJsonRawGetEndpoint(ctx);
4282
+ registerResourceJsonRawPutEndpoint(ctx);
4283
+ registerResourceJsonPatchEndpoint(ctx);
4284
+ registerResourceJsonEndpoint(ctx);
4285
+ registerResourceColumnsEndpoint(ctx);
4286
+ registerDefinitionEndpoint(ctx);
4287
+ registerEndpoint(ctx);
4288
+ registerActionRoutes(ctx);
4289
+ registerTableActionRoutes(ctx);
4290
+ }, "registerEndpoints");
4205
4291
  var registerSubResourceRoutes = /* @__PURE__ */ __name((ctx) => {
4206
4292
  for (const sub of ctx.config.subResources ?? []) {
4207
- registerSubResourceSchemas(ctx, sub);
4208
- registerSubResourceFindAll(ctx, sub);
4209
- registerSubResourceCreate(ctx, sub);
4210
- registerSubResourceFindOne(ctx, sub);
4211
- registerSubResourceUpdate(ctx, sub);
4212
- registerSubResourcePatch(ctx, sub);
4213
- registerSubResourceDelete(ctx, sub);
4293
+ registerEndpoint(ctx, sub);
4214
4294
  }
4215
4295
  }, "registerSubResourceRoutes");
4296
+ var registerEndpoint = /* @__PURE__ */ __name((ctx, sub) => {
4297
+ registerSchemas(ctx, sub);
4298
+ registerFindAll(ctx, sub);
4299
+ registerFindOne(ctx, sub);
4300
+ registerCreate(ctx, sub);
4301
+ registerUpdate(ctx, sub);
4302
+ registerPatch(ctx, sub);
4303
+ registerDelete(ctx, sub);
4304
+ }, "registerEndpoint");
4216
4305
 
4217
4306
  // src/lib/crud/crud-controller.factory.ts
4218
4307
  function createCrudController(config, baseUrl) {
@@ -4229,9 +4318,9 @@ function createCrudController(config, baseUrl) {
4229
4318
  throw new Error(`Resource "${name}" declares 'upsert' but no upsertOn`);
4230
4319
  }
4231
4320
  const bodyDecorator = /* @__PURE__ */ __name((schema, options) => {
4232
- if (!schema) return (0, import_common11.Body)();
4233
- if (isZodSchema(schema)) return (0, import_common11.Body)(new ZodValidationPipe(schema, options));
4234
- return (0, import_common11.Body)();
4321
+ if (!schema) return (0, import_common16.Body)();
4322
+ if (isZodSchema(schema)) return (0, import_common16.Body)(new ZodValidationPipe(schema, options));
4323
+ return (0, import_common16.Body)();
4235
4324
  }, "bodyDecorator");
4236
4325
  let CrudControllerBase = class CrudControllerBase {
4237
4326
  static {
@@ -4262,25 +4351,9 @@ function createCrudController(config, baseUrl) {
4262
4351
  bodyDecorator,
4263
4352
  baseUrl
4264
4353
  };
4265
- registerFindAll(ctx);
4266
- registerDefinitionEndpoint(ctx);
4267
- registerSchemasEndpoint(ctx);
4268
- registerResourceJsonEndpoint(ctx);
4269
- registerResourceColumnsEndpoint(ctx);
4270
- registerResourceJsonPatchEndpoint(ctx);
4271
- registerResourceJsonRawGetEndpoint(ctx);
4272
- registerResourceJsonRawPutEndpoint(ctx);
4273
- registerActionRoutes(ctx);
4274
- registerTableActionRoutes(ctx);
4275
- registerSubResourceRoutes(ctx);
4276
- registerFindOne(ctx);
4277
- registerCreate(ctx);
4278
- registerUpdate(ctx);
4279
- registerPatch(ctx);
4280
- registerUpsert(ctx);
4281
- registerDelete(ctx);
4282
- (0, import_common11.Controller)(route)(CrudControllerBase);
4283
- (0, import_swagger6.ApiTags)(tag)(CrudControllerBase);
4354
+ registerEndpoints(ctx);
4355
+ (0, import_common16.Controller)(route)(CrudControllerBase);
4356
+ (0, import_swagger11.ApiTags)(tag)(CrudControllerBase);
4284
4357
  Object.defineProperty(CrudControllerBase, "name", {
4285
4358
  value: `${name.charAt(0).toUpperCase() + name.slice(1)}Controller`
4286
4359
  });
@@ -4293,8 +4366,8 @@ function createCrudController(config, baseUrl) {
4293
4366
  __name(createCrudController, "createCrudController");
4294
4367
 
4295
4368
  // src/lib/crud/dev-tools/dev-resources.controller.ts
4296
- var import_common12 = require("@nestjs/common");
4297
- var import_swagger7 = require("@nestjs/swagger");
4369
+ var import_common17 = require("@nestjs/common");
4370
+ var import_swagger12 = require("@nestjs/swagger");
4298
4371
 
4299
4372
  // ../crouton-codegen/src/naming.ts
4300
4373
  var clientAccessor = /* @__PURE__ */ __name((prismaModelName) => {
@@ -4368,6 +4441,20 @@ var resolveRelationType = /* @__PURE__ */ __name((field, index) => {
4368
4441
  if (field.isList) return otherIsList ? "manyToMany" : "oneToMany";
4369
4442
  return otherIsList ? "manyToOne" : "oneToOne";
4370
4443
  }, "resolveRelationType");
4444
+ var literalDefault = /* @__PURE__ */ __name((value) => {
4445
+ if (value === void 0 || value === null) return void 0;
4446
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4447
+ return value;
4448
+ }
4449
+ if (Array.isArray(value) && value.every((v) => [
4450
+ "string",
4451
+ "number",
4452
+ "boolean"
4453
+ ].includes(typeof v))) {
4454
+ return value;
4455
+ }
4456
+ return void 0;
4457
+ }, "literalDefault");
4371
4458
  var dmmfToDbModels = /* @__PURE__ */ __name((dmmf) => {
4372
4459
  const { models, enums } = dmmf.datamodel;
4373
4460
  const enumNames = new Set(enums.map((e) => e.name));
@@ -4401,6 +4488,9 @@ var dmmfToDbModels = /* @__PURE__ */ __name((dmmf) => {
4401
4488
  isUnique: f.isUnique,
4402
4489
  isUpdatedAt: f.isUpdatedAt,
4403
4490
  hasDefault: f.hasDefaultValue,
4491
+ ...f.hasDefaultValue && literalDefault(f.default) !== void 0 ? {
4492
+ defaultValue: literalDefault(f.default)
4493
+ } : {},
4404
4494
  isTimestamp: kind === "scalar" && isTimestampField({
4405
4495
  name: f.name,
4406
4496
  type: f.type,
@@ -4482,7 +4572,10 @@ var classify = /* @__PURE__ */ __name((model, ctx = {}) => {
4482
4572
  } : {
4483
4573
  fieldInput: {
4484
4574
  type: "date",
4485
- position: position++
4575
+ position: position++,
4576
+ ...field.defaultValue !== void 0 ? {
4577
+ defaultValue: field.defaultValue
4578
+ } : {}
4486
4579
  }
4487
4580
  };
4488
4581
  } else if (field.kind === "foreignKey") {
@@ -4494,7 +4587,10 @@ var classify = /* @__PURE__ */ __name((model, ctx = {}) => {
4494
4587
  } : {
4495
4588
  fieldInput: {
4496
4589
  type: "text",
4497
- position: position++
4590
+ position: position++,
4591
+ ...field.defaultValue !== void 0 ? {
4592
+ defaultValue: field.defaultValue
4593
+ } : {}
4498
4594
  }
4499
4595
  };
4500
4596
  } else if (field.kind === "relation") {
@@ -4503,7 +4599,6 @@ var classify = /* @__PURE__ */ __name((model, ctx = {}) => {
4503
4599
  col = {
4504
4600
  hiddenInTable: ruleset.hideRelationsInTable,
4505
4601
  fieldInput: {
4506
- type: "relation",
4507
4602
  format: "relation",
4508
4603
  resource: target,
4509
4604
  relationType: field.relationType
@@ -4539,14 +4634,20 @@ var classify = /* @__PURE__ */ __name((model, ctx = {}) => {
4539
4634
  fieldInput: {
4540
4635
  type: "select",
4541
4636
  position: position++,
4542
- options
4637
+ options,
4638
+ ...field.defaultValue !== void 0 ? {
4639
+ defaultValue: field.defaultValue
4640
+ } : {}
4543
4641
  }
4544
4642
  };
4545
4643
  } else {
4546
4644
  col = {
4547
4645
  fieldInput: {
4548
4646
  type: scalarFieldInputType(field.name, field.type),
4549
- position: position++
4647
+ position: position++,
4648
+ ...field.defaultValue !== void 0 ? {
4649
+ defaultValue: field.defaultValue
4650
+ } : {}
4550
4651
  }
4551
4652
  };
4552
4653
  }
@@ -4952,12 +5053,12 @@ var makeSchemaExportName = /* @__PURE__ */ __name((config) => {
4952
5053
  var resolveFromRoot = /* @__PURE__ */ __name((root, p) => (0, import_node_path7.isAbsolute)(p) ? p : (0, import_node_path7.join)(root, p), "resolveFromRoot");
4953
5054
 
4954
5055
  // ../crouton-codegen/src/scaffold.ts
4955
- var import_zod22 = require("zod");
5056
+ var import_zod24 = require("zod");
4956
5057
  var import_promises5 = require("fs/promises");
4957
5058
  var import_node_path8 = require("path");
4958
5059
  var ScallfoldDatasourceSchema = DataSourceShape.extend({
4959
5060
  /** Folder name under `dataSourcesDir`. */
4960
- folder: import_zod22.z.string().default("default")
5061
+ folder: import_zod24.z.string().default("default")
4961
5062
  }).transform(transformDataSource);
4962
5063
 
4963
5064
  // ../crouton-codegen/src/datasource-scaffold.ts
@@ -5026,7 +5127,7 @@ var import_node_child_process = require("child_process");
5026
5127
  var import_node_fs4 = require("fs");
5027
5128
  var import_promises7 = require("fs/promises");
5028
5129
  var import_node_path11 = require("path");
5029
- var run = /* @__PURE__ */ __name((cmd, args, cwd) => new Promise((resolve6) => {
5130
+ var run = /* @__PURE__ */ __name((cmd, args, cwd) => new Promise((resolve7) => {
5030
5131
  const child = (0, import_node_child_process.spawn)(cmd, args, {
5031
5132
  cwd,
5032
5133
  shell: process.platform === "win32"
@@ -5035,12 +5136,12 @@ var run = /* @__PURE__ */ __name((cmd, args, cwd) => new Promise((resolve6) => {
5035
5136
  let stderr = "";
5036
5137
  child.stdout?.on("data", (d) => stdout += d.toString());
5037
5138
  child.stderr?.on("data", (d) => stderr += d.toString());
5038
- child.on("error", (err) => resolve6({
5139
+ child.on("error", (err) => resolve7({
5039
5140
  code: 1,
5040
5141
  stdout,
5041
5142
  stderr: stderr + String(err)
5042
5143
  }));
5043
- child.on("close", (code) => resolve6({
5144
+ child.on("close", (code) => resolve7({
5044
5145
  code: code ?? 0,
5045
5146
  stdout,
5046
5147
  stderr
@@ -5183,13 +5284,85 @@ var normalizeSchema = /* @__PURE__ */ __name(async (schemaPath, configDir) => {
5183
5284
  return `${head}${body}${tail}`;
5184
5285
  });
5185
5286
  }
5186
- if (renamed > 0) {
5187
- await (0, import_promises7.writeFile)(schemaPath, schema, "utf-8");
5287
+ if (renamed > 0) {
5288
+ await (0, import_promises7.writeFile)(schemaPath, schema, "utf-8");
5289
+ }
5290
+ return {
5291
+ renamed
5292
+ };
5293
+ }, "normalizeSchema");
5294
+
5295
+ // src/lib/crud/resource/ResourceFlags.ts
5296
+ var import_node_path12 = require("path");
5297
+ var applyResourceFlagPatch = /* @__PURE__ */ __name((raw, patch) => {
5298
+ const result = {
5299
+ ...raw
5300
+ };
5301
+ if (patch.draft !== void 0) {
5302
+ if (patch.draft) {
5303
+ result["draft"] = true;
5304
+ } else {
5305
+ delete result["draft"];
5306
+ }
5307
+ }
5308
+ if (patch.sidebar !== void 0) {
5309
+ const existing = raw["sidebar"] && typeof raw["sidebar"] === "object" ? {
5310
+ ...raw["sidebar"]
5311
+ } : {};
5312
+ if (patch.sidebar.hide !== void 0) {
5313
+ if (patch.sidebar.hide) {
5314
+ existing["hide"] = true;
5315
+ } else {
5316
+ delete existing["hide"];
5317
+ }
5318
+ }
5319
+ if (patch.sidebar.group !== void 0) {
5320
+ existing["group"] = patch.sidebar.group;
5321
+ }
5322
+ if (patch.sidebar.position !== void 0) {
5323
+ existing["position"] = patch.sidebar.position;
5324
+ }
5325
+ if (patch.sidebar.label !== void 0) {
5326
+ existing["label"] = patch.sidebar.label;
5327
+ }
5328
+ if (Object.keys(existing).length === 0) {
5329
+ delete result["sidebar"];
5330
+ } else {
5331
+ result["sidebar"] = existing;
5332
+ }
5333
+ }
5334
+ return result;
5335
+ }, "applyResourceFlagPatch");
5336
+ var resolveResourcePath = /* @__PURE__ */ __name((resourcesDir, name) => {
5337
+ const resolved = (0, import_node_path12.resolve)(resourcesDir, name, "resource.json");
5338
+ if (!resolved.startsWith((0, import_node_path12.resolve)(resourcesDir) + "/")) {
5339
+ throw new Error(`Invalid resource name: "${name}"`);
5188
5340
  }
5189
- return {
5190
- renamed
5191
- };
5192
- }, "normalizeSchema");
5341
+ return resolved;
5342
+ }, "resolveResourcePath");
5343
+
5344
+ // src/lib/crud/resource/resource-load-report.registry.ts
5345
+ var ResourceLoadReportRegistry = class ResourceLoadReportRegistry2 {
5346
+ static {
5347
+ __name(this, "ResourceLoadReportRegistry");
5348
+ }
5349
+ notices = [];
5350
+ record(n) {
5351
+ this.notices.push(n);
5352
+ }
5353
+ getAll() {
5354
+ return [
5355
+ ...this.notices
5356
+ ];
5357
+ }
5358
+ getByState(state) {
5359
+ return this.notices.filter((n) => n.state === state);
5360
+ }
5361
+ clear() {
5362
+ this.notices = [];
5363
+ }
5364
+ };
5365
+ var resourceLoadReportRegistry = new ResourceLoadReportRegistry();
5193
5366
 
5194
5367
  // src/lib/crud/dev-tools/dev-resources.controller.ts
5195
5368
  function _ts_decorate5(decorators, target, key, desc2) {
@@ -5214,12 +5387,14 @@ var DevResourcesController = class {
5214
5387
  __name(this, "DevResourcesController");
5215
5388
  }
5216
5389
  dataSourceRegistry;
5217
- constructor(dataSourceRegistry) {
5390
+ resourceConfigRegistry;
5391
+ constructor(dataSourceRegistry, resourceConfigRegistry) {
5218
5392
  this.dataSourceRegistry = dataSourceRegistry;
5393
+ this.resourceConfigRegistry = resourceConfigRegistry;
5219
5394
  }
5220
5395
  assertDev() {
5221
5396
  if (!IS_DEV) {
5222
- throw new import_common12.ForbiddenException("The database sync tools are only available when CROUTON_SCHEMA_EDITOR is enabled.");
5397
+ throw new import_common17.ForbiddenException("The database sync tools are only available when CROUTON_SCHEMA_EDITOR is enabled.");
5223
5398
  }
5224
5399
  }
5225
5400
  /** Loads project config + resolves the datasource + Prisma schema path. Throws 404/400 with a clear message on misconfiguration. */
@@ -5228,14 +5403,14 @@ var DevResourcesController = class {
5228
5403
  try {
5229
5404
  loaded = await loadConfig2(process.cwd());
5230
5405
  } catch (e) {
5231
- throw new import_common12.NotFoundException(e.message ?? "No crouton.json config found.");
5406
+ throw new import_common17.NotFoundException(e.message ?? "No crouton.json config found.");
5232
5407
  }
5233
5408
  const datasources = await loadDatasources(loaded);
5234
5409
  let ds;
5235
5410
  try {
5236
5411
  ds = resolveDatasource(datasources, datasourceName);
5237
5412
  } catch (e) {
5238
- throw new import_common12.BadRequestException(e.message);
5413
+ throw new import_common17.BadRequestException(e.message);
5239
5414
  }
5240
5415
  const schemaPath = resolveFromRoot(loaded.root, ds.prismaSchema);
5241
5416
  return {
@@ -5250,7 +5425,7 @@ var DevResourcesController = class {
5250
5425
  schemaPath
5251
5426
  });
5252
5427
  } catch (e) {
5253
- throw new import_common12.BadRequestException(`Failed to read Prisma schema at ${schemaPath}: ${e.message}`);
5428
+ throw new import_common17.BadRequestException(`Failed to read Prisma schema at ${schemaPath}: ${e.message}`);
5254
5429
  }
5255
5430
  }
5256
5431
  buildApplyContext(loaded, ds) {
@@ -5320,7 +5495,7 @@ var DevResourcesController = class {
5320
5495
  zodOutputDir: zodDir
5321
5496
  });
5322
5497
  if (!result.ok) {
5323
- throw new import_common12.BadRequestException(`prisma db pull failed:
5498
+ throw new import_common17.BadRequestException(`prisma db pull failed:
5324
5499
  ${result.dbPull.output}`);
5325
5500
  }
5326
5501
  return {
@@ -5337,13 +5512,13 @@ ${result.dbPull.output}`);
5337
5512
  async sync(body) {
5338
5513
  this.assertDev();
5339
5514
  if (!body?.model) {
5340
- throw new import_common12.BadRequestException('"model" is required.');
5515
+ throw new import_common17.BadRequestException('"model" is required.');
5341
5516
  }
5342
5517
  const { loaded, ds, schemaPath } = await this.loadProject(body.datasource);
5343
5518
  const models = await this.introspectModels(schemaPath);
5344
5519
  const model = models.find((m) => m.prismaName === body.model || m.clientAccessor === body.model);
5345
5520
  if (!model) {
5346
- throw new import_common12.NotFoundException(`Model "${body.model}" not found in ${schemaPath}.`);
5521
+ throw new import_common17.NotFoundException(`Model "${body.model}" not found in ${schemaPath}.`);
5347
5522
  }
5348
5523
  const resolveRelationResource = await makeRelationResolver(loaded);
5349
5524
  const diff2 = await buildResourceDiff(model, {
@@ -5423,13 +5598,136 @@ ${result.dbPull.output}`);
5423
5598
  results
5424
5599
  };
5425
5600
  }
5601
+ // ─── Visibility & flag endpoints ────────────────────────────────────
5602
+ async visibility() {
5603
+ this.assertDev();
5604
+ const loaded = await this.resourceConfigRegistry.getAll();
5605
+ const fromLoaded = loaded.map((c) => ({
5606
+ name: c.name,
5607
+ path: c.route,
5608
+ state: c.sidebar?.hide ? "hidden" : "in-menu",
5609
+ group: c.sidebar?.group,
5610
+ position: c.sidebar?.position,
5611
+ editable: true
5612
+ }));
5613
+ const drafts = resourceLoadReportRegistry.getByState("draft").map((d) => ({
5614
+ name: d.name,
5615
+ path: d.path,
5616
+ state: "draft",
5617
+ group: void 0,
5618
+ position: void 0,
5619
+ editable: !d.path.endsWith(".ts")
5620
+ }));
5621
+ const errors = resourceLoadErrorsRegistry.getAll().map((e) => ({
5622
+ name: e.name,
5623
+ path: e.path,
5624
+ state: "error",
5625
+ group: void 0,
5626
+ position: void 0,
5627
+ editable: !e.path.endsWith(".ts")
5628
+ }));
5629
+ return {
5630
+ resources: [
5631
+ ...fromLoaded,
5632
+ ...drafts,
5633
+ ...errors
5634
+ ]
5635
+ };
5636
+ }
5637
+ async publish(name) {
5638
+ this.assertDev();
5639
+ const { loaded } = await this.loadProject();
5640
+ const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
5641
+ const jsonPath = resolveResourcePath(resourcesDir, name);
5642
+ const raw = readRawResourceJson(jsonPath);
5643
+ if (!raw) throw new import_common17.NotFoundException(`Resource "${name}" not found.`);
5644
+ if (jsonPath.endsWith(".ts")) throw new import_common17.ForbiddenException("TypeScript resources cannot be edited.");
5645
+ const patched = applyResourceFlagPatch(raw, {
5646
+ draft: false
5647
+ });
5648
+ const result = validateResourceJson(patched);
5649
+ if (!result.success) {
5650
+ throw new import_common17.BadRequestException(`Validation failed: ${result.error.message}`);
5651
+ }
5652
+ writeRawResourceJson(jsonPath, patched);
5653
+ return {
5654
+ ok: true
5655
+ };
5656
+ }
5657
+ async removeFromMenu(name) {
5658
+ this.assertDev();
5659
+ const { loaded } = await this.loadProject();
5660
+ const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
5661
+ const jsonPath = resolveResourcePath(resourcesDir, name);
5662
+ const raw = readRawResourceJson(jsonPath);
5663
+ if (!raw) throw new import_common17.NotFoundException(`Resource "${name}" not found.`);
5664
+ if (jsonPath.endsWith(".ts")) throw new import_common17.ForbiddenException("TypeScript resources cannot be edited.");
5665
+ const patched = applyResourceFlagPatch(raw, {
5666
+ sidebar: {
5667
+ hide: true
5668
+ }
5669
+ });
5670
+ const result = validateResourceJson(patched);
5671
+ if (!result.success) {
5672
+ throw new import_common17.BadRequestException(`Validation failed: ${result.error.message}`);
5673
+ }
5674
+ writeRawResourceJson(jsonPath, patched);
5675
+ return {
5676
+ ok: true
5677
+ };
5678
+ }
5679
+ async addToMenu(name, body = {}) {
5680
+ this.assertDev();
5681
+ const { loaded } = await this.loadProject();
5682
+ const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
5683
+ const jsonPath = resolveResourcePath(resourcesDir, name);
5684
+ const raw = readRawResourceJson(jsonPath);
5685
+ if (!raw) throw new import_common17.NotFoundException(`Resource "${name}" not found.`);
5686
+ if (jsonPath.endsWith(".ts")) throw new import_common17.ForbiddenException("TypeScript resources cannot be edited.");
5687
+ const sidebarPatch = {
5688
+ hide: false
5689
+ };
5690
+ if (body.group !== void 0) sidebarPatch.group = body.group;
5691
+ if (body.position !== void 0) sidebarPatch.position = body.position;
5692
+ if (body.label !== void 0) sidebarPatch.label = body.label;
5693
+ const patched = applyResourceFlagPatch(raw, {
5694
+ draft: false,
5695
+ sidebar: sidebarPatch
5696
+ });
5697
+ const result = validateResourceJson(patched);
5698
+ if (!result.success) {
5699
+ throw new import_common17.BadRequestException(`Validation failed: ${result.error.message}`);
5700
+ }
5701
+ writeRawResourceJson(jsonPath, patched);
5702
+ return {
5703
+ ok: true
5704
+ };
5705
+ }
5706
+ async updateFlags(name, body) {
5707
+ this.assertDev();
5708
+ const { loaded } = await this.loadProject();
5709
+ const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
5710
+ const jsonPath = resolveResourcePath(resourcesDir, name);
5711
+ const raw = readRawResourceJson(jsonPath);
5712
+ if (!raw) throw new import_common17.NotFoundException(`Resource "${name}" not found.`);
5713
+ if (jsonPath.endsWith(".ts")) throw new import_common17.ForbiddenException("TypeScript resources cannot be edited.");
5714
+ const patched = applyResourceFlagPatch(raw, body);
5715
+ const result = validateResourceJson(patched);
5716
+ if (!result.success) {
5717
+ throw new import_common17.BadRequestException(`Validation failed: ${result.error.message}`);
5718
+ }
5719
+ writeRawResourceJson(jsonPath, patched);
5720
+ return {
5721
+ ok: true
5722
+ };
5723
+ }
5426
5724
  };
5427
5725
  _ts_decorate5([
5428
- (0, import_common12.Get)("models"),
5429
- (0, import_swagger7.ApiOperation)({
5726
+ (0, import_common17.Get)("models"),
5727
+ (0, import_swagger12.ApiOperation)({
5430
5728
  summary: "Dev-only: list DB models from the Prisma schema, flagging which already have a resource.json and whether the running backend can actually use them yet"
5431
5729
  }),
5432
- (0, import_swagger7.ApiResponse)({
5730
+ (0, import_swagger12.ApiResponse)({
5433
5731
  status: 200,
5434
5732
  description: "DB models and their resource/client status"
5435
5733
  }),
@@ -5438,11 +5736,11 @@ _ts_decorate5([
5438
5736
  _ts_metadata4("design:returntype", Promise)
5439
5737
  ], DevResourcesController.prototype, "listModels", null);
5440
5738
  _ts_decorate5([
5441
- (0, import_common12.Post)("restart"),
5442
- (0, import_swagger7.ApiOperation)({
5739
+ (0, import_common17.Post)("restart"),
5740
+ (0, import_swagger12.ApiOperation)({
5443
5741
  summary: "Dev-only: exit this process so a dev watcher/process manager (nodemon, `nest start --watch`, pm2, a Docker restart policy, ...) restarts it with a fresh Prisma client. Does nothing useful if this process isn't supervised by one of those."
5444
5742
  }),
5445
- (0, import_swagger7.ApiResponse)({
5743
+ (0, import_swagger12.ApiResponse)({
5446
5744
  status: 200,
5447
5745
  description: "Restart scheduled \u2014 the connection will drop shortly after"
5448
5746
  }),
@@ -5451,15 +5749,15 @@ _ts_decorate5([
5451
5749
  _ts_metadata4("design:returntype", Object)
5452
5750
  ], DevResourcesController.prototype, "restart", null);
5453
5751
  _ts_decorate5([
5454
- (0, import_common12.Post)("pull"),
5455
- (0, import_swagger7.ApiOperation)({
5752
+ (0, import_common17.Post)("pull"),
5753
+ (0, import_swagger12.ApiOperation)({
5456
5754
  summary: "Dev-only: run `prisma db pull` + case-format + `prisma generate` for a datasource, refreshing schema.prisma and the generated Prisma client/Zod types from the live database"
5457
5755
  }),
5458
- (0, import_swagger7.ApiResponse)({
5756
+ (0, import_swagger12.ApiResponse)({
5459
5757
  status: 200,
5460
5758
  description: "Result of each step, or requiresConfirmation if schema.prisma has uncommitted changes"
5461
5759
  }),
5462
- _ts_param(0, (0, import_common12.Body)()),
5760
+ _ts_param(0, (0, import_common17.Body)()),
5463
5761
  _ts_metadata4("design:type", Function),
5464
5762
  _ts_metadata4("design:paramtypes", [
5465
5763
  Object
@@ -5467,15 +5765,15 @@ _ts_decorate5([
5467
5765
  _ts_metadata4("design:returntype", Promise)
5468
5766
  ], DevResourcesController.prototype, "pull", null);
5469
5767
  _ts_decorate5([
5470
- (0, import_common12.Post)("sync"),
5471
- (0, import_swagger7.ApiOperation)({
5768
+ (0, import_common17.Post)("sync"),
5769
+ (0, import_swagger12.ApiOperation)({
5472
5770
  summary: "Dev-only: generate or update a single resource.json from its DB model, using recommended defaults (non-interactive)"
5473
5771
  }),
5474
- (0, import_swagger7.ApiResponse)({
5772
+ (0, import_swagger12.ApiResponse)({
5475
5773
  status: 200,
5476
5774
  description: "Files written for this resource"
5477
5775
  }),
5478
- _ts_param(0, (0, import_common12.Body)()),
5776
+ _ts_param(0, (0, import_common17.Body)()),
5479
5777
  _ts_metadata4("design:type", Function),
5480
5778
  _ts_metadata4("design:paramtypes", [
5481
5779
  Object
@@ -5483,15 +5781,15 @@ _ts_decorate5([
5483
5781
  _ts_metadata4("design:returntype", Promise)
5484
5782
  ], DevResourcesController.prototype, "sync", null);
5485
5783
  _ts_decorate5([
5486
- (0, import_common12.Post)("plan"),
5487
- (0, import_swagger7.ApiOperation)({
5784
+ (0, import_common17.Post)("plan"),
5785
+ (0, import_swagger12.ApiOperation)({
5488
5786
  summary: "Dev-only: dry-run introspect + diff across all (or selected) DB models using recommended defaults \u2014 computes what would change, writes nothing"
5489
5787
  }),
5490
- (0, import_swagger7.ApiResponse)({
5788
+ (0, import_swagger12.ApiResponse)({
5491
5789
  status: 200,
5492
5790
  description: "Proposed per-resource changes"
5493
5791
  }),
5494
- _ts_param(0, (0, import_common12.Body)()),
5792
+ _ts_param(0, (0, import_common17.Body)()),
5495
5793
  _ts_metadata4("design:type", Function),
5496
5794
  _ts_metadata4("design:paramtypes", [
5497
5795
  Object
@@ -5499,53 +5797,135 @@ _ts_decorate5([
5499
5797
  _ts_metadata4("design:returntype", Promise)
5500
5798
  ], DevResourcesController.prototype, "plan", null);
5501
5799
  _ts_decorate5([
5502
- (0, import_common12.Post)("apply"),
5503
- (0, import_swagger7.ApiOperation)({
5800
+ (0, import_common17.Post)("apply"),
5801
+ (0, import_swagger12.ApiOperation)({
5504
5802
  summary: "Dev-only: commit resource.json/schema.ts changes to disk for the given resources (or all, if omitted), using recommended defaults"
5505
5803
  }),
5506
- (0, import_swagger7.ApiResponse)({
5804
+ (0, import_swagger12.ApiResponse)({
5507
5805
  status: 200,
5508
5806
  description: "Per-resource commit results"
5509
5807
  }),
5510
- _ts_param(0, (0, import_common12.Body)()),
5808
+ _ts_param(0, (0, import_common17.Body)()),
5511
5809
  _ts_metadata4("design:type", Function),
5512
5810
  _ts_metadata4("design:paramtypes", [
5513
5811
  Object
5514
5812
  ]),
5515
5813
  _ts_metadata4("design:returntype", Promise)
5516
5814
  ], DevResourcesController.prototype, "apply", null);
5815
+ _ts_decorate5([
5816
+ (0, import_common17.Get)("visibility"),
5817
+ (0, import_swagger12.ApiOperation)({
5818
+ summary: "Dev-only: list all resources with their menu visibility state (in-menu, hidden, draft, error)"
5819
+ }),
5820
+ (0, import_swagger12.ApiResponse)({
5821
+ status: 200,
5822
+ description: "Resource visibility list"
5823
+ }),
5824
+ _ts_metadata4("design:type", Function),
5825
+ _ts_metadata4("design:paramtypes", []),
5826
+ _ts_metadata4("design:returntype", Promise)
5827
+ ], DevResourcesController.prototype, "visibility", null);
5828
+ _ts_decorate5([
5829
+ (0, import_common17.Post)(":name/publish"),
5830
+ (0, import_swagger12.ApiOperation)({
5831
+ summary: "Dev-only: publish a draft resource (removes `draft: true` from resource.json)"
5832
+ }),
5833
+ (0, import_swagger12.ApiResponse)({
5834
+ status: 200,
5835
+ description: "Resource published"
5836
+ }),
5837
+ _ts_param(0, (0, import_common17.Param)("name")),
5838
+ _ts_metadata4("design:type", Function),
5839
+ _ts_metadata4("design:paramtypes", [
5840
+ String
5841
+ ]),
5842
+ _ts_metadata4("design:returntype", Promise)
5843
+ ], DevResourcesController.prototype, "publish", null);
5844
+ _ts_decorate5([
5845
+ (0, import_common17.Post)(":name/remove-from-menu"),
5846
+ (0, import_swagger12.ApiOperation)({
5847
+ summary: "Dev-only: hide a resource from the sidebar menu (sets `sidebar.hide: true` in resource.json)"
5848
+ }),
5849
+ (0, import_swagger12.ApiResponse)({
5850
+ status: 200,
5851
+ description: "Resource removed from menu"
5852
+ }),
5853
+ _ts_param(0, (0, import_common17.Param)("name")),
5854
+ _ts_metadata4("design:type", Function),
5855
+ _ts_metadata4("design:paramtypes", [
5856
+ String
5857
+ ]),
5858
+ _ts_metadata4("design:returntype", Promise)
5859
+ ], DevResourcesController.prototype, "removeFromMenu", null);
5860
+ _ts_decorate5([
5861
+ (0, import_common17.Post)(":name/add-to-menu"),
5862
+ (0, import_swagger12.ApiOperation)({
5863
+ summary: "Dev-only: publish + un-hide a resource and optionally set sidebar group/position/label"
5864
+ }),
5865
+ (0, import_swagger12.ApiResponse)({
5866
+ status: 200,
5867
+ description: "Resource added to menu"
5868
+ }),
5869
+ _ts_param(0, (0, import_common17.Param)("name")),
5870
+ _ts_param(1, (0, import_common17.Body)()),
5871
+ _ts_metadata4("design:type", Function),
5872
+ _ts_metadata4("design:paramtypes", [
5873
+ String,
5874
+ Object
5875
+ ]),
5876
+ _ts_metadata4("design:returntype", Promise)
5877
+ ], DevResourcesController.prototype, "addToMenu", null);
5878
+ _ts_decorate5([
5879
+ (0, import_common17.Patch)(":name/flags"),
5880
+ (0, import_swagger12.ApiOperation)({
5881
+ summary: "Dev-only: set arbitrary resource flags (draft, sidebar.hide, group, position, label)"
5882
+ }),
5883
+ (0, import_swagger12.ApiResponse)({
5884
+ status: 200,
5885
+ description: "Flags updated"
5886
+ }),
5887
+ _ts_param(0, (0, import_common17.Param)("name")),
5888
+ _ts_param(1, (0, import_common17.Body)()),
5889
+ _ts_metadata4("design:type", Function),
5890
+ _ts_metadata4("design:paramtypes", [
5891
+ String,
5892
+ typeof ResourceFlagPatch === "undefined" ? Object : ResourceFlagPatch
5893
+ ]),
5894
+ _ts_metadata4("design:returntype", Promise)
5895
+ ], DevResourcesController.prototype, "updateFlags", null);
5517
5896
  DevResourcesController = _ts_decorate5([
5518
- (0, import_common12.Controller)("_app/resources"),
5519
- (0, import_swagger7.ApiTags)("Dev tools"),
5897
+ (0, import_common17.Controller)("_app/resources"),
5898
+ (0, import_swagger12.ApiTags)("Dev tools"),
5520
5899
  _ts_metadata4("design:type", Function),
5521
5900
  _ts_metadata4("design:paramtypes", [
5522
- typeof DataSourceRegistry === "undefined" ? Object : DataSourceRegistry
5901
+ typeof DataSourceRegistry === "undefined" ? Object : DataSourceRegistry,
5902
+ typeof ResourceConfigRegistry === "undefined" ? Object : ResourceConfigRegistry
5523
5903
  ])
5524
5904
  ], DevResourcesController);
5525
5905
 
5526
5906
  // src/lib/crud/enum-registry/enum-registry.types.ts
5527
- var import_zod23 = require("zod");
5528
- var EnumEntrySchema = import_zod23.z.object({
5529
- value: import_zod23.z.unknown(),
5530
- label: import_zod23.z.string()
5907
+ var import_zod25 = require("zod");
5908
+ var EnumEntrySchema = import_zod25.z.object({
5909
+ value: import_zod25.z.unknown(),
5910
+ label: import_zod25.z.string()
5531
5911
  });
5532
- var EnumRegistrySchema = import_zod23.z.record(import_zod23.z.string(), import_zod23.z.array(EnumEntrySchema)).default({});
5912
+ var EnumRegistrySchema = import_zod25.z.record(import_zod25.z.string(), import_zod25.z.array(EnumEntrySchema)).default({});
5533
5913
 
5534
5914
  // src/lib/crud/enum-registry/enum-registry.loader.ts
5535
5915
  var import_node_fs5 = require("fs");
5536
- var import_node_path12 = require("path");
5916
+ var import_node_path13 = require("path");
5537
5917
  var ENUMS_FILE = "crouton.enums.json";
5538
5918
  var loadEnumRegistry = /* @__PURE__ */ __name((startDir, enumsFile) => {
5539
5919
  let file = enumsFile;
5540
5920
  if (!file) {
5541
5921
  let dir = startDir;
5542
5922
  while (true) {
5543
- const candidate = (0, import_node_path12.join)(dir, ENUMS_FILE);
5923
+ const candidate = (0, import_node_path13.join)(dir, ENUMS_FILE);
5544
5924
  if ((0, import_node_fs5.existsSync)(candidate)) {
5545
5925
  file = candidate;
5546
5926
  break;
5547
5927
  }
5548
- const parent = (0, import_node_path12.dirname)(dir);
5928
+ const parent = (0, import_node_path13.dirname)(dir);
5549
5929
  if (parent === dir) break;
5550
5930
  dir = parent;
5551
5931
  }
@@ -5574,10 +5954,46 @@ var injectEnumValues = /* @__PURE__ */ __name((columns, enums) => {
5574
5954
  }
5575
5955
  }, "injectEnumValues");
5576
5956
 
5957
+ // src/lib/crud/builder/schema.helpers.ts
5958
+ var pickByColumns = /* @__PURE__ */ __name((schema, columns, filter) => {
5959
+ if (!schema) return void 0;
5960
+ if (!columns?.length) return schema;
5961
+ const baseFilter = /* @__PURE__ */ __name((c) => !isRelation(c) && (filter ? filter(c) : true), "baseFilter");
5962
+ const schemaKeys = new Set(Object.keys(schema.shape));
5963
+ const filtered = columns.filter((c) => baseFilter(c) && schemaKeys.has(c.id));
5964
+ if (!filtered.length) return void 0;
5965
+ const mask = Object.fromEntries(filtered.map((c) => [
5966
+ c.id,
5967
+ true
5968
+ ]));
5969
+ return schema.pick(mask);
5970
+ }, "pickByColumns");
5971
+ var opWithSchema = /* @__PURE__ */ __name((enabled, schema) => {
5972
+ if (enabled === false) return void 0;
5973
+ return schema ? {
5974
+ schema
5975
+ } : true;
5976
+ }, "opWithSchema");
5977
+ var upsertOp = /* @__PURE__ */ __name((entry, schema) => {
5978
+ if (!entry) return void 0;
5979
+ if (entry === true) {
5980
+ throw new Error("`operations.upsert` must be an object with `upsertOn`, not `true`.");
5981
+ }
5982
+ if (typeof entry === "object") {
5983
+ return {
5984
+ upsertOn: entry.upsertOn,
5985
+ ...schema && {
5986
+ schema
5987
+ }
5988
+ };
5989
+ }
5990
+ return void 0;
5991
+ }, "upsertOp");
5992
+
5577
5993
  // src/lib/crud/adapter/relation-type.ts
5578
- var import_zod24 = require("zod");
5994
+ var import_zod26 = require("zod");
5579
5995
  var unwrapZodType = /* @__PURE__ */ __name((type) => {
5580
- if (type instanceof import_zod24.ZodOptional || type instanceof import_zod24.ZodNullable) {
5996
+ if (type instanceof import_zod26.ZodOptional || type instanceof import_zod26.ZodNullable) {
5581
5997
  return unwrapZodType(type.unwrap());
5582
5998
  }
5583
5999
  return type;
@@ -5587,7 +6003,7 @@ var deriveRelationType = /* @__PURE__ */ __name((schema, columnId) => {
5587
6003
  const field = schema.shape[columnId];
5588
6004
  if (!field) return void 0;
5589
6005
  const inner = unwrapZodType(field);
5590
- return inner instanceof import_zod24.ZodArray ? "oneToMany" : "manyToOne";
6006
+ return inner instanceof import_zod26.ZodArray ? "oneToMany" : "manyToOne";
5591
6007
  }, "deriveRelationType");
5592
6008
  var deriveRelationTypeFromColumns = /* @__PURE__ */ __name((col, cols) => {
5593
6009
  const base = col.column ?? col.id;
@@ -5619,7 +6035,7 @@ var enrichRelationTypes = /* @__PURE__ */ __name((columns, schema) => {
5619
6035
 
5620
6036
  // src/lib/crud/resource/ReadResourceJson.ts
5621
6037
  var import_node_fs6 = require("fs");
5622
- var import_node_path13 = require("path");
6038
+ var import_node_path14 = require("path");
5623
6039
  var readResourceJson = /* @__PURE__ */ __name((jsonPath) => {
5624
6040
  if (!(0, import_node_fs6.existsSync)(jsonPath)) return void 0;
5625
6041
  let fileContent;
@@ -5642,26 +6058,26 @@ var readResourceJson = /* @__PURE__ */ __name((jsonPath) => {
5642
6058
  success: true,
5643
6059
  data: {
5644
6060
  json: resource.data,
5645
- dir: (0, import_node_path13.dirname)(jsonPath)
6061
+ dir: (0, import_node_path14.dirname)(jsonPath)
5646
6062
  }
5647
6063
  };
5648
6064
  }, "readResourceJson");
5649
6065
 
5650
6066
  // src/lib/crud/adapter/resource-resolver.ts
5651
6067
  var import_node_fs7 = require("fs");
5652
- var import_node_path14 = require("path");
6068
+ var import_node_path15 = require("path");
5653
6069
  var unwrap2 = /* @__PURE__ */ __name((result) => {
5654
6070
  if (result?.success) return result.data;
5655
6071
  return void 0;
5656
6072
  }, "unwrap");
5657
6073
  var resolveChildResource = /* @__PURE__ */ __name((resourcePath, parentDir) => {
5658
- const directPath = (0, import_node_path14.resolve)(parentDir, resourcePath);
6074
+ const directPath = (0, import_node_path15.resolve)(parentDir, resourcePath);
5659
6075
  try {
5660
6076
  if (resourcePath.endsWith(".json") && (0, import_node_fs7.existsSync)(directPath)) {
5661
6077
  return unwrap2(readResourceJson(directPath));
5662
6078
  }
5663
6079
  const childName = resourcePath.replace(/^\.\//, "").replace(/\.resource$/, "");
5664
- const childJsonPath = (0, import_node_path14.resolve)((0, import_node_path14.dirname)(parentDir), childName, "resource.json");
6080
+ const childJsonPath = (0, import_node_path15.resolve)((0, import_node_path15.dirname)(parentDir), childName, "resource.json");
5665
6081
  return unwrap2(readResourceJson(childJsonPath));
5666
6082
  } catch {
5667
6083
  return void 0;
@@ -5875,7 +6291,7 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
5875
6291
  const childOps = childJson?.operations ?? {};
5876
6292
  return {
5877
6293
  column: c.id,
5878
- relation: c.id,
6294
+ relation: c.fieldInput?.relation ?? c.id,
5879
6295
  childRoute,
5880
6296
  childModel: c.id,
5881
6297
  foreignKey: c.fieldInput?.foreignKey ?? `${parentModel}Id`,
@@ -5908,7 +6324,14 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
5908
6324
  calculatedColumns: childJson.calculatedColumns
5909
6325
  },
5910
6326
  ...(c.hiddenInForm === false || c.hiddenInView === false) && {
5911
- includeInFindOne: true
6327
+ includeInFindOne: true,
6328
+ ...(() => {
6329
+ const opts = c.fieldInput?.options;
6330
+ if (opts?.sort) return {
6331
+ findOneOrderBy: buildChildSortClause(opts.sort, opts.sortDir)
6332
+ };
6333
+ return {};
6334
+ })()
5912
6335
  },
5913
6336
  ...buildValueLabelColumns(childColumns).length && {
5914
6337
  valueLabelColumns: buildValueLabelColumns(childColumns)
@@ -6031,33 +6454,33 @@ var buildLookup = /* @__PURE__ */ __name((columns) => {
6031
6454
  }, "buildLookup");
6032
6455
 
6033
6456
  // src/lib/crud/hooks/hooks.types.ts
6034
- var import_zod25 = require("zod");
6035
- var WriteOpSchema = import_zod25.z.enum([
6457
+ var import_zod27 = require("zod");
6458
+ var WriteOpSchema = import_zod27.z.enum([
6036
6459
  "create",
6037
6460
  "update",
6038
6461
  "patch",
6039
6462
  "upsert",
6040
6463
  "delete"
6041
6464
  ]);
6042
- var ReadOpSchema = import_zod25.z.enum([
6465
+ var ReadOpSchema = import_zod27.z.enum([
6043
6466
  "findAll",
6044
6467
  "findOne"
6045
6468
  ]);
6046
- var ResourceHooksSchema = import_zod25.z.object({
6047
- beforeWrite: import_zod25.z.custom().optional(),
6048
- afterWrite: import_zod25.z.custom().optional(),
6049
- afterRead: import_zod25.z.custom().optional()
6469
+ var ResourceHooksSchema = import_zod27.z.object({
6470
+ beforeWrite: import_zod27.z.custom().optional(),
6471
+ afterWrite: import_zod27.z.custom().optional(),
6472
+ afterRead: import_zod27.z.custom().optional()
6050
6473
  });
6051
6474
 
6052
6475
  // src/lib/crud/hooks/hooks.loader.ts
6053
- var import_node_path15 = require("path");
6476
+ var import_node_path16 = require("path");
6054
6477
  var loadResourceHooks = /* @__PURE__ */ __name(async (basePath) => {
6055
6478
  const file = findModule(basePath, "hooks");
6056
6479
  return file ? importDefault(file) : void 0;
6057
6480
  }, "loadResourceHooks");
6058
6481
  var loadSubResourceHooks = /* @__PURE__ */ __name(async (subResources, basePath) => {
6059
6482
  for (const sub of subResources) {
6060
- const file = sub.name ? findModule((0, import_node_path15.join)(basePath, "hooks"), sub.name) : void 0;
6483
+ const file = sub.name ? findModule((0, import_node_path16.join)(basePath, "hooks"), sub.name) : void 0;
6061
6484
  if (!file) continue;
6062
6485
  const hooks = await importDefault(file);
6063
6486
  if (hooks) sub.hooks = hooks;
@@ -6124,32 +6547,9 @@ var migrateResourceJsonFile = /* @__PURE__ */ __name((jsonPath, opts) => {
6124
6547
  }
6125
6548
  }, "migrateResourceJsonFile");
6126
6549
 
6127
- // src/lib/crud/resource/resource-load-report.registry.ts
6128
- var ResourceLoadReportRegistry = class ResourceLoadReportRegistry2 {
6129
- static {
6130
- __name(this, "ResourceLoadReportRegistry");
6131
- }
6132
- notices = [];
6133
- record(n) {
6134
- this.notices.push(n);
6135
- }
6136
- getAll() {
6137
- return [
6138
- ...this.notices
6139
- ];
6140
- }
6141
- getByState(state) {
6142
- return this.notices.filter((n) => n.state === state);
6143
- }
6144
- clear() {
6145
- this.notices = [];
6146
- }
6147
- };
6148
- var resourceLoadReportRegistry = new ResourceLoadReportRegistry();
6149
-
6150
6550
  // src/lib/crud/loader/index.ts
6151
6551
  var import_node_fs9 = require("fs");
6152
- var import_node_path16 = require("path");
6552
+ var import_node_path17 = require("path");
6153
6553
  var loadResourceConfigsFromDir = /* @__PURE__ */ __name(async (dirPath, baseUrl, enumsFile, onResourceDir) => {
6154
6554
  if (!(0, import_node_fs9.existsSync)(dirPath)) return [];
6155
6555
  resourceLoadErrorsRegistry.clear();
@@ -6161,11 +6561,11 @@ var loadResourceConfigsFromDir = /* @__PURE__ */ __name(async (dirPath, baseUrl,
6161
6561
  const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
6162
6562
  const configs = [];
6163
6563
  for (const dir of dirs) {
6164
- const basePath = (0, import_node_path16.join)(dirPath, dir);
6564
+ const basePath = (0, import_node_path17.join)(dirPath, dir);
6165
6565
  const schemaFile = findModule(basePath, "schema");
6166
6566
  const schema = schemaFile ? await importDefault(schemaFile) : void 0;
6167
6567
  const hooks = await loadResourceHooks(basePath);
6168
- const jsonFile = (0, import_node_path16.join)(basePath, "resource.json");
6568
+ const jsonFile = (0, import_node_path17.join)(basePath, "resource.json");
6169
6569
  if ((0, import_node_fs9.existsSync)(jsonFile)) {
6170
6570
  const migration = migrateResourceJsonFile(jsonFile, {
6171
6571
  isDev: IS_DEV
@@ -6284,22 +6684,22 @@ var FileSystemResourceConfigLoader = class extends ResourceConfigLoader2 {
6284
6684
 
6285
6685
  // src/lib/crud/status/status.service.ts
6286
6686
  var import_node_fs10 = require("fs");
6287
- var import_node_path17 = require("path");
6687
+ var import_node_path18 = require("path");
6288
6688
  var import_node_url = require("url");
6289
6689
  var DB_CHECK_TIMEOUT_MS = 3e3;
6290
6690
  var CONNECTION_STRING_PATTERN = /(?:postgresql|postgres|mysql|mongodb|sqlserver|sqlite):\/\/[^\s"')]+/gi;
6291
6691
  var stripConnectionStrings = /* @__PURE__ */ __name((message) => message.replace(CONNECTION_STRING_PATTERN, "[REDACTED]"), "stripConnectionStrings");
6292
6692
  var getCroutonVersion = /* @__PURE__ */ __name(() => {
6293
6693
  try {
6294
- const startDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path17.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
6694
+ const startDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path18.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
6295
6695
  let dir = startDir;
6296
- while (dir !== (0, import_node_path17.dirname)(dir)) {
6297
- const pkgPath = (0, import_node_path17.join)(dir, "package.json");
6696
+ while (dir !== (0, import_node_path18.dirname)(dir)) {
6697
+ const pkgPath = (0, import_node_path18.join)(dir, "package.json");
6298
6698
  if ((0, import_node_fs10.existsSync)(pkgPath)) {
6299
6699
  const pkg = JSON.parse((0, import_node_fs10.readFileSync)(pkgPath, "utf-8"));
6300
6700
  if (pkg.name === "@ghentcdh/crouton-api") return pkg.version;
6301
6701
  }
6302
- dir = (0, import_node_path17.dirname)(dir);
6702
+ dir = (0, import_node_path18.dirname)(dir);
6303
6703
  }
6304
6704
  } catch {
6305
6705
  }
@@ -6335,7 +6735,10 @@ var getResourceStatus = /* @__PURE__ */ __name((loadedConfigs) => {
6335
6735
  name: c.name,
6336
6736
  path: c.route,
6337
6737
  valid: true,
6338
- version: c.schemaVersion ?? CURRENT_RESOURCE_VERSION
6738
+ version: c.schemaVersion ?? CURRENT_RESOURCE_VERSION,
6739
+ ...c.sidebar?.hide ? {
6740
+ hidden: true
6741
+ } : {}
6339
6742
  }));
6340
6743
  const failed = resourceLoadErrorsRegistry.getAll().map((e) => ({
6341
6744
  name: e.name,
@@ -6367,7 +6770,114 @@ var buildSummary = /* @__PURE__ */ __name((databases, resources) => {
6367
6770
  resourceErrors
6368
6771
  };
6369
6772
  }, "buildSummary");
6370
- var buildStatus = /* @__PURE__ */ __name(async (registry, loadedConfigs) => {
6773
+ var getSystemEnums = /* @__PURE__ */ __name(() => {
6774
+ return [
6775
+ {
6776
+ name: "RelationType",
6777
+ category: "Relationships",
6778
+ values: [
6779
+ {
6780
+ value: "oneToOne",
6781
+ label: "One to One"
6782
+ },
6783
+ {
6784
+ value: "manyToOne",
6785
+ label: "Many to One"
6786
+ },
6787
+ {
6788
+ value: "oneToMany",
6789
+ label: "One to Many"
6790
+ },
6791
+ {
6792
+ value: "manyToMany",
6793
+ label: "Many to Many"
6794
+ }
6795
+ ]
6796
+ },
6797
+ {
6798
+ name: "DetailLayout",
6799
+ category: "Layout",
6800
+ values: [
6801
+ {
6802
+ value: "collapse",
6803
+ label: "Collapse"
6804
+ },
6805
+ {
6806
+ value: "row",
6807
+ label: "Row"
6808
+ }
6809
+ ]
6810
+ },
6811
+ {
6812
+ name: "SortDirection",
6813
+ category: "Sorting",
6814
+ values: [
6815
+ {
6816
+ value: "asc",
6817
+ label: "Ascending"
6818
+ },
6819
+ {
6820
+ value: "desc",
6821
+ label: "Descending"
6822
+ }
6823
+ ]
6824
+ },
6825
+ {
6826
+ name: "DisplayMode",
6827
+ category: "Display",
6828
+ values: [
6829
+ {
6830
+ value: "page",
6831
+ label: "Page"
6832
+ },
6833
+ {
6834
+ value: "modal",
6835
+ label: "Modal"
6836
+ }
6837
+ ]
6838
+ },
6839
+ {
6840
+ name: "ModalSize",
6841
+ category: "Display",
6842
+ values: [
6843
+ {
6844
+ value: "xs",
6845
+ label: "Extra Small"
6846
+ },
6847
+ {
6848
+ value: "sm",
6849
+ label: "Small"
6850
+ },
6851
+ {
6852
+ value: "lg",
6853
+ label: "Large"
6854
+ },
6855
+ {
6856
+ value: "xl",
6857
+ label: "Extra Large"
6858
+ }
6859
+ ]
6860
+ }
6861
+ ];
6862
+ }, "getSystemEnums");
6863
+ var getProjectEnums = /* @__PURE__ */ __name((enumRegistry) => {
6864
+ if (!enumRegistry) return [];
6865
+ return Object.entries(enumRegistry).map(([name, entries]) => ({
6866
+ name,
6867
+ category: "Project",
6868
+ values: entries.map((entry) => ({
6869
+ value: entry.value,
6870
+ label: entry.label
6871
+ }))
6872
+ }));
6873
+ }, "getProjectEnums");
6874
+ var getEnums = /* @__PURE__ */ __name((enumRegistry) => {
6875
+ return {
6876
+ system: getSystemEnums(),
6877
+ project: getProjectEnums(enumRegistry)
6878
+ };
6879
+ }, "getEnums");
6880
+ var buildStatus = /* @__PURE__ */ __name(async (registry, loadedConfigs, enumRegistry) => {
6371
6881
  const databases = await checkDatabases(registry);
6372
6882
  const resources = getResourceStatus(loadedConfigs);
6373
6883
  const summary = buildSummary(databases, resources);
@@ -6377,13 +6887,14 @@ var buildStatus = /* @__PURE__ */ __name(async (registry, loadedConfigs) => {
6377
6887
  environment: getEnvironment(),
6378
6888
  summary,
6379
6889
  databases,
6380
- resources
6890
+ resources,
6891
+ enums: getEnums(enumRegistry)
6381
6892
  };
6382
6893
  }, "buildStatus");
6383
6894
 
6384
6895
  // src/lib/crud/status/status.controller.ts
6385
- var import_common13 = require("@nestjs/common");
6386
- var import_swagger8 = require("@nestjs/swagger");
6896
+ var import_common18 = require("@nestjs/common");
6897
+ var import_swagger13 = require("@nestjs/swagger");
6387
6898
  function _ts_decorate6(decorators, target, key, desc2) {
6388
6899
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
6389
6900
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
@@ -6395,7 +6906,7 @@ function _ts_metadata5(k, v) {
6395
6906
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6396
6907
  }
6397
6908
  __name(_ts_metadata5, "_ts_metadata");
6398
- var createStatusController = /* @__PURE__ */ __name(() => {
6909
+ var createStatusController = /* @__PURE__ */ __name((enumRegistry) => {
6399
6910
  let StatusController = class StatusController {
6400
6911
  static {
6401
6912
  __name(this, "StatusController");
@@ -6408,15 +6919,15 @@ var createStatusController = /* @__PURE__ */ __name(() => {
6408
6919
  }
6409
6920
  async getStatus() {
6410
6921
  const configs = await this.configRegistry.getAll();
6411
- return buildStatus(this.dataSourceRegistry, configs);
6922
+ return buildStatus(this.dataSourceRegistry, configs, enumRegistry);
6412
6923
  }
6413
6924
  };
6414
6925
  _ts_decorate6([
6415
- (0, import_common13.Get)("status.json"),
6416
- (0, import_swagger8.ApiOperation)({
6926
+ (0, import_common18.Get)("status.json"),
6927
+ (0, import_swagger13.ApiOperation)({
6417
6928
  summary: "Crouton system status (db, resources, version)"
6418
6929
  }),
6419
- (0, import_swagger8.ApiResponse)({
6930
+ (0, import_swagger13.ApiResponse)({
6420
6931
  status: 200,
6421
6932
  description: "System status"
6422
6933
  }),
@@ -6425,8 +6936,8 @@ var createStatusController = /* @__PURE__ */ __name(() => {
6425
6936
  _ts_metadata5("design:returntype", Promise)
6426
6937
  ], StatusController.prototype, "getStatus", null);
6427
6938
  StatusController = _ts_decorate6([
6428
- (0, import_common13.Controller)("crouton"),
6429
- (0, import_swagger8.ApiTags)("Status"),
6939
+ (0, import_common18.Controller)("crouton"),
6940
+ (0, import_swagger13.ApiTags)("Status"),
6430
6941
  _ts_metadata5("design:type", Function),
6431
6942
  _ts_metadata5("design:paramtypes", [
6432
6943
  typeof DataSourceRegistry === "undefined" ? Object : DataSourceRegistry,
@@ -6441,6 +6952,8 @@ var createStatusController = /* @__PURE__ */ __name(() => {
6441
6952
  }, "createStatusController");
6442
6953
 
6443
6954
  // src/lib/crouton-api.module.ts
6955
+ var import_node_path19 = require("path");
6956
+ var import_node_url2 = require("url");
6444
6957
  function _ts_decorate7(decorators, target, key, desc2) {
6445
6958
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
6446
6959
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
@@ -6461,6 +6974,8 @@ var CroutonApiModule = class _CroutonApiModule {
6461
6974
  }
6462
6975
  static forResources(configs, dataSources, loader, { baseUrl }, config) {
6463
6976
  const dataSourceRegistry = new DataSourceRegistry(dataSources);
6977
+ const startDir = typeof __dirname !== "undefined" ? __dirname : (0, import_node_path19.dirname)((0, import_node_url2.fileURLToPath)(importMetaUrl));
6978
+ const enumRegistry = loadEnumRegistry(startDir, config.enumsFile);
6464
6979
  const validConfigs = [];
6465
6980
  for (const c of configs) {
6466
6981
  try {
@@ -6492,7 +7007,7 @@ var CroutonApiModule = class _CroutonApiModule {
6492
7007
  ...IS_DEV ? [
6493
7008
  DevResourcesController
6494
7009
  ] : [],
6495
- createStatusController()
7010
+ createStatusController(enumRegistry)
6496
7011
  ];
6497
7012
  return {
6498
7013
  module: _CroutonApiModule,
@@ -6528,7 +7043,7 @@ var CroutonApiModule = class _CroutonApiModule {
6528
7043
  }
6529
7044
  };
6530
7045
  CroutonApiModule = _ts_decorate7([
6531
- (0, import_common14.Module)({
7046
+ (0, import_common19.Module)({
6532
7047
  controllers: [],
6533
7048
  providers: [],
6534
7049
  exports: []