@ghentcdh/crouton-api 0.0.1-alpha.77 → 0.0.1-alpha.79

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
@@ -670,7 +670,7 @@ var LayoutControlSchema = import_zod8.z.union([
670
670
  import_zod8.z.object({
671
671
  id: import_zod8.z.string(),
672
672
  colspan: import_zod8.z.number().int().min(1).max(12).optional(),
673
- rowspan: import_zod8.z.number().int().min(1).optional(),
673
+ rowspan: import_zod8.z.number().int().min(1).max(6).optional(),
674
674
  width: import_zod8.z.string().optional(),
675
675
  label: import_zod8.z.string().optional(),
676
676
  hideLabel: import_zod8.z.boolean().optional(),
@@ -690,7 +690,7 @@ var LayoutNodeSchema = import_zod8.z.lazy(() => import_zod8.z.object({
690
690
  title: import_zod8.z.string().optional(),
691
691
  titleKey: import_zod8.z.string().optional(),
692
692
  colspan: import_zod8.z.number().int().min(1).max(12).optional(),
693
- rowspan: import_zod8.z.number().int().min(1).optional(),
693
+ rowspan: import_zod8.z.number().int().min(1).max(6).optional(),
694
694
  label: import_zod8.z.string().optional(),
695
695
  controls: import_zod8.z.array(LayoutControlSchema).optional(),
696
696
  items: import_zod8.z.array(LayoutNodeSchema).optional()
@@ -702,152 +702,526 @@ var LayoutSchema = import_zod8.z.object({
702
702
  });
703
703
 
704
704
  // ../crouton-core/src/lib/resource/ResourceJson.schema.ts
705
- var import_zod30 = require("zod");
705
+ var import_zod31 = require("zod");
706
706
 
707
707
  // ../crouton-core/src/lib/resource/CalculatedColumn.schema.ts
708
- var import_zod20 = require("zod");
708
+ var import_zod21 = require("zod");
709
709
 
710
710
  // ../crouton-core/src/lib/resource/FieldInput.schema.ts
711
- var import_zod19 = require("zod");
711
+ var import_zod20 = require("zod");
712
712
 
713
713
  // ../crouton-core/src/lib/resource/field-input/types/array.options.ts
714
714
  var import_zod10 = require("zod");
715
715
 
716
716
  // ../crouton-core/src/lib/resource/field-input/base.options.ts
717
717
  var import_zod9 = require("zod");
718
+
719
+ // ../crouton-core/src/lib/resource/field-input/option-meta.ts
720
+ var opt = /* @__PURE__ */ __name((schema, meta) => schema.meta(meta), "opt");
721
+
722
+ // ../crouton-core/src/lib/resource/field-input/base.options.ts
718
723
  var BaseOptionsSchema = import_zod9.z.object({
719
- label: import_zod9.z.string().optional().describe("Override the field label"),
720
- hideLabel: import_zod9.z.boolean().optional().describe("Hide the label"),
721
- placeholder: import_zod9.z.string().optional(),
722
- readonly: import_zod9.z.boolean().optional(),
723
- colspan: import_zod9.z.number().optional(),
724
- width: import_zod9.z.string().optional(),
725
- styles: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.unknown()).optional(),
726
- customRender: import_zod9.z.string().optional()
727
- }).catchall(import_zod9.z.unknown());
724
+ label: opt(import_zod9.z.string().optional(), {
725
+ description: "Override the field label",
726
+ examples: [
727
+ "My label"
728
+ ]
729
+ }),
730
+ hideLabel: opt(import_zod9.z.boolean().optional(), {
731
+ description: "Hide the field label"
732
+ }),
733
+ placeholder: opt(import_zod9.z.string().optional(), {
734
+ description: "Placeholder text shown inside the input when empty",
735
+ examples: [
736
+ "Search..."
737
+ ]
738
+ }),
739
+ readonly: opt(import_zod9.z.boolean().optional(), {
740
+ description: "Render the field in read-only mode"
741
+ }),
742
+ colspan: opt(import_zod9.z.number().optional(), {
743
+ description: "Grid column span (1\u201312)",
744
+ examples: [
745
+ 6,
746
+ 12
747
+ ]
748
+ }),
749
+ width: opt(import_zod9.z.string().optional(), {
750
+ description: "Named width (xs, sm, md, lg, xl, full) or any CSS value",
751
+ examples: [
752
+ "md",
753
+ "240px"
754
+ ]
755
+ }),
756
+ styles: opt(import_zod9.z.record(import_zod9.z.string(), import_zod9.z.unknown()).optional(), {
757
+ description: "Inline CSS style object applied to the field wrapper",
758
+ examples: [
759
+ {
760
+ color: "red"
761
+ }
762
+ ]
763
+ }),
764
+ customRender: opt(import_zod9.z.string().optional(), {
765
+ description: "Name of a globally registered Vue component to render instead of the default input",
766
+ examples: [
767
+ "MyCustomInput"
768
+ ]
769
+ })
770
+ }).catchall(import_zod9.z.unknown()).meta({
771
+ title: "Base options",
772
+ description: "Accepted by every field input type"
773
+ });
728
774
 
729
775
  // ../crouton-core/src/lib/resource/field-input/types/array.options.ts
730
776
  var ArrayOptionsSchema = BaseOptionsSchema.extend({
731
- layout: import_zod10.z.enum([
777
+ layout: opt(import_zod10.z.enum([
732
778
  "row",
733
779
  "column"
734
- ]).optional(),
735
- elementLabelProp: import_zod10.z.string().optional(),
736
- hideActions: import_zod10.z.boolean().optional()
780
+ ]).optional(), {
781
+ description: "Direction in which array items are stacked",
782
+ examples: [
783
+ "row",
784
+ "column"
785
+ ]
786
+ }),
787
+ elementLabelProp: opt(import_zod10.z.string().optional(), {
788
+ description: "Property of each array item used as its label in the UI",
789
+ examples: [
790
+ "name",
791
+ "title"
792
+ ]
793
+ }),
794
+ hideActions: opt(import_zod10.z.boolean().optional(), {
795
+ description: "Hide the add/remove action buttons"
796
+ })
797
+ }).meta({
798
+ title: "Array options",
799
+ description: "Options for the detail/array layout control"
737
800
  });
738
801
 
739
802
  // ../crouton-core/src/lib/resource/field-input/types/autocomplete.options.ts
740
803
  var import_zod11 = require("zod");
741
804
  var AutocompleteBaseSchema = BaseOptionsSchema.extend({
742
- labelKey: import_zod11.z.string().optional(),
743
- valueKey: import_zod11.z.string().optional(),
744
- freeText: import_zod11.z.boolean().optional(),
745
- enableCreate: import_zod11.z.boolean().optional(),
746
- storeValue: import_zod11.z.boolean().optional()
805
+ labelKey: opt(import_zod11.z.string().optional(), {
806
+ description: "Object key used as the display label in the dropdown",
807
+ examples: [
808
+ "label",
809
+ "name"
810
+ ]
811
+ }),
812
+ valueKey: opt(import_zod11.z.string().optional(), {
813
+ description: "Object key used as the stored value",
814
+ examples: [
815
+ "value",
816
+ "id"
817
+ ]
818
+ }),
819
+ freeText: opt(import_zod11.z.boolean().optional(), {
820
+ description: "Allow the user to type a value not in the options list"
821
+ }),
822
+ enableCreate: opt(import_zod11.z.boolean().optional(), {
823
+ description: 'Show a "create" action when no matching option is found'
824
+ }),
825
+ storeValue: opt(import_zod11.z.boolean().optional(), {
826
+ description: "Store option[valueKey] instead of the whole option object"
827
+ })
828
+ }).meta({
829
+ title: "Common autocomplete options",
830
+ description: "Shared options available on all autocomplete variants"
747
831
  });
748
832
  var AutocompleteInlineOptionsSchema = AutocompleteBaseSchema.extend({
749
- options: import_zod11.z.array(import_zod11.z.unknown()),
750
- values: import_zod11.z.array(import_zod11.z.unknown()).optional()
833
+ options: opt(import_zod11.z.array(import_zod11.z.unknown()), {
834
+ description: "Inline array of option items",
835
+ examples: [
836
+ [
837
+ {
838
+ label: "Active",
839
+ value: "active"
840
+ }
841
+ ]
842
+ ]
843
+ }),
844
+ values: opt(import_zod11.z.array(import_zod11.z.unknown()).optional(), {
845
+ description: "Alias for options (deprecated \u2014 prefer options)",
846
+ deprecated: true
847
+ })
848
+ }).meta({
849
+ title: "Inline autocomplete",
850
+ description: "Options provided as an inline array"
751
851
  });
752
852
  var AutocompleteRemoteOptionsSchema = AutocompleteBaseSchema.extend({
753
- uri: import_zod11.z.string().describe("Remote endpoint URL"),
754
- dataField: import_zod11.z.string().optional().describe("Field in response containing the data array"),
755
- skipAuth: import_zod11.z.boolean().optional()
853
+ uri: opt(import_zod11.z.string(), {
854
+ description: "Remote endpoint URL that returns option items",
855
+ examples: [
856
+ "/api/authors"
857
+ ]
858
+ }),
859
+ dataField: opt(import_zod11.z.string().optional(), {
860
+ description: "Field in the API response containing the data array",
861
+ examples: [
862
+ "data",
863
+ "results"
864
+ ]
865
+ }),
866
+ skipAuth: opt(import_zod11.z.boolean().optional(), {
867
+ description: "Send the request without authentication headers"
868
+ })
869
+ }).meta({
870
+ title: "Remote autocomplete",
871
+ description: "Options fetched from a remote endpoint"
756
872
  });
757
873
  var AutocompleteResourceOptionsSchema = AutocompleteBaseSchema.extend({
758
- resource: import_zod11.z.string().describe("Relative path to a resource.json"),
759
- skipAuth: import_zod11.z.boolean().optional(),
760
- dataField: import_zod11.z.string().optional()
874
+ resource: opt(import_zod11.z.string(), {
875
+ description: "Relative path to a resource.json used to populate the options",
876
+ examples: [
877
+ "./author.resource.json"
878
+ ]
879
+ }),
880
+ skipAuth: opt(import_zod11.z.boolean().optional(), {
881
+ description: "Send the request without authentication headers"
882
+ }),
883
+ dataField: opt(import_zod11.z.string().optional(), {
884
+ description: "Field in the API response containing the data array",
885
+ examples: [
886
+ "data",
887
+ "results"
888
+ ]
889
+ })
890
+ }).meta({
891
+ title: "Resource autocomplete",
892
+ description: "Options populated from a crouton resource"
761
893
  });
762
894
  var AutocompleteOptionsSchema = import_zod11.z.union([
763
895
  AutocompleteInlineOptionsSchema,
764
896
  AutocompleteRemoteOptionsSchema,
765
897
  AutocompleteResourceOptionsSchema,
766
898
  AutocompleteBaseSchema
767
- ]).describe("Autocomplete options: inline array, remote URI, or resource reference");
899
+ ]).meta({
900
+ title: "Autocomplete options",
901
+ description: "Autocomplete options: inline array, remote URI, or resource reference"
902
+ });
768
903
 
769
904
  // ../crouton-core/src/lib/resource/field-input/types/boolean.options.ts
770
- var BooleanOptionsSchema = BaseOptionsSchema.extend({});
905
+ var BooleanOptionsSchema = BaseOptionsSchema.extend({}).meta({
906
+ title: "Boolean options",
907
+ description: "Checkbox input. All common options apply; no type-specific options."
908
+ });
771
909
 
772
910
  // ../crouton-core/src/lib/resource/field-input/types/custom.options.ts
773
911
  var import_zod12 = require("zod");
774
912
  var CustomOptionsSchema = BaseOptionsSchema.extend({
775
- customRender: import_zod12.z.string().optional()
776
- }).catchall(import_zod12.z.unknown());
913
+ customRender: opt(import_zod12.z.string().optional(), {
914
+ description: "Name of the globally registered Vue component to render (overrides the base option)",
915
+ examples: [
916
+ "MyWidget"
917
+ ]
918
+ })
919
+ }).catchall(import_zod12.z.unknown()).meta({
920
+ title: "Custom options",
921
+ description: "Options for app-registered custom renderer components"
922
+ });
777
923
 
778
924
  // ../crouton-core/src/lib/resource/field-input/types/date-range.options.ts
779
925
  var import_zod13 = require("zod");
780
926
  var DateRangeOptionsSchema = BaseOptionsSchema.extend({
781
- fromLabel: import_zod13.z.string().optional().describe('Label above the start date input. Defaults to "From"'),
782
- toLabel: import_zod13.z.string().optional().describe('Label above the end date input. Defaults to "To"'),
783
- fromField: import_zod13.z.string().optional().describe('JSON object key for the start date. Defaults to "from"'),
784
- toField: import_zod13.z.string().optional().describe('JSON object key for the end date. Defaults to "to"')
927
+ fromLabel: opt(import_zod13.z.string().optional(), {
928
+ description: "Label above the start date input",
929
+ default: "From",
930
+ examples: [
931
+ "From",
932
+ "Start date"
933
+ ]
934
+ }),
935
+ toLabel: opt(import_zod13.z.string().optional(), {
936
+ description: "Label above the end date input",
937
+ default: "To",
938
+ examples: [
939
+ "To",
940
+ "End date"
941
+ ]
942
+ }),
943
+ fromField: opt(import_zod13.z.string().optional(), {
944
+ description: "JSON object key for the start date",
945
+ default: "from",
946
+ examples: [
947
+ "from",
948
+ "startDate"
949
+ ]
950
+ }),
951
+ toField: opt(import_zod13.z.string().optional(), {
952
+ description: "JSON object key for the end date",
953
+ default: "to",
954
+ examples: [
955
+ "to",
956
+ "endDate"
957
+ ]
958
+ })
959
+ }).meta({
960
+ title: "Date-range options",
961
+ description: "Options for the from/to date range picker"
785
962
  });
786
963
 
787
964
  // ../crouton-core/src/lib/resource/field-input/types/date.options.ts
788
965
  var import_zod14 = require("zod");
789
966
  var DateOptionsSchema = BaseOptionsSchema.extend({
790
- withTime: import_zod14.z.boolean().optional().describe("Force time input on/off, overrides format"),
791
- min: import_zod14.z.string().optional().describe("Earliest selectable date, ISO or yyyy-MM-dd"),
792
- max: import_zod14.z.string().optional().describe("Latest selectable date, ISO or yyyy-MM-dd"),
793
- locale: import_zod14.z.string().optional().describe("BCP-47 locale for month heading and weekday labels").default("en-GB"),
794
- firstDayOfWeek: import_zod14.z.number().optional().describe("0 = Sunday, 1 = Monday").default(1)
967
+ withTime: opt(import_zod14.z.boolean().optional(), {
968
+ description: "Force time input on/off, overrides format detection"
969
+ }),
970
+ min: opt(import_zod14.z.string().optional(), {
971
+ description: "Earliest selectable date (ISO 8601 or yyyy-MM-dd)",
972
+ examples: [
973
+ "2020-01-01"
974
+ ]
975
+ }),
976
+ max: opt(import_zod14.z.string().optional(), {
977
+ description: "Latest selectable date (ISO 8601 or yyyy-MM-dd)",
978
+ examples: [
979
+ "2099-12-31"
980
+ ]
981
+ }),
982
+ locale: opt(import_zod14.z.string().optional().default("en-GB"), {
983
+ description: "BCP-47 locale for month heading and weekday labels",
984
+ examples: [
985
+ "en-GB",
986
+ "nl-BE"
987
+ ],
988
+ default: "en-GB"
989
+ }),
990
+ firstDayOfWeek: opt(import_zod14.z.number().optional().default(1), {
991
+ description: "First day of the week: 0 = Sunday, 1 = Monday",
992
+ examples: [
993
+ 0,
994
+ 1
995
+ ],
996
+ default: 1
997
+ })
998
+ }).meta({
999
+ title: "Date options",
1000
+ description: "Options for the date picker (and dateTime alias)"
795
1001
  });
796
1002
 
797
1003
  // ../crouton-core/src/lib/resource/field-input/types/markdown.options.ts
798
1004
  var import_zod15 = require("zod");
799
1005
  var MarkdownOptionsSchema = BaseOptionsSchema.extend({
800
- minHeight: import_zod15.z.string().optional().describe('Minimum editor height, e.g. "200px"')
1006
+ minHeight: opt(import_zod15.z.string().optional(), {
1007
+ description: "Minimum editor height",
1008
+ examples: [
1009
+ "200px",
1010
+ "400px"
1011
+ ]
1012
+ })
1013
+ }).meta({
1014
+ title: "Markdown options",
1015
+ description: "Options for the Markdown rich-text editor"
801
1016
  });
802
1017
 
803
1018
  // ../crouton-core/src/lib/resource/field-input/types/number.options.ts
804
- var NumberOptionsSchema = BaseOptionsSchema.extend({});
1019
+ var NumberOptionsSchema = BaseOptionsSchema.extend({}).meta({
1020
+ title: "Number options",
1021
+ description: "Numeric input (also used for Integer alias). All common options apply; no type-specific options."
1022
+ });
805
1023
 
806
1024
  // ../crouton-core/src/lib/resource/field-input/types/relation.options.ts
807
1025
  var import_zod16 = require("zod");
808
1026
  var RelationOptionsSchema = BaseOptionsSchema.extend({
809
- colspan: import_zod16.z.number().optional().default(12),
810
- sort: import_zod16.z.string().optional().describe("Field to sort related records by"),
811
- sortDir: import_zod16.z.enum([
1027
+ colspan: opt(import_zod16.z.number().optional().default(12), {
1028
+ description: "Grid column span (1\u201312); defaults to 12 for relation controls",
1029
+ default: 12,
1030
+ examples: [
1031
+ 12,
1032
+ 6
1033
+ ]
1034
+ }),
1035
+ sort: opt(import_zod16.z.string().optional(), {
1036
+ description: "Field to sort related records by",
1037
+ examples: [
1038
+ "name",
1039
+ "createdAt"
1040
+ ]
1041
+ }),
1042
+ sortDir: opt(import_zod16.z.enum([
812
1043
  "asc",
813
1044
  "desc"
814
- ]).optional().describe("Sort direction, defaults to asc"),
815
- direction: import_zod16.z.string().optional().describe("CSS flex direction for the button layout"),
816
- displayKey: import_zod16.z.string().optional().describe("Key used as display label"),
817
- resource: import_zod16.z.string().optional().describe("Path to the related resource")
818
- }).catchall(import_zod16.z.unknown());
1045
+ ]).optional(), {
1046
+ description: "Sort direction",
1047
+ default: "asc",
1048
+ examples: [
1049
+ "asc",
1050
+ "desc"
1051
+ ]
1052
+ }),
1053
+ direction: opt(import_zod16.z.string().optional(), {
1054
+ description: "CSS flex direction for the button layout",
1055
+ examples: [
1056
+ "row",
1057
+ "column"
1058
+ ]
1059
+ }),
1060
+ displayKey: opt(import_zod16.z.string().optional(), {
1061
+ description: "Key used as the display label for related records",
1062
+ examples: [
1063
+ "name",
1064
+ "title"
1065
+ ]
1066
+ }),
1067
+ resource: opt(import_zod16.z.string().optional(), {
1068
+ description: "Path to the related resource.json",
1069
+ examples: [
1070
+ "./author.resource.json"
1071
+ ]
1072
+ })
1073
+ }).catchall(import_zod16.z.unknown()).meta({
1074
+ title: "Relation options",
1075
+ description: "Options for the sub-resource relation control"
1076
+ });
819
1077
 
820
1078
  // ../crouton-core/src/lib/resource/field-input/types/select.options.ts
821
1079
  var import_zod17 = require("zod");
