@ghentcdh/crouton-core 0.0.1-alpha.39 → 0.0.1-alpha.41

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
@@ -36,6 +36,8 @@ __export(index_exports, {
36
36
  CONFIG_FILES: () => CONFIG_FILES,
37
37
  CURRENT_RESOURCE_VERSION: () => CURRENT_RESOURCE_VERSION,
38
38
  CalculatedColumnSchema: () => CalculatedColumnSchema,
39
+ ColumnTypeSchema: () => ColumnTypeSchema,
40
+ ColumnTypeShorthandSchema: () => ColumnTypeShorthandSchema,
39
41
  ContainerBuilder: () => ContainerBuilder,
40
42
  ControlBuilder: () => ControlBuilder,
41
43
  ControlType: () => ControlType,
@@ -47,12 +49,14 @@ __export(index_exports, {
47
49
  ElementBuilder: () => ElementBuilder,
48
50
  FieldInputSchema: () => FieldInputSchema,
49
51
  FieldVariantSchema: () => FieldVariantSchema,
52
+ I18nConfigSchema: () => I18nConfigSchema,
50
53
  JsonActionSchema: () => JsonActionSchema,
51
54
  JsonColumnSchema: () => JsonColumnSchema,
52
55
  JsonIncludeEntrySchema: () => JsonIncludeEntrySchema,
53
56
  JsonLinkActionSchema: () => JsonLinkActionSchema,
54
57
  JsonOperationsSchema: () => JsonOperationsSchema,
55
58
  JsonProcedureActionSchema: () => JsonProcedureActionSchema,
59
+ JsonSchemaFragmentSchema: () => JsonSchemaFragmentSchema,
56
60
  LayoutBuilder: () => LayoutBuilder,
57
61
  LayoutType: () => LayoutType,
58
62
  MIGRATIONS: () => MIGRATIONS,
@@ -64,6 +68,7 @@ __export(index_exports, {
64
68
  OperatorNoValue: () => OperatorNoValue,
65
69
  OperatorOptions: () => OperatorOptions,
66
70
  OperatorsByType: () => OperatorsByType,
71
+ ParentRefSchema: () => ParentRefSchema,
67
72
  PositiveRequestNumber: () => PositiveRequestNumber,
68
73
  RelationFieldInputOptionsSchema: () => RelationFieldInputOptionsSchema,
69
74
  RelationType: () => RelationType,
@@ -71,6 +76,7 @@ __export(index_exports, {
71
76
  RequestSchemaWithOffset: () => RequestSchemaWithOffset,
72
77
  ResourceJsonSchema: () => ResourceJsonSchema,
73
78
  ResourceJsonShape: () => ResourceJsonShape,
79
+ ResourceKindSchema: () => ResourceKindSchema,
74
80
  ResponseRequestSchema: () => ResponseRequestSchema,
75
81
  ResponseSchema: () => ResponseSchema,
76
82
  RulesetSchema: () => RulesetSchema,
@@ -81,9 +87,11 @@ __export(index_exports, {
81
87
  StringOrArray: () => StringOrArray,
82
88
  TableBuilder: () => TableBuilder,
83
89
  TextCellBuilder: () => TextCellBuilder,
90
+ TranslationBundleSchema: () => TranslationBundleSchema,
84
91
  ViewColumnConfigSchema: () => ViewColumnConfigSchema,
85
92
  ViewConfigSchema: () => ViewConfigSchema,
86
93
  WhenConditionSchema: () => WhenConditionSchema,
94
+ actionKey: () => actionKey,
87
95
  applySchemaTransforms: () => applySchemaTransforms,
88
96
  buildConditionSchema: () => buildConditionSchema,
89
97
  buildFilter: () => buildFilter,
@@ -93,13 +101,22 @@ __export(index_exports, {
93
101
  buildSortKey: () => buildSortKey,
94
102
  buildTableUiSchema: () => buildTableUiSchema,
95
103
  buildViews: () => buildViews,
104
+ buildViewsFromColumnTypes: () => buildViewsFromColumnTypes,
96
105
  buildViewsFromColumns: () => buildViewsFromColumns,
106
+ buildViewsWithSource: () => buildViewsWithSource,
97
107
  colPosition: () => colPosition,
98
108
  columnForContext: () => columnForContext,
109
+ columnKey: () => columnKey,
110
+ columnToJsonSchemaProperty: () => columnToJsonSchemaProperty,
111
+ columnTypeName: () => columnTypeName,
112
+ columnTypeSchemaSource: () => columnTypeSchemaSource,
113
+ columnTypeToJsonSchema: () => columnTypeToJsonSchema,
99
114
  createSchema: () => createSchema,
115
+ createTranslator: () => createTranslator,
100
116
  deriveSortId: () => deriveSortId,
101
117
  dropNullableFromRequired: () => dropNullableFromRequired,
102
118
  enforceRequiredStringMinLength: () => enforceRequiredStringMinLength,
119
+ enumKey: () => enumKey,
103
120
  extractFilters: () => extractFilters,
104
121
  filterToString: () => filterToString,
105
122
  findColumnDef: () => findColumnDef,
@@ -107,29 +124,46 @@ __export(index_exports, {
107
124
  fromValueLabel: () => fromValueLabel,
108
125
  injectCalculatedColumns: () => injectCalculatedColumns,
109
126
  injectCalculatedColumnsToView: () => injectCalculatedColumnsToView,
127
+ isArrayColumn: () => isArrayColumn,
128
+ isArrayColumnType: () => isArrayColumnType,
110
129
  isAutocomplete: () => isAutocomplete,
111
130
  isBoolean: () => isBoolean,
131
+ isCustomKind: () => isCustomKind,
112
132
  isDateRange: () => isDateRange,
133
+ isObjectCell: () => isObjectCell,
134
+ isObjectColumn: () => isObjectColumn,
135
+ isObjectColumnType: () => isObjectColumnType,
113
136
  isRecordCell: () => isRecordCell,
114
137
  isRelation: () => isRelation,
115
138
  jsonSchemaOpts: () => jsonSchemaOpts,
116
139
  labelFromId: () => labelFromId,
117
140
  mergeFieldVariant: () => mergeFieldVariant,
118
141
  normalizeLabel: () => normalizeLabel,
142
+ offsetOf: () => offsetOf,
119
143
  patchFilterProperties: () => patchFilterProperties,
120
144
  pickSharedCellOptions: () => pickSharedCellOptions,
121
145
  resolveDefaultSort: () => resolveDefaultSort,
122
146
  resolveFormField: () => resolveFormField,
147
+ resolveLanguage: () => resolveLanguage,
123
148
  resolveTableField: () => resolveTableField,
124
149
  resolveViewField: () => resolveViewField,
150
+ resourceControllerPath: () => resourceControllerPath,
151
+ resourceSidebarKey: () => resourceSidebarKey,
152
+ resourceTitleKey: () => resourceTitleKey,
125
153
  resourceVersionOf: () => resourceVersionOf,
126
154
  runResourceMigrations: () => runResourceMigrations,
155
+ sidebarGroupKey: () => sidebarGroupKey,
127
156
  sortByPosition: () => sortByPosition,
128
157
  stringToFilter: () => stringToFilter,
158
+ subResourceColumnKey: () => subResourceColumnKey,
159
+ subResourceTitleKey: () => subResourceTitleKey,
129
160
  toValueLabel: () => toValueLabel,
130
161
  toViewColumn: () => toViewColumn,
131
162
  transformDataSource: () => transformDataSource,
132
- uiFromJsonSchema: () => uiFromJsonSchema
163
+ uiFromJsonSchema: () => uiFromJsonSchema,
164
+ uiKey: () => uiKey,
165
+ validationKey: () => validationKey,
166
+ zodSchemaSource: () => zodSchemaSource
133
167
  });
134
168
  module.exports = __toCommonJS(index_exports);
135
169
 
@@ -162,6 +196,7 @@ var RequestSchemaWithOffset = RequestSchema.transform((schema) => {
162
196
  offset: (page - 1) * pageSize
163
197
  };
164
198
  });
199
+ var offsetOf = (params) => params.offset ?? (params.page - 1) * params.pageSize;
165
200
 
166
201
  // src/lib/filter.ts
167
202
  var Operator = [
@@ -344,8 +379,12 @@ var enforceRequiredStringMinLength = (schema) => {
344
379
  };
345
380
  var findProperty = (column, schema) => {
346
381
  if (!column.scope) return { id: null, property: null };
347
- const id = column.scope?.substring("#/properties/".length);
348
- const property = schema?.properties?.[id] ?? {};
382
+ const id = column.scope.substring("#/properties/".length);
383
+ const segments = id.split("/properties/");
384
+ let property = schema?.properties?.[segments[0]] ?? {};
385
+ for (const segment of segments.slice(1)) {
386
+ property = property?.properties?.[segment] ?? {};
387
+ }
349
388
  return { id, property };
350
389
  };
351
390
 
@@ -428,6 +467,7 @@ var ControlType = {
428
467
  textArea: "textarea",
429
468
  markdown: "markdown",
430
469
  array: "array",
470
+ object: "object",
431
471
  custom: "custom",
432
472
  select: "select",
433
473
  mutliSelect: "mutliSelect",
@@ -511,6 +551,16 @@ var ControlBuilder = class _ControlBuilder extends ElementBuilder {
511
551
  ...options ?? {}
512
552
  });
513
553
  }
554
+ /**
555
+ * Render a datepicker. Defaults to a day-only picker; pass
556
+ * `{ format: 'dateTime' }` (or `{ withTime: true }`) to include a time.
557
+ */
558
+ date(options) {
559
+ return this.opt({
560
+ format: ControlType.date,
561
+ ...options ?? {}
562
+ });
563
+ }
514
564
  textArea(options) {
515
565
  return this.opt({
516
566
  format: ControlType.textArea,
@@ -785,7 +835,7 @@ var JsonOperationsSchema = import_zod8.z.object({
785
835
  });
786
836
 
787
837
  // src/lib/resource/ResourceJson.schema.ts
788
- var import_zod15 = require("zod");
838
+ var import_zod18 = require("zod");
789
839
 
790
840
  // src/lib/resource/CalculatedColumn.schema.ts
791
841
  var import_zod10 = require("zod");
@@ -936,61 +986,190 @@ var CalculatedColumnSchema = import_zod10.z.object({
936
986
  }).transform(normalizeLabel);
937
987
 
938
988
  // src/lib/resource/Column.ts
989
+ var import_zod12 = require("zod");
990
+
991
+ // src/lib/resource/ColumnType.schema.ts
939
992
  var import_zod11 = require("zod");
940
- var WhenConditionSchema = import_zod11.z.object({
941
- field: import_zod11.z.string(),
993
+ var ColumnTypeShorthandSchema = import_zod11.z.enum([
994
+ "string",
995
+ "number",
996
+ "integer",
997
+ "boolean",
998
+ "date",
999
+ "date-time",
1000
+ "object",
1001
+ "array"
1002
+ ]);
1003
+ var JsonSchemaFragmentSchema = import_zod11.z.lazy(
1004
+ () => import_zod11.z.object({
1005
+ type: import_zod11.z.union([import_zod11.z.string(), import_zod11.z.array(import_zod11.z.string())]).optional(),
1006
+ format: import_zod11.z.string().optional(),
1007
+ enum: import_zod11.z.array(import_zod11.z.unknown()).optional(),
1008
+ const: import_zod11.z.unknown().optional(),
1009
+ nullable: import_zod11.z.boolean().optional(),
1010
+ properties: import_zod11.z.record(import_zod11.z.string(), JsonSchemaFragmentSchema).optional(),
1011
+ required: import_zod11.z.array(import_zod11.z.string()).optional(),
1012
+ items: JsonSchemaFragmentSchema.optional(),
1013
+ additionalProperties: import_zod11.z.union([import_zod11.z.boolean(), JsonSchemaFragmentSchema]).optional(),
1014
+ title: import_zod11.z.string().optional(),
1015
+ description: import_zod11.z.string().optional(),
1016
+ default: import_zod11.z.unknown().optional(),
1017
+ minimum: import_zod11.z.number().optional(),
1018
+ maximum: import_zod11.z.number().optional(),
1019
+ minLength: import_zod11.z.number().optional(),
1020
+ maxLength: import_zod11.z.number().optional(),
1021
+ pattern: import_zod11.z.string().optional()
1022
+ }).catchall(import_zod11.z.unknown())
1023
+ );
1024
+ var ColumnTypeSchema = import_zod11.z.union([
1025
+ ColumnTypeShorthandSchema,
1026
+ JsonSchemaFragmentSchema
1027
+ ]);
1028
+ var SHORTHAND_FRAGMENTS = {
1029
+ string: { type: "string" },
1030
+ number: { type: "number" },
1031
+ integer: { type: "integer" },
1032
+ boolean: { type: "boolean" },
1033
+ date: { type: "string", format: "date" },
1034
+ "date-time": { type: "string", format: "date-time" },
1035
+ object: { type: "object" },
1036
+ array: { type: "array" }
1037
+ };
1038
+ var isShorthand = (type) => typeof type === "string";
1039
+ var NESTED_SHORTHANDS = {
1040
+ date: SHORTHAND_FRAGMENTS.date,
1041
+ "date-time": SHORTHAND_FRAGMENTS["date-time"]
1042
+ };
1043
+ var normalizeFragment = (fragment) => {
1044
+ const out = { ...fragment };
1045
+ if (typeof out.type === "string") {
1046
+ const expanded = NESTED_SHORTHANDS[out.type];
1047
+ if (expanded) {
1048
+ out.type = expanded.type;
1049
+ if (out.format === void 0) out.format = expanded.format;
1050
+ }
1051
+ }
1052
+ if (out.properties) {
1053
+ out.properties = Object.fromEntries(
1054
+ Object.entries(out.properties).map(([key, value]) => [
1055
+ key,
1056
+ normalizeFragment(value)
1057
+ ])
1058
+ );
1059
+ }
1060
+ if (out.items) out.items = normalizeFragment(out.items);
1061
+ if (out.additionalProperties && typeof out.additionalProperties === "object") {
1062
+ out.additionalProperties = normalizeFragment(out.additionalProperties);
1063
+ }
1064
+ return out;
1065
+ };
1066
+ var columnTypeToJsonSchema = (type) => {
1067
+ if (type === void 0) return { type: "string" };
1068
+ if (isShorthand(type)) return { ...SHORTHAND_FRAGMENTS[type] };
1069
+ return normalizeFragment(type);
1070
+ };
1071
+ var columnTypeName = (type) => {
1072
+ const fragment = columnTypeToJsonSchema(type);
1073
+ const raw = fragment.type;
1074
+ if (Array.isArray(raw)) return raw.find((t) => t !== "null") ?? "string";
1075
+ if (typeof raw === "string") return raw;
1076
+ if (fragment.properties) return "object";
1077
+ if (fragment.items) return "array";
1078
+ return "string";
1079
+ };
1080
+ var isObjectColumnType = (type) => columnTypeName(type) === "object";
1081
+ var isArrayColumnType = (type) => columnTypeName(type) === "array";
1082
+
1083
+ // src/lib/resource/Column.ts
1084
+ var WhenConditionSchema = import_zod12.z.object({
1085
+ field: import_zod12.z.string(),
942
1086
  // required
943
- eq: import_zod11.z.unknown().optional(),
944
- neq: import_zod11.z.unknown().optional(),
945
- exists: import_zod11.z.boolean().optional(),
946
- notExists: import_zod11.z.boolean().optional()
1087
+ eq: import_zod12.z.unknown().optional(),
1088
+ neq: import_zod12.z.unknown().optional(),
1089
+ exists: import_zod12.z.boolean().optional(),
1090
+ notExists: import_zod12.z.boolean().optional()
947
1091
  });
948
- var JsonColumnSchema = import_zod11.z.object({
949
- id: import_zod11.z.string(),
1092
+ var JsonColumnSchema = import_zod12.z.object({
1093
+ id: import_zod12.z.string(),
950
1094
  // required in array form; implied by the map key in map form
951
- column: import_zod11.z.string().optional(),
1095
+ column: import_zod12.z.string().optional(),
952
1096
  // default: same as `id`
953
- label: import_zod11.z.string().optional(),
1097
+ label: import_zod12.z.string().optional(),
954
1098
  // default: title-cased version of `id`
955
- hiddenInTable: import_zod11.z.boolean().default(false),
1099
+ hiddenInTable: import_zod12.z.boolean().default(false),
956
1100
  // default: false
957
- hiddenInForm: import_zod11.z.boolean().default(false),
1101
+ hiddenInForm: import_zod12.z.boolean().default(false),
958
1102
  // default: false
959
- hiddenInView: import_zod11.z.boolean().default(false),
1103
+ hiddenInView: import_zod12.z.boolean().default(false),
960
1104
  // default: false
961
- sortable: import_zod11.z.boolean().default(false),
1105
+ sortable: import_zod12.z.boolean().default(false),
962
1106
  // default: false
963
- defaultSort: import_zod11.z.boolean().default(false),
1107
+ defaultSort: import_zod12.z.boolean().default(false),
964
1108
  // default: false
965
- searchable: import_zod11.z.boolean().default(false),
1109
+ searchable: import_zod12.z.boolean().default(false),
966
1110
  // default: false
967
- filterable: import_zod11.z.boolean().default(false),
1111
+ filterable: import_zod12.z.boolean().default(false),
968
1112
  // default: false
969
- createable: import_zod11.z.boolean().default(true),
1113
+ createable: import_zod12.z.boolean().default(true),
970
1114
  // default: true (only `false` excludes it)
971
- updateable: import_zod11.z.boolean().default(true),
1115
+ updateable: import_zod12.z.boolean().default(true),
972
1116
  // default: true (only `false` excludes it)
973
- hideLabel: import_zod11.z.boolean().default(false),
1117
+ hideLabel: import_zod12.z.boolean().default(false),
974
1118
  // default: false
975
1119
  showWhen: WhenConditionSchema.optional(),
976
1120
  hideWhen: WhenConditionSchema.optional(),
977
1121
  disabledWhen: WhenConditionSchema.optional(),
978
- displayKey: import_zod11.z.string().optional(),
1122
+ displayKey: import_zod12.z.string().optional(),
979
1123
  // e.g. "author.name" for nested display
980
- sortId: import_zod11.z.string().optional(),
1124
+ sortId: import_zod12.z.string().optional(),
981
1125
  // overrides the sort
982
1126
  /**
983
1127
  * Name of a shared enum in the project enum registry (`crouton.enums.json`).
984
1128
  * At load time the loader injects that enum's `{ value, label }[]` into
985
1129
  * `fieldInput.options.values`, so columns don't duplicate the option list.
986
1130
  */
987
- enum: import_zod11.z.string().optional(),
1131
+ enum: import_zod12.z.string().optional(),
988
1132
  // name of a shared enum in crouton.enums.json
989
- idField: import_zod11.z.boolean().default(false),
1133
+ idField: import_zod12.z.boolean().default(false),
990
1134
  // default: false — exactly one column should set this
991
- showInLookup: import_zod11.z.boolean().default(false),
1135
+ showInLookup: import_zod12.z.boolean().default(false),
992
1136
  // default: false
993
- columnType: import_zod11.z.string().default("string"),
1137
+ /**
1138
+ * Data type of the column, as a shorthand name (`"string"`, `"integer"`, …)
1139
+ * or a full JSON Schema fragment:
1140
+ *
1141
+ * ```json
1142
+ * "type": {
1143
+ * "type": "object",
1144
+ * "properties": { "id": { "type": "string" }, "name": { "type": "string" } }
1145
+ * }
1146
+ * ```
1147
+ *
1148
+ * Required on every column of a `kind: "custom"` resource, where it is the
1149
+ * only source of the resource's json_schema. Optional on a `prisma`
1150
+ * resource, whose schema is derived from the Zod model.
1151
+ */
1152
+ type: ColumnTypeSchema.optional(),
1153
+ /**
1154
+ * Whether the form requires a value.
1155
+ *
1156
+ * A prisma resource derives `required` from its Zod model, so this is an
1157
+ * override in either direction: `true` adds the column to the form schema's
1158
+ * `required` array, `false` removes it, and omitting it leaves the model's
1159
+ * answer alone. A `kind: "custom"` resource has no model, so this is the only
1160
+ * way to mark one of its fields required.
1161
+ *
1162
+ * Applies to the **form** view only — a required filter input would make the
1163
+ * filter panel unsubmittable, and the table and view schemas are read-only.
1164
+ * Ignored on the id column and on columns that are neither createable nor
1165
+ * updateable, since the form cannot supply a value for those.
1166
+ */
1167
+ required: import_zod12.z.boolean().optional(),
1168
+ /**
1169
+ * @deprecated Use `type` instead. Retained because it is still consumed by
1170
+ * the boolean predicate and the schema-less sub-resource view builder.
1171
+ */
1172
+ columnType: import_zod12.z.string().default("string"),
994
1173
  // default: 'string'
995
1174
  fieldInput: FieldInputSchema.optional(),
996
1175
  /**
@@ -1015,46 +1194,66 @@ var JsonColumnSchema = import_zod11.z.object({
1015
1194
  * Visibility (`hiddenInTable/Form/View`) on this column is inherited by all sub-columns as a
1016
1195
  * default; the referenced resource's own column visibility further restricts it.
1017
1196
  */
1018
- extend: import_zod11.z.string().optional(),
1197
+ extend: import_zod12.z.string().optional(),
1019
1198
  // path to another resource.json expanded as virtual sub-columns
1020
1199
  /**
1021
1200
  * Per-sub-column overrides when using `extend`.
1022
1201
  * Key: the virtual column id (`"{extendId}_{refColId}"`) or just the referenced column id.
1023
1202
  * Value: any `JsonColumn` fields to merge over the generated virtual column.
1024
1203
  */
1025
- columns: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.record(import_zod11.z.string(), import_zod11.z.unknown())).optional()
1204
+ columns: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.record(import_zod12.z.string(), import_zod12.z.unknown())).optional()
1026
1205
  // per-sub-column overrides, only used with `extend`
1027
1206
  });
1028
1207
 
1208
+ // src/lib/resource/ParentRef.schema.ts
1209
+ var import_zod13 = require("zod");
1210
+ var ParentRefSchema = import_zod13.z.object({
1211
+ /** Route segment of the parent, e.g. `"group"`. */
1212
+ route: import_zod13.z.string(),
1213
+ /**
1214
+ * Name of the path parameter carrying the parent id, e.g. `"groupId"`.
1215
+ * Avoid `"id"` — that is the child's own id in `/:id` routes.
1216
+ */
1217
+ param: import_zod13.z.string().default("parentId"),
1218
+ /** Type of the parent id, used to coerce the path param. Defaults to `'string'`. */
1219
+ idType: import_zod13.z.enum(["string", "number"]).optional()
1220
+ });
1221
+ var resourceControllerPath = (route, parent) => parent ? `${parent.route}/:${parent.param}/${route}` : route;
1222
+
1223
+ // src/lib/resource/ResourceKind.ts
1224
+ var import_zod14 = require("zod");
1225
+ var ResourceKindSchema = import_zod14.z.enum(["prisma", "custom"]).default("prisma");
1226
+ var isCustomKind = (kind) => kind === "custom";
1227
+
1029
1228
  // src/lib/resource/Sidebar.schema.ts
1030
- var import_zod12 = __toESM(require("zod"), 1);
1031
- var SidebarGroupSchema = import_zod12.default.object({
1229
+ var import_zod15 = __toESM(require("zod"), 1);
1230
+ var SidebarGroupSchema = import_zod15.default.object({
1032
1231
  /** Human-readable heading shown in the sidebar. Defaults to a title-cased version of the slug. */
1033
- label: import_zod12.default.string().optional(),
1232
+ label: import_zod15.default.string().optional(),
1034
1233
  /** Controls the order of this group among top-level sidebar items. */
1035
- position: import_zod12.default.number().optional()
1234
+ position: import_zod15.default.number().optional()
1036
1235
  });
1037
- var SidebarSchema = import_zod12.default.object({
1038
- hide: import_zod12.default.boolean().default(false),
1236
+ var SidebarSchema = import_zod15.default.object({
1237
+ hide: import_zod15.default.boolean().default(false),
1039
1238
  // default: false
1040
- position: import_zod12.default.number().optional(),
1239
+ position: import_zod15.default.number().optional(),
1041
1240
  // default: sorted alphabetically after positioned entries
1042
- label: import_zod12.default.string().optional(),
1241
+ label: import_zod15.default.string().optional(),
1043
1242
  // default: resource `title`
1044
1243
  /**
1045
1244
  * Slug of the group this resource belongs to.
1046
1245
  * Must match a key in `sidebarGroups` in `crouton.json`.
1047
1246
  * Resources with the same `group` are nested under a shared collapsible section.
1048
1247
  */
1049
- group: import_zod12.default.string().optional()
1248
+ group: import_zod15.default.string().optional()
1050
1249
  // must match a key in `sidebarGroups` (crouton.json)
1051
1250
  });
1052
1251
 
1053
1252
  // src/lib/resource/TableAction.schema.ts
1054
- var import_zod13 = require("zod");
1055
- var ActionConditionSchema = import_zod13.z.object({
1253
+ var import_zod16 = require("zod");
1254
+ var ActionConditionSchema = import_zod16.z.object({
1056
1255
  /** Row field to evaluate. */
1057
- field: import_zod13.z.string(),
1256
+ field: import_zod16.z.string(),
1058
1257
  //
1059
1258
  /**
1060
1259
  * Comparison operator. Defaults to `"eq"`.
@@ -1063,49 +1262,49 @@ var ActionConditionSchema = import_zod13.z.object({
1063
1262
  * - `exists` — field is not null / undefined / empty string
1064
1263
  * - `notExists` — field is null / undefined / empty string
1065
1264
  */
1066
- op: import_zod13.z.enum(["eq", "neq", "gt", "gte", "lt", "lte", "exists", "notExists"]).default("eq"),
1265
+ op: import_zod16.z.enum(["eq", "neq", "gt", "gte", "lt", "lte", "exists", "notExists"]).default("eq"),
1067
1266
  // default: 'eq'
1068
1267
  /** Comparison value. Not required for `exists` / `notExists`. */
1069
- value: import_zod13.z.unknown().optional()
1268
+ value: import_zod16.z.unknown().optional()
1070
1269
  // not required for exists/notExists
1071
1270
  });
1072
- var ActionSchema = import_zod13.z.object({
1073
- type: import_zod13.z.string(),
1271
+ var ActionSchema = import_zod16.z.object({
1272
+ type: import_zod16.z.string(),
1074
1273
  /** Unique identifier for the action. */
1075
- id: import_zod13.z.string(),
1274
+ id: import_zod16.z.string(),
1076
1275
  // required
1077
1276
  /** Human-readable button label. */
1078
- label: import_zod13.z.string(),
1277
+ label: import_zod16.z.string(),
1079
1278
  /** MDI icon name, e.g. `"mdi:open-in-new"`. */
1080
- icon: import_zod13.z.string().optional(),
1279
+ icon: import_zod16.z.string().optional(),
1081
1280
  /** Tooltip text. Falls back to `label` when omitted. */
1082
- tooltip: import_zod13.z.string().optional(),
1281
+ tooltip: import_zod16.z.string().optional(),
1083
1282
  // default: falls back to `label`
1084
1283
  condition: ActionConditionSchema.optional()
1085
1284
  });
1086
1285
  var JsonProcedureActionSchema = ActionSchema.extend({
1087
- type: import_zod13.z.literal("procedure").default("procedure"),
1286
+ type: import_zod16.z.literal("procedure").default("procedure"),
1088
1287
  // default: 'procedure'
1089
1288
  /**
1090
1289
  * Filename (without extension) inside the resource's `actions/` directory
1091
1290
  * that exports the table-action procedure, e.g. `"syncZotero"`.
1092
1291
  */
1093
- procedure: import_zod13.z.string(),
1292
+ procedure: import_zod16.z.string(),
1094
1293
  // required
1095
1294
  /** HTTP method for the frontend. Defaults to `"post"`. */
1096
- method: import_zod13.z.string().optional().default("post"),
1295
+ method: import_zod16.z.string().optional().default("post"),
1097
1296
  // default: 'post'
1098
1297
  /** Static query / body params passed to the endpoint. */
1099
- data: import_zod13.z.record(import_zod13.z.string(), import_zod13.z.unknown()).optional()
1298
+ data: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.unknown()).optional()
1100
1299
  });
1101
1300
  var JsonLinkActionSchema = ActionSchema.extend({
1102
1301
  /** Optional condition evaluated per row. Button is hidden when the condition is false. */
1103
- type: import_zod13.z.literal("link").default("link"),
1302
+ type: import_zod16.z.literal("link").default("link"),
1104
1303
  // required
1105
1304
  /** URL to open. May contain `{env.VAR}` placeholders. */
1106
- href: import_zod13.z.string(),
1305
+ href: import_zod16.z.string(),
1107
1306
  // required,
1108
- blank: import_zod13.z.boolean().optional().default(true)
1307
+ blank: import_zod16.z.boolean().optional().default(true)
1109
1308
  });
1110
1309
  var transformAction = (action) => {
1111
1310
  const label = action.label;
@@ -1114,21 +1313,21 @@ var transformAction = (action) => {
1114
1313
  ...action
1115
1314
  };
1116
1315
  };
1117
- var JsonActionSchema = import_zod13.z.union([JsonProcedureActionSchema, JsonLinkActionSchema]).transform(transformAction);
1316
+ var JsonActionSchema = import_zod16.z.union([JsonProcedureActionSchema, JsonLinkActionSchema]).transform(transformAction);
1118
1317
 
1119
1318
  // src/lib/resource/include.schema.ts
1120
- var import_zod14 = require("zod");
1121
- var JsonIncludeEntrySchema = import_zod14.z.lazy(
1122
- () => import_zod14.z.union([
1123
- import_zod14.z.string(),
1319
+ var import_zod17 = require("zod");
1320
+ var JsonIncludeEntrySchema = import_zod17.z.lazy(
1321
+ () => import_zod17.z.union([
1322
+ import_zod17.z.string(),
1124
1323
  // plain relation name → `{ [name]: true }`
1125
- import_zod14.z.object({
1126
- relation: import_zod14.z.string(),
1324
+ import_zod17.z.object({
1325
+ relation: import_zod17.z.string(),
1127
1326
  // required
1128
- include: import_zod14.z.array(JsonIncludeEntrySchema).optional(),
1327
+ include: import_zod17.z.array(JsonIncludeEntrySchema).optional(),
1129
1328
  // nested includes;
1130
1329
  /** Prisma-format orderBy clause applied to the included records. */
1131
- orderBy: import_zod14.z.record(import_zod14.z.string(), import_zod14.z.unknown()).optional()
1330
+ orderBy: import_zod17.z.record(import_zod17.z.string(), import_zod17.z.unknown()).optional()
1132
1331
  })
1133
1332
  ])
1134
1333
  );
@@ -1138,15 +1337,15 @@ var BASELINE_RESOURCE_VERSION = 1;
1138
1337
  var CURRENT_RESOURCE_VERSION = 1;
1139
1338
 
1140
1339
  // src/lib/resource/ResourceJson.schema.ts
1141
- var JsonColumnsMapSchema = import_zod15.z.record(
1142
- import_zod15.z.string(),
1143
- JsonColumnSchema.omit({ id: true }).catchall(import_zod15.z.unknown())
1340
+ var JsonColumnsMapSchema = import_zod18.z.record(
1341
+ import_zod18.z.string(),
1342
+ JsonColumnSchema.omit({ id: true }).catchall(import_zod18.z.unknown())
1144
1343
  );
1145
1344
  var ColumnsSchema = JsonColumnsMapSchema;
1146
- var JsonDisplaySchema = import_zod15.z.object({
1147
- mode: import_zod15.z.enum(["page", "modal"]).default("modal"),
1345
+ var JsonDisplaySchema = import_zod18.z.object({
1346
+ mode: import_zod18.z.enum(["page", "modal"]).default("modal"),
1148
1347
  // default: 'modal'
1149
- customComponent: import_zod15.z.string().nullable().optional().default(null)
1348
+ customComponent: import_zod18.z.string().nullable().optional().default(null)
1150
1349
  // default: null
1151
1350
  });
1152
1351
  var normalizeColumns = (columns) => {
@@ -1157,34 +1356,54 @@ var normalizeColumns = (columns) => {
1157
1356
  label: col.label ?? labelFromId(col.id)
1158
1357
  }));
1159
1358
  };
1160
- var ResourceJsonShape = import_zod15.z.object({
1359
+ var ResourceJsonShape = import_zod18.z.object({
1161
1360
  /**
1162
1361
  * URL of the generated JSON Schema, for editor autocomplete/validation. Declared so the
1163
1362
  * key is *allowed* (not stripped, and not flagged by the very schema it points at).
1164
1363
  * Ignored at runtime.
1165
1364
  */
1166
- $schema: import_zod15.z.string().optional(),
1365
+ $schema: import_zod18.z.string().optional(),
1167
1366
  /**
1168
1367
  * resource.json shape version. Missing ⇒ baseline (see `./version`). Auto-migrated
1169
1368
  * toward `CURRENT_RESOURCE_VERSION` on load in the dev environment.
1170
1369
  */
1171
- schemaVersion: import_zod15.z.number().int().positive().optional(),
1370
+ schemaVersion: import_zod18.z.number().int().positive().optional(),
1172
1371
  /** When `true`, the resource lives in the repo but is NOT loaded/served (work in progress). */
1173
- draft: import_zod15.z.boolean().optional().default(false),
1174
- name: import_zod15.z.string(),
1372
+ draft: import_zod18.z.boolean().optional().default(false),
1373
+ /**
1374
+ * Where the data comes from. `prisma` (the default) is backed by a Prisma
1375
+ * model plus a `schema.ts`; `custom` is configuration only and the developer
1376
+ * supplies a `repository.ts`. See `./ResourceKind`.
1377
+ */
1378
+ kind: ResourceKindSchema,
1379
+ name: import_zod18.z.string(),
1175
1380
  // required — unique id, used as the frontend form id
1176
- route: import_zod15.z.string(),
1381
+ route: import_zod18.z.string(),
1177
1382
  // required — URL segment for generated endpoints
1178
- model: import_zod15.z.string(),
1179
- // required — Prisma model name
1180
- tag: import_zod15.z.string(),
1383
+ /**
1384
+ * Prisma model name. Required when `kind` is `prisma` (enforced by the
1385
+ * refinement on `ResourceJsonSchema`), and must be absent when `kind` is
1386
+ * `custom` — there is no Prisma delegate to address.
1387
+ */
1388
+ model: import_zod18.z.string().optional(),
1389
+ tag: import_zod18.z.string(),
1181
1390
  // required — OpenAPI tag
1182
- title: import_zod15.z.string().optional(),
1391
+ title: import_zod18.z.string().optional(),
1183
1392
  // no computed default — used as UI display title
1184
- table: import_zod15.z.string().optional(),
1393
+ table: import_zod18.z.string().optional(),
1185
1394
  // default: same as `model`
1186
- database: import_zod15.z.string().optional(),
1395
+ /**
1396
+ * Type of the resource's primary key, used to coerce `:id` route params.
1397
+ * Written by codegen from the Prisma model; defaults to `'string'`.
1398
+ */
1399
+ idType: import_zod18.z.enum(["string", "number"]).optional(),
1400
+ database: import_zod18.z.string().optional(),
1187
1401
  // default: project's default data source
1402
+ /**
1403
+ * Mount this resource under a parent route instead of at the top level —
1404
+ * see `./ParentRef.schema`. Only valid on a `kind: "custom"` resource.
1405
+ */
1406
+ parent: ParentRefSchema.optional(),
1188
1407
  sidebar: SidebarSchema.default(SidebarSchema.parse({})),
1189
1408
  // default: shown, alphabetically ordered, ungrouped
1190
1409
  display: JsonDisplaySchema.default(JsonDisplaySchema.parse({})),
@@ -1193,12 +1412,12 @@ var ResourceJsonShape = import_zod15.z.object({
1193
1412
  // required key — but every sub-field defaults to enabled
1194
1413
  columns: ColumnsSchema.default(ColumnsSchema.parse({})),
1195
1414
  // id-keyed map; omit for a columnless resource
1196
- calculatedColumns: import_zod15.z.array(CalculatedColumnSchema).default([]),
1197
- actions: import_zod15.z.array(JsonActionSchema).default([]),
1415
+ calculatedColumns: import_zod18.z.array(CalculatedColumnSchema).default([]),
1416
+ actions: import_zod18.z.array(JsonActionSchema).default([]),
1198
1417
  /** Global table-level actions (no record id). Shown as toolbar buttons. */
1199
- tableActions: import_zod15.z.array(JsonActionSchema).default([]),
1418
+ tableActions: import_zod18.z.array(JsonActionSchema).default([]),
1200
1419
  /** Modal width when opening a form for this resource. */
1201
- modalSize: import_zod15.z.enum(["xs", "sm", "lg", "xl"]).default("sm"),
1420
+ modalSize: import_zod18.z.enum(["xs", "sm", "lg", "xl"]).default("sm"),
1202
1421
  // default: 'sm' for create/edit modal
1203
1422
  /**
1204
1423
  * Relations to include when querying this resource.
@@ -1207,9 +1426,60 @@ var ResourceJsonShape = import_zod15.z.object({
1207
1426
  * `{ "relation": "text_author", "include": ["author"] }` →
1208
1427
  * `include: { text_author: { include: { author: true } } }`
1209
1428
  */
1210
- include: import_zod15.z.array(JsonIncludeEntrySchema).default([])
1429
+ include: import_zod18.z.array(JsonIncludeEntrySchema).default([])
1211
1430
  });
1212
- var ResourceJsonSchema = ResourceJsonShape.transform((obj) => {
1431
+ var refineByKind = (obj, ctx) => {
1432
+ if (obj.kind === "custom") {
1433
+ if (obj.model !== void 0) {
1434
+ ctx.addIssue({
1435
+ code: "custom",
1436
+ path: ["model"],
1437
+ message: 'A custom resource has no Prisma model. Remove "model" \u2014 data access comes from repository.ts.'
1438
+ });
1439
+ }
1440
+ if (obj.calculatedColumns?.length) {
1441
+ ctx.addIssue({
1442
+ code: "custom",
1443
+ path: ["calculatedColumns"],
1444
+ message: "calculatedColumns run raw SQL against a database table and are not supported on a custom resource. Compute the value in repository.ts instead."
1445
+ });
1446
+ }
1447
+ for (const [id, col] of Object.entries(obj.columns ?? {})) {
1448
+ if (col.fieldInput?.format === "relation" || col.fieldInput?.type === "autocomplete") {
1449
+ continue;
1450
+ }
1451
+ if (col.type === void 0) {
1452
+ ctx.addIssue({
1453
+ code: "custom",
1454
+ path: ["columns", id, "type"],
1455
+ message: `Column "${id}" needs a "type": a custom resource has no schema.ts, so its json_schema is built from the column types.`
1456
+ });
1457
+ }
1458
+ }
1459
+ if (obj.parent && obj.parent.param === "id") {
1460
+ ctx.addIssue({
1461
+ code: "custom",
1462
+ path: ["parent", "param"],
1463
+ message: `parent.param cannot be "id" \u2014 that is the child's own id in /:id routes. Use something like "groupId".`
1464
+ });
1465
+ }
1466
+ } else if (obj.parent !== void 0) {
1467
+ ctx.addIssue({
1468
+ code: "custom",
1469
+ path: ["parent"],
1470
+ message: '"parent" is only supported on a custom resource. A prisma resource is nested by declaring a relation column on its parent.'
1471
+ });
1472
+ } else if (obj.model === void 0) {
1473
+ ctx.addIssue({
1474
+ code: "custom",
1475
+ path: ["model"],
1476
+ message: '"model" is required for a prisma-backed resource. Set "kind": "custom" for a resource with no Prisma model.'
1477
+ });
1478
+ }
1479
+ };
1480
+ var ResourceJsonSchema = ResourceJsonShape.superRefine(
1481
+ refineByKind
1482
+ ).transform((obj) => {
1213
1483
  const title = obj.title ?? labelFromId(obj.name);
1214
1484
  const schemaVersion = obj.schemaVersion ?? BASELINE_RESOURCE_VERSION;
1215
1485
  return {
@@ -1277,52 +1547,62 @@ var resolveViewField = (c) => mergeFieldVariant(c.fieldInput, c.fieldView);
1277
1547
  var resolveTableField = (c) => mergeFieldVariant(resolveViewField(c), c.fieldTable);
1278
1548
 
1279
1549
  // src/lib/config/CroutonConfig.schema.ts
1280
- var import_zod16 = require("zod");
1281
- var RulesetSchema = import_zod16.z.object({
1282
- hideIdInTable: import_zod16.z.boolean().default(true),
1283
- hideIdInForm: import_zod16.z.boolean().default(true),
1284
- hideIdInView: import_zod16.z.boolean().default(true),
1285
- hideTimestamps: import_zod16.z.boolean().default(true),
1286
- hideForeignKeys: import_zod16.z.boolean().default(true),
1287
- includeRelations: import_zod16.z.boolean().default(false),
1288
- hideRelationsInTable: import_zod16.z.boolean().default(true),
1289
- showRelationsInForm: import_zod16.z.boolean().default(true),
1290
- enumValueLabel: import_zod16.z.boolean().default(true),
1291
- sharedEnums: import_zod16.z.boolean().default(true),
1550
+ var import_zod19 = require("zod");
1551
+ var RulesetSchema = import_zod19.z.object({
1552
+ hideIdInTable: import_zod19.z.boolean().default(true),
1553
+ hideIdInForm: import_zod19.z.boolean().default(true),
1554
+ hideIdInView: import_zod19.z.boolean().default(true),
1555
+ hideTimestamps: import_zod19.z.boolean().default(true),
1556
+ hideForeignKeys: import_zod19.z.boolean().default(true),
1557
+ includeRelations: import_zod19.z.boolean().default(true),
1558
+ hideRelationsInTable: import_zod19.z.boolean().default(true),
1559
+ showRelationsInForm: import_zod19.z.boolean().default(true),
1560
+ enumValueLabel: import_zod19.z.boolean().default(true),
1561
+ sharedEnums: import_zod19.z.boolean().default(true),
1292
1562
  defaultOperations: JsonOperationsSchema.default(
1293
1563
  JsonOperationsSchema.parse({})
1294
1564
  )
1295
1565
  });
1296
- var CroutonConfigSchema = import_zod16.z.object({
1566
+ var I18nConfigSchema = import_zod19.z.object({
1567
+ /** Default / fallback language. */
1568
+ defaultLanguage: import_zod19.z.string().default("en"),
1569
+ /** All supported language codes. */
1570
+ languages: import_zod19.z.array(import_zod19.z.string()).min(1),
1571
+ /** Directory containing `<lang>.json` bundles, relative to project root. */
1572
+ translationsDir: import_zod19.z.string().default("translations")
1573
+ });
1574
+ var CroutonConfigSchema = import_zod19.z.object({
1297
1575
  /**
1298
1576
  * Application title served to the frontend via `GET /_app/layout`.
1299
1577
  * Displayed in the admin sidebar header.
1300
1578
  */
1301
- title: import_zod16.z.string(),
1579
+ title: import_zod19.z.string(),
1302
1580
  /** Where resource directories live, relative to the project root. */
1303
- resourcesDir: import_zod16.z.string(),
1581
+ resourcesDir: import_zod19.z.string(),
1304
1582
  /** Where datasource folders live (each with a `data-source.json`). */
1305
- dataSourcesDir: import_zod16.z.string(),
1583
+ dataSourcesDir: import_zod19.z.string(),
1306
1584
  /**
1307
1585
  * Template for a model's Zod export name. `{Model}` → Prisma model name.
1308
1586
  * Defaults to `{Model}WithRelationsSchema` (the relations-aware schema
1309
1587
  * emitted by zod-prisma-types when `createRelationValuesTypes` is on).
1310
1588
  */
1311
- schemaExportName: import_zod16.z.string().default("{Model}WithRelationsSchema"),
1589
+ schemaExportName: import_zod19.z.string().default("{Model}WithRelationsSchema"),
1312
1590
  /** Path to the shared enum registry, relative to project root. Default `crouton.enums.json`. */
1313
- enumsFile: import_zod16.z.string().default("crouton.enums.json"),
1591
+ enumsFile: import_zod19.z.string().default("crouton.enums.json"),
1314
1592
  /** Optional overrides of the default visibility ruleset. */
1315
1593
  rules: RulesetSchema.default(RulesetSchema.parse({})),
1316
1594
  /**
1317
1595
  * Sidebar group definitions, keyed by group slug (e.g. `"metadata"`).
1318
1596
  * Resources reference a group via `sidebar.group` in their `resource.json`.
1319
1597
  */
1320
- sidebarGroups: import_zod16.z.record(import_zod16.z.string(), SidebarGroupSchema).default({}),
1598
+ sidebarGroups: import_zod19.z.record(import_zod19.z.string(), SidebarGroupSchema).default({}),
1321
1599
  /**
1322
1600
  * Whether form fields are saved automatically as the user edits them.
1323
1601
  * @default true
1324
1602
  */
1325
- autoSave: import_zod16.z.boolean().default(true)
1603
+ autoSave: import_zod19.z.boolean().default(true),
1604
+ /** Optional i18n / translation configuration. */
1605
+ i18n: I18nConfigSchema.optional()
1326
1606
  });
1327
1607
 
1328
1608
  // src/lib/config/readConfig.ts
@@ -1334,6 +1614,9 @@ var isRelation = (col) => col.fieldInput?.format === "relation";
1334
1614
  var isAutocomplete = (col) => col.fieldInput?.type === "autocomplete";
1335
1615
  var isRecordCell = (col) => isRelation(col) || isAutocomplete(col);
1336
1616
  var isDateRange = (col) => col.fieldInput?.format === "date-range";
1617
+ var isObjectColumn = (col) => isObjectColumnType(col.type);
1618
+ var isArrayColumn = (col) => isArrayColumnType(col.type);
1619
+ var isObjectCell = (col) => isObjectColumn(col) && !!col.displayKey;
1337
1620
 
1338
1621
  // src/lib/view/column.utils.ts
1339
1622
  var colPosition = (col, i) => col.fieldInput?.position ?? i;
@@ -1449,7 +1732,7 @@ var buildTableUiSchema = (cols) => {
1449
1732
  const id = el.scope?.replace("#/properties/", "");
1450
1733
  const col = id ? colMap[id] : void 0;
1451
1734
  if (!col) return el;
1452
- const fieldInputOptions = isRecordCell(col) || isDateRange(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
1735
+ const fieldInputOptions = isRecordCell(col) || isDateRange(col) || isObjectCell(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
1453
1736
  const dataPathOption = col.column ? { dataPath: col.column } : {};
1454
1737
  const derivedSortId = isRecordCell(col) || isDateRange(col) ? null : deriveSortId(col);
1455
1738
  const sortOptions = derivedSortId ? { sortId: derivedSortId } : { sortable: false };
@@ -1465,7 +1748,9 @@ var buildTableUiSchema = (cols) => {
1465
1748
  ...isDateRange(col) && { format: "date-range" },
1466
1749
  label: col.label
1467
1750
  },
1468
- ...isRecordCell(col) && { type: "RecordCell" },
1751
+ // An object column with a displayKey renders the same way as a relation:
1752
+ // one nested key out of an object value.
1753
+ ...(isRecordCell(col) || isObjectCell(col)) && { type: "RecordCell" },
1469
1754
  ...isDateRange(col) && { type: "Control" }
1470
1755
  };
1471
1756
  });
@@ -1533,6 +1818,11 @@ var buildDetailLayout = (detail) => {
1533
1818
  if (detail.titleKey) inner.titleKey(detail.titleKey);
1534
1819
  return inner;
1535
1820
  };
1821
+ var defaultControlFormat = (col) => {
1822
+ if (isObjectColumn(col)) return "object";
1823
+ if (isArrayColumn(col)) return "array";
1824
+ return "text";
1825
+ };
1536
1826
  var buildFormControl = (col) => {
1537
1827
  const control = ControlBuilder.properties(col.id);
1538
1828
  const fieldInput = col.fieldInput;
@@ -1552,6 +1842,11 @@ var buildFormControl = (col) => {
1552
1842
  control.detailFixed(detailLayout, {
1553
1843
  layout: fieldInput.detail.layout === "collapse" ? "row" : void 0
1554
1844
  });
1845
+ } else if (fieldInput?.format !== "date-range" && (fieldInput?.type === "date" || fieldInput?.format === "dateTime")) {
1846
+ const options = { ...fieldInput.options };
1847
+ if (!options.colspan) options.colspan = 12;
1848
+ const format = fieldInput.format === "dateTime" ? "dateTime" : "date";
1849
+ control.control(format, options).width("full");
1555
1850
  } else if (fieldInput?.format === "date-range") {
1556
1851
  const options = { ...fieldInput.options };
1557
1852
  if (!options.colspan) options.colspan = 12;
@@ -1559,10 +1854,11 @@ var buildFormControl = (col) => {
1559
1854
  } else {
1560
1855
  const options = fieldInput?.options ?? {};
1561
1856
  if (!options.colspan) options.colspan = 12;
1562
- const type = fieldInput?.type ?? "text";
1857
+ const type = fieldInput?.type ?? defaultControlFormat(col);
1563
1858
  control.control(type, options).width("full");
1564
1859
  }
1565
- if (fieldInput?.customRender) control.setCustomRender(fieldInput?.customRender);
1860
+ if (fieldInput?.customRender)
1861
+ control.setCustomRender(fieldInput?.customRender);
1566
1862
  if (col.hideLabel) control.hideLabel();
1567
1863
  if (col.label) control.label(col.label);
1568
1864
  applyColumnRule(control, col);
@@ -1637,19 +1933,63 @@ var injectCalculatedColumns = (tableView, calculated) => injectCalculatedColumns
1637
1933
  var injectCalculatedColumnsToView = (viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view");
1638
1934
 
1639
1935
  // src/lib/view/view.builder.ts
1640
- var import_zod18 = require("zod");
1936
+ var import_zod21 = require("zod");
1937
+
1938
+ // src/lib/view/column-type-schema.source.ts
1939
+ var optionValues = (col) => {
1940
+ const values = col.fieldInput?.options?.["values"];
1941
+ if (!Array.isArray(values)) return void 0;
1942
+ const unwrapped = values.map(
1943
+ (v) => v && typeof v === "object" && "value" in v ? v.value : v
1944
+ );
1945
+ return unwrapped.length ? unwrapped : void 0;
1946
+ };
1947
+ var columnToJsonSchemaProperty = (col) => {
1948
+ const property = col.type === void 0 && isAutocomplete(col) ? {} : { ...columnTypeToJsonSchema(col.type) };
1949
+ if (property.title === void 0) property.title = col.label ?? col.id;
1950
+ if (col.fieldInput?.defaultValue !== void 0 && property.default === void 0) {
1951
+ property.default = col.fieldInput.defaultValue;
1952
+ }
1953
+ if (property.enum === void 0) {
1954
+ const values = optionValues(col);
1955
+ if (values) property.enum = values;
1956
+ }
1957
+ return property;
1958
+ };
1959
+ var columnTypeSchemaSource = (schemaCols) => {
1960
+ if (!schemaCols.length) return void 0;
1961
+ const properties = {};
1962
+ for (const col of schemaCols) {
1963
+ properties[col.id] = columnToJsonSchemaProperty(col);
1964
+ }
1965
+ return { type: "object", properties };
1966
+ };
1641
1967
 
1642
1968
  // src/lib/view/json-schema.opts.ts
1643
- var import_zod17 = require("zod");
1644
- var dateOverride = ({ zodSchema, jsonSchema }) => {
1645
- if (zodSchema instanceof import_zod17.z.ZodDate) {
1969
+ var import_zod20 = require("zod");
1970
+ var isInstanceOf = (zodSchema, className) => {
1971
+ if (!(zodSchema instanceof import_zod20.z.ZodCustom)) return false;
1972
+ const result = zodSchema.safeParse(null);
1973
+ return !result.success && result.error.issues[0]?.expected === className;
1974
+ };
1975
+ var jsonSchemaOverride = ({ zodSchema, jsonSchema }) => {
1976
+ if (zodSchema instanceof import_zod20.z.ZodDate) {
1646
1977
  jsonSchema.type = "string";
1647
1978
  jsonSchema.format = "date-time";
1648
1979
  }
1980
+ if (zodSchema instanceof import_zod20.z.ZodBigInt) {
1981
+ jsonSchema.type = "integer";
1982
+ }
1983
+ if (isInstanceOf(zodSchema, "Decimal")) {
1984
+ jsonSchema.type = "number";
1985
+ }
1986
+ if (isInstanceOf(zodSchema, "Buffer")) {
1987
+ jsonSchema.type = "string";
1988
+ }
1649
1989
  };
1650
1990
  var jsonSchemaOpts = {
1651
1991
  unrepresentable: "any",
1652
- override: dateOverride
1992
+ override: jsonSchemaOverride
1653
1993
  };
1654
1994
 
1655
1995
  // src/lib/view/view.builder.ts
@@ -1706,20 +2046,51 @@ var injectFieldDefaults = (jsonSchema, columns) => {
1706
2046
  if (prop) prop.default = col.fieldInput.defaultValue;
1707
2047
  }
1708
2048
  };
1709
- var buildView = (schema, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
1710
- if (!schema || !columns?.length) return void 0;
1711
- const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
1712
- if (!visibleCols.length) return void 0;
1713
- const schemaCols = (schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols).filter((c) => !isRelation(c));
2049
+ var zodSchemaSource = (schema) => (schemaCols) => {
1714
2050
  const schemaKeys = new Set(Object.keys(schema.shape));
1715
2051
  const schemaIds = schemaCols.map((c) => c.id).filter((id) => schemaKeys.has(id));
1716
2052
  if (!schemaIds.length) return void 0;
1717
2053
  const mask = Object.fromEntries(schemaIds.map((id) => [id, true]));
1718
2054
  const picked = schema.pick(mask);
1719
- const jsonSchema = (0, import_zod18.toJSONSchema)(picked, {
2055
+ return (0, import_zod21.toJSONSchema)(picked, {
1720
2056
  target: "draft-07",
1721
2057
  ...jsonSchemaOpts
1722
2058
  });
2059
+ };
2060
+ var NON_NULL_TYPES = ["object", "array", "string", "number", "boolean"];
2061
+ var SHAPE_KEYS = ["type", "enum", "const", "anyOf", "oneOf", "allOf", "$ref"];
2062
+ var applyRequiredColumns = (jsonSchema, columns) => {
2063
+ if (!columns?.length) return;
2064
+ const properties = jsonSchema["properties"];
2065
+ if (!properties) return;
2066
+ const required = new Set(
2067
+ Array.isArray(jsonSchema["required"]) ? jsonSchema["required"] : []
2068
+ );
2069
+ for (const col of columns) {
2070
+ if (col.required === void 0) continue;
2071
+ if (!(col.id in properties)) continue;
2072
+ if (!col.required) {
2073
+ required.delete(col.id);
2074
+ continue;
2075
+ }
2076
+ if (col.idField) continue;
2077
+ if (col.createable === false && col.updateable === false) continue;
2078
+ required.add(col.id);
2079
+ const property = properties[col.id];
2080
+ if (property && typeof property === "object" && !SHAPE_KEYS.some((key) => key in property)) {
2081
+ property["type"] = [...NON_NULL_TYPES];
2082
+ }
2083
+ }
2084
+ if (required.size) jsonSchema["required"] = [...required];
2085
+ else delete jsonSchema["required"];
2086
+ };
2087
+ var buildView = (source, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
2088
+ if (!source || !columns?.length) return void 0;
2089
+ const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
2090
+ if (!visibleCols.length) return void 0;
2091
+ const schemaCols = (schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols).filter((c) => !isRelation(c));
2092
+ const jsonSchema = source(schemaCols);
2093
+ if (!jsonSchema) return void 0;
1723
2094
  applySchemaTransforms(jsonSchema);
1724
2095
  injectFieldDefaults(jsonSchema, visibleCols);
1725
2096
  if (schemaVisible) {
@@ -1742,10 +2113,10 @@ var emptyTableView = () => ({
1742
2113
  ui_schema: buildTableUiSchema([]),
1743
2114
  columns: []
1744
2115
  });
1745
- var buildViews = (schema, columns) => {
2116
+ var buildViewsWithSource = (source, columns) => {
1746
2117
  const views = {};
1747
2118
  const table = buildView(
1748
- schema,
2119
+ source,
1749
2120
  columns?.map((c) => columnForContext(c, "table")),
1750
2121
  (c) => !c.hiddenInTable,
1751
2122
  buildTableUiSchema,
@@ -1761,16 +2132,19 @@ var buildViews = (schema, columns) => {
1761
2132
  views.table = emptyTableView();
1762
2133
  }
1763
2134
  const form = buildView(
1764
- schema,
2135
+ source,
1765
2136
  columns,
1766
2137
  (c) => !c.hiddenInForm,
1767
2138
  buildFormUiSchema,
1768
2139
  true,
1769
2140
  (c) => !c.idField && (c.createable === true || c.updateable === true)
1770
2141
  );
1771
- if (form) views.form = form;
2142
+ if (form) {
2143
+ applyRequiredColumns(form.json_schema, columns);
2144
+ views.form = form;
2145
+ }
1772
2146
  const filter = buildView(
1773
- schema,
2147
+ source,
1774
2148
  columns,
1775
2149
  (c) => !!c.filterable,
1776
2150
  buildFormUiSchema,
@@ -1784,7 +2158,7 @@ var buildViews = (schema, columns) => {
1784
2158
  views.filter = filter;
1785
2159
  }
1786
2160
  const view = buildView(
1787
- schema,
2161
+ source,
1788
2162
  columns?.map((c) => columnForContext(c, "view")),
1789
2163
  (c) => !c.hiddenInView,
1790
2164
  buildFormUiSchema,
@@ -1793,6 +2167,8 @@ var buildViews = (schema, columns) => {
1793
2167
  if (view) views.view = view;
1794
2168
  return Object.keys(views).length ? views : void 0;
1795
2169
  };
2170
+ var buildViews = (schema, columns) => buildViewsWithSource(schema ? zodSchemaSource(schema) : void 0, columns);
2171
+ var buildViewsFromColumnTypes = (columns) => buildViewsWithSource(columnTypeSchemaSource, columns);
1796
2172
  var buildViewsFromColumns = (columns) => {
1797
2173
  if (!columns?.length) return void 0;
1798
2174
  const buildJsonSchema = (cols) => {
@@ -1821,6 +2197,8 @@ var buildViewsFromColumns = (columns) => {
1821
2197
  type: "string",
1822
2198
  title: c.label ?? c.id
1823
2199
  };
2200
+ } else if (c.type !== void 0) {
2201
+ properties[c.id] = columnToJsonSchemaProperty(c);
1824
2202
  } else {
1825
2203
  const opts = c.fieldInput?.options;
1826
2204
  const isObject = opts?.emitObject === true || c.fieldInput?.type === "autocomplete";
@@ -1878,7 +2256,10 @@ var buildViewsFromColumns = (columns) => {
1878
2256
  }
1879
2257
  const formCols = sortByPosition(columns.filter((c) => !c.hiddenInForm));
1880
2258
  const form = makeView(formCols, buildFormUiSchema);
1881
- if (form) views.form = form;
2259
+ if (form) {
2260
+ applyRequiredColumns(form.json_schema, formCols);
2261
+ views.form = form;
2262
+ }
1882
2263
  const viewCols = sortByPosition(
1883
2264
  columns.filter((c) => !c.hiddenInView).map((c) => columnForContext(c, "view"))
1884
2265
  );
@@ -1888,20 +2269,131 @@ var buildViewsFromColumns = (columns) => {
1888
2269
  };
1889
2270
 
1890
2271
  // src/lib/view/view.schema.ts
1891
- var import_zod19 = require("zod");
1892
- var ViewColumnConfigSchema = import_zod19.z.object({
1893
- id: import_zod19.z.string(),
1894
- label: import_zod19.z.string().optional(),
1895
- sortable: import_zod19.z.boolean().optional(),
1896
- searchable: import_zod19.z.boolean().optional(),
2272
+ var import_zod22 = require("zod");
2273
+ var ViewColumnConfigSchema = import_zod22.z.object({
2274
+ id: import_zod22.z.string(),
2275
+ label: import_zod22.z.string().optional(),
2276
+ sortable: import_zod22.z.boolean().optional(),
2277
+ searchable: import_zod22.z.boolean().optional(),
1897
2278
  fieldInput: FieldInputSchema.optional()
1898
2279
  });
1899
- var ViewConfigSchema = import_zod19.z.object({
1900
- json_schema: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.unknown()),
1901
- ui_schema: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.unknown()),
1902
- columns: import_zod19.z.array(ViewColumnConfigSchema),
1903
- defaultSort: import_zod19.z.string().optional()
2280
+ var ViewConfigSchema = import_zod22.z.object({
2281
+ json_schema: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.unknown()),
2282
+ ui_schema: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.unknown()),
2283
+ columns: import_zod22.z.array(ViewColumnConfigSchema),
2284
+ defaultSort: import_zod22.z.string().optional()
1904
2285
  });
2286
+
2287
+ // src/lib/i18n/Translations.schema.ts
2288
+ var import_zod23 = require("zod");
2289
+ var NestedStringRecord = import_zod23.z.lazy(
2290
+ () => import_zod23.z.record(import_zod23.z.string(), import_zod23.z.union([import_zod23.z.string(), NestedStringRecord]))
2291
+ );
2292
+ var TranslationBundleSchema = import_zod23.z.object({
2293
+ app: import_zod23.z.object({
2294
+ title: import_zod23.z.string().optional()
2295
+ }).optional(),
2296
+ sidebarGroups: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string()).optional(),
2297
+ resources: import_zod23.z.record(
2298
+ import_zod23.z.string(),
2299
+ import_zod23.z.object({
2300
+ title: import_zod23.z.string().optional(),
2301
+ sidebar: import_zod23.z.string().optional(),
2302
+ columns: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string()).optional(),
2303
+ actions: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string()).optional(),
2304
+ subResources: import_zod23.z.record(
2305
+ import_zod23.z.string(),
2306
+ import_zod23.z.object({
2307
+ title: import_zod23.z.string().optional(),
2308
+ columns: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string()).optional()
2309
+ }).optional()
2310
+ ).optional()
2311
+ }).optional()
2312
+ ).optional(),
2313
+ enums: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string())).optional(),
2314
+ ui: NestedStringRecord.optional(),
2315
+ validation: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.string()).optional()
2316
+ }).passthrough();
2317
+
2318
+ // src/lib/i18n/translation-keys.ts
2319
+ var resourceTitleKey = (resource) => `resources.${resource}.title`;
2320
+ var resourceSidebarKey = (resource) => `resources.${resource}.sidebar`;
2321
+ var columnKey = (resource, column) => `resources.${resource}.columns.${column}`;
2322
+ var actionKey = (resource, action) => `resources.${resource}.actions.${action}`;
2323
+ var subResourceTitleKey = (resource, sub) => `resources.${resource}.subResources.${sub}.title`;
2324
+ var subResourceColumnKey = (resource, sub, column) => `resources.${resource}.subResources.${sub}.columns.${column}`;
2325
+ var enumKey = (enumName, value) => `enums.${enumName}.${value}`;
2326
+ var sidebarGroupKey = (group) => `sidebarGroups.${group}`;
2327
+ var uiKey = (path) => `ui.${path}`;
2328
+ var validationKey = (code) => `validation.${code}`;
2329
+
2330
+ // src/lib/i18n/translator.ts
2331
+ var getNestedValue = (obj, path) => {
2332
+ const keys = path.split(".");
2333
+ let current = obj;
2334
+ for (const key of keys) {
2335
+ if (current == null || typeof current !== "object") return void 0;
2336
+ current = current[key];
2337
+ }
2338
+ if (typeof current === "string" && current !== "") return current;
2339
+ return void 0;
2340
+ };
2341
+ var createTranslator = (bundles, language, defaultLanguage = "en") => {
2342
+ const langBundle = bundles[language];
2343
+ const defaultBundle = language !== defaultLanguage ? bundles[defaultLanguage] : void 0;
2344
+ return (path, fallback) => {
2345
+ if (langBundle) {
2346
+ const value = getNestedValue(
2347
+ langBundle,
2348
+ path
2349
+ );
2350
+ if (value !== void 0) return value;
2351
+ }
2352
+ if (defaultBundle) {
2353
+ const value = getNestedValue(
2354
+ defaultBundle,
2355
+ path
2356
+ );
2357
+ if (value !== void 0) return value;
2358
+ }
2359
+ return fallback ?? path;
2360
+ };
2361
+ };
2362
+
2363
+ // src/lib/i18n/negotiate.ts
2364
+ var parseAcceptLanguage = (header) => {
2365
+ const entries = [];
2366
+ for (const part of header.split(",")) {
2367
+ const trimmed = part.trim();
2368
+ if (!trimmed) continue;
2369
+ const [tag, ...params] = trimmed.split(";");
2370
+ let q = 1;
2371
+ for (const param of params) {
2372
+ const match = param.trim().match(/^q\s*=\s*([0-9.]+)$/);
2373
+ if (match) {
2374
+ q = parseFloat(match[1]);
2375
+ if (isNaN(q)) q = 0;
2376
+ }
2377
+ }
2378
+ entries.push({ tag: tag.trim().toLowerCase(), q });
2379
+ }
2380
+ return entries.sort((a, b) => b.q - a.q);
2381
+ };
2382
+ var resolveLanguage = (acceptLanguage, supported, defaultLanguage) => {
2383
+ if (!acceptLanguage) return defaultLanguage;
2384
+ const supportedLower = supported.map((s) => s.toLowerCase());
2385
+ const entries = parseAcceptLanguage(acceptLanguage);
2386
+ for (const { tag, q } of entries) {
2387
+ if (q <= 0) continue;
2388
+ if (tag === "*") return defaultLanguage;
2389
+ const exactIdx = supportedLower.indexOf(tag);
2390
+ if (exactIdx !== -1) return supported[exactIdx];
2391
+ const base = tag.split("-")[0];
2392
+ const baseIdx = supportedLower.indexOf(base);
2393
+ if (baseIdx !== -1) return supported[baseIdx];
2394
+ }
2395
+ return defaultLanguage;
2396
+ };
1905
2397
  // Annotate the CommonJS export names for ESM import in node:
1906
2398
  0 && (module.exports = {
1907
2399
  BASELINE_RESOURCE_VERSION,
@@ -1910,6 +2402,8 @@ var ViewConfigSchema = import_zod19.z.object({
1910
2402
  CONFIG_FILES,
1911
2403
  CURRENT_RESOURCE_VERSION,
1912
2404
  CalculatedColumnSchema,
2405
+ ColumnTypeSchema,
2406
+ ColumnTypeShorthandSchema,
1913
2407
  ContainerBuilder,
1914
2408
  ControlBuilder,
1915
2409
  ControlType,
@@ -1921,12 +2415,14 @@ var ViewConfigSchema = import_zod19.z.object({
1921
2415
  ElementBuilder,
1922
2416
  FieldInputSchema,
1923
2417
  FieldVariantSchema,
2418
+ I18nConfigSchema,
1924
2419
  JsonActionSchema,
1925
2420
  JsonColumnSchema,
1926
2421
  JsonIncludeEntrySchema,
1927
2422
  JsonLinkActionSchema,
1928
2423
  JsonOperationsSchema,
1929
2424
  JsonProcedureActionSchema,
2425
+ JsonSchemaFragmentSchema,
1930
2426
  LayoutBuilder,
1931
2427
  LayoutType,
1932
2428
  MIGRATIONS,
@@ -1938,6 +2434,7 @@ var ViewConfigSchema = import_zod19.z.object({
1938
2434
  OperatorNoValue,
1939
2435
  OperatorOptions,
1940
2436
  OperatorsByType,
2437
+ ParentRefSchema,
1941
2438
  PositiveRequestNumber,
1942
2439
  RelationFieldInputOptionsSchema,
1943
2440
  RelationType,
@@ -1945,6 +2442,7 @@ var ViewConfigSchema = import_zod19.z.object({
1945
2442
  RequestSchemaWithOffset,
1946
2443
  ResourceJsonSchema,
1947
2444
  ResourceJsonShape,
2445
+ ResourceKindSchema,
1948
2446
  ResponseRequestSchema,
1949
2447
  ResponseSchema,
1950
2448
  RulesetSchema,
@@ -1955,9 +2453,11 @@ var ViewConfigSchema = import_zod19.z.object({
1955
2453
  StringOrArray,
1956
2454
  TableBuilder,
1957
2455
  TextCellBuilder,
2456
+ TranslationBundleSchema,
1958
2457
  ViewColumnConfigSchema,
1959
2458
  ViewConfigSchema,
1960
2459
  WhenConditionSchema,
2460
+ actionKey,
1961
2461
  applySchemaTransforms,
1962
2462
  buildConditionSchema,
1963
2463
  buildFilter,
@@ -1967,13 +2467,22 @@ var ViewConfigSchema = import_zod19.z.object({
1967
2467
  buildSortKey,
1968
2468
  buildTableUiSchema,
1969
2469
  buildViews,
2470
+ buildViewsFromColumnTypes,
1970
2471
  buildViewsFromColumns,
2472
+ buildViewsWithSource,
1971
2473
  colPosition,
1972
2474
  columnForContext,
2475
+ columnKey,
2476
+ columnToJsonSchemaProperty,
2477
+ columnTypeName,
2478
+ columnTypeSchemaSource,
2479
+ columnTypeToJsonSchema,
1973
2480
  createSchema,
2481
+ createTranslator,
1974
2482
  deriveSortId,
1975
2483
  dropNullableFromRequired,
1976
2484
  enforceRequiredStringMinLength,
2485
+ enumKey,
1977
2486
  extractFilters,
1978
2487
  filterToString,
1979
2488
  findColumnDef,
@@ -1981,28 +2490,45 @@ var ViewConfigSchema = import_zod19.z.object({
1981
2490
  fromValueLabel,
1982
2491
  injectCalculatedColumns,
1983
2492
  injectCalculatedColumnsToView,
2493
+ isArrayColumn,
2494
+ isArrayColumnType,
1984
2495
  isAutocomplete,
1985
2496
  isBoolean,
2497
+ isCustomKind,
1986
2498
  isDateRange,
2499
+ isObjectCell,
2500
+ isObjectColumn,
2501
+ isObjectColumnType,
1987
2502
  isRecordCell,
1988
2503
  isRelation,
1989
2504
  jsonSchemaOpts,
1990
2505
  labelFromId,
1991
2506
  mergeFieldVariant,
1992
2507
  normalizeLabel,
2508
+ offsetOf,
1993
2509
  patchFilterProperties,
1994
2510
  pickSharedCellOptions,
1995
2511
  resolveDefaultSort,
1996
2512
  resolveFormField,
2513
+ resolveLanguage,
1997
2514
  resolveTableField,
1998
2515
  resolveViewField,
2516
+ resourceControllerPath,
2517
+ resourceSidebarKey,
2518
+ resourceTitleKey,
1999
2519
  resourceVersionOf,
2000
2520
  runResourceMigrations,
2521
+ sidebarGroupKey,
2001
2522
  sortByPosition,
2002
2523
  stringToFilter,
2524
+ subResourceColumnKey,
2525
+ subResourceTitleKey,
2003
2526
  toValueLabel,
2004
2527
  toViewColumn,
2005
2528
  transformDataSource,
2006
- uiFromJsonSchema
2529
+ uiFromJsonSchema,
2530
+ uiKey,
2531
+ validationKey,
2532
+ zodSchemaSource
2007
2533
  });
2008
2534
  //# sourceMappingURL=index.cjs.map