822
1080
  var SelectOptionsSchema = BaseOptionsSchema.extend({
823
- options: import_zod17.z.array(import_zod17.z.unknown()).optional(),
824
- values: import_zod17.z.array(import_zod17.z.unknown()).optional(),
825
- resource: import_zod17.z.string().optional(),
826
- labelKey: import_zod17.z.string().optional(),
827
- valueKey: import_zod17.z.string().optional(),
828
- uri: import_zod17.z.string().optional().describe("Remote endpoint URL for fetching options"),
829
- dataField: import_zod17.z.string().optional().describe("Field in response containing the data array"),
830
- clearable: import_zod17.z.boolean().optional().describe("Allow clearing the selected value"),
831
- storeValue: import_zod17.z.boolean().optional().describe("Store option[valueKey] instead of the whole object")
1081
+ options: opt(import_zod17.z.array(import_zod17.z.unknown()).optional(), {
1082
+ description: "Inline array of option items",
1083
+ examples: [
1084
+ [
1085
+ {
1086
+ label: "Active",
1087
+ value: "active"
1088
+ }
1089
+ ]
1090
+ ]
1091
+ }),
1092
+ values: opt(import_zod17.z.array(import_zod17.z.unknown()).optional(), {
1093
+ description: "Alias for options (deprecated \u2014 prefer options)",
1094
+ deprecated: true
1095
+ }),
1096
+ resource: opt(import_zod17.z.string().optional(), {
1097
+ description: "Relative path to a resource.json used to populate the options",
1098
+ examples: [
1099
+ "./status.resource.json"
1100
+ ]
1101
+ }),
1102
+ labelKey: opt(import_zod17.z.string().optional(), {
1103
+ description: "Object key used as the display label",
1104
+ examples: [
1105
+ "label",
1106
+ "name"
1107
+ ]
1108
+ }),
1109
+ valueKey: opt(import_zod17.z.string().optional(), {
1110
+ description: "Object key used as the stored value",
1111
+ examples: [
1112
+ "value",
1113
+ "id"
1114
+ ]
1115
+ }),
1116
+ uri: opt(import_zod17.z.string().optional(), {
1117
+ description: "Remote endpoint URL for fetching options",
1118
+ examples: [
1119
+ "/api/statuses"
1120
+ ]
1121
+ }),
1122
+ dataField: opt(import_zod17.z.string().optional(), {
1123
+ description: "Field in the API response containing the data array",
1124
+ examples: [
1125
+ "data",
1126
+ "results"
1127
+ ]
1128
+ }),
1129
+ clearable: opt(import_zod17.z.boolean().optional(), {
1130
+ description: "Show a clear button to remove the selected value"
1131
+ }),
1132
+ storeValue: opt(import_zod17.z.boolean().optional(), {
1133
+ description: "Store option[valueKey] instead of the whole option object"
1134
+ })
1135
+ }).meta({
1136
+ title: "Select options",
1137
+ description: "Options for the select dropdown and multi-select (mutliSelect alias)"
832
1138
  });
833
1139
 
834
1140
  // ../crouton-core/src/lib/resource/field-input/types/string.options.ts
835
- var StringOptionsSchema = BaseOptionsSchema.extend({});
1141
+ var import_zod18 = require("zod");
1142
+ var NormalizedUniqueSchema = import_zod18.z.object({
1143
+ enabled: import_zod18.z.boolean(),
1144
+ scope: import_zod18.z.array(import_zod18.z.string()).optional(),
1145
+ caseInsensitive: import_zod18.z.boolean().optional(),
1146
+ message: import_zod18.z.string().optional()
1147
+ });
1148
+ var StringOptionsSchema = BaseOptionsSchema.extend({
1149
+ /** Async uniqueness validation: present when the column declares `unique`. */
1150
+ unique: NormalizedUniqueSchema.optional().meta({
1151
+ description: "Async uniqueness validation: present when the column declares `unique`."
1152
+ })
1153
+ }).meta({
1154
+ title: "String options",
1155
+ description: "Plain text input. All common options apply; no type-specific options."
1156
+ });
836
1157
 
837
1158
  // ../crouton-core/src/lib/resource/field-input/types/textarea.options.ts
838
- var TextareaOptionsSchema = BaseOptionsSchema.extend({});
1159
+ var TextareaOptionsSchema = BaseOptionsSchema.extend({}).meta({
1160
+ title: "Textarea options",
1161
+ description: "Multi-line text input. All common options apply; no type-specific options."
1162
+ });
839
1163
 
840
1164
  // ../crouton-core/src/lib/resource/field-input/types/toggle.options.ts
841
- var import_zod18 = require("zod");
1165
+ var import_zod19 = require("zod");
842
1166
  var ToggleOptionsSchema = BaseOptionsSchema.extend({
843
- options: import_zod18.z.array(import_zod18.z.unknown()).optional(),
844
- values: import_zod18.z.array(import_zod18.z.unknown()).optional(),
845
- labelKey: import_zod18.z.string().optional(),
846
- valueKey: import_zod18.z.string().optional(),
847
- storeValue: import_zod18.z.boolean().optional().describe("Store option[valueKey] instead of the whole object"),
848
- clearable: import_zod18.z.boolean().optional().describe("Clicking active button clears the value").default(true),
849
- color: import_zod18.z.string().optional().describe("Btn color for the active button").default("primary"),
850
- size: import_zod18.z.string().optional().describe("Btn size").default("sm")
1167
+ options: opt(import_zod19.z.array(import_zod19.z.unknown()).optional(), {
1168
+ description: "Array of option items rendered as toggle buttons",
1169
+ examples: [
1170
+ [
1171
+ {
1172
+ label: "Yes",
1173
+ value: true
1174
+ },
1175
+ {
1176
+ label: "No",
1177
+ value: false
1178
+ }
1179
+ ]
1180
+ ]
1181
+ }),
1182
+ values: opt(import_zod19.z.array(import_zod19.z.unknown()).optional(), {
1183
+ description: "Alias for options (deprecated \u2014 prefer options)",
1184
+ deprecated: true
1185
+ }),
1186
+ labelKey: opt(import_zod19.z.string().optional(), {
1187
+ description: "Object key used as the button label",
1188
+ examples: [
1189
+ "label"
1190
+ ]
1191
+ }),
1192
+ valueKey: opt(import_zod19.z.string().optional(), {
1193
+ description: "Object key used as the stored value",
1194
+ examples: [
1195
+ "value"
1196
+ ]
1197
+ }),
1198
+ storeValue: opt(import_zod19.z.boolean().optional(), {
1199
+ description: "Store option[valueKey] instead of the whole option object"
1200
+ }),
1201
+ clearable: opt(import_zod19.z.boolean().optional().default(true), {
1202
+ description: "Clicking the active button again clears the value",
1203
+ default: true
1204
+ }),
1205
+ color: opt(import_zod19.z.string().optional().default("primary"), {
1206
+ description: "Color token applied to the active button",
1207
+ examples: [
1208
+ "primary",
1209
+ "secondary"
1210
+ ],
1211
+ default: "primary"
1212
+ }),
1213
+ size: opt(import_zod19.z.string().optional().default("sm"), {
1214
+ description: "Size token for the buttons",
1215
+ examples: [
1216
+ "sm",
1217
+ "md",
1218
+ "lg"
1219
+ ],
1220
+ default: "sm"
1221
+ })
1222
+ }).meta({
1223
+ title: "Toggle options",
1224
+ description: "Options for the toggle button-group control"
851
1225
  });
852
1226
 
853
1227
  // ../crouton-core/src/lib/resource/field-input/registry.ts
@@ -967,42 +1341,42 @@ var fieldInputRegistry = /* @__PURE__ */ new Map([
967
1341
  ]);
968
1342
 
969
1343
  // ../crouton-core/src/lib/resource/FieldInput.schema.ts
970
- var RelationType = import_zod19.z.enum([
1344
+ var RelationType = import_zod20.z.enum([
971
1345
  "oneToOne",
972
1346
  "manyToOne",
973
1347
  "oneToMany",
974
1348
  "manyToMany"
975
1349
  ]);
976
- var DetailControlSchema = import_zod19.z.object({
977
- property: import_zod19.z.string(),
978
- type: import_zod19.z.string().optional(),
979
- options: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.unknown()).optional(),
980
- hideLabel: import_zod19.z.boolean().optional(),
981
- width: import_zod19.z.string().optional()
1350
+ var DetailControlSchema = import_zod20.z.object({
1351
+ property: import_zod20.z.string(),
1352
+ type: import_zod20.z.string().optional(),
1353
+ options: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.unknown()).optional(),
1354
+ hideLabel: import_zod20.z.boolean().optional(),
1355
+ width: import_zod20.z.string().optional()
982
1356
  });
983
- var DetailConfigSchema = import_zod19.z.object({
984
- layout: import_zod19.z.enum([
1357
+ var DetailConfigSchema = import_zod20.z.object({
1358
+ layout: import_zod20.z.enum([
985
1359
  "collapse",
986
1360
  "row"
987
1361
  ]),
988
- titleKey: import_zod19.z.string().optional(),
989
- controls: import_zod19.z.array(DetailControlSchema)
1362
+ titleKey: import_zod20.z.string().optional(),
1363
+ controls: import_zod20.z.array(DetailControlSchema)
990
1364
  });
991
- var FieldInputSchema = import_zod19.z.object({
992
- $schema: import_zod19.z.string().optional(),
993
- type: import_zod19.z.string().optional(),
994
- customRender: import_zod19.z.string().optional(),
1365
+ var FieldInputSchema = import_zod20.z.object({
1366
+ $schema: import_zod20.z.string().optional(),
1367
+ type: import_zod20.z.string().optional(),
1368
+ customRender: import_zod20.z.string().optional(),
995
1369
  /**
996
1370
  * Render format hint for the frontend (e.g. `"relation"` for sub-resource
997
1371
  * relations). When `format` is `"relation"`, `resource` must also be set.
998
1372
  */
999
- format: import_zod19.z.string().optional(),
1373
+ format: import_zod20.z.string().optional(),
1000
1374
  /**
1001
1375
  * Relative path to the child resource definition, e.g. `"./author.resource"`.
1002
1376
  * Only used when `format` is `"relation"`. Resolved at load time to inject
1003
1377
  * sub-resource URIs into `options`.
1004
1378
  */
1005
- resource: import_zod19.z.string().optional(),
1379
+ resource: import_zod20.z.string().optional(),
1006
1380
  /**
1007
1381
  * Cardinality of the relation. Used by the frontend to determine the correct
1008
1382
  * renderer and behaviour (e.g. single-select vs multi-select).
@@ -1011,11 +1385,11 @@ var FieldInputSchema = import_zod19.z.object({
1011
1385
  */
1012
1386
  relationType: RelationType.optional(),
1013
1387
  /** FK field on the child model pointing back to the parent, e.g. `"workId"`. Defaults to `${parentModel}Id`. */
1014
- foreignKey: import_zod19.z.string().optional(),
1388
+ foreignKey: import_zod20.z.string().optional(),
1015
1389
  /** Override the Prisma relation field name when it differs from the column id. */
1016
- relation: import_zod19.z.string().optional(),
1390
+ relation: import_zod20.z.string().optional(),
1017
1391
  /** Override the display order in form views. Lower values come first. */
1018
- position: import_zod19.z.number().optional(),
1392
+ position: import_zod20.z.number().optional(),
1019
1393
  /**
1020
1394
  * Value to pre-fill this field with on the create form. Injected into the
1021
1395
  * generated form view's JSON Schema as `properties[id].default`, which
@@ -1028,10 +1402,10 @@ var FieldInputSchema = import_zod19.z.object({
1028
1402
  * - `"$today"` — current date ISO string (date only, e.g. `"2025-03-14"`)
1029
1403
  * - `"$user"` — current user object (requires `defaults: { '$user': ... }` on `CroutonPlugin` config, or `useCrouton().setDefault('$user', ...)`)
1030
1404
  */
1031
- defaultValue: import_zod19.z.unknown().optional(),
1032
- options: import_zod19.z.union([
1405
+ defaultValue: import_zod20.z.unknown().optional(),
1406
+ options: import_zod20.z.union([
1033
1407
  RelationOptionsSchema,
1034
- import_zod19.z.unknown()
1408
+ import_zod20.z.unknown()
1035
1409
  ]).optional(),
1036
1410
  /** Nested array detail layout (renders via `detailFixed`). */
1037
1411
  detail: DetailConfigSchema.optional()
@@ -1082,16 +1456,16 @@ var normalizeLabel = /* @__PURE__ */ __name((obj) => {
1082
1456
  }, "normalizeLabel");
1083
1457
 
1084
1458
  // ../crouton-core/src/lib/resource/CalculatedColumn.schema.ts
1085
- var CalculatedColumnSchema = import_zod20.z.object({
1459
+ var CalculatedColumnSchema = import_zod21.z.object({
1086
1460
  /** Column identifier used in the response payload and UI schema. */
1087
- id: import_zod20.z.string(),
1461
+ id: import_zod21.z.string(),
1088
1462
  /** SQL alias — must match `id`. */
1089
- alias: import_zod20.z.string(),
1463
+ alias: import_zod21.z.string(),
1090
1464
  /** Human-readable label shown in the table header. */
1091
- label: import_zod20.z.string().optional(),
1092
- sqlExpression: import_zod20.z.string(),
1465
+ label: import_zod21.z.string().optional(),
1466
+ sqlExpression: import_zod21.z.string(),
1093
1467
  /** JSON schema / cast type for the computed value. Defaults to `"number"`. `"string"` skips CAST. */
1094
- type: import_zod20.z.enum([
1468
+ type: import_zod21.z.enum([
1095
1469
  "number",
1096
1470
  "boolean",
1097
1471
  "string"
@@ -1102,9 +1476,9 @@ var CalculatedColumnSchema = import_zod20.z.object({
1102
1476
  * `position: N` places this column before the element currently at index N.
1103
1477
  * Can also be set via `fieldInput.position` (takes precedence).
1104
1478
  */
1105
- position: import_zod20.z.number().optional(),
1106
- hiddenInTable: import_zod20.z.boolean().optional(),
1107
- hiddenInView: import_zod20.z.boolean().optional(),
1479
+ position: import_zod21.z.number().optional(),
1480
+ hiddenInTable: import_zod21.z.boolean().optional(),
1481
+ hiddenInView: import_zod21.z.boolean().optional(),
1108
1482
  /**
1109
1483
  * Field input options for rendering in form / view schemas.
1110
1484
  * Supports `colspan`, `format`, and any other options passed to the Control renderer.
@@ -1114,11 +1488,11 @@ var CalculatedColumnSchema = import_zod20.z.object({
1114
1488
  }).transform(normalizeLabel);
1115
1489
 
1116
1490
  // ../crouton-core/src/lib/resource/Column.ts
1117
- var import_zod22 = require("zod");
1491
+ var import_zod23 = require("zod");
1118
1492
 
1119
1493
  // ../crouton-core/src/lib/resource/ColumnType.schema.ts
1120
- var import_zod21 = require("zod");
1121
- var ColumnTypeShorthandSchema = import_zod21.z.enum([
1494
+ var import_zod22 = require("zod");
1495
+ var ColumnTypeShorthandSchema = import_zod22.z.enum([
1122
1496
  "string",
1123
1497
  "number",
1124
1498
  "integer",
@@ -1128,36 +1502,36 @@ var ColumnTypeShorthandSchema = import_zod21.z.enum([
1128
1502
  "object",
1129
1503
  "array"
1130
1504
  ]);
1131
- var JsonSchemaFragmentSchema = import_zod21.z.lazy(() => import_zod21.z.object({
1132
- type: import_zod21.z.union([
1133
- import_zod21.z.string(),
1134
- import_zod21.z.array(import_zod21.z.string()),
1135
- import_zod21.z.lazy(() => JsonSchemaFragmentSchema)
1505
+ var JsonSchemaFragmentSchema = import_zod22.z.lazy(() => import_zod22.z.object({
1506
+ type: import_zod22.z.union([
1507
+ import_zod22.z.string(),
1508
+ import_zod22.z.array(import_zod22.z.string()),
1509
+ import_zod22.z.lazy(() => JsonSchemaFragmentSchema)
1136
1510
  ]).optional(),
1137
- format: import_zod21.z.string().optional(),
1138
- enum: import_zod21.z.array(import_zod21.z.unknown()).optional(),
1139
- const: import_zod21.z.unknown().optional(),
1140
- nullable: import_zod21.z.boolean().optional(),
1141
- properties: import_zod21.z.record(import_zod21.z.string(), JsonSchemaFragmentSchema).optional(),
1142
- required: import_zod21.z.union([
1143
- import_zod21.z.array(import_zod21.z.string()),
1144
- import_zod21.z.boolean()
1511
+ format: import_zod22.z.string().optional(),
1512
+ enum: import_zod22.z.array(import_zod22.z.unknown()).optional(),
1513
+ const: import_zod22.z.unknown().optional(),
1514
+ nullable: import_zod22.z.boolean().optional(),
1515
+ properties: import_zod22.z.record(import_zod22.z.string(), JsonSchemaFragmentSchema).optional(),
1516
+ required: import_zod22.z.union([
1517
+ import_zod22.z.array(import_zod22.z.string()),
1518
+ import_zod22.z.boolean()
1145
1519
  ]).optional(),
1146
1520
  items: JsonSchemaFragmentSchema.optional(),
1147
- additionalProperties: import_zod21.z.union([
1148
- import_zod21.z.boolean(),
1521
+ additionalProperties: import_zod22.z.union([
1522
+ import_zod22.z.boolean(),
1149
1523
  JsonSchemaFragmentSchema
1150
1524
  ]).optional(),
1151
- title: import_zod21.z.string().optional(),
1152
- description: import_zod21.z.string().optional(),
1153
- default: import_zod21.z.unknown().optional(),
1154
- minimum: import_zod21.z.number().optional(),
1155
- maximum: import_zod21.z.number().optional(),
1156
- minLength: import_zod21.z.number().optional(),
1157
- maxLength: import_zod21.z.number().optional(),
1158
- pattern: import_zod21.z.string().optional()
1159
- }).catchall(import_zod21.z.unknown()));
1160
- var ColumnTypeSchema = import_zod21.z.union([
1525
+ title: import_zod22.z.string().optional(),
1526
+ description: import_zod22.z.string().optional(),
1527
+ default: import_zod22.z.unknown().optional(),
1528
+ minimum: import_zod22.z.number().optional(),
1529
+ maximum: import_zod22.z.number().optional(),
1530
+ minLength: import_zod22.z.number().optional(),
1531
+ maxLength: import_zod22.z.number().optional(),
1532
+ pattern: import_zod22.z.string().optional()
1533
+ }).catchall(import_zod22.z.unknown()));
1534
+ var ColumnTypeSchema = import_zod22.z.union([
1161
1535
  ColumnTypeShorthandSchema,
1162
1536
  JsonSchemaFragmentSchema
1163
1537
  ]);
@@ -1261,40 +1635,73 @@ var isObjectColumnType = /* @__PURE__ */ __name((type) => columnTypeName(type) =
1261
1635
  var isArrayColumnType = /* @__PURE__ */ __name((type) => columnTypeName(type) === "array", "isArrayColumnType");
1262
1636
 
1263
1637
  // ../crouton-core/src/lib/resource/Column.ts
1264
- var WhenConditionSchema = import_zod22.z.object({
1265
- field: import_zod22.z.string(),
1266
- eq: import_zod22.z.unknown().optional(),
1267
- neq: import_zod22.z.unknown().optional(),
1268
- exists: import_zod22.z.boolean().optional(),
1269
- notExists: import_zod22.z.boolean().optional()
1638
+ var UniqueConfigSchema = import_zod23.z.object({
1639
+ /** Other column ids the uniqueness is scoped by (composite unique). */
1640
+ scope: import_zod23.z.array(import_zod23.z.string()).optional(),
1641
+ /** Compare case-insensitively (maps to Prisma `mode: 'insensitive'`). */
1642
+ caseInsensitive: import_zod23.z.boolean().optional(),
1643
+ /** Message shown on the field when the value is already taken. */
1644
+ message: import_zod23.z.string().optional()
1645
+ });
1646
+ var UniqueSchema = import_zod23.z.union([
1647
+ import_zod23.z.boolean(),
1648
+ UniqueConfigSchema
1649
+ ]);
1650
+ var normalizeUnique = /* @__PURE__ */ __name((unique) => {
1651
+ if (!unique) return void 0;
1652
+ if (unique === true) return {
1653
+ enabled: true
1654
+ };
1655
+ return {
1656
+ enabled: true,
1657
+ ...unique
1658
+ };
1659
+ }, "normalizeUnique");
1660
+ var WhenConditionSchema = import_zod23.z.object({
1661
+ field: import_zod23.z.string(),
1662
+ eq: import_zod23.z.unknown().optional(),
1663
+ neq: import_zod23.z.unknown().optional(),
1664
+ exists: import_zod23.z.boolean().optional(),
1665
+ notExists: import_zod23.z.boolean().optional()
1270
1666
  });
1271
- var JsonColumnSchema = import_zod22.z.object({
1272
- id: import_zod22.z.string(),
1273
- column: import_zod22.z.string().optional(),
1274
- label: import_zod22.z.string().optional(),
1275
- hiddenInTable: import_zod22.z.boolean().default(false),
1276
- hiddenInForm: import_zod22.z.boolean().default(false),
1277
- hiddenInView: import_zod22.z.boolean().default(false),
1278
- sortable: import_zod22.z.boolean().default(false),
1279
- defaultSort: import_zod22.z.boolean().default(false),
1280
- searchable: import_zod22.z.boolean().default(false),
1281
- filterable: import_zod22.z.boolean().default(false),
1282
- createable: import_zod22.z.boolean().default(true),
1283
- updateable: import_zod22.z.boolean().default(true),
1284
- hideLabel: import_zod22.z.boolean().default(false),
1667
+ var JsonColumnSchema = import_zod23.z.object({
1668
+ id: import_zod23.z.string(),
1669
+ column: import_zod23.z.string().optional(),
1670
+ label: import_zod23.z.string().optional(),
1671
+ hiddenInTable: import_zod23.z.boolean().default(false),
1672
+ hiddenInForm: import_zod23.z.boolean().default(false),
1673
+ hiddenInView: import_zod23.z.boolean().default(false),
1674
+ sortable: import_zod23.z.boolean().default(false),
1675
+ defaultSort: import_zod23.z.boolean().default(false),
1676
+ searchable: import_zod23.z.boolean().default(false),
1677
+ filterable: import_zod23.z.boolean().default(false),
1678
+ createable: import_zod23.z.boolean().default(true),
1679
+ updateable: import_zod23.z.boolean().default(true),
1680
+ hideLabel: import_zod23.z.boolean().default(false),
1285
1681
  showWhen: WhenConditionSchema.optional(),
1286
1682
  hideWhen: WhenConditionSchema.optional(),
1287
1683
  disabledWhen: WhenConditionSchema.optional(),
1288
- displayKey: import_zod22.z.string().optional(),
1289
- sortId: import_zod22.z.string().optional(),
1684
+ displayKey: import_zod23.z.string().optional(),
1685
+ sortId: import_zod23.z.string().optional(),
1290
1686
  /**
1291
1687
  * Name of a shared enum in the project enum registry (`crouton.enums.json`).
1292
1688
  * At load time the loader injects that enum's `{ value, label }[]` into
1293
1689
  * `fieldInput.options.values`, so columns don't duplicate the option list.
1294
1690
  */
1295
- enum: import_zod22.z.string().optional(),
1296
- idField: import_zod22.z.boolean().default(false),
1297
- showInLookup: import_zod22.z.boolean().default(false),
1691
+ enum: import_zod23.z.string().optional(),
1692
+ idField: import_zod23.z.boolean().default(false),
1693
+ showInLookup: import_zod23.z.boolean().default(false),
1694
+ /**
1695
+ * Enforce that this column's value is unique across the resource. `true` is
1696
+ * simple single-column uniqueness; the object form allows a composite
1697
+ * `scope`, case-insensitive comparison, and a custom `message`.
1698
+ *
1699
+ * Requires a matching DB unique constraint (`@unique` in Prisma) to be
1700
+ * actually enforceable - the flag alone does not create one. The form uses it
1701
+ * to validate as-you-type against a lightweight check endpoint; the API maps
1702
+ * a Prisma unique violation back to this field on write.
1703
+ */
1704
+ unique: UniqueSchema.optional(),
1298
1705
  /**
1299
1706
  * Data type of the column, as a shorthand name (`"string"`, `"integer"`, …)
1300
1707
  * or a full JSON Schema fragment:
@@ -1325,12 +1732,12 @@ var JsonColumnSchema = import_zod22.z.object({
1325
1732
  * Ignored on the id column and on columns that are neither createable nor
1326
1733
  * updateable, since the form cannot supply a value for those.
1327
1734
  */
1328
- required: import_zod22.z.boolean().optional(),
1735
+ required: import_zod23.z.boolean().optional(),
1329
1736
  /**
1330
1737
  * @deprecated Use `type` instead. Retained because it is still consumed by
1331
1738
  * the boolean predicate and the schema-less sub-resource view builder.
1332
1739
  */
1333
- columnType: import_zod22.z.string().default("string"),
1740
+ columnType: import_zod23.z.string().default("string"),
1334
1741
  fieldInput: FieldInputSchema.optional(),
1335
1742
  /**
1336
1743
  * Optional per-context override for the read-only VIEW ui. Same shape as
@@ -1354,27 +1761,27 @@ var JsonColumnSchema = import_zod22.z.object({
1354
1761
  * Visibility (`hiddenInTable/Form/View`) on this column is inherited by all sub-columns as a
1355
1762
  * default; the referenced resource's own column visibility further restricts it.
1356
1763
  */
1357
- extend: import_zod22.z.string().optional(),
1764
+ extend: import_zod23.z.string().optional(),
1358
1765
  /**
1359
1766
  * Per-sub-column overrides when using `extend`.
1360
1767
  * Key: the virtual column id (`"{extendId}_{refColId}"`) or just the referenced column id.
1361
1768
  * Value: any `JsonColumn` fields to merge over the generated virtual column.
1362
1769
  */
1363
- columns: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.record(import_zod22.z.string(), import_zod22.z.unknown())).optional()
1770
+ columns: import_zod23.z.record(import_zod23.z.string(), import_zod23.z.record(import_zod23.z.string(), import_zod23.z.unknown())).optional()
1364
1771
  });
1365
1772
 
1366
1773
  // ../crouton-core/src/lib/resource/ParentRef.schema.ts
1367
- var import_zod23 = require("zod");
1368
- var ParentRefSchema = import_zod23.z.object({
1774
+ var import_zod24 = require("zod");
1775
+ var ParentRefSchema = import_zod24.z.object({
1369
1776
  /** Route segment of the parent, e.g. `"group"`. */
1370
- route: import_zod23.z.string(),
1777
+ route: import_zod24.z.string(),
1371
1778
  /**
1372
1779
  * Name of the path parameter carrying the parent id, e.g. `"groupId"`.
1373
1780
  * Avoid `"id"` — that is the child's own id in `/:id` routes.
1374
1781
  */
1375
- param: import_zod23.z.string().default("parentId"),
1782
+ param: import_zod24.z.string().default("parentId"),
1376
1783
  /** Type of the parent id, used to coerce the path param. Defaults to `'string'`. */
1377
- idType: import_zod23.z.enum([
1784
+ idType: import_zod24.z.enum([
1378
1785
  "string",
1379
1786
  "number"
1380
1787
  ]).optional()
@@ -1382,37 +1789,37 @@ var ParentRefSchema = import_zod23.z.object({
1382
1789
  var resourceControllerPath = /* @__PURE__ */ __name((route, parent) => parent ? `${parent.route}/:${parent.param}/${route}` : route, "resourceControllerPath");
1383
1790
 
1384
1791
  // ../crouton-core/src/lib/resource/ResourceKind.ts
1385
- var import_zod24 = require("zod");
1386
- var ResourceKindSchema = import_zod24.z.enum([
1792
+ var import_zod25 = require("zod");
1793
+ var ResourceKindSchema = import_zod25.z.enum([
1387
1794
  "prisma",
1388
1795
  "custom"
1389
1796
  ]).default("prisma");
1390
1797
 
1391
1798
  // ../crouton-core/src/lib/resource/Sidebar.schema.ts
1392
- var import_zod25 = __toESM(require("zod"), 1);
1393
- var SidebarGroupSchema = import_zod25.default.object({
1799
+ var import_zod26 = __toESM(require("zod"), 1);
1800
+ var SidebarGroupSchema = import_zod26.default.object({
1394
1801
  /** Human-readable heading shown in the sidebar. Defaults to a title-cased version of the slug. */
1395
- label: import_zod25.default.string().optional(),
1802
+ label: import_zod26.default.string().optional(),
1396
1803
  /** Controls the order of this group among top-level sidebar items. */
1397
- position: import_zod25.default.number().optional()
1804
+ position: import_zod26.default.number().optional()
1398
1805
  });
1399
- var SidebarSchema = import_zod25.default.object({
1400
- hide: import_zod25.default.boolean().default(false),
1401
- position: import_zod25.default.number().optional(),
1402
- label: import_zod25.default.string().optional(),
1806
+ var SidebarSchema = import_zod26.default.object({
1807
+ hide: import_zod26.default.boolean().default(false),
1808
+ position: import_zod26.default.number().optional(),
1809
+ label: import_zod26.default.string().optional(),
1403
1810
  /**
1404
1811
  * Slug of the group this resource belongs to.
1405
1812
  * Must match a key in `sidebarGroups` in `crouton.json`.
1406
1813
  * Resources with the same `group` are nested under a shared collapsible section.
1407
1814
  */
1408
- group: import_zod25.default.string().optional()
1815
+ group: import_zod26.default.string().optional()
1409
1816
  });
1410
1817
 
1411
1818
  // ../crouton-core/src/lib/resource/TableAction.schema.ts
1412
- var import_zod26 = require("zod");
1413
- var ActionConditionSchema = import_zod26.z.object({
1819
+ var import_zod27 = require("zod");
1820
+ var ActionConditionSchema = import_zod27.z.object({
1414
1821
  /** Row field to evaluate. */
1415
- field: import_zod26.z.string(),
1822
+ field: import_zod27.z.string(),
1416
1823
  /**
1417
1824
  * Comparison operator. Defaults to `"eq"`.
1418
1825
  * - `eq` / `neq` — strict equality
@@ -1420,7 +1827,7 @@ var ActionConditionSchema = import_zod26.z.object({
1420
1827
  * - `exists` — field is not null / undefined / empty string
1421
1828
  * - `notExists` — field is null / undefined / empty string
1422
1829
  */
1423
- op: import_zod26.z.enum([
1830
+ op: import_zod27.z.enum([
1424
1831
  "eq",
1425
1832
  "neq",
1426
1833
  "gt",
@@ -1431,38 +1838,38 @@ var ActionConditionSchema = import_zod26.z.object({
1431
1838
  "notExists"
1432
1839
  ]).default("eq"),
1433
1840
  /** Comparison value. Not required for `exists` / `notExists`. */
1434
- value: import_zod26.z.unknown().optional()
1841
+ value: import_zod27.z.unknown().optional()
1435
1842
  });
1436
- var ActionSchema = import_zod26.z.object({
1437
- type: import_zod26.z.string(),
1843
+ var ActionSchema = import_zod27.z.object({
1844
+ type: import_zod27.z.string(),
1438
1845
  /** Unique identifier for the action. */
1439
- id: import_zod26.z.string(),
1846
+ id: import_zod27.z.string(),
1440
1847
  /** Human-readable button label. */
1441
- label: import_zod26.z.string(),
1848
+ label: import_zod27.z.string(),
1442
1849
  /** MDI icon name, e.g. `"mdi:open-in-new"`. */
1443
- icon: import_zod26.z.string().optional(),
1850
+ icon: import_zod27.z.string().optional(),
1444
1851
  /** Tooltip text. Falls back to `label` when omitted. */
1445
- tooltip: import_zod26.z.string().optional(),
1852
+ tooltip: import_zod27.z.string().optional(),
1446
1853
  condition: ActionConditionSchema.optional()
1447
1854
  });
1448
1855
  var JsonProcedureActionSchema = ActionSchema.extend({
1449
- type: import_zod26.z.literal("procedure").default("procedure"),
1856
+ type: import_zod27.z.literal("procedure").default("procedure"),
1450
1857
  /**
1451
1858
  * Filename (without extension) inside the resource's `actions/` directory
1452
1859
  * that exports the table-action procedure, e.g. `"syncZotero"`.
1453
1860
  */
1454
- procedure: import_zod26.z.string(),
1861
+ procedure: import_zod27.z.string(),
1455
1862
  /** HTTP method for the frontend. Defaults to `"post"`. */
1456
- method: import_zod26.z.string().optional().default("post"),
1863
+ method: import_zod27.z.string().optional().default("post"),
1457
1864
  /** Static query / body params passed to the endpoint. */
1458
- data: import_zod26.z.record(import_zod26.z.string(), import_zod26.z.unknown()).optional()
1865
+ data: import_zod27.z.record(import_zod27.z.string(), import_zod27.z.unknown()).optional()
1459
1866
  });
1460
1867
  var JsonLinkActionSchema = ActionSchema.extend({
1461
1868
  /** Optional condition evaluated per row. Button is hidden when the condition is false. */
1462
- type: import_zod26.z.literal("link").default("link"),
1869
+ type: import_zod27.z.literal("link").default("link"),
1463
1870
  /** URL to open. May contain `{env.VAR}` placeholders. */
1464
- href: import_zod26.z.string(),
1465
- blank: import_zod26.z.boolean().optional().default(true)
1871
+ href: import_zod27.z.string(),
1872
+ blank: import_zod27.z.boolean().optional().default(true)
1466
1873
  });
1467
1874
  var transformAction = /* @__PURE__ */ __name((action) => {
1468
1875
  const label = action.label;
@@ -1471,7 +1878,7 @@ var transformAction = /* @__PURE__ */ __name((action) => {
1471
1878
  ...action
1472
1879
  };
1473
1880
  }, "transformAction");
1474
- var JsonActionSchema = import_zod26.z.union([
1881
+ var JsonActionSchema = import_zod27.z.union([
1475
1882
  JsonProcedureActionSchema,
1476
1883
  JsonLinkActionSchema
1477
1884
  ]).transform(transformAction);
@@ -1513,14 +1920,14 @@ var registerResourceExtensions = /* @__PURE__ */ __name((map = {}) => Object.ent
1513
1920
  var getResourceExtensions = /* @__PURE__ */ __name(() => registry, "getResourceExtensions");
1514
1921
 
1515
1922
  // ../crouton-core/src/lib/resource/include.schema.ts
1516
- var import_zod27 = require("zod");
1517
- var JsonIncludeEntrySchema = import_zod27.z.lazy(() => import_zod27.z.union([
1518
- import_zod27.z.string(),
1519
- import_zod27.z.object({
1520
- relation: import_zod27.z.string(),
1521
- include: import_zod27.z.array(JsonIncludeEntrySchema).optional(),
1923
+ var import_zod28 = require("zod");
1924
+ var JsonIncludeEntrySchema = import_zod28.z.lazy(() => import_zod28.z.union([
1925
+ import_zod28.z.string(),
1926
+ import_zod28.z.object({
1927
+ relation: import_zod28.z.string(),
1928
+ include: import_zod28.z.array(JsonIncludeEntrySchema).optional(),
1522
1929
  /** Prisma-format orderBy clause applied to the included records. */
1523
- orderBy: import_zod27.z.record(import_zod27.z.string(), import_zod27.z.unknown()).optional()
1930
+ orderBy: import_zod28.z.record(import_zod28.z.string(), import_zod28.z.unknown()).optional()
1524
1931
  })
1525
1932
  ]));
1526
1933
 
@@ -1529,36 +1936,36 @@ var BASELINE_RESOURCE_VERSION = 1;
1529
1936
  var CURRENT_RESOURCE_VERSION = 1;
1530
1937
 
1531
1938
  // ../crouton-core/src/lib/data-source/Operations.schema.ts
1532
- var import_zod29 = require("zod");
1939
+ var import_zod30 = require("zod");
1533
1940
 
1534
1941
  // ../crouton-core/src/lib/data-source/Security.schema.ts
1535
- var import_zod28 = require("zod");
1536
- var SecuritySchema = import_zod28.z.union([
1537
- import_zod28.z.object({
1538
- public: import_zod28.z.literal(true)
1942
+ var import_zod29 = require("zod");
1943
+ var SecuritySchema = import_zod29.z.union([
1944
+ import_zod29.z.object({
1945
+ public: import_zod29.z.literal(true)
1539
1946
  }),
1540
- import_zod28.z.object({
1541
- guard: import_zod28.z.union([
1542
- import_zod28.z.string(),
1543
- import_zod28.z.array(import_zod28.z.string()).min(1)
1947
+ import_zod29.z.object({
1948
+ guard: import_zod29.z.union([
1949
+ import_zod29.z.string(),
1950
+ import_zod29.z.array(import_zod29.z.string()).min(1)
1544
1951
  ])
1545
1952
  })
1546
1953
  ]);
1547
1954
 
1548
1955
  // ../crouton-core/src/lib/data-source/Operations.schema.ts
1549
- var ExternalOpEntry = import_zod29.z.object({
1550
- uri: import_zod29.z.string(),
1551
- method: import_zod29.z.string().optional(),
1956
+ var ExternalOpEntry = import_zod30.z.object({
1957
+ uri: import_zod30.z.string(),
1958
+ method: import_zod30.z.string().optional(),
1552
1959
  security: SecuritySchema.optional()
1553
1960
  });
1554
- var OpEntry = import_zod29.z.union([
1555
- import_zod29.z.boolean(),
1961
+ var OpEntry = import_zod30.z.union([
1962
+ import_zod30.z.boolean(),
1556
1963
  ExternalOpEntry,
1557
- import_zod29.z.object({
1964
+ import_zod30.z.object({
1558
1965
  security: SecuritySchema.optional()
1559
1966
  })
1560
1967
  ]);
1561
- var JsonOperationsSchema = import_zod29.z.object({
1968
+ var JsonOperationsSchema = import_zod30.z.object({
1562
1969
  findAll: OpEntry.default(true),
1563
1970
  findOne: OpEntry.default(true),
1564
1971
  create: OpEntry.default(true),
@@ -1610,16 +2017,16 @@ var buildSubResourceOperations = /* @__PURE__ */ __name((ops, baseUri, idField =
1610
2017
  }, "buildSubResourceOperations");
1611
2018
 
1612
2019
  // ../crouton-core/src/lib/resource/ResourceJson.schema.ts
1613
- var JsonColumnsMapSchema = import_zod30.z.record(import_zod30.z.string(), JsonColumnSchema.omit({
2020
+ var JsonColumnsMapSchema = import_zod31.z.record(import_zod31.z.string(), JsonColumnSchema.omit({
1614
2021
  id: true
1615
- }).catchall(import_zod30.z.unknown()));
2022
+ }).catchall(import_zod31.z.unknown()));
1616
2023
  var ColumnsSchema = JsonColumnsMapSchema;
1617
- var JsonDisplaySchema = import_zod30.z.object({
1618
- mode: import_zod30.z.enum([
2024
+ var JsonDisplaySchema = import_zod31.z.object({
2025
+ mode: import_zod31.z.enum([
1619
2026
  "page",
1620
2027
  "modal"
1621
2028
  ]).default("modal"),
1622
- customComponent: import_zod30.z.string().nullable().optional().default(null)
2029
+ customComponent: import_zod31.z.string().nullable().optional().default(null)
1623
2030
  });
1624
2031
  var normalizeColumns = /* @__PURE__ */ __name((columns) => {
1625
2032
  if (!columns) return void 0;
@@ -1632,47 +2039,47 @@ var normalizeColumns = /* @__PURE__ */ __name((columns) => {
1632
2039
  label: col.label ?? labelFromId(col.id)
1633
2040
  }));
1634
2041
  }, "normalizeColumns");
1635
- var ResourceJsonShape = import_zod30.z.object({
2042
+ var ResourceJsonShape = import_zod31.z.object({
1636
2043
  /**
1637
2044
  * URL of the generated JSON Schema, for editor autocomplete/validation. Declared so the
1638
2045
  * key is *allowed* (not stripped, and not flagged by the very schema it points at).
1639
2046
  * Ignored at runtime.
1640
2047
  */
1641
- $schema: import_zod30.z.string().optional(),
2048
+ $schema: import_zod31.z.string().optional(),
1642
2049
  /**
1643
2050
  * resource.json shape version. Missing ⇒ baseline (see `./version`). Auto-migrated
1644
2051
  * toward `CURRENT_RESOURCE_VERSION` on load in the dev environment.
1645
2052
  */
1646
- schemaVersion: import_zod30.z.number().int().positive().optional(),
2053
+ schemaVersion: import_zod31.z.number().int().positive().optional(),
1647
2054
  /** When `true`, the resource lives in the repo but is NOT loaded/served (work in progress). */
1648
- draft: import_zod30.z.boolean().optional().default(false),
2055
+ draft: import_zod31.z.boolean().optional().default(false),
1649
2056
  /**
1650
2057
  * Where the data comes from. `prisma` (the default) is backed by a Prisma
1651
2058
  * model plus a `schema.ts`; `custom` is configuration only and the developer
1652
2059
  * supplies a `repository.ts`. See `./ResourceKind`.
1653
2060
  */
1654
2061
  kind: ResourceKindSchema,
1655
- name: import_zod30.z.string(),
1656
- route: import_zod30.z.string().optional(),
1657
- id: import_zod30.z.string().optional(),
2062
+ name: import_zod31.z.string(),
2063
+ route: import_zod31.z.string().optional(),
2064
+ id: import_zod31.z.string().optional(),
1658
2065
  /**
1659
2066
  * Prisma model name. Required when `kind` is `prisma` (enforced by the
1660
2067
  * refinement on `ResourceJsonSchema`), and must be absent when `kind` is
1661
2068
  * `custom` — there is no Prisma delegate to address.
1662
2069
  */
1663
- model: import_zod30.z.string().optional(),
1664
- tag: import_zod30.z.string().optional().default("Crouton"),
1665
- title: import_zod30.z.string().optional(),
1666
- table: import_zod30.z.string().optional(),
2070
+ model: import_zod31.z.string().optional(),
2071
+ tag: import_zod31.z.string().optional().default("Crouton"),
2072
+ title: import_zod31.z.string().optional(),
2073
+ table: import_zod31.z.string().optional(),
1667
2074
  /**
1668
2075
  * Type of the resource's primary key, used to coerce `:id` route params.
1669
2076
  * Written by codegen from the Prisma model; defaults to `'string'`.
1670
2077
  */
1671
- idType: import_zod30.z.enum([
2078
+ idType: import_zod31.z.enum([
1672
2079
  "string",
1673
2080
  "number"
1674
2081
  ]).optional(),
1675
- database: import_zod30.z.string().optional(),
2082
+ database: import_zod31.z.string().optional(),
1676
2083
  /**
1677
2084
  * Mount this resource under a parent route instead of at the top level —
1678
2085
  * see `./ParentRef.schema`. Only valid on a `kind: "custom"` resource.
@@ -1684,12 +2091,12 @@ var ResourceJsonShape = import_zod30.z.object({
1684
2091
  security: SecuritySchema.optional(),
1685
2092
  operations: JsonOperationsSchema.optional(),
1686
2093
  columns: ColumnsSchema.optional().default(ColumnsSchema.parse({})),
1687
- calculatedColumns: import_zod30.z.array(CalculatedColumnSchema).default([]),
1688
- actions: import_zod30.z.array(JsonActionSchema).default([]),
2094
+ calculatedColumns: import_zod31.z.array(CalculatedColumnSchema).default([]),
2095
+ actions: import_zod31.z.array(JsonActionSchema).default([]),
1689
2096
  /** Global table-level actions (no record id). Shown as toolbar buttons. */
1690
- tableActions: import_zod30.z.array(JsonActionSchema).default([]),
2097
+ tableActions: import_zod31.z.array(JsonActionSchema).default([]),
1691
2098
  /** Modal width when opening a form for this resource. */
1692
- modalSize: import_zod30.z.enum([
2099
+ modalSize: import_zod31.z.enum([
1693
2100
  "xs",
1694
2101
  "sm",
1695
2102
  "lg",
@@ -1702,7 +2109,7 @@ var ResourceJsonShape = import_zod30.z.object({
1702
2109
  * `{ "relation": "text_author", "include": ["author"] }` →
1703
2110
  * `include: { text_author: { include: { author: true } } }`
1704
2111
  */
1705
- include: import_zod30.z.array(JsonIncludeEntrySchema).default([]),
2112
+ include: import_zod31.z.array(JsonIncludeEntrySchema).default([]),
1706
2113
  /** Explicit layout for generated views (`form`, `view`, `table`). When absent, source-order grid is used. */
1707
2114
  layout: LayoutSchema.optional()
1708
2115
  });
@@ -1771,7 +2178,7 @@ var buildResourceJsonSchema = /* @__PURE__ */ __name(() => {
1771
2178
  schema.optional()
1772
2179
  ]));
1773
2180
  const shape = ext.size ? ResourceJsonShape.extend(extShape) : ResourceJsonShape;
1774
- return import_zod30.z.preprocess((raw) => {
2181
+ return import_zod31.z.preprocess((raw) => {
1775
2182
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
1776
2183
  const obj = raw;
1777
2184
  if (obj["kind"] === void 0 && obj["model"] !== void 0) {
@@ -2165,6 +2572,9 @@ var buildFormControl = /* @__PURE__ */ __name((col) => {
2165
2572
  const type = fieldInput?.type ?? defaultControlFormat(col);
2166
2573
  control.control(type, options).width("full");
2167
2574
  }
2575
+ if (col.unique) control.opt({
2576
+ unique: normalizeUnique(col.unique)
2577
+ });
2168
2578
  if (fieldInput?.customRender) control.setCustomRender(fieldInput?.customRender);
2169
2579
  if (col.hideLabel) control.hideLabel();
2170
2580
  if (col.label) control.label(col.label);
@@ -2258,7 +2668,7 @@ var injectCalculatedColumns = /* @__PURE__ */ __name((tableView, calculated) =>
2258
2668
  var injectCalculatedColumnsToView = /* @__PURE__ */ __name((viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view"), "injectCalculatedColumnsToView");
2259
2669
 
2260
2670
  // ../crouton-core/src/lib/view/view.builder.ts
2261
- var import_zod32 = require("zod");
2671
+ var import_zod33 = require("zod");
2262
2672
 
2263
2673
  // ../crouton-core/src/lib/view/column-type-schema.source.ts
2264
2674
  var optionValues = /* @__PURE__ */ __name((col) => {
@@ -2294,20 +2704,20 @@ var columnTypeSchemaSource = /* @__PURE__ */ __name((schemaCols) => {
2294
2704
  }, "columnTypeSchemaSource");
2295
2705
 
2296
2706
  // ../crouton-core/src/lib/view/json-schema.opts.ts
2297
- var import_zod31 = require("zod");
2707
+ var import_zod32 = require("zod");
2298
2708
  var isInstanceOf = /* @__PURE__ */ __name((zodSchema, className) => {
2299
- if (!(zodSchema instanceof import_zod31.z.ZodCustom)) return false;
2709
+ if (!(zodSchema instanceof import_zod32.z.ZodCustom)) return false;
2300
2710
  const result = zodSchema.safeParse(null);
2301
2711
  if (result.success) return false;
2302
2712
  const issue = result.error.issues[0];
2303
2713
  return issue?.expected === className || (issue?.message?.includes(className) ?? false);
2304
2714
  }, "isInstanceOf");
2305
2715
  var jsonSchemaOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
2306
- if (zodSchema instanceof import_zod31.z.ZodDate) {
2716
+ if (zodSchema instanceof import_zod32.z.ZodDate) {
2307
2717
  jsonSchema.type = "string";
2308
2718
  jsonSchema.format = "date-time";
2309
2719
  }
2310
- if (zodSchema instanceof import_zod31.z.ZodBigInt) {
2720
+ if (zodSchema instanceof import_zod32.z.ZodBigInt) {
2311
2721
  jsonSchema.type = "integer";
2312
2722
  }
2313
2723
  if (isInstanceOf(zodSchema, "Decimal") || isInstanceOf(zodSchema, "Decimal2")) {
@@ -2372,8 +2782,10 @@ var applyControlOverrides = /* @__PURE__ */ __name((base, ctrl) => {
2372
2782
  if (ctrl.width !== void 0) base.width(ctrl.width);
2373
2783
  if (ctrl.label !== void 0) base.label(ctrl.label);
2374
2784
  if (ctrl.hideLabel) base.hideLabel();
2375
- if (ctrl.type !== void 0 || ctrl.options !== void 0) {
2376
- base.control(ctrl.type ?? "text", ctrl.options);
2785
+ if (ctrl.type !== void 0) {
2786
+ base.control(ctrl.type, ctrl.options);
2787
+ } else if (ctrl.options !== void 0) {
2788
+ base.opt(ctrl.options);
2377
2789
  }
2378
2790
  return base;
2379
2791
  }, "applyControlOverrides");
@@ -2387,10 +2799,6 @@ var buildNodeInto = /* @__PURE__ */ __name((node, colMap, warn, seenIds) => {
2387
2799
  warn(`layout: unknown or hidden column id "${id}" \u2014 skipping`);
2388
2800
  continue;
2389
2801
  }
2390
- if (typeof ctrl !== "string") {
2391
- if (ctrl.colspan !== void 0 && ctrl.rowspan !== void 0) {
2392
- }
2393
- }
2394
2802
  seenIds.add(id);
2395
2803
  const control = buildFormControl(col);
2396
2804
  container.addControl(applyControlOverrides(control, ctrl));
@@ -2420,7 +2828,7 @@ var buildFormUiSchemaFromLayout = /* @__PURE__ */ __name((node, cols, warn = con
2420
2828
  }
2421
2829
  return root.build();
2422
2830
  }, "buildFormUiSchemaFromLayout");
2423
- var flattenLayoutControlIds = /* @__PURE__ */ __name((node) => {
2831
+ var flattenLayoutControlIds = /* @__PURE__ */ __name((node, warn = console.warn) => {
2424
2832
  const ids = [];
2425
2833
  const visit = /* @__PURE__ */ __name((n) => {
2426
2834
  for (const ctrl of n.controls ?? []) {
@@ -2428,6 +2836,7 @@ var flattenLayoutControlIds = /* @__PURE__ */ __name((node) => {
2428
2836
  if (typeof ctrl !== "string") {
2429
2837
  const c = ctrl;
2430
2838
  if (c.colspan !== void 0 || c.rowspan !== void 0) {
2839
+ warn(`layout: colspan/rowspan on table control "${id}" \u2014 ignored, table is flat`);
2431
2840
  }
2432
2841
  }
2433
2842
  ids.push(id);
@@ -2440,7 +2849,7 @@ var flattenLayoutControlIds = /* @__PURE__ */ __name((node) => {
2440
2849
 
2441
2850
  // ../crouton-core/src/lib/view/layout-table-order.ts
2442
2851
  var orderTableColumnsFromLayout = /* @__PURE__ */ __name((view, node, warn = console.warn) => {
2443
- const orderedIds = flattenLayoutControlIds(node);
2852
+ const orderedIds = flattenLayoutControlIds(node, warn);
2444
2853
  if (!orderedIds.length) return view;
2445
2854
  const elements = view.ui_schema?.elements ?? [];
2446
2855
  const columns = view.columns ?? [];
@@ -2451,7 +2860,7 @@ var orderTableColumnsFromLayout = /* @__PURE__ */ __name((view, node, warn = con
2451
2860
  el
2452
2861
  ];
2453
2862
  }));
2454
- const columnById = new Map(columns.map((c) => [
2863
+ const columnById2 = new Map(columns.map((c) => [
2455
2864
  c.id,
2456
2865
  c
2457
2866
  ]));
@@ -2469,14 +2878,14 @@ var orderTableColumnsFromLayout = /* @__PURE__ */ __name((view, node, warn = con
2469
2878
  seen.add(id);
2470
2879
  const el = elementById.get(id);
2471
2880
  if (el) reorderedElements.push(el);
2472
- const col = columnById.get(id);
2881
+ const col = columnById2.get(id);
2473
2882
  if (col) reorderedColumns.push(col);
2474
2883
  }
2475
2884
  for (const el of elements) {
2476
2885
  const id = el.scope?.replace("#/properties/", "");
2477
2886
  if (id && !seen.has(id)) {
2478
2887
  reorderedElements.push(el);
2479
- const col = columnById.get(id);
2888
+ const col = columnById2.get(id);
2480
2889
  if (col) reorderedColumns.push(col);
2481
2890
  }
2482
2891
  }
@@ -2550,7 +2959,7 @@ var zodSchemaSource = /* @__PURE__ */ __name((schema) => (schemaCols) => {
2550
2959
  ]));
2551
2960
  const picked = schema.pick(mask);
2552
2961
  try {
2553
- return (0, import_zod32.toJSONSchema)(picked, {
2962
+ return (0, import_zod33.toJSONSchema)(picked, {
2554
2963
  target: "draft-07",
2555
2964
  ...jsonSchemaOpts
2556
2965
  });
@@ -2764,81 +3173,81 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
2764
3173
  }, "buildViewsFromColumns");
2765
3174
 
2766
3175
  // ../crouton-core/src/lib/view/view.schema.ts
2767
- var import_zod33 = require("zod");
2768
- var ViewColumnConfigSchema = import_zod33.z.object({
2769
- id: import_zod33.z.string(),
2770
- label: import_zod33.z.string().optional(),
2771
- sortable: import_zod33.z.boolean().optional(),
2772
- searchable: import_zod33.z.boolean().optional(),
3176
+ var import_zod34 = require("zod");
3177
+ var ViewColumnConfigSchema = import_zod34.z.object({
3178
+ id: import_zod34.z.string(),
3179
+ label: import_zod34.z.string().optional(),
3180
+ sortable: import_zod34.z.boolean().optional(),
3181
+ searchable: import_zod34.z.boolean().optional(),
2773
3182
  fieldInput: FieldInputSchema.optional()
2774
3183
  });
2775
- var ViewConfigSchema = import_zod33.z.object({
3184
+ var ViewConfigSchema = import_zod34.z.object({
2776
3185
  // TODO why we have both?
2777
- json_schema: import_zod33.z.record(import_zod33.z.string(), import_zod33.z.unknown()).optional(),
2778
- ui_schema: import_zod33.z.record(import_zod33.z.string(), import_zod33.z.unknown()).optional(),
3186
+ json_schema: import_zod34.z.record(import_zod34.z.string(), import_zod34.z.unknown()).optional(),
3187
+ ui_schema: import_zod34.z.record(import_zod34.z.string(), import_zod34.z.unknown()).optional(),
2779
3188
  // TODO why we have both?
2780
- ui: import_zod33.z.record(import_zod33.z.string(), import_zod33.z.unknown()).optional(),
2781
- data: import_zod33.z.record(import_zod33.z.string(), import_zod33.z.unknown()).optional(),
2782
- columns: import_zod33.z.array(ViewColumnConfigSchema),
2783
- defaultSort: import_zod33.z.string().optional()
3189
+ ui: import_zod34.z.record(import_zod34.z.string(), import_zod34.z.unknown()).optional(),
3190
+ data: import_zod34.z.record(import_zod34.z.string(), import_zod34.z.unknown()).optional(),
3191
+ columns: import_zod34.z.array(ViewColumnConfigSchema),
3192
+ defaultSort: import_zod34.z.string().optional()
2784
3193
  });
2785
3194
  var viewDefList = [
2786
3195
  "table",
2787
3196
  "view",
2788
3197
  "form"
2789
3198
  ];
2790
- var ViewDefEnum = import_zod33.z.enum(viewDefList);
3199
+ var ViewDefEnum = import_zod34.z.enum(viewDefList);
2791
3200
 
2792
3201
  // ../crouton-core/src/lib/compile/enum-registry.ts
2793
- var import_zod34 = require("zod");
2794
- var EnumEntrySchema = import_zod34.z.object({
2795
- value: import_zod34.z.unknown(),
2796
- label: import_zod34.z.string()
3202
+ var import_zod35 = require("zod");
3203
+ var EnumEntrySchema = import_zod35.z.object({
3204
+ value: import_zod35.z.unknown(),
3205
+ label: import_zod35.z.string()
2797
3206
  });
2798
- var EnumRegistrySchema = import_zod34.z.record(import_zod34.z.string(), import_zod34.z.array(EnumEntrySchema)).default({});
3207
+ var EnumRegistrySchema = import_zod35.z.record(import_zod35.z.string(), import_zod35.z.array(EnumEntrySchema)).default({});
2799
3208
 
2800
3209
  // ../crouton-core/src/lib/compile/relation-type.ts
2801
- var import_zod35 = require("zod");
3210
+ var import_zod36 = require("zod");
2802
3211
 
2803
3212
  // ../crouton-core/src/lib/compile/compiled-resource.schema.ts
2804
- var import_zod42 = require("zod");
3213
+ var import_zod43 = require("zod");
2805
3214
 
2806
3215
  // ../crouton-core/src/lib/compile/compiled-sub-resource.schema.ts
2807
- var import_zod38 = require("zod");
3216
+ var import_zod39 = require("zod");
2808
3217
 
2809
3218
  // ../crouton-core/src/lib/data-source/DataSourceSchema.ts
2810
- var import_zod36 = __toESM(require("zod"), 1);
2811
- var DataSourceShape = import_zod36.default.object({
3219
+ var import_zod37 = __toESM(require("zod"), 1);
3220
+ var DataSourceShape = import_zod37.default.object({
2812
3221
  /** Datasource name (folder + `name` in data-source.json). */
2813
- name: import_zod36.default.string(),
3222
+ name: import_zod37.default.string(),
2814
3223
  /**
2815
3224
  * Env var holding the connection URL. Required for Prisma adapters; optional
2816
3225
  * for custom adapters whose connection config may differ (secrets file, SDK
2817
3226
  * key, etc.).
2818
3227
  */
2819
- urlEnv: import_zod36.default.string().optional(),
3228
+ urlEnv: import_zod37.default.string().optional(),
2820
3229
  /** Import path for this datasource's generated Zod types (for resource `schema.ts`). */
2821
- generatedTypesImport: import_zod36.default.string().optional(),
3230
+ generatedTypesImport: import_zod37.default.string().optional(),
2822
3231
  /** Datasource type tag. Default `postgres`. */
2823
- type: import_zod36.default.string().default("postgres"),
3232
+ type: import_zod37.default.string().default("postgres"),
2824
3233
  /** Mark this as the default datasource. Default `false`. */
2825
- default: import_zod36.default.boolean().default(false),
3234
+ default: import_zod37.default.boolean().default(false),
2826
3235
  /**
2827
3236
  * Adapter kind. `"prisma"` (default) expects `index.ts` to default-export a
2828
3237
  * `PrismaClient`; `"custom"` expects it to default-export a `DataSourceAdapter`.
2829
3238
  */
2830
- adapter: import_zod36.default.enum([
3239
+ adapter: import_zod37.default.enum([
2831
3240
  "prisma",
2832
3241
  "custom"
2833
3242
  ]).default("prisma"),
2834
3243
  /** Prisma schema path, relative to project root. Default `prisma/<name>/schema.prisma`. */
2835
- prismaSchema: import_zod36.default.string().optional(),
3244
+ prismaSchema: import_zod37.default.string().optional(),
2836
3245
  /** Prisma config path, relative to project root. Default `prisma/<name>/prisma.config.ts`. */
2837
- prismaConfig: import_zod36.default.string().optional(),
3246
+ prismaConfig: import_zod37.default.string().optional(),
2838
3247
  /** zod-prisma-types output dir, relative to project root. Default `generated/<name>/src`. */
2839
- zodOutput: import_zod36.default.string().optional(),
3248
+ zodOutput: import_zod37.default.string().optional(),
2840
3249
  /** Prisma client output dir, relative to project root. Default `generated/<name>/client`. */
2841
- clientOutput: import_zod36.default.string().optional()
3250
+ clientOutput: import_zod37.default.string().optional()
2842
3251
  });
2843
3252
  var transformDataSource = /* @__PURE__ */ __name((data) => {
2844
3253
  const name = data.name ?? "name";
@@ -2854,88 +3263,88 @@ var transformDataSource = /* @__PURE__ */ __name((data) => {
2854
3263
  var DataSourceSchema = DataSourceShape.transform(transformDataSource);
2855
3264
 
2856
3265
  // ../crouton-core/src/lib/compile/value-label-column.ts
2857
- var import_zod37 = require("zod");
2858
- var ValueSchema = import_zod37.z.object({
2859
- value: import_zod37.z.unknown(),
2860
- label: import_zod37.z.string()
3266
+ var import_zod38 = require("zod");
3267
+ var ValueSchema = import_zod38.z.object({
3268
+ value: import_zod38.z.unknown(),
3269
+ label: import_zod38.z.string()
2861
3270
  });
2862
- var ValueLabelColumnSchema = import_zod37.z.object({
2863
- field: import_zod37.z.string(),
2864
- values: import_zod37.z.array(ValueSchema).default([]),
2865
- enumName: import_zod37.z.string().optional()
3271
+ var ValueLabelColumnSchema = import_zod38.z.object({
3272
+ field: import_zod38.z.string(),
3273
+ values: import_zod38.z.array(ValueSchema).default([]),
3274
+ enumName: import_zod38.z.string().optional()
2866
3275
  });
2867
3276
 
2868
3277
  // ../crouton-core/src/lib/compile/compiled-sub-resource.schema.ts
2869
- var CompiledSubResourceConfigSchema = import_zod38.z.object({
2870
- column: import_zod38.z.string(),
2871
- relation: import_zod38.z.string(),
2872
- childRoute: import_zod38.z.string(),
2873
- childKind: import_zod38.z.enum([
3278
+ var CompiledSubResourceConfigSchema = import_zod39.z.object({
3279
+ column: import_zod39.z.string(),
3280
+ relation: import_zod39.z.string(),
3281
+ childRoute: import_zod39.z.string(),
3282
+ childKind: import_zod39.z.enum([
2874
3283
  "prisma",
2875
3284
  "custom"
2876
3285
  ]).default("prisma"),
2877
- childDir: import_zod38.z.string().optional(),
2878
- childModel: import_zod38.z.string(),
2879
- foreignKey: import_zod38.z.string(),
2880
- name: import_zod38.z.string().optional(),
2881
- title: import_zod38.z.string().optional(),
2882
- idField: import_zod38.z.string().optional(),
2883
- idType: import_zod38.z.enum([
3286
+ childDir: import_zod39.z.string().optional(),
3287
+ childModel: import_zod39.z.string(),
3288
+ foreignKey: import_zod39.z.string(),
3289
+ name: import_zod39.z.string().optional(),
3290
+ title: import_zod39.z.string().optional(),
3291
+ idField: import_zod39.z.string().optional(),
3292
+ idType: import_zod39.z.enum([
2884
3293
  "string",
2885
3294
  "number"
2886
3295
  ]).optional(),
2887
- views: import_zod38.z.record(import_zod38.z.string(), ViewConfigSchema).optional(),
3296
+ views: import_zod39.z.record(import_zod39.z.string(), ViewConfigSchema).optional(),
2888
3297
  operations: JsonOperationsSchema,
2889
- actions: import_zod38.z.array(JsonActionSchema).optional(),
2890
- modalSize: import_zod38.z.enum([
3298
+ actions: import_zod39.z.array(JsonActionSchema).optional(),
3299
+ modalSize: import_zod39.z.enum([
2891
3300
  "xs",
2892
3301
  "sm",
2893
3302
  "lg",
2894
3303
  "xl"
2895
3304
  ]).optional(),
2896
3305
  display: JsonDisplaySchema.optional(),
2897
- include: import_zod38.z.array(JsonIncludeEntrySchema).optional(),
2898
- calculatedColumns: import_zod38.z.array(CalculatedColumnSchema).optional(),
2899
- includeInFindOne: import_zod38.z.boolean().optional(),
2900
- hiddenInTable: import_zod38.z.boolean().optional(),
2901
- findOneOrderBy: import_zod38.z.record(import_zod38.z.string(), import_zod38.z.unknown()).optional(),
2902
- valueLabelColumns: import_zod38.z.array(ValueLabelColumnSchema).optional(),
2903
- relationType: import_zod38.z.enum([
3306
+ include: import_zod39.z.array(JsonIncludeEntrySchema).optional(),
3307
+ calculatedColumns: import_zod39.z.array(CalculatedColumnSchema).optional(),
3308
+ includeInFindOne: import_zod39.z.boolean().optional(),
3309
+ hiddenInTable: import_zod39.z.boolean().optional(),
3310
+ findOneOrderBy: import_zod39.z.record(import_zod39.z.string(), import_zod39.z.unknown()).optional(),
3311
+ valueLabelColumns: import_zod39.z.array(ValueLabelColumnSchema).optional(),
3312
+ relationType: import_zod39.z.enum([
2904
3313
  "oneToMany",
2905
3314
  "manyToOne"
2906
3315
  ]).optional()
2907
3316
  });
2908
3317
 
2909
3318
  // ../crouton-core/src/lib/compile/definition.schema.ts
2910
- var import_zod40 = require("zod");
3319
+ var import_zod41 = require("zod");
2911
3320
 
2912
3321
  // ../crouton-core/src/lib/compile/schema-input.ts
2913
- var import_zod39 = require("zod");
2914
- var JsonSchemaInputSchema = import_zod39.z.object({
2915
- type: import_zod39.z.literal("object"),
2916
- properties: import_zod39.z.record(import_zod39.z.string(), import_zod39.z.unknown()),
2917
- required: import_zod39.z.array(import_zod39.z.string()).optional(),
2918
- additionalProperties: import_zod39.z.boolean().optional()
3322
+ var import_zod40 = require("zod");
3323
+ var JsonSchemaInputSchema = import_zod40.z.object({
3324
+ type: import_zod40.z.literal("object"),
3325
+ properties: import_zod40.z.record(import_zod40.z.string(), import_zod40.z.unknown()),
3326
+ required: import_zod40.z.array(import_zod40.z.string()).optional(),
3327
+ additionalProperties: import_zod40.z.boolean().optional()
2919
3328
  });
2920
- var SchemaInputSchema = import_zod39.z.union([
2921
- import_zod39.z.custom((val) => val instanceof import_zod39.ZodObject),
3329
+ var SchemaInputSchema = import_zod40.z.union([
3330
+ import_zod40.z.custom((val) => val instanceof import_zod40.ZodObject),
2922
3331
  JsonSchemaInputSchema
2923
3332
  ]);
2924
3333
 
2925
3334
  // ../crouton-core/src/lib/compile/definition.schema.ts
2926
- var ExternalOperationDefSchema = import_zod40.z.object({
2927
- uri: import_zod40.z.string(),
2928
- method: import_zod40.z.string().optional()
3335
+ var ExternalOperationDefSchema = import_zod41.z.object({
3336
+ uri: import_zod41.z.string(),
3337
+ method: import_zod41.z.string().optional()
2929
3338
  });
2930
- var OperationDefSchema = import_zod40.z.union([
2931
- import_zod40.z.literal(true),
3339
+ var OperationDefSchema = import_zod41.z.union([
3340
+ import_zod41.z.literal(true),
2932
3341
  ExternalOperationDefSchema,
2933
- import_zod40.z.object({
3342
+ import_zod41.z.object({
2934
3343
  schema: SchemaInputSchema.optional(),
2935
3344
  security: SecuritySchema.optional()
2936
3345
  })
2937
3346
  ]);
2938
- var ResourceDefinitionSchema = import_zod40.z.object({
3347
+ var ResourceDefinitionSchema = import_zod41.z.object({
2939
3348
  findAll: OperationDefSchema.optional(),
2940
3349
  findOne: OperationDefSchema.optional(),
2941
3350
  create: OperationDefSchema.optional(),
@@ -2943,93 +3352,93 @@ var ResourceDefinitionSchema = import_zod40.z.object({
2943
3352
  patch: OperationDefSchema.optional(),
2944
3353
  delete: OperationDefSchema.optional()
2945
3354
  });
2946
- var DefinitionCallbackSchema = import_zod40.z.function({
3355
+ var DefinitionCallbackSchema = import_zod41.z.function({
2947
3356
  input: [],
2948
3357
  output: ResourceDefinitionSchema
2949
3358
  });
2950
- var DefinitionSchema = import_zod40.z.union([
3359
+ var DefinitionSchema = import_zod41.z.union([
2951
3360
  ResourceDefinitionSchema,
2952
3361
  DefinitionCallbackSchema
2953
3362
  ]);
2954
3363
 
2955
3364
  // ../crouton-core/src/lib/compile/lookup.schema.ts
2956
- var import_zod41 = require("zod");
2957
- var LookupSchema = import_zod41.z.object({
2958
- key: import_zod41.z.string(),
2959
- label: import_zod41.z.string().optional()
3365
+ var import_zod42 = require("zod");
3366
+ var LookupSchema = import_zod42.z.object({
3367
+ key: import_zod42.z.string(),
3368
+ label: import_zod42.z.string().optional()
2960
3369
  });
2961
3370
 
2962
3371
  // ../crouton-core/src/lib/compile/compiled-resource.schema.ts
2963
3372
  var CompiledResourceSchema = ResourceJsonShape.extend({
2964
- route: import_zod42.z.string(),
2965
- valueLabelColumns: import_zod42.z.array(ValueLabelColumnSchema).default([]),
2966
- subResources: import_zod42.z.lazy(() => import_zod42.z.array(CompiledSubResourceConfigSchema)).default([]),
3373
+ route: import_zod43.z.string(),
3374
+ valueLabelColumns: import_zod43.z.array(ValueLabelColumnSchema).default([]),
3375
+ subResources: import_zod43.z.lazy(() => import_zod43.z.array(CompiledSubResourceConfigSchema)).default([]),
2967
3376
  lookup: LookupSchema.optional(),
2968
- views: import_zod42.z.record(import_zod42.z.string(), ViewConfigSchema),
3377
+ views: import_zod43.z.record(import_zod43.z.string(), ViewConfigSchema),
2969
3378
  definition: DefinitionSchema,
2970
- idField: import_zod42.z.string(),
2971
- idType: import_zod42.z.enum([
3379
+ idField: import_zod43.z.string(),
3380
+ idType: import_zod43.z.enum([
2972
3381
  "string",
2973
3382
  "number"
2974
3383
  ]).default("string")
2975
3384
  });
2976
3385
 
2977
3386
  // ../crouton-core/src/lib/compile/to-json-schema.ts
2978
- var import_zod43 = require("zod");
3387
+ var import_zod44 = require("zod");
2979
3388
 
2980
3389
  // ../crouton-core/src/lib/config/CroutonConfig.schema.ts
2981
- var import_zod44 = require("zod");
2982
- var RulesetSchema = import_zod44.z.object({
2983
- hideIdInTable: import_zod44.z.boolean().default(true),
2984
- hideIdInForm: import_zod44.z.boolean().default(true),
2985
- hideIdInView: import_zod44.z.boolean().default(true),
2986
- hideTimestamps: import_zod44.z.boolean().default(true),
2987
- hideForeignKeys: import_zod44.z.boolean().default(true),
2988
- includeRelations: import_zod44.z.boolean().default(true),
2989
- hideRelationsInTable: import_zod44.z.boolean().default(true),
2990
- showRelationsInForm: import_zod44.z.boolean().default(true),
2991
- enumValueLabel: import_zod44.z.boolean().default(true),
2992
- sharedEnums: import_zod44.z.boolean().default(true),
3390
+ var import_zod45 = require("zod");
3391
+ var RulesetSchema = import_zod45.z.object({
3392
+ hideIdInTable: import_zod45.z.boolean().default(true),
3393
+ hideIdInForm: import_zod45.z.boolean().default(true),
3394
+ hideIdInView: import_zod45.z.boolean().default(true),
3395
+ hideTimestamps: import_zod45.z.boolean().default(true),
3396
+ hideForeignKeys: import_zod45.z.boolean().default(true),
3397
+ includeRelations: import_zod45.z.boolean().default(true),
3398
+ hideRelationsInTable: import_zod45.z.boolean().default(true),
3399
+ showRelationsInForm: import_zod45.z.boolean().default(true),
3400
+ enumValueLabel: import_zod45.z.boolean().default(true),
3401
+ sharedEnums: import_zod45.z.boolean().default(true),
2993
3402
  defaultOperations: JsonOperationsSchema.default(JsonOperationsSchema.parse({}))
2994
3403
  });
2995
- var I18nConfigSchema = import_zod44.z.object({
3404
+ var I18nConfigSchema = import_zod45.z.object({
2996
3405
  /** Default / fallback language. */
2997
- defaultLanguage: import_zod44.z.string().default("en"),
3406
+ defaultLanguage: import_zod45.z.string().default("en"),
2998
3407
  /** All supported language codes. */
2999
- languages: import_zod44.z.array(import_zod44.z.string()).min(1),
3408
+ languages: import_zod45.z.array(import_zod45.z.string()).min(1),
3000
3409
  /** Directory containing `<lang>.json` bundles, relative to project root. */
3001
- translationsDir: import_zod44.z.string().default("translations")
3410
+ translationsDir: import_zod45.z.string().default("translations")
3002
3411
  });
3003
- var CroutonConfigSchema = import_zod44.z.object({
3412
+ var CroutonConfigSchema = import_zod45.z.object({
3004
3413
  /**
3005
3414
  * Application title served to the frontend via `GET /_app/layout`.
3006
3415
  * Displayed in the admin sidebar header.
3007
3416
  */
3008
- title: import_zod44.z.string(),
3417
+ title: import_zod45.z.string(),
3009
3418
  /** Where resource directories live, relative to the project root. */
3010
- resourcesDir: import_zod44.z.string(),
3419
+ resourcesDir: import_zod45.z.string(),
3011
3420
  /** Where datasource folders live (each with a `data-source.json`). */
3012
- dataSourcesDir: import_zod44.z.string(),
3421
+ dataSourcesDir: import_zod45.z.string(),
3013
3422
  /**
3014
3423
  * Template for a model's Zod export name. `{Model}` → Prisma model name.
3015
3424
  * Defaults to `{Model}WithRelationsSchema` (the relations-aware schema
3016
3425
  * emitted by zod-prisma-types when `createRelationValuesTypes` is on).
3017
3426
  */
3018
- schemaExportName: import_zod44.z.string().default("{Model}WithRelationsSchema"),
3427
+ schemaExportName: import_zod45.z.string().default("{Model}WithRelationsSchema"),
3019
3428
  /** Path to the shared enum registry, relative to project root. Default `crouton.enums.json`. */
3020
- enumsFile: import_zod44.z.string().default("crouton.enums.json"),
3429
+ enumsFile: import_zod45.z.string().default("crouton.enums.json"),
3021
3430
  /** Optional overrides of the default visibility ruleset. */
3022
3431
  rules: RulesetSchema.default(RulesetSchema.parse({})),
3023
3432
  /**
3024
3433
  * Sidebar group definitions, keyed by group slug (e.g. `"metadata"`).
3025
3434
  * Resources reference a group via `sidebar.group` in their `resource.json`.
3026
3435
  */
3027
- sidebarGroups: import_zod44.z.record(import_zod44.z.string(), SidebarGroupSchema).default({}),
3436
+ sidebarGroups: import_zod45.z.record(import_zod45.z.string(), SidebarGroupSchema).default({}),
3028
3437
  /**
3029
3438
  * Whether form fields are saved automatically as the user edits them.
3030
3439
  * @default true
3031
3440
  */
3032
- autoSave: import_zod44.z.boolean().default(true),
3441
+ autoSave: import_zod45.z.boolean().default(true),
3033
3442
  /** Optional i18n / translation configuration. */
3034
3443
  i18n: I18nConfigSchema.optional()
3035
3444
  });
@@ -3042,29 +3451,29 @@ var CONFIG_FILES = [
3042
3451
  ];
3043
3452
 
3044
3453
  // ../crouton-core/src/lib/i18n/Translations.schema.ts
3045
- var import_zod45 = require("zod");
3046
- var NestedStringRecord = import_zod45.z.lazy(() => import_zod45.z.record(import_zod45.z.string(), import_zod45.z.union([
3047
- import_zod45.z.string(),
3454
+ var import_zod46 = require("zod");
3455
+ var NestedStringRecord = import_zod46.z.lazy(() => import_zod46.z.record(import_zod46.z.string(), import_zod46.z.union([
3456
+ import_zod46.z.string(),
3048
3457
  NestedStringRecord
3049
3458
  ])));
3050
- var TranslationBundleSchema = import_zod45.z.object({
3051
- app: import_zod45.z.object({
3052
- title: import_zod45.z.string().optional()
3459
+ var TranslationBundleSchema = import_zod46.z.object({
3460
+ app: import_zod46.z.object({
3461
+ title: import_zod46.z.string().optional()
3053
3462
  }).optional(),
3054
- sidebarGroups: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string()).optional(),
3055
- resources: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.object({
3056
- title: import_zod45.z.string().optional(),
3057
- sidebar: import_zod45.z.string().optional(),
3058
- columns: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string()).optional(),
3059
- actions: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string()).optional(),
3060
- subResources: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.object({
3061
- title: import_zod45.z.string().optional(),
3062
- columns: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string()).optional()
3463
+ sidebarGroups: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string()).optional(),
3464
+ resources: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.object({
3465
+ title: import_zod46.z.string().optional(),
3466
+ sidebar: import_zod46.z.string().optional(),
3467
+ columns: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string()).optional(),
3468
+ actions: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string()).optional(),
3469
+ subResources: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.object({
3470
+ title: import_zod46.z.string().optional(),
3471
+ columns: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string()).optional()
3063
3472
  }).optional()).optional()
3064
3473
  }).optional()).optional(),
3065
- enums: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string())).optional(),
3474
+ enums: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string())).optional(),
3066
3475
  ui: NestedStringRecord.optional(),
3067
- validation: import_zod45.z.record(import_zod45.z.string(), import_zod45.z.string()).optional()
3476
+ validation: import_zod46.z.record(import_zod46.z.string(), import_zod46.z.string()).optional()
3068
3477
  }).passthrough();
3069
3478
 
3070
3479
  // ../crouton-core/src/lib/i18n/translation-keys.ts
@@ -3144,25 +3553,25 @@ var resolveLanguage = /* @__PURE__ */ __name((acceptLanguage, supported, default
3144
3553
  }, "resolveLanguage");
3145
3554
 
3146
3555
  // src/lib/crouton-api.module.ts
3147
- var import_common27 = require("@nestjs/common");
3556
+ var import_common28 = require("@nestjs/common");
3148
3557
  var import_core2 = require("@nestjs/core");
3149
3558
 
3150
3559
  // src/lib/crud/app-layout/app-layout.types.ts
3151
- var import_zod46 = require("zod");
3152
- var SidebarLeafSchema = import_zod46.z.object({
3153
- kind: import_zod46.z.literal("item").default("item"),
3154
- id: import_zod46.z.string(),
3155
- label: import_zod46.z.string(),
3156
- position: import_zod46.z.number().optional()
3560
+ var import_zod47 = require("zod");
3561
+ var SidebarLeafSchema = import_zod47.z.object({
3562
+ kind: import_zod47.z.literal("item").default("item"),
3563
+ id: import_zod47.z.string(),
3564
+ label: import_zod47.z.string(),
3565
+ position: import_zod47.z.number().optional()
3157
3566
  });
3158
- var SidebarGroupSchema2 = import_zod46.z.object({
3159
- kind: import_zod46.z.literal("group").default("group"),
3160
- id: import_zod46.z.string(),
3161
- label: import_zod46.z.string(),
3162
- position: import_zod46.z.number().optional(),
3163
- children: import_zod46.z.array(SidebarLeafSchema).default([])
3567
+ var SidebarGroupSchema2 = import_zod47.z.object({
3568
+ kind: import_zod47.z.literal("group").default("group"),
3569
+ id: import_zod47.z.string(),
3570
+ label: import_zod47.z.string(),
3571
+ position: import_zod47.z.number().optional(),
3572
+ children: import_zod47.z.array(SidebarLeafSchema).default([])
3164
3573
  });
3165
- var SidebarNodeSchema = import_zod46.z.discriminatedUnion("kind", [
3574
+ var SidebarNodeSchema = import_zod47.z.discriminatedUnion("kind", [
3166
3575
  SidebarLeafSchema,
3167
3576
  SidebarGroupSchema2
3168
3577
  ]);
@@ -3994,54 +4403,54 @@ CroutonValidationExceptionFilter = _ts_decorate3([
3994
4403
  ], CroutonValidationExceptionFilter);
3995
4404
 
3996
4405
  // src/lib/crud/crud-controller.factory.ts
3997
- var import_common23 = require("@nestjs/common");
3998
- var import_swagger11 = require("@nestjs/swagger");
4406
+ var import_common24 = require("@nestjs/common");
4407
+ var import_swagger12 = require("@nestjs/swagger");
3999
4408
 
4000
4409
  // src/lib/crud/crud-repository.factory.ts
4001
4410
  var import_common10 = require("@nestjs/common");
4002
4411
 
4003
4412
  // src/lib/crud/action/action.types.ts
4004
- var import_zod47 = require("zod");
4005
- var ActionMetadataSchema2 = import_zod47.z.object({
4413
+ var import_zod48 = require("zod");
4414
+ var ActionMetadataSchema2 = import_zod48.z.object({
4006
4415
  /** URL segment used in the endpoint. */
4007
- id: import_zod47.z.string(),
4416
+ id: import_zod48.z.string(),
4008
4417
  /** Human-readable label shown as a button. */
4009
- label: import_zod47.z.string().optional(),
4418
+ label: import_zod48.z.string().optional(),
4010
4419
  /** MDI icon name, e.g. `"mdi:open-in-new"`. */
4011
- icon: import_zod47.z.string().optional(),
4420
+ icon: import_zod48.z.string().optional(),
4012
4421
  /** Tooltip text. Falls back to `label` when omitted. */
4013
- tooltip: import_zod47.z.string().optional(),
4422
+ tooltip: import_zod48.z.string().optional(),
4014
4423
  /** Per-row condition — button is hidden when false. */
4015
- condition: import_zod47.z.custom().optional()
4424
+ condition: import_zod48.z.custom().optional()
4016
4425
  });
4017
4426
  var ResourceLinkActionSchema2 = ActionMetadataSchema2.extend({
4018
- type: import_zod47.z.literal("link"),
4427
+ type: import_zod48.z.literal("link"),
4019
4428
  /** URL to open. May contain `{id}` or `{env.VAR}` placeholders. */
4020
- href: import_zod47.z.string()
4429
+ href: import_zod48.z.string()
4021
4430
  });
4022
4431
  var ResourceRowProcedureActionSchema2 = ActionMetadataSchema2.extend({
4023
- type: import_zod47.z.literal("procedure").optional(),
4432
+ type: import_zod48.z.literal("procedure").optional(),
4024
4433
  /** HTTP method for the endpoint. Defaults to `"post"`. */
4025
- method: import_zod47.z.string().optional(),
4434
+ method: import_zod48.z.string().optional(),
4026
4435
  /** Static data payload merged into the request body by the frontend. */
4027
- data: import_zod47.z.record(import_zod47.z.string(), import_zod47.z.unknown()).optional(),
4436
+ data: import_zod48.z.record(import_zod48.z.string(), import_zod48.z.unknown()).optional(),
4028
4437
  /** Procedure called with `(prisma, recordId)`. */
4029
- procedure: import_zod47.z.custom((v) => typeof v === "function")
4438
+ procedure: import_zod48.z.custom((v) => typeof v === "function")
4030
4439
  });
4031
4440
  var ResourceTableProcedureActionSchema2 = ActionMetadataSchema2.extend({
4032
- type: import_zod47.z.literal("procedure").optional(),
4441
+ type: import_zod48.z.literal("procedure").optional(),
4033
4442
  /** HTTP method for the endpoint. Defaults to `"post"`. */
4034
- method: import_zod47.z.string().optional(),
4443
+ method: import_zod48.z.string().optional(),
4035
4444
  /** Static data payload merged into the request body by the frontend. */
4036
- data: import_zod47.z.record(import_zod47.z.string(), import_zod47.z.unknown()).optional(),
4445
+ data: import_zod48.z.record(import_zod48.z.string(), import_zod48.z.unknown()).optional(),
4037
4446
  /** Procedure called with `(prisma)` — no record id. */
4038
- procedure: import_zod47.z.custom((v) => typeof v === "function")
4447
+ procedure: import_zod48.z.custom((v) => typeof v === "function")
4039
4448
  });
4040
- var ResourceRowActionSchema2 = import_zod47.z.union([
4449
+ var ResourceRowActionSchema2 = import_zod48.z.union([
4041
4450
  ResourceRowProcedureActionSchema2,
4042
4451
  ResourceLinkActionSchema2
4043
4452
  ]);
4044
- var ResourceTableActionSchema2 = import_zod47.z.union([
4453
+ var ResourceTableActionSchema2 = import_zod48.z.union([
4045
4454
  ResourceTableProcedureActionSchema2,
4046
4455
  ResourceLinkActionSchema2
4047
4456
  ]);
@@ -4148,7 +4557,7 @@ var securityForSub = /* @__PURE__ */ __name((config, sub, op, moduleDefault) =>
4148
4557
  }, "securityForSub");
4149
4558
 
4150
4559
  // src/lib/crud/custom-repository/custom-repository.types.ts
4151
- var import_zod48 = require("zod");
4560
+ var import_zod49 = require("zod");
4152
4561
  var CUSTOM_OPS = [
4153
4562
  "findAll",
4154
4563
  "findOne",
@@ -4165,7 +4574,7 @@ var PARENT_METHOD = {
4165
4574
  patch: "patchByParent",
4166
4575
  delete: "deleteByParent"
4167
4576
  };
4168
- var CustomRepositorySchema = import_zod48.z.custom((value) => typeof value === "object" && value !== null);
4577
+ var CustomRepositorySchema = import_zod49.z.custom((value) => typeof value === "object" && value !== null);
4169
4578
 
4170
4579
  // src/lib/crud/resource/resource-load-errors.registry.ts
4171
4580
  var ResourceLoadErrorsRegistry = class ResourceLoadErrorsRegistry2 {
@@ -4235,6 +4644,7 @@ var import_common9 = require("@nestjs/common");
4235
4644
 
4236
4645
  // src/lib/crud/constants.ts
4237
4646
  var PRISMA_NOT_FOUND_CODE = "P2025";
4647
+ var PRISMA_UNIQUE_CONSTRAINT_CODE = "P2002";
4238
4648
  var DEFAULT_ID_TYPE = "string";
4239
4649
  var DEFAULT_ID_FIELD = "id";
4240
4650
 
@@ -4970,6 +5380,129 @@ var ReadRepository = class {
4970
5380
 
4971
5381
  // src/lib/crud/write.repository.ts
4972
5382
  var import_common7 = require("@nestjs/common");
5383
+
5384
+ // src/lib/crud/unique-validation.ts
5385
+ var columnsOf = /* @__PURE__ */ __name((config) => config.columns ?? [], "columnsOf");
5386
+ var columnDbName = /* @__PURE__ */ __name((col) => col.column ?? col.id, "columnDbName");
5387
+ var columnById = /* @__PURE__ */ __name((config, id) => columnsOf(config).find((c) => c.id === id), "columnById");
5388
+ var dbNameForId = /* @__PURE__ */ __name((config, id) => {
5389
+ const col = columnById(config, id);
5390
+ return col ? columnDbName(col) : id;
5391
+ }, "dbNameForId");
5392
+ var uniqueColumns = /* @__PURE__ */ __name((config) => columnsOf(config).map((column) => ({
5393
+ column,
5394
+ unique: normalizeUnique(column.unique)
5395
+ })).filter((e) => e.unique != null), "uniqueColumns");
5396
+ var findUniqueColumn = /* @__PURE__ */ __name((config, fieldId) => uniqueColumns(config).find((e) => e.column.id === fieldId), "findUniqueColumn");
5397
+ var extractTargets = /* @__PURE__ */ __name((meta) => {
5398
+ const target = meta?.target;
5399
+ if (Array.isArray(target)) return target.map(String);
5400
+ if (typeof target === "string") return [
5401
+ target
5402
+ ];
5403
+ return [];
5404
+ }, "extractTargets");
5405
+ var columnForTarget = /* @__PURE__ */ __name((config, target) => columnsOf(config).find((c) => columnDbName(c) === target || c.id === target), "columnForTarget");
5406
+ var sameSet = /* @__PURE__ */ __name((a, b) => a.length === b.length && (/* @__PURE__ */ new Set([
5407
+ ...a,
5408
+ ...b
5409
+ ])).size === a.length, "sameSet");
5410
+ var mapPrismaUniqueError = /* @__PURE__ */ __name((e, config) => {
5411
+ if (e?.code !== PRISMA_UNIQUE_CONSTRAINT_CODE) return void 0;
5412
+ const targets = extractTargets(e.meta);
5413
+ if (targets.length > 1) {
5414
+ for (const { column, unique } of uniqueColumns(config)) {
5415
+ const names = [
5416
+ columnDbName(column),
5417
+ ...(unique.scope ?? []).map((id) => dbNameForId(config, id))
5418
+ ];
5419
+ if (sameSet(names, targets)) {
5420
+ const label = column.label ?? column.id;
5421
+ return new CroutonValidationError([
5422
+ {
5423
+ field: column.id,
5424
+ message: unique.message ?? `${label} already exists`,
5425
+ code: "unique"
5426
+ }
5427
+ ]);
5428
+ }
5429
+ }
5430
+ }
5431
+ const resolved = targets.length ? targets : [
5432
+ "value"
5433
+ ];
5434
+ const errors = resolved.map((target) => {
5435
+ const col = columnForTarget(config, target);
5436
+ const unique = normalizeUnique(col?.unique);
5437
+ const label = col?.label ?? col?.id ?? target;
5438
+ return {
5439
+ field: col?.id ?? target,
5440
+ message: unique?.message ?? `${label} already exists`,
5441
+ code: "unique"
5442
+ };
5443
+ });
5444
+ return new CroutonValidationError(errors);
5445
+ }, "mapPrismaUniqueError");
5446
+ var dmmfUniqueInfo = /* @__PURE__ */ __name((client, modelName) => {
5447
+ try {
5448
+ const dmmf = client?._baseDmmf;
5449
+ if (!dmmf) return null;
5450
+ const model = dmmf.modelMap?.[modelName];
5451
+ if (!model) return null;
5452
+ const singleColumns = /* @__PURE__ */ new Set();
5453
+ for (const field of model.fields ?? []) {
5454
+ if (field.isUnique || field.isId) singleColumns.add(field.dbName ?? field.name);
5455
+ }
5456
+ const compositeFieldSets = (model.uniqueFields ?? []).filter((f) => f.length > 1).map((f) => new Set(f));
5457
+ return {
5458
+ singleColumns,
5459
+ compositeFieldSets
5460
+ };
5461
+ } catch {
5462
+ return null;
5463
+ }
5464
+ }, "dmmfUniqueInfo");
5465
+ var warnMissingUniqueConstraints = /* @__PURE__ */ __name((configs, dataSourceRegistry) => {
5466
+ if (process.env["NODE_ENV"] === "production") return;
5467
+ for (const config of configs) {
5468
+ const cols = uniqueColumns(config);
5469
+ if (!cols.length || !config.model) continue;
5470
+ let client;
5471
+ try {
5472
+ const adapter = dataSourceRegistry.resolveAdapter(config.database);
5473
+ if (adapter.kind !== "prisma") continue;
5474
+ client = adapter.client;
5475
+ } catch {
5476
+ continue;
5477
+ }
5478
+ const info = dmmfUniqueInfo(client, config.model);
5479
+ if (!info) continue;
5480
+ for (const { column, unique } of cols) {
5481
+ const dbName = columnDbName(column);
5482
+ if (!unique.scope?.length) {
5483
+ if (!info.singleColumns.has(dbName)) {
5484
+ console.warn(`[crouton] WARNING: column "${column.id}" on resource "${config.name}" declares unique: true but Prisma model "${config.model}" has no @unique on "${dbName}". Add @unique to the Prisma schema to enforce the race-condition guarantee.`);
5485
+ }
5486
+ } else {
5487
+ const expectedSet = /* @__PURE__ */ new Set([
5488
+ dbName,
5489
+ ...unique.scope.map((id) => dbNameForId(config, id))
5490
+ ]);
5491
+ const hasComposite = info.compositeFieldSets.some((s) => s.size === expectedSet.size && [
5492
+ ...expectedSet
5493
+ ].every((f) => s.has(f)));
5494
+ if (!hasComposite) {
5495
+ console.warn(`[crouton] WARNING: column "${column.id}" on resource "${config.name}" declares composite unique (scope: ${JSON.stringify(unique.scope)}) but Prisma model "${config.model}" has no matching @@unique constraint. Add @@unique([${[
5496
+ dbName,
5497
+ ...expectedSet
5498
+ ].join(", ")}]) to the Prisma schema.`);
5499
+ }
5500
+ }
5501
+ }
5502
+ }
5503
+ }, "warnMissingUniqueConstraints");
5504
+
5505
+ // src/lib/crud/write.repository.ts
4973
5506
  var includeRelationNames = /* @__PURE__ */ __name((include) => new Set((include ?? []).map((e) => typeof e === "string" ? e : e.relation)), "includeRelationNames");
4974
5507
  var PRISMA_RELATION_WRITE_KEYS = /* @__PURE__ */ new Set([
4975
5508
  "connect",
@@ -5040,9 +5573,13 @@ var WriteRepository = class {
5040
5573
  * normalized, and run through `beforeWrite` by the factory wrapper.
5041
5574
  */
5042
5575
  async create(data, _request) {
5043
- return this.prismaModel.create({
5044
- data
5045
- });
5576
+ try {
5577
+ return await this.prismaModel.create({
5578
+ data
5579
+ });
5580
+ } catch (e) {
5581
+ throw mapPrismaUniqueError(e, this.config) ?? e;
5582
+ }
5046
5583
  }
5047
5584
  /**
5048
5585
  * Update a record. The data has already been stripped, normalized, and hooked
@@ -5059,6 +5596,8 @@ var WriteRepository = class {
5059
5596
  });
5060
5597
  } catch (e) {
5061
5598
  if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
5599
+ const uniqueError = mapPrismaUniqueError(e, this.config);
5600
+ if (uniqueError) throw uniqueError;
5062
5601
  throw e;
5063
5602
  }
5064
5603
  }
@@ -5076,6 +5615,8 @@ var WriteRepository = class {
5076
5615
  });
5077
5616
  } catch (e) {
5078
5617
  if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
5618
+ const uniqueError = mapPrismaUniqueError(e, this.config);
5619
+ if (uniqueError) throw uniqueError;
5079
5620
  throw e;
5080
5621
  }
5081
5622
  }
@@ -5089,6 +5630,8 @@ var WriteRepository = class {
5089
5630
  });
5090
5631
  } catch (e) {
5091
5632
  if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
5633
+ const uniqueError = mapPrismaUniqueError(e, this.config);
5634
+ if (uniqueError) throw uniqueError;
5092
5635
  throw e;
5093
5636
  }
5094
5637
  }
@@ -5358,23 +5901,23 @@ var PrismaDataSourceAdapter = class {
5358
5901
  };
5359
5902
 
5360
5903
  // src/lib/crud/hooks/hooks.types.ts
5361
- var import_zod49 = require("zod");
5362
- var WriteOpSchema = import_zod49.z.enum([
5904
+ var import_zod50 = require("zod");
5905
+ var WriteOpSchema = import_zod50.z.enum([
5363
5906
  "create",
5364
5907
  "update",
5365
5908
  "patch",
5366
5909
  "delete"
5367
5910
  ]);
5368
- var ReadOpSchema = import_zod49.z.enum([
5911
+ var ReadOpSchema = import_zod50.z.enum([
5369
5912
  "findAll",
5370
5913
  "findOne"
5371
5914
  ]);
5372
- var ResourceHooksSchema = import_zod49.z.object({
5373
- beforeWrite: import_zod49.z.custom().optional(),
5374
- afterWrite: import_zod49.z.custom().optional(),
5375
- afterRead: import_zod49.z.custom().optional(),
5376
- beforeFindAll: import_zod49.z.custom().optional(),
5377
- afterFindAll: import_zod49.z.custom().optional()
5915
+ var ResourceHooksSchema = import_zod50.z.object({
5916
+ beforeWrite: import_zod50.z.custom().optional(),
5917
+ afterWrite: import_zod50.z.custom().optional(),
5918
+ afterRead: import_zod50.z.custom().optional(),
5919
+ beforeFindAll: import_zod50.z.custom().optional(),
5920
+ afterFindAll: import_zod50.z.custom().optional()
5378
5921
  });
5379
5922
 
5380
5923
  // src/lib/crud/hooks/hooks.loader.ts
@@ -5624,9 +6167,9 @@ var validateCustomRepository = /* @__PURE__ */ __name((config, repository, adapt
5624
6167
  }, "validateCustomRepository");
5625
6168
 
5626
6169
  // src/lib/crud/schema.utils.ts
5627
- var import_zod50 = require("zod");
6170
+ var import_zod51 = require("zod");
5628
6171
  function isZodSchema(schema) {
5629
- return schema instanceof import_zod50.ZodObject;
6172
+ return schema instanceof import_zod51.ZodObject;
5630
6173
  }
5631
6174
  __name(isZodSchema, "isZodSchema");
5632
6175
  var dropNullableFromRequired2 = /* @__PURE__ */ __name((jsonSchema) => {
@@ -5638,7 +6181,7 @@ function toJsonSchema2(schema) {
5638
6181
  if (isZodSchema(schema)) {
5639
6182
  let jsonSchema;
5640
6183
  try {
5641
- jsonSchema = (0, import_zod50.toJSONSchema)(schema, {
6184
+ jsonSchema = (0, import_zod51.toJSONSchema)(schema, {
5642
6185
  target: "openApi3",
5643
6186
  ...jsonSchemaOpts
5644
6187
  });
@@ -6767,26 +7310,26 @@ var import_common20 = require("@nestjs/common");
6767
7310
  var import_swagger8 = require("@nestjs/swagger");
6768
7311
 
6769
7312
  // src/lib/crud/resource/PatchResourceJson.schema.ts
6770
- var import_zod51 = require("zod");
6771
- var FieldVariantPatchSchema = import_zod51.z.object({
6772
- type: import_zod51.z.string().nullable().optional(),
6773
- format: import_zod51.z.string().nullable().optional(),
6774
- resource: import_zod51.z.string().nullable().optional(),
6775
- position: import_zod51.z.number().nullable().optional(),
6776
- options: import_zod51.z.record(import_zod51.z.string(), import_zod51.z.unknown().nullable()).optional()
7313
+ var import_zod52 = require("zod");
7314
+ var FieldVariantPatchSchema = import_zod52.z.object({
7315
+ type: import_zod52.z.string().nullable().optional(),
7316
+ format: import_zod52.z.string().nullable().optional(),
7317
+ resource: import_zod52.z.string().nullable().optional(),
7318
+ position: import_zod52.z.number().nullable().optional(),
7319
+ options: import_zod52.z.record(import_zod52.z.string(), import_zod52.z.unknown().nullable()).optional()
6777
7320
  }).partial();
6778
- var PatchColumnSchema = import_zod51.z.object({
6779
- label: import_zod51.z.string().optional(),
6780
- column: import_zod51.z.string().optional(),
6781
- hiddenInTable: import_zod51.z.boolean().optional(),
6782
- hiddenInForm: import_zod51.z.boolean().optional(),
6783
- hiddenInView: import_zod51.z.boolean().optional(),
7321
+ var PatchColumnSchema = import_zod52.z.object({
7322
+ label: import_zod52.z.string().optional(),
7323
+ column: import_zod52.z.string().optional(),
7324
+ hiddenInTable: import_zod52.z.boolean().optional(),
7325
+ hiddenInForm: import_zod52.z.boolean().optional(),
7326
+ hiddenInView: import_zod52.z.boolean().optional(),
6784
7327
  fieldInput: FieldVariantPatchSchema.optional(),
6785
7328
  fieldView: FieldVariantPatchSchema.optional(),
6786
7329
  fieldTable: FieldVariantPatchSchema.optional()
6787
7330
  }).partial();
6788
- var PatchResourceJsonSchema = import_zod51.z.object({
6789
- columns: import_zod51.z.record(import_zod51.z.string(), PatchColumnSchema)
7331
+ var PatchResourceJsonSchema = import_zod52.z.object({
7332
+ columns: import_zod52.z.record(import_zod52.z.string(), PatchColumnSchema)
6790
7333
  });
6791
7334
 
6792
7335
  // src/lib/crud/resource/WriteResourceJson.ts
@@ -7338,9 +7881,103 @@ var registerSchemas = /* @__PURE__ */ __name((ctx, sub) => {
7338
7881
  }
7339
7882
  }, "registerSchemas");
7340
7883
 
7341
- // src/lib/crud/operations/register-update.ts
7884
+ // src/lib/crud/operations/register-unique-check.ts
7342
7885
  var import_common22 = require("@nestjs/common");
7343
7886
  var import_swagger10 = require("@nestjs/swagger");
7887
+ var import_zod53 = require("zod");
7888
+ var UniqueQuerySchema = import_zod53.z.object({
7889
+ /** The column id to check (must be a declared-unique column). */
7890
+ field: import_zod53.z.string(),
7891
+ /** The candidate value. */
7892
+ value: import_zod53.z.string(),
7893
+ /** On edit, the id of the record being edited, so it does not match itself. */
7894
+ excludeId: import_zod53.z.string().optional(),
7895
+ /** JSON object of `{ scopeFieldId: value }` for composite (scoped) uniqueness. */
7896
+ scope: import_zod53.z.string().optional()
7897
+ });
7898
+ var registerUniqueCheck = /* @__PURE__ */ __name((ctx) => {
7899
+ const { cls, config } = ctx;
7900
+ if (!isOperationEnabled2(ctx.definition, "findAll")) return;
7901
+ if (!config.model) return;
7902
+ if (uniqueColumns(config).length === 0) return;
7903
+ const methodName = "uniqueCheck";
7904
+ const idField = config.idField ?? "id";
7905
+ const idType = config.idType ?? "string";
7906
+ const modelName = config.model;
7907
+ const handler = /* @__PURE__ */ __name(async function(query) {
7908
+ const { field, value, excludeId } = query;
7909
+ const entry = findUniqueColumn(config, field);
7910
+ if (!entry) {
7911
+ throw new CroutonValidationError([
7912
+ {
7913
+ field,
7914
+ message: `"${field}" is not a unique field`,
7915
+ code: "unknown_field"
7916
+ }
7917
+ ]);
7918
+ }
7919
+ const dbName = columnDbName(entry.column);
7920
+ const where = {
7921
+ [dbName]: entry.unique.caseInsensitive ? {
7922
+ equals: value,
7923
+ mode: "insensitive"
7924
+ } : value
7925
+ };
7926
+ if (excludeId !== void 0 && excludeId !== "") {
7927
+ where.NOT = {
7928
+ [idField]: idType === "number" ? Number(excludeId) : String(excludeId)
7929
+ };
7930
+ }
7931
+ if (entry.unique.scope?.length && query.scope) {
7932
+ let parsed;
7933
+ try {
7934
+ parsed = JSON.parse(query.scope);
7935
+ } catch {
7936
+ parsed = {};
7937
+ }
7938
+ for (const scopeId of entry.unique.scope) {
7939
+ where[dbNameForId(config, scopeId)] = parsed[scopeId] ?? null;
7940
+ }
7941
+ }
7942
+ const model = this.repo.prisma?.[modelName];
7943
+ if (!model) return {
7944
+ unique: true
7945
+ };
7946
+ const row = await model.findFirst({
7947
+ where,
7948
+ select: {
7949
+ [idField]: true
7950
+ }
7951
+ });
7952
+ return {
7953
+ unique: !row
7954
+ };
7955
+ }, "handler");
7956
+ def(cls, methodName, handler);
7957
+ const d = desc(cls, methodName);
7958
+ (0, import_common22.Get)("unique")(cls.prototype, methodName, d);
7959
+ (0, import_common22.Query)(new ZodValidationPipe(UniqueQuerySchema))(cls.prototype, methodName, 0);
7960
+ (0, import_swagger10.ApiOperation)({
7961
+ summary: `Check whether a ${config.name} field value is unique`
7962
+ })(cls.prototype, methodName, d);
7963
+ (0, import_swagger10.ApiResponse)({
7964
+ status: 200,
7965
+ description: "Uniqueness result",
7966
+ schema: {
7967
+ type: "object",
7968
+ properties: {
7969
+ unique: {
7970
+ type: "boolean"
7971
+ }
7972
+ }
7973
+ }
7974
+ })(cls.prototype, methodName, d);
7975
+ ctx.secure(methodName, "findAll");
7976
+ }, "registerUniqueCheck");
7977
+
7978
+ // src/lib/crud/operations/register-update.ts
7979
+ var import_common23 = require("@nestjs/common");
7980
+ var import_swagger11 = require("@nestjs/swagger");
7344
7981
  var defaultUpdate = /* @__PURE__ */ __name((ctx) => {
7345
7982
  if (!isOperationEnabled2(ctx.definition, "update")) return null;
7346
7983
  if (isOperationExternal2(ctx.definition, "update")) return null;
@@ -7354,9 +7991,9 @@ var defaultUpdate = /* @__PURE__ */ __name((ctx) => {
7354
7991
  return this.repo.update(id, body, req);
7355
7992
  }, "updateFn"),
7356
7993
  decorators: /* @__PURE__ */ __name(() => {
7357
- (0, import_common22.Param)("id")(cls.prototype, methodName, 0);
7994
+ (0, import_common23.Param)("id")(cls.prototype, methodName, 0);
7358
7995
  bodyDecorator(updateSchema)(cls.prototype, methodName, 1);
7359
- (0, import_common22.Req)()(cls.prototype, methodName, 2);
7996
+ (0, import_common23.Req)()(cls.prototype, methodName, 2);
7360
7997
  }, "decorators")
7361
7998
  };
7362
7999
  }, "defaultUpdate");
@@ -7368,10 +8005,10 @@ var childUpdate = /* @__PURE__ */ __name((sub) => (ctx) => {
7368
8005
  return this.repo.updateChild(sub, childId, body, req);
7369
8006
  }, "updateFn");
7370
8007
  const decorators = /* @__PURE__ */ __name(() => {
7371
- (0, import_common22.Param)("id")(cls.prototype, methodName, 0);
7372
- (0, import_common22.Param)("childId")(cls.prototype, methodName, 1);
7373
- (0, import_common22.Body)()(cls.prototype, methodName, 2);
7374
- (0, import_common22.Req)()(cls.prototype, methodName, 3);
8008
+ (0, import_common23.Param)("id")(cls.prototype, methodName, 0);
8009
+ (0, import_common23.Param)("childId")(cls.prototype, methodName, 1);
8010
+ (0, import_common23.Body)()(cls.prototype, methodName, 2);
8011
+ (0, import_common23.Req)()(cls.prototype, methodName, 3);
7375
8012
  }, "decorators");
7376
8013
  return {
7377
8014
  route: `:id/${sub.childRoute}/:childId`,
@@ -7389,15 +8026,15 @@ var registerUpdate = /* @__PURE__ */ __name((ctx, sub) => {
7389
8026
  const { cls } = ctx;
7390
8027
  def(cls, methodName, properties.updateFn);
7391
8028
  const d = desc(cls, methodName);
7392
- (0, import_common22.Put)(route)(cls.prototype, methodName, d);
7393
- (0, import_swagger10.ApiOperation)({
8029
+ (0, import_common23.Put)(route)(cls.prototype, methodName, d);
8030
+ (0, import_swagger11.ApiOperation)({
7394
8031
  summary: `Replace a ${name}`
7395
8032
  })(cls.prototype, methodName, d);
7396
- (0, import_swagger10.ApiResponse)({
8033
+ (0, import_swagger11.ApiResponse)({
7397
8034
  status: 200,
7398
8035
  description: `${name} replaced`
7399
8036
  })(cls.prototype, methodName, d);
7400
- (0, import_swagger10.ApiNotFoundResponse)({
8037
+ (0, import_swagger11.ApiNotFoundResponse)({
7401
8038
  description: "Not found"
7402
8039
  })(cls.prototype, methodName, d);
7403
8040
  properties.decorators();
@@ -7414,6 +8051,7 @@ var registerEndpoints = /* @__PURE__ */ __name((ctx) => {
7414
8051
  registerResourceJsonEndpoint(ctx);
7415
8052
  registerResourceColumnsEndpoint(ctx);
7416
8053
  registerDefinitionEndpoint(ctx);
8054
+ registerUniqueCheck(ctx);
7417
8055
  registerEndpoint(ctx);
7418
8056
  registerActionRoutes(ctx);
7419
8057
  registerTableActionRoutes(ctx);
@@ -7444,9 +8082,9 @@ function createCrudController(config, baseUrl, moduleDefaultSecurity, securityEn
7444
8082
  const explicitPatchSchema = schemaFor2(definition, "patch");
7445
8083
  const patchSchema = explicitPatchSchema ?? (updateSchema && isZodSchema(updateSchema) ? updateSchema.partial() : void 0) ?? updateSchema;
7446
8084
  const bodyDecorator = /* @__PURE__ */ __name((schema, options) => {
7447
- if (!schema) return (0, import_common23.Body)();
7448
- if (isZodSchema(schema)) return (0, import_common23.Body)(new ZodValidationPipe(schema, options));
7449
- return (0, import_common23.Body)();
8085
+ if (!schema) return (0, import_common24.Body)();
8086
+ if (isZodSchema(schema)) return (0, import_common24.Body)(new ZodValidationPipe(schema, options));
8087
+ return (0, import_common24.Body)();
7450
8088
  }, "bodyDecorator");
7451
8089
  const resolveClient = /* @__PURE__ */ __name((registry2, resource) => {
7452
8090
  try {
@@ -7476,7 +8114,7 @@ function createCrudController(config, baseUrl, moduleDefaultSecurity, securityEn
7476
8114
  const secure = /* @__PURE__ */ __name((methodName, op, sub) => {
7477
8115
  const sec = sub ? securityForSub(config, sub, op, moduleDefaultSecurity) : securityFor(config, definition, op, moduleDefaultSecurity);
7478
8116
  if (sec) {
7479
- (0, import_common23.SetMetadata)(CROUTON_SECURITY, sec)(CrudControllerBase.prototype, methodName, Object.getOwnPropertyDescriptor(CrudControllerBase.prototype, methodName));
8117
+ (0, import_common24.SetMetadata)(CROUTON_SECURITY, sec)(CrudControllerBase.prototype, methodName, Object.getOwnPropertyDescriptor(CrudControllerBase.prototype, methodName));
7480
8118
  }
7481
8119
  }, "secure");
7482
8120
  const ctx = {
@@ -7500,11 +8138,11 @@ function createCrudController(config, baseUrl, moduleDefaultSecurity, securityEn
7500
8138
  };
7501
8139
  registerEndpoints(ctx);
7502
8140
  const controllerPath = resourceControllerPath(route, config.parent);
7503
- (0, import_common23.Controller)(prefix ? `${prefix}/${controllerPath}` : controllerPath)(CrudControllerBase);
8141
+ (0, import_common24.Controller)(prefix ? `${prefix}/${controllerPath}` : controllerPath)(CrudControllerBase);
7504
8142
  if (securityEnabled) {
7505
- (0, import_common23.UseGuards)(CroutonSecurityGuard)(CrudControllerBase);
8143
+ (0, import_common24.UseGuards)(CroutonSecurityGuard)(CrudControllerBase);
7506
8144
  }
7507
- (0, import_swagger11.ApiTags)(tag)(CrudControllerBase);
8145
+ (0, import_swagger12.ApiTags)(tag)(CrudControllerBase);
7508
8146
  Object.defineProperty(CrudControllerBase, "name", {
7509
8147
  value: `${name.charAt(0).toUpperCase() + name.slice(1)}Controller`
7510
8148
  });
@@ -7517,8 +8155,8 @@ function createCrudController(config, baseUrl, moduleDefaultSecurity, securityEn
7517
8155
  __name(createCrudController, "createCrudController");
7518
8156
 
7519
8157
  // src/lib/crud/dev-tools/dev-resources.controller.ts
7520
- var import_common24 = require("@nestjs/common");
7521
- var import_swagger12 = require("@nestjs/swagger");
8158
+ var import_common25 = require("@nestjs/common");
8159
+ var import_swagger13 = require("@nestjs/swagger");
7522
8160
 
7523
8161
  // ../crouton-codegen/src/naming.ts
7524
8162
  var clientAccessor = /* @__PURE__ */ __name((prismaModelName) => {
@@ -8236,12 +8874,12 @@ var makeSchemaExportName = /* @__PURE__ */ __name((config) => {
8236
8874
  var resolveFromRoot = /* @__PURE__ */ __name((root, p) => (0, import_node_path11.isAbsolute)(p) ? p : (0, import_node_path11.join)(root, p), "resolveFromRoot");
8237
8875
 
8238
8876
  // ../crouton-codegen/src/scaffold.ts
8239
- var import_zod52 = require("zod");
8877
+ var import_zod54 = require("zod");
8240
8878
  var import_promises5 = require("fs/promises");
8241
8879
  var import_node_path12 = require("path");
8242
8880
  var ScallfoldDatasourceSchema = DataSourceShape.extend({
8243
8881
  /** Folder name under `dataSourcesDir`. */
8244
- folder: import_zod52.z.string().default("default")
8882
+ folder: import_zod54.z.string().default("default")
8245
8883
  }).transform(transformDataSource);
8246
8884
 
8247
8885
  // ../crouton-codegen/src/datasource-scaffold.ts
@@ -8619,7 +9257,7 @@ var DevResourcesController = class {
8619
9257
  }
8620
9258
  assertDev() {
8621
9259
  if (!IS_DEV) {
8622
- throw new import_common24.ForbiddenException("The database sync tools are only available when CROUTON_SCHEMA_EDITOR is enabled.");
9260
+ throw new import_common25.ForbiddenException("The database sync tools are only available when CROUTON_SCHEMA_EDITOR is enabled.");
8623
9261
  }
8624
9262
  }
8625
9263
  /** Loads project config + resolves the datasource + Prisma schema path. Throws 404/400 with a clear message on misconfiguration. */
@@ -8628,14 +9266,14 @@ var DevResourcesController = class {
8628
9266
  try {
8629
9267
  loaded = await loadConfig2(process.cwd());
8630
9268
  } catch (e) {
8631
- throw new import_common24.NotFoundException(e.message ?? "No crouton.json config found.");
9269
+ throw new import_common25.NotFoundException(e.message ?? "No crouton.json config found.");
8632
9270
  }
8633
9271
  const datasources = await loadDatasources(loaded);
8634
9272
  let ds;
8635
9273
  try {
8636
9274
  ds = resolveDatasource(datasources, datasourceName);
8637
9275
  } catch (e) {
8638
- throw new import_common24.BadRequestException(e.message);
9276
+ throw new import_common25.BadRequestException(e.message);
8639
9277
  }
8640
9278
  const schemaPath = resolveFromRoot(loaded.root, ds.prismaSchema);
8641
9279
  return {
@@ -8650,7 +9288,7 @@ var DevResourcesController = class {
8650
9288
  schemaPath
8651
9289
  });
8652
9290
  } catch (e) {
8653
- throw new import_common24.BadRequestException(`Failed to read Prisma schema at ${schemaPath}: ${e.message}`);
9291
+ throw new import_common25.BadRequestException(`Failed to read Prisma schema at ${schemaPath}: ${e.message}`);
8654
9292
  }
8655
9293
  }
8656
9294
  buildApplyContext(loaded, ds) {
@@ -8720,7 +9358,7 @@ var DevResourcesController = class {
8720
9358
  zodOutputDir: zodDir
8721
9359
  });
8722
9360
  if (!result.ok) {
8723
- throw new import_common24.BadRequestException(`prisma db pull failed:
9361
+ throw new import_common25.BadRequestException(`prisma db pull failed:
8724
9362
  ${result.dbPull.output}`);
8725
9363
  }
8726
9364
  return {
@@ -8737,13 +9375,13 @@ ${result.dbPull.output}`);
8737
9375
  async sync(body) {
8738
9376
  this.assertDev();
8739
9377
  if (!body?.model) {
8740
- throw new import_common24.BadRequestException('"model" is required.');
9378
+ throw new import_common25.BadRequestException('"model" is required.');
8741
9379
  }
8742
9380
  const { loaded, ds, schemaPath } = await this.loadProject(body.datasource);
8743
9381
  const models = await this.introspectModels(schemaPath);
8744
9382
  const model = models.find((m) => m.prismaName === body.model || m.clientAccessor === body.model);
8745
9383
  if (!model) {
8746
- throw new import_common24.NotFoundException(`Model "${body.model}" not found in ${schemaPath}.`);
9384
+ throw new import_common25.NotFoundException(`Model "${body.model}" not found in ${schemaPath}.`);
8747
9385
  }
8748
9386
  const resolveRelationResource = await makeRelationResolver(loaded);
8749
9387
  const diff2 = await buildResourceDiff(model, {
@@ -8865,14 +9503,14 @@ ${result.dbPull.output}`);
8865
9503
  const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
8866
9504
  const jsonPath = resolveResourcePath(resourcesDir, name);
8867
9505
  const raw = readRawResourceJson(jsonPath);
8868
- if (!raw) throw new import_common24.NotFoundException(`Resource "${name}" not found.`);
8869
- if (jsonPath.endsWith(".ts")) throw new import_common24.ForbiddenException("TypeScript resources cannot be edited.");
9506
+ if (!raw) throw new import_common25.NotFoundException(`Resource "${name}" not found.`);
9507
+ if (jsonPath.endsWith(".ts")) throw new import_common25.ForbiddenException("TypeScript resources cannot be edited.");
8870
9508
  const patched = applyResourceFlagPatch(raw, {
8871
9509
  draft: false
8872
9510
  });
8873
9511
  const result = validateResourceJson(patched);
8874
9512
  if (!result.success) {
8875
- throw new import_common24.BadRequestException(`Validation failed: ${result.error.message}`);
9513
+ throw new import_common25.BadRequestException(`Validation failed: ${result.error.message}`);
8876
9514
  }
8877
9515
  writeRawResourceJson(jsonPath, patched);
8878
9516
  return {
@@ -8885,8 +9523,8 @@ ${result.dbPull.output}`);
8885
9523
  const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
8886
9524
  const jsonPath = resolveResourcePath(resourcesDir, name);
8887
9525
  const raw = readRawResourceJson(jsonPath);
8888
- if (!raw) throw new import_common24.NotFoundException(`Resource "${name}" not found.`);
8889
- if (jsonPath.endsWith(".ts")) throw new import_common24.ForbiddenException("TypeScript resources cannot be edited.");
9526
+ if (!raw) throw new import_common25.NotFoundException(`Resource "${name}" not found.`);
9527
+ if (jsonPath.endsWith(".ts")) throw new import_common25.ForbiddenException("TypeScript resources cannot be edited.");
8890
9528
  const patched = applyResourceFlagPatch(raw, {
8891
9529
  sidebar: {
8892
9530
  hide: true
@@ -8894,7 +9532,7 @@ ${result.dbPull.output}`);
8894
9532
  });
8895
9533
  const result = validateResourceJson(patched);
8896
9534
  if (!result.success) {
8897
- throw new import_common24.BadRequestException(`Validation failed: ${result.error.message}`);
9535
+ throw new import_common25.BadRequestException(`Validation failed: ${result.error.message}`);
8898
9536
  }
8899
9537
  writeRawResourceJson(jsonPath, patched);
8900
9538
  return {
@@ -8907,8 +9545,8 @@ ${result.dbPull.output}`);
8907
9545
  const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
8908
9546
  const jsonPath = resolveResourcePath(resourcesDir, name);
8909
9547
  const raw = readRawResourceJson(jsonPath);
8910
- if (!raw) throw new import_common24.NotFoundException(`Resource "${name}" not found.`);
8911
- if (jsonPath.endsWith(".ts")) throw new import_common24.ForbiddenException("TypeScript resources cannot be edited.");
9548
+ if (!raw) throw new import_common25.NotFoundException(`Resource "${name}" not found.`);
9549
+ if (jsonPath.endsWith(".ts")) throw new import_common25.ForbiddenException("TypeScript resources cannot be edited.");
8912
9550
  const sidebarPatch = {
8913
9551
  hide: false
8914
9552
  };
@@ -8921,7 +9559,7 @@ ${result.dbPull.output}`);
8921
9559
  });
8922
9560
  const result = validateResourceJson(patched);
8923
9561
  if (!result.success) {
8924
- throw new import_common24.BadRequestException(`Validation failed: ${result.error.message}`);
9562
+ throw new import_common25.BadRequestException(`Validation failed: ${result.error.message}`);
8925
9563
  }
8926
9564
  writeRawResourceJson(jsonPath, patched);
8927
9565
  return {
@@ -8934,12 +9572,12 @@ ${result.dbPull.output}`);
8934
9572
  const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
8935
9573
  const jsonPath = resolveResourcePath(resourcesDir, name);
8936
9574
  const raw = readRawResourceJson(jsonPath);
8937
- if (!raw) throw new import_common24.NotFoundException(`Resource "${name}" not found.`);
8938
- if (jsonPath.endsWith(".ts")) throw new import_common24.ForbiddenException("TypeScript resources cannot be edited.");
9575
+ if (!raw) throw new import_common25.NotFoundException(`Resource "${name}" not found.`);
9576
+ if (jsonPath.endsWith(".ts")) throw new import_common25.ForbiddenException("TypeScript resources cannot be edited.");
8939
9577
  const patched = applyResourceFlagPatch(raw, body);
8940
9578
  const result = validateResourceJson(patched);
8941
9579
  if (!result.success) {
8942
- throw new import_common24.BadRequestException(`Validation failed: ${result.error.message}`);
9580
+ throw new import_common25.BadRequestException(`Validation failed: ${result.error.message}`);
8943
9581
  }
8944
9582
  writeRawResourceJson(jsonPath, patched);
8945
9583
  return {
@@ -8948,11 +9586,11 @@ ${result.dbPull.output}`);
8948
9586
  }
8949
9587
  };
8950
9588
  _ts_decorate8([
8951
- (0, import_common24.Get)("models"),
8952
- (0, import_swagger12.ApiOperation)({
9589
+ (0, import_common25.Get)("models"),
9590
+ (0, import_swagger13.ApiOperation)({
8953
9591
  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"
8954
9592
  }),
8955
- (0, import_swagger12.ApiResponse)({
9593
+ (0, import_swagger13.ApiResponse)({
8956
9594
  status: 200,
8957
9595
  description: "DB models and their resource/client status"
8958
9596
  }),
@@ -8961,11 +9599,11 @@ _ts_decorate8([
8961
9599
  _ts_metadata8("design:returntype", Promise)
8962
9600
  ], DevResourcesController.prototype, "listModels", null);
8963
9601
  _ts_decorate8([
8964
- (0, import_common24.Post)("restart"),
8965
- (0, import_swagger12.ApiOperation)({
9602
+ (0, import_common25.Post)("restart"),
9603
+ (0, import_swagger13.ApiOperation)({
8966
9604
  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."
8967
9605
  }),
8968
- (0, import_swagger12.ApiResponse)({
9606
+ (0, import_swagger13.ApiResponse)({
8969
9607
  status: 200,
8970
9608
  description: "Restart scheduled \u2014 the connection will drop shortly after"
8971
9609
  }),
@@ -8974,15 +9612,15 @@ _ts_decorate8([
8974
9612
  _ts_metadata8("design:returntype", Object)
8975
9613
  ], DevResourcesController.prototype, "restart", null);
8976
9614
  _ts_decorate8([
8977
- (0, import_common24.Post)("pull"),
8978
- (0, import_swagger12.ApiOperation)({
9615
+ (0, import_common25.Post)("pull"),
9616
+ (0, import_swagger13.ApiOperation)({
8979
9617
  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"
8980
9618
  }),
8981
- (0, import_swagger12.ApiResponse)({
9619
+ (0, import_swagger13.ApiResponse)({
8982
9620
  status: 200,
8983
9621
  description: "Result of each step, or requiresConfirmation if schema.prisma has uncommitted changes"
8984
9622
  }),
8985
- _ts_param2(0, (0, import_common24.Body)()),
9623
+ _ts_param2(0, (0, import_common25.Body)()),
8986
9624
  _ts_metadata8("design:type", Function),
8987
9625
  _ts_metadata8("design:paramtypes", [
8988
9626
  Object
@@ -8990,15 +9628,15 @@ _ts_decorate8([
8990
9628
  _ts_metadata8("design:returntype", Promise)
8991
9629
  ], DevResourcesController.prototype, "pull", null);
8992
9630
  _ts_decorate8([
8993
- (0, import_common24.Post)("sync"),
8994
- (0, import_swagger12.ApiOperation)({
9631
+ (0, import_common25.Post)("sync"),
9632
+ (0, import_swagger13.ApiOperation)({
8995
9633
  summary: "Dev-only: generate or update a single resource.json from its DB model, using recommended defaults (non-interactive)"
8996
9634
  }),
8997
- (0, import_swagger12.ApiResponse)({
9635
+ (0, import_swagger13.ApiResponse)({
8998
9636
  status: 200,
8999
9637
  description: "Files written for this resource"
9000
9638
  }),
9001
- _ts_param2(0, (0, import_common24.Body)()),
9639
+ _ts_param2(0, (0, import_common25.Body)()),
9002
9640
  _ts_metadata8("design:type", Function),
9003
9641
  _ts_metadata8("design:paramtypes", [
9004
9642
  Object
@@ -9006,15 +9644,15 @@ _ts_decorate8([
9006
9644
  _ts_metadata8("design:returntype", Promise)
9007
9645
  ], DevResourcesController.prototype, "sync", null);
9008
9646
  _ts_decorate8([
9009
- (0, import_common24.Post)("plan"),
9010
- (0, import_swagger12.ApiOperation)({
9647
+ (0, import_common25.Post)("plan"),
9648
+ (0, import_swagger13.ApiOperation)({
9011
9649
  summary: "Dev-only: dry-run introspect + diff across all (or selected) DB models using recommended defaults \u2014 computes what would change, writes nothing"
9012
9650
  }),
9013
- (0, import_swagger12.ApiResponse)({
9651
+ (0, import_swagger13.ApiResponse)({
9014
9652
  status: 200,
9015
9653
  description: "Proposed per-resource changes"
9016
9654
  }),
9017
- _ts_param2(0, (0, import_common24.Body)()),
9655
+ _ts_param2(0, (0, import_common25.Body)()),
9018
9656
  _ts_metadata8("design:type", Function),
9019
9657
  _ts_metadata8("design:paramtypes", [
9020
9658
  Object
@@ -9022,15 +9660,15 @@ _ts_decorate8([
9022
9660
  _ts_metadata8("design:returntype", Promise)
9023
9661
  ], DevResourcesController.prototype, "plan", null);
9024
9662
  _ts_decorate8([
9025
- (0, import_common24.Post)("apply"),
9026
- (0, import_swagger12.ApiOperation)({
9663
+ (0, import_common25.Post)("apply"),
9664
+ (0, import_swagger13.ApiOperation)({
9027
9665
  summary: "Dev-only: commit resource.json/schema.ts changes to disk for the given resources (or all, if omitted), using recommended defaults"
9028
9666
  }),
9029
- (0, import_swagger12.ApiResponse)({
9667
+ (0, import_swagger13.ApiResponse)({
9030
9668
  status: 200,
9031
9669
  description: "Per-resource commit results"
9032
9670
  }),
9033
- _ts_param2(0, (0, import_common24.Body)()),
9671
+ _ts_param2(0, (0, import_common25.Body)()),
9034
9672
  _ts_metadata8("design:type", Function),
9035
9673
  _ts_metadata8("design:paramtypes", [
9036
9674
  Object
@@ -9038,11 +9676,11 @@ _ts_decorate8([
9038
9676
  _ts_metadata8("design:returntype", Promise)
9039
9677
  ], DevResourcesController.prototype, "apply", null);
9040
9678
  _ts_decorate8([
9041
- (0, import_common24.Get)("visibility"),
9042
- (0, import_swagger12.ApiOperation)({
9679
+ (0, import_common25.Get)("visibility"),
9680
+ (0, import_swagger13.ApiOperation)({
9043
9681
  summary: "Dev-only: list all resources with their menu visibility state (in-menu, hidden, draft, error)"
9044
9682
  }),
9045
- (0, import_swagger12.ApiResponse)({
9683
+ (0, import_swagger13.ApiResponse)({
9046
9684
  status: 200,
9047
9685
  description: "Resource visibility list"
9048
9686
  }),
@@ -9051,15 +9689,15 @@ _ts_decorate8([
9051
9689
  _ts_metadata8("design:returntype", Promise)
9052
9690
  ], DevResourcesController.prototype, "visibility", null);
9053
9691
  _ts_decorate8([
9054
- (0, import_common24.Post)(":name/publish"),
9055
- (0, import_swagger12.ApiOperation)({
9692
+ (0, import_common25.Post)(":name/publish"),
9693
+ (0, import_swagger13.ApiOperation)({
9056
9694
  summary: "Dev-only: publish a draft resource (removes `draft: true` from resource.json)"
9057
9695
  }),
9058
- (0, import_swagger12.ApiResponse)({
9696
+ (0, import_swagger13.ApiResponse)({
9059
9697
  status: 200,
9060
9698
  description: "Resource published"
9061
9699
  }),
9062
- _ts_param2(0, (0, import_common24.Param)("name")),
9700
+ _ts_param2(0, (0, import_common25.Param)("name")),
9063
9701
  _ts_metadata8("design:type", Function),
9064
9702
  _ts_metadata8("design:paramtypes", [
9065
9703
  String
@@ -9067,15 +9705,15 @@ _ts_decorate8([
9067
9705
  _ts_metadata8("design:returntype", Promise)
9068
9706
  ], DevResourcesController.prototype, "publish", null);
9069
9707
  _ts_decorate8([
9070
- (0, import_common24.Post)(":name/remove-from-menu"),
9071
- (0, import_swagger12.ApiOperation)({
9708
+ (0, import_common25.Post)(":name/remove-from-menu"),
9709
+ (0, import_swagger13.ApiOperation)({
9072
9710
  summary: "Dev-only: hide a resource from the sidebar menu (sets `sidebar.hide: true` in resource.json)"
9073
9711
  }),
9074
- (0, import_swagger12.ApiResponse)({
9712
+ (0, import_swagger13.ApiResponse)({
9075
9713
  status: 200,
9076
9714
  description: "Resource removed from menu"
9077
9715
  }),
9078
- _ts_param2(0, (0, import_common24.Param)("name")),
9716
+ _ts_param2(0, (0, import_common25.Param)("name")),
9079
9717
  _ts_metadata8("design:type", Function),
9080
9718
  _ts_metadata8("design:paramtypes", [
9081
9719
  String
@@ -9083,16 +9721,16 @@ _ts_decorate8([
9083
9721
  _ts_metadata8("design:returntype", Promise)
9084
9722
  ], DevResourcesController.prototype, "removeFromMenu", null);
9085
9723
  _ts_decorate8([
9086
- (0, import_common24.Post)(":name/add-to-menu"),
9087
- (0, import_swagger12.ApiOperation)({
9724
+ (0, import_common25.Post)(":name/add-to-menu"),
9725
+ (0, import_swagger13.ApiOperation)({
9088
9726
  summary: "Dev-only: publish + un-hide a resource and optionally set sidebar group/position/label"
9089
9727
  }),
9090
- (0, import_swagger12.ApiResponse)({
9728
+ (0, import_swagger13.ApiResponse)({
9091
9729
  status: 200,
9092
9730
  description: "Resource added to menu"
9093
9731
  }),
9094
- _ts_param2(0, (0, import_common24.Param)("name")),
9095
- _ts_param2(1, (0, import_common24.Body)()),
9732
+ _ts_param2(0, (0, import_common25.Param)("name")),
9733
+ _ts_param2(1, (0, import_common25.Body)()),
9096
9734
  _ts_metadata8("design:type", Function),
9097
9735
  _ts_metadata8("design:paramtypes", [
9098
9736
  String,
@@ -9101,16 +9739,16 @@ _ts_decorate8([
9101
9739
  _ts_metadata8("design:returntype", Promise)
9102
9740
  ], DevResourcesController.prototype, "addToMenu", null);
9103
9741
  _ts_decorate8([
9104
- (0, import_common24.Patch)(":name/flags"),
9105
- (0, import_swagger12.ApiOperation)({
9742
+ (0, import_common25.Patch)(":name/flags"),
9743
+ (0, import_swagger13.ApiOperation)({
9106
9744
  summary: "Dev-only: set arbitrary resource flags (draft, sidebar.hide, group, position, label)"
9107
9745
  }),
9108
- (0, import_swagger12.ApiResponse)({
9746
+ (0, import_swagger13.ApiResponse)({
9109
9747
  status: 200,
9110
9748
  description: "Flags updated"
9111
9749
  }),
9112
- _ts_param2(0, (0, import_common24.Param)("name")),
9113
- _ts_param2(1, (0, import_common24.Body)()),
9750
+ _ts_param2(0, (0, import_common25.Param)("name")),
9751
+ _ts_param2(1, (0, import_common25.Body)()),
9114
9752
  _ts_metadata8("design:type", Function),
9115
9753
  _ts_metadata8("design:paramtypes", [
9116
9754
  String,
@@ -9119,8 +9757,8 @@ _ts_decorate8([
9119
9757
  _ts_metadata8("design:returntype", Promise)
9120
9758
  ], DevResourcesController.prototype, "updateFlags", null);
9121
9759
  DevResourcesController = _ts_decorate8([
9122
- (0, import_common24.Controller)("_app/resources"),
9123
- (0, import_swagger12.ApiTags)("Dev tools"),
9760
+ (0, import_common25.Controller)("_app/resources"),
9761
+ (0, import_swagger13.ApiTags)("Dev tools"),
9124
9762
  _ts_metadata8("design:type", Function),
9125
9763
  _ts_metadata8("design:paramtypes", [
9126
9764
  typeof DataSourceRegistry === "undefined" ? Object : DataSourceRegistry,
@@ -9129,12 +9767,12 @@ DevResourcesController = _ts_decorate8([
9129
9767
  ], DevResourcesController);
9130
9768
 
9131
9769
  // src/lib/crud/enum-registry/enum-registry.types.ts
9132
- var import_zod53 = require("zod");
9133
- var EnumEntrySchema2 = import_zod53.z.object({
9134
- value: import_zod53.z.unknown(),
9135
- label: import_zod53.z.string()
9770
+ var import_zod55 = require("zod");
9771
+ var EnumEntrySchema2 = import_zod55.z.object({
9772
+ value: import_zod55.z.unknown(),
9773
+ label: import_zod55.z.string()
9136
9774
  });
9137
- var EnumRegistrySchema2 = import_zod53.z.record(import_zod53.z.string(), import_zod53.z.array(EnumEntrySchema2)).default({});
9775
+ var EnumRegistrySchema2 = import_zod55.z.record(import_zod55.z.string(), import_zod55.z.array(EnumEntrySchema2)).default({});
9138
9776
 
9139
9777
  // src/lib/crud/enum-registry/enum-registry.loader.ts
9140
9778
  var import_node_fs8 = require("fs");
@@ -9182,7 +9820,7 @@ var injectEnumValues2 = /* @__PURE__ */ __name((columns, enums) => {
9182
9820
  }, "injectEnumValues");
9183
9821
 
9184
9822
  // src/lib/crud/interceptors/no-cache.interceptor.ts
9185
- var import_common25 = require("@nestjs/common");
9823
+ var import_common26 = require("@nestjs/common");
9186
9824
  function _ts_decorate9(decorators, target, key, desc2) {
9187
9825
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
9188
9826
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -9210,7 +9848,7 @@ var NoCacheInterceptor = class {
9210
9848
  }
9211
9849
  };
9212
9850
  NoCacheInterceptor = _ts_decorate9([
9213
- (0, import_common25.Injectable)()
9851
+ (0, import_common26.Injectable)()
9214
9852
  ], NoCacheInterceptor);
9215
9853
 
9216
9854
  // src/lib/crud/builder/schema.helpers.ts
@@ -9269,9 +9907,9 @@ var buildResourceDefinitions2 = /* @__PURE__ */ __name((schema, operations, enri
9269
9907
  }, "buildResourceDefinitions");
9270
9908
 
9271
9909
  // src/lib/crud/adapter/relation-type.ts
9272
- var import_zod54 = require("zod");
9910
+ var import_zod56 = require("zod");
9273
9911
  var unwrapZodType = /* @__PURE__ */ __name((type) => {
9274
- if (type instanceof import_zod54.ZodOptional || type instanceof import_zod54.ZodNullable) {
9912
+ if (type instanceof import_zod56.ZodOptional || type instanceof import_zod56.ZodNullable) {
9275
9913
  return unwrapZodType(type.unwrap());
9276
9914
  }
9277
9915
  return type;
@@ -9281,7 +9919,7 @@ var deriveRelationType = /* @__PURE__ */ __name((schema, columnId) => {
9281
9919
  const field = schema.shape[columnId];
9282
9920
  if (!field) return void 0;
9283
9921
  const inner = unwrapZodType(field);
9284
- return inner instanceof import_zod54.ZodArray ? "oneToMany" : "manyToOne";
9922
+ return inner instanceof import_zod56.ZodArray ? "oneToMany" : "manyToOne";
9285
9923
  }, "deriveRelationType");
9286
9924
  var deriveRelationTypeFromColumns = /* @__PURE__ */ __name((col, cols) => {
9287
9925
  const base = col.column ?? col.id;
@@ -9535,7 +10173,7 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
9535
10173
  const subResources = isCustom ? [] : buildSubResources(columns, json.route, json.model ?? "", dirPath, enums, baseUrl);
9536
10174
  const enrichedColumns = resolveColumnFieldVariants2(enrichResourceRefColumns(enrichActionColumns2(columns, json.route, subResources, baseUrl), dirPath, baseUrl) ?? columns);
9537
10175
  const calculatedColumns = json.calculatedColumns ?? [];
9538
- let views = buildViews(schema, enrichedColumns);
10176
+ let views = buildViews(schema, enrichedColumns, json.layout);
9539
10177
  if (views && calculatedColumns.length) {
9540
10178
  views = {
9541
10179
  ...views,
@@ -9717,6 +10355,20 @@ var validateResourceConfig = /* @__PURE__ */ __name((config, opts) => {
9717
10355
  warnings.push('repository.ts is present on a prisma resource and will be ignored \u2014 set kind: "custom" if you want to use it for data access.');
9718
10356
  }
9719
10357
  }
10358
+ const columns = config.columns ?? [];
10359
+ const columnIds = new Set(columns.map((c) => c.id));
10360
+ for (const col of columns) {
10361
+ const unique = normalizeUnique(col.unique);
10362
+ if (!unique) continue;
10363
+ if (col.idField) {
10364
+ warnings.push(`Column "${col.id}" is the id field; "unique" is redundant there.`);
10365
+ }
10366
+ for (const scopeId of unique.scope ?? []) {
10367
+ if (!columnIds.has(scopeId)) {
10368
+ errors.push(`Column "${col.id}" has unique.scope referencing unknown column "${scopeId}".`);
10369
+ }
10370
+ }
10371
+ }
9720
10372
  return {
9721
10373
  errors,
9722
10374
  warnings
@@ -10176,8 +10828,8 @@ var buildStatus = /* @__PURE__ */ __name(async (registry2, loadedConfigs, enumRe
10176
10828
  }, "buildStatus");
10177
10829
 
10178
10830
  // src/lib/crud/status/status.controller.ts
10179
- var import_common26 = require("@nestjs/common");
10180
- var import_swagger13 = require("@nestjs/swagger");
10831
+ var import_common27 = require("@nestjs/common");
10832
+ var import_swagger14 = require("@nestjs/swagger");
10181
10833
  function _ts_decorate10(decorators, target, key, desc2) {
10182
10834
  var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
10183
10835
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
@@ -10215,11 +10867,11 @@ var createStatusController = /* @__PURE__ */ __name((enumRegistry, translationRe
10215
10867
  }
10216
10868
  };
10217
10869
  _ts_decorate10([
10218
- (0, import_common26.Get)("status.json"),
10219
- (0, import_swagger13.ApiOperation)({
10870
+ (0, import_common27.Get)("status.json"),
10871
+ (0, import_swagger14.ApiOperation)({
10220
10872
  summary: "Crouton system status (db, resources, version)"
10221
10873
  }),
10222
- (0, import_swagger13.ApiResponse)({
10874
+ (0, import_swagger14.ApiResponse)({
10223
10875
  status: 200,
10224
10876
  description: "System status"
10225
10877
  }),
@@ -10228,8 +10880,8 @@ var createStatusController = /* @__PURE__ */ __name((enumRegistry, translationRe
10228
10880
  _ts_metadata9("design:returntype", Promise)
10229
10881
  ], StatusController.prototype, "getStatus", null);
10230
10882
  StatusController = _ts_decorate10([
10231
- (0, import_common26.Controller)(prefix ? `${prefix}/crouton` : "crouton"),
10232
- (0, import_swagger13.ApiTags)("Status"),
10883
+ (0, import_common27.Controller)(prefix ? `${prefix}/crouton` : "crouton"),
10884
+ (0, import_swagger14.ApiTags)("Status"),
10233
10885
  _ts_metadata9("design:type", Function),
10234
10886
  _ts_metadata9("design:paramtypes", [
10235
10887
  typeof DataSourceRegistry === "undefined" ? Object : DataSourceRegistry,
@@ -10344,6 +10996,7 @@ var CroutonApiModule = class _CroutonApiModule {
10344
10996
  }
10345
10997
  validConfigs.push(c);
10346
10998
  }
10999
+ warnMissingUniqueConstraints(validConfigs, dataSourceRegistry);
10347
11000
  const configRegistry = new ResourceConfigRegistry(loader, validConfigs);
10348
11001
  let translationRegistry;
10349
11002
  if (config.i18n) {
@@ -10421,7 +11074,7 @@ var CroutonApiModule = class _CroutonApiModule {
10421
11074
  }
10422
11075
  };
10423
11076
  CroutonApiModule = _ts_decorate11([
10424
- (0, import_common27.Module)({
11077
+ (0, import_common28.Module)({
10425
11078
  controllers: [],
10426
11079
  providers: [],
10427
11080
  exports: []