@ghentcdh/crouton-api 0.0.1-alpha.39 → 0.0.1-alpha.40
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 +1667 -589
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +586 -5
- package/dist/index.d.ts +586 -5
- package/dist/index.js +1554 -487
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,14 +34,25 @@ __export(index_exports, {
|
|
|
34
34
|
CroutonApiModule: () => CroutonApiModule,
|
|
35
35
|
DataSourceRegistry: () => DataSourceRegistry,
|
|
36
36
|
FileSystemResourceConfigLoader: () => FileSystemResourceConfigLoader,
|
|
37
|
+
ReadOpSchema: () => ReadOpSchema,
|
|
37
38
|
ResourceConfigLoader: () => ResourceConfigLoader2,
|
|
38
39
|
ResourceConfigRegistry: () => ResourceConfigRegistry,
|
|
40
|
+
ResourceHooksSchema: () => ResourceHooksSchema,
|
|
41
|
+
WriteOpSchema: () => WriteOpSchema,
|
|
42
|
+
buildFilterWhere: () => buildFilterWhere,
|
|
39
43
|
buildViews: () => buildViews,
|
|
44
|
+
decorateRow: () => decorateRow,
|
|
45
|
+
decorateRows: () => decorateRows,
|
|
40
46
|
isOperationEnabled: () => isOperationEnabled,
|
|
41
47
|
isRowProcedureAction: () => isRowProcedureAction,
|
|
42
48
|
isTableProcedureAction: () => isTableProcedureAction,
|
|
43
49
|
loadDataSourcesFromDir: () => loadDataSourcesFromDir,
|
|
44
50
|
loadResourceConfigsFromDir: () => loadResourceConfigsFromDir,
|
|
51
|
+
loadResourceHooks: () => loadResourceHooks,
|
|
52
|
+
loadSubResourceHooks: () => loadSubResourceHooks,
|
|
53
|
+
parseFilterString: () => parseFilterString,
|
|
54
|
+
postWrite: () => postWrite,
|
|
55
|
+
prepareWrite: () => prepareWrite,
|
|
45
56
|
resolveDefinition: () => resolveDefinition,
|
|
46
57
|
schemaFor: () => schemaFor,
|
|
47
58
|
upsertOnFor: () => upsertOnFor
|
|
@@ -86,6 +97,7 @@ var RequestSchemaWithOffset = RequestSchema.transform((schema) => {
|
|
|
86
97
|
offset: (page - 1) * pageSize
|
|
87
98
|
};
|
|
88
99
|
});
|
|
100
|
+
var offsetOf = /* @__PURE__ */ __name((params) => params.offset ?? (params.page - 1) * params.pageSize, "offsetOf");
|
|
89
101
|
|
|
90
102
|
// ../crouton-core/src/lib/filter.ts
|
|
91
103
|
var Operator = [
|
|
@@ -240,6 +252,7 @@ var ControlType = {
|
|
|
240
252
|
textArea: "textarea",
|
|
241
253
|
markdown: "markdown",
|
|
242
254
|
array: "array",
|
|
255
|
+
object: "object",
|
|
243
256
|
custom: "custom",
|
|
244
257
|
select: "select",
|
|
245
258
|
mutliSelect: "mutliSelect",
|
|
@@ -329,6 +342,16 @@ var ControlBuilder = class _ControlBuilder extends ElementBuilder {
|
|
|
329
342
|
...options ?? {}
|
|
330
343
|
});
|
|
331
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Render a datepicker. Defaults to a day-only picker; pass
|
|
347
|
+
* `{ format: 'dateTime' }` (or `{ withTime: true }`) to include a time.
|
|
348
|
+
*/
|
|
349
|
+
date(options) {
|
|
350
|
+
return this.opt({
|
|
351
|
+
format: ControlType.date,
|
|
352
|
+
...options ?? {}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
332
355
|
textArea(options) {
|
|
333
356
|
return this.opt({
|
|
334
357
|
format: ControlType.textArea,
|
|
@@ -617,7 +640,7 @@ var JsonOperationsSchema = import_zod8.z.object({
|
|
|
617
640
|
});
|
|
618
641
|
|
|
619
642
|
// ../crouton-core/src/lib/resource/ResourceJson.schema.ts
|
|
620
|
-
var
|
|
643
|
+
var import_zod18 = require("zod");
|
|
621
644
|
|
|
622
645
|
// ../crouton-core/src/lib/resource/CalculatedColumn.schema.ts
|
|
623
646
|
var import_zod10 = require("zod");
|
|
@@ -771,42 +794,197 @@ var CalculatedColumnSchema = import_zod10.z.object({
|
|
|
771
794
|
}).transform(normalizeLabel);
|
|
772
795
|
|
|
773
796
|
// ../crouton-core/src/lib/resource/Column.ts
|
|
797
|
+
var import_zod12 = require("zod");
|
|
798
|
+
|
|
799
|
+
// ../crouton-core/src/lib/resource/ColumnType.schema.ts
|
|
774
800
|
var import_zod11 = require("zod");
|
|
775
|
-
var
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
801
|
+
var ColumnTypeShorthandSchema = import_zod11.z.enum([
|
|
802
|
+
"string",
|
|
803
|
+
"number",
|
|
804
|
+
"integer",
|
|
805
|
+
"boolean",
|
|
806
|
+
"date",
|
|
807
|
+
"date-time",
|
|
808
|
+
"object",
|
|
809
|
+
"array"
|
|
810
|
+
]);
|
|
811
|
+
var JsonSchemaFragmentSchema = import_zod11.z.lazy(() => import_zod11.z.object({
|
|
812
|
+
type: import_zod11.z.union([
|
|
813
|
+
import_zod11.z.string(),
|
|
814
|
+
import_zod11.z.array(import_zod11.z.string())
|
|
815
|
+
]).optional(),
|
|
816
|
+
format: import_zod11.z.string().optional(),
|
|
817
|
+
enum: import_zod11.z.array(import_zod11.z.unknown()).optional(),
|
|
818
|
+
const: import_zod11.z.unknown().optional(),
|
|
819
|
+
nullable: import_zod11.z.boolean().optional(),
|
|
820
|
+
properties: import_zod11.z.record(import_zod11.z.string(), JsonSchemaFragmentSchema).optional(),
|
|
821
|
+
required: import_zod11.z.array(import_zod11.z.string()).optional(),
|
|
822
|
+
items: JsonSchemaFragmentSchema.optional(),
|
|
823
|
+
additionalProperties: import_zod11.z.union([
|
|
824
|
+
import_zod11.z.boolean(),
|
|
825
|
+
JsonSchemaFragmentSchema
|
|
826
|
+
]).optional(),
|
|
827
|
+
title: import_zod11.z.string().optional(),
|
|
828
|
+
description: import_zod11.z.string().optional(),
|
|
829
|
+
default: import_zod11.z.unknown().optional(),
|
|
830
|
+
minimum: import_zod11.z.number().optional(),
|
|
831
|
+
maximum: import_zod11.z.number().optional(),
|
|
832
|
+
minLength: import_zod11.z.number().optional(),
|
|
833
|
+
maxLength: import_zod11.z.number().optional(),
|
|
834
|
+
pattern: import_zod11.z.string().optional()
|
|
835
|
+
}).catchall(import_zod11.z.unknown()));
|
|
836
|
+
var ColumnTypeSchema = import_zod11.z.union([
|
|
837
|
+
ColumnTypeShorthandSchema,
|
|
838
|
+
JsonSchemaFragmentSchema
|
|
839
|
+
]);
|
|
840
|
+
var SHORTHAND_FRAGMENTS = {
|
|
841
|
+
string: {
|
|
842
|
+
type: "string"
|
|
843
|
+
},
|
|
844
|
+
number: {
|
|
845
|
+
type: "number"
|
|
846
|
+
},
|
|
847
|
+
integer: {
|
|
848
|
+
type: "integer"
|
|
849
|
+
},
|
|
850
|
+
boolean: {
|
|
851
|
+
type: "boolean"
|
|
852
|
+
},
|
|
853
|
+
date: {
|
|
854
|
+
type: "string",
|
|
855
|
+
format: "date"
|
|
856
|
+
},
|
|
857
|
+
"date-time": {
|
|
858
|
+
type: "string",
|
|
859
|
+
format: "date-time"
|
|
860
|
+
},
|
|
861
|
+
object: {
|
|
862
|
+
type: "object"
|
|
863
|
+
},
|
|
864
|
+
array: {
|
|
865
|
+
type: "array"
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
var isShorthand = /* @__PURE__ */ __name((type) => typeof type === "string", "isShorthand");
|
|
869
|
+
var NESTED_SHORTHANDS = {
|
|
870
|
+
date: SHORTHAND_FRAGMENTS.date,
|
|
871
|
+
"date-time": SHORTHAND_FRAGMENTS["date-time"]
|
|
872
|
+
};
|
|
873
|
+
var normalizeFragment = /* @__PURE__ */ __name((fragment) => {
|
|
874
|
+
const out = {
|
|
875
|
+
...fragment
|
|
876
|
+
};
|
|
877
|
+
if (typeof out.type === "string") {
|
|
878
|
+
const expanded = NESTED_SHORTHANDS[out.type];
|
|
879
|
+
if (expanded) {
|
|
880
|
+
out.type = expanded.type;
|
|
881
|
+
if (out.format === void 0) out.format = expanded.format;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (out.properties) {
|
|
885
|
+
out.properties = Object.fromEntries(Object.entries(out.properties).map(([key, value]) => [
|
|
886
|
+
key,
|
|
887
|
+
normalizeFragment(value)
|
|
888
|
+
]));
|
|
889
|
+
}
|
|
890
|
+
if (out.items) out.items = normalizeFragment(out.items);
|
|
891
|
+
if (out.additionalProperties && typeof out.additionalProperties === "object") {
|
|
892
|
+
out.additionalProperties = normalizeFragment(out.additionalProperties);
|
|
893
|
+
}
|
|
894
|
+
return out;
|
|
895
|
+
}, "normalizeFragment");
|
|
896
|
+
var columnTypeToJsonSchema = /* @__PURE__ */ __name((type) => {
|
|
897
|
+
if (type === void 0) return {
|
|
898
|
+
type: "string"
|
|
899
|
+
};
|
|
900
|
+
if (isShorthand(type)) return {
|
|
901
|
+
...SHORTHAND_FRAGMENTS[type]
|
|
902
|
+
};
|
|
903
|
+
return normalizeFragment(type);
|
|
904
|
+
}, "columnTypeToJsonSchema");
|
|
905
|
+
var columnTypeName = /* @__PURE__ */ __name((type) => {
|
|
906
|
+
const fragment = columnTypeToJsonSchema(type);
|
|
907
|
+
const raw = fragment.type;
|
|
908
|
+
if (Array.isArray(raw)) return raw.find((t) => t !== "null") ?? "string";
|
|
909
|
+
if (typeof raw === "string") return raw;
|
|
910
|
+
if (fragment.properties) return "object";
|
|
911
|
+
if (fragment.items) return "array";
|
|
912
|
+
return "string";
|
|
913
|
+
}, "columnTypeName");
|
|
914
|
+
var isObjectColumnType = /* @__PURE__ */ __name((type) => columnTypeName(type) === "object", "isObjectColumnType");
|
|
915
|
+
var isArrayColumnType = /* @__PURE__ */ __name((type) => columnTypeName(type) === "array", "isArrayColumnType");
|
|
916
|
+
|
|
917
|
+
// ../crouton-core/src/lib/resource/Column.ts
|
|
918
|
+
var WhenConditionSchema = import_zod12.z.object({
|
|
919
|
+
field: import_zod12.z.string(),
|
|
920
|
+
eq: import_zod12.z.unknown().optional(),
|
|
921
|
+
neq: import_zod12.z.unknown().optional(),
|
|
922
|
+
exists: import_zod12.z.boolean().optional(),
|
|
923
|
+
notExists: import_zod12.z.boolean().optional()
|
|
781
924
|
});
|
|
782
|
-
var JsonColumnSchema =
|
|
783
|
-
id:
|
|
784
|
-
column:
|
|
785
|
-
label:
|
|
786
|
-
hiddenInTable:
|
|
787
|
-
hiddenInForm:
|
|
788
|
-
hiddenInView:
|
|
789
|
-
sortable:
|
|
790
|
-
defaultSort:
|
|
791
|
-
searchable:
|
|
792
|
-
filterable:
|
|
793
|
-
createable:
|
|
794
|
-
updateable:
|
|
795
|
-
hideLabel:
|
|
925
|
+
var JsonColumnSchema = import_zod12.z.object({
|
|
926
|
+
id: import_zod12.z.string(),
|
|
927
|
+
column: import_zod12.z.string().optional(),
|
|
928
|
+
label: import_zod12.z.string().optional(),
|
|
929
|
+
hiddenInTable: import_zod12.z.boolean().default(false),
|
|
930
|
+
hiddenInForm: import_zod12.z.boolean().default(false),
|
|
931
|
+
hiddenInView: import_zod12.z.boolean().default(false),
|
|
932
|
+
sortable: import_zod12.z.boolean().default(false),
|
|
933
|
+
defaultSort: import_zod12.z.boolean().default(false),
|
|
934
|
+
searchable: import_zod12.z.boolean().default(false),
|
|
935
|
+
filterable: import_zod12.z.boolean().default(false),
|
|
936
|
+
createable: import_zod12.z.boolean().default(true),
|
|
937
|
+
updateable: import_zod12.z.boolean().default(true),
|
|
938
|
+
hideLabel: import_zod12.z.boolean().default(false),
|
|
796
939
|
showWhen: WhenConditionSchema.optional(),
|
|
797
940
|
hideWhen: WhenConditionSchema.optional(),
|
|
798
941
|
disabledWhen: WhenConditionSchema.optional(),
|
|
799
|
-
displayKey:
|
|
800
|
-
sortId:
|
|
942
|
+
displayKey: import_zod12.z.string().optional(),
|
|
943
|
+
sortId: import_zod12.z.string().optional(),
|
|
801
944
|
/**
|
|
802
945
|
* Name of a shared enum in the project enum registry (`crouton.enums.json`).
|
|
803
946
|
* At load time the loader injects that enum's `{ value, label }[]` into
|
|
804
947
|
* `fieldInput.options.values`, so columns don't duplicate the option list.
|
|
805
948
|
*/
|
|
806
|
-
enum:
|
|
807
|
-
idField:
|
|
808
|
-
showInLookup:
|
|
809
|
-
|
|
949
|
+
enum: import_zod12.z.string().optional(),
|
|
950
|
+
idField: import_zod12.z.boolean().default(false),
|
|
951
|
+
showInLookup: import_zod12.z.boolean().default(false),
|
|
952
|
+
/**
|
|
953
|
+
* Data type of the column, as a shorthand name (`"string"`, `"integer"`, …)
|
|
954
|
+
* or a full JSON Schema fragment:
|
|
955
|
+
*
|
|
956
|
+
* ```json
|
|
957
|
+
* "type": {
|
|
958
|
+
* "type": "object",
|
|
959
|
+
* "properties": { "id": { "type": "string" }, "name": { "type": "string" } }
|
|
960
|
+
* }
|
|
961
|
+
* ```
|
|
962
|
+
*
|
|
963
|
+
* Required on every column of a `kind: "custom"` resource, where it is the
|
|
964
|
+
* only source of the resource's json_schema. Optional on a `prisma`
|
|
965
|
+
* resource, whose schema is derived from the Zod model.
|
|
966
|
+
*/
|
|
967
|
+
type: ColumnTypeSchema.optional(),
|
|
968
|
+
/**
|
|
969
|
+
* Whether the form requires a value.
|
|
970
|
+
*
|
|
971
|
+
* A prisma resource derives `required` from its Zod model, so this is an
|
|
972
|
+
* override in either direction: `true` adds the column to the form schema's
|
|
973
|
+
* `required` array, `false` removes it, and omitting it leaves the model's
|
|
974
|
+
* answer alone. A `kind: "custom"` resource has no model, so this is the only
|
|
975
|
+
* way to mark one of its fields required.
|
|
976
|
+
*
|
|
977
|
+
* Applies to the **form** view only — a required filter input would make the
|
|
978
|
+
* filter panel unsubmittable, and the table and view schemas are read-only.
|
|
979
|
+
* Ignored on the id column and on columns that are neither createable nor
|
|
980
|
+
* updateable, since the form cannot supply a value for those.
|
|
981
|
+
*/
|
|
982
|
+
required: import_zod12.z.boolean().optional(),
|
|
983
|
+
/**
|
|
984
|
+
* @deprecated Use `type` instead. Retained because it is still consumed by
|
|
985
|
+
* the boolean predicate and the schema-less sub-resource view builder.
|
|
986
|
+
*/
|
|
987
|
+
columnType: import_zod12.z.string().default("string"),
|
|
810
988
|
fieldInput: FieldInputSchema.optional(),
|
|
811
989
|
/**
|
|
812
990
|
* Optional per-context override for the read-only VIEW ui. Same shape as
|
|
@@ -830,40 +1008,65 @@ var JsonColumnSchema = import_zod11.z.object({
|
|
|
830
1008
|
* Visibility (`hiddenInTable/Form/View`) on this column is inherited by all sub-columns as a
|
|
831
1009
|
* default; the referenced resource's own column visibility further restricts it.
|
|
832
1010
|
*/
|
|
833
|
-
extend:
|
|
1011
|
+
extend: import_zod12.z.string().optional(),
|
|
834
1012
|
/**
|
|
835
1013
|
* Per-sub-column overrides when using `extend`.
|
|
836
1014
|
* Key: the virtual column id (`"{extendId}_{refColId}"`) or just the referenced column id.
|
|
837
1015
|
* Value: any `JsonColumn` fields to merge over the generated virtual column.
|
|
838
1016
|
*/
|
|
839
|
-
columns:
|
|
1017
|
+
columns: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.record(import_zod12.z.string(), import_zod12.z.unknown())).optional()
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
// ../crouton-core/src/lib/resource/ParentRef.schema.ts
|
|
1021
|
+
var import_zod13 = require("zod");
|
|
1022
|
+
var ParentRefSchema = import_zod13.z.object({
|
|
1023
|
+
/** Route segment of the parent, e.g. `"group"`. */
|
|
1024
|
+
route: import_zod13.z.string(),
|
|
1025
|
+
/**
|
|
1026
|
+
* Name of the path parameter carrying the parent id, e.g. `"groupId"`.
|
|
1027
|
+
* Avoid `"id"` — that is the child's own id in `/:id` routes.
|
|
1028
|
+
*/
|
|
1029
|
+
param: import_zod13.z.string().default("parentId"),
|
|
1030
|
+
/** Type of the parent id, used to coerce the path param. Defaults to `'string'`. */
|
|
1031
|
+
idType: import_zod13.z.enum([
|
|
1032
|
+
"string",
|
|
1033
|
+
"number"
|
|
1034
|
+
]).optional()
|
|
840
1035
|
});
|
|
1036
|
+
var resourceControllerPath = /* @__PURE__ */ __name((route, parent) => parent ? `${parent.route}/:${parent.param}/${route}` : route, "resourceControllerPath");
|
|
1037
|
+
|
|
1038
|
+
// ../crouton-core/src/lib/resource/ResourceKind.ts
|
|
1039
|
+
var import_zod14 = require("zod");
|
|
1040
|
+
var ResourceKindSchema = import_zod14.z.enum([
|
|
1041
|
+
"prisma",
|
|
1042
|
+
"custom"
|
|
1043
|
+
]).default("prisma");
|
|
841
1044
|
|
|
842
1045
|
// ../crouton-core/src/lib/resource/Sidebar.schema.ts
|
|
843
|
-
var
|
|
844
|
-
var SidebarGroupSchema =
|
|
1046
|
+
var import_zod15 = __toESM(require("zod"), 1);
|
|
1047
|
+
var SidebarGroupSchema = import_zod15.default.object({
|
|
845
1048
|
/** Human-readable heading shown in the sidebar. Defaults to a title-cased version of the slug. */
|
|
846
|
-
label:
|
|
1049
|
+
label: import_zod15.default.string().optional(),
|
|
847
1050
|
/** Controls the order of this group among top-level sidebar items. */
|
|
848
|
-
position:
|
|
1051
|
+
position: import_zod15.default.number().optional()
|
|
849
1052
|
});
|
|
850
|
-
var SidebarSchema =
|
|
851
|
-
hide:
|
|
852
|
-
position:
|
|
853
|
-
label:
|
|
1053
|
+
var SidebarSchema = import_zod15.default.object({
|
|
1054
|
+
hide: import_zod15.default.boolean().default(false),
|
|
1055
|
+
position: import_zod15.default.number().optional(),
|
|
1056
|
+
label: import_zod15.default.string().optional(),
|
|
854
1057
|
/**
|
|
855
1058
|
* Slug of the group this resource belongs to.
|
|
856
1059
|
* Must match a key in `sidebarGroups` in `crouton.json`.
|
|
857
1060
|
* Resources with the same `group` are nested under a shared collapsible section.
|
|
858
1061
|
*/
|
|
859
|
-
group:
|
|
1062
|
+
group: import_zod15.default.string().optional()
|
|
860
1063
|
});
|
|
861
1064
|
|
|
862
1065
|
// ../crouton-core/src/lib/resource/TableAction.schema.ts
|
|
863
|
-
var
|
|
864
|
-
var ActionConditionSchema =
|
|
1066
|
+
var import_zod16 = require("zod");
|
|
1067
|
+
var ActionConditionSchema = import_zod16.z.object({
|
|
865
1068
|
/** Row field to evaluate. */
|
|
866
|
-
field:
|
|
1069
|
+
field: import_zod16.z.string(),
|
|
867
1070
|
/**
|
|
868
1071
|
* Comparison operator. Defaults to `"eq"`.
|
|
869
1072
|
* - `eq` / `neq` — strict equality
|
|
@@ -871,7 +1074,7 @@ var ActionConditionSchema = import_zod13.z.object({
|
|
|
871
1074
|
* - `exists` — field is not null / undefined / empty string
|
|
872
1075
|
* - `notExists` — field is null / undefined / empty string
|
|
873
1076
|
*/
|
|
874
|
-
op:
|
|
1077
|
+
op: import_zod16.z.enum([
|
|
875
1078
|
"eq",
|
|
876
1079
|
"neq",
|
|
877
1080
|
"gt",
|
|
@@ -882,38 +1085,38 @@ var ActionConditionSchema = import_zod13.z.object({
|
|
|
882
1085
|
"notExists"
|
|
883
1086
|
]).default("eq"),
|
|
884
1087
|
/** Comparison value. Not required for `exists` / `notExists`. */
|
|
885
|
-
value:
|
|
1088
|
+
value: import_zod16.z.unknown().optional()
|
|
886
1089
|
});
|
|
887
|
-
var ActionSchema =
|
|
888
|
-
type:
|
|
1090
|
+
var ActionSchema = import_zod16.z.object({
|
|
1091
|
+
type: import_zod16.z.string(),
|
|
889
1092
|
/** Unique identifier for the action. */
|
|
890
|
-
id:
|
|
1093
|
+
id: import_zod16.z.string(),
|
|
891
1094
|
/** Human-readable button label. */
|
|
892
|
-
label:
|
|
1095
|
+
label: import_zod16.z.string(),
|
|
893
1096
|
/** MDI icon name, e.g. `"mdi:open-in-new"`. */
|
|
894
|
-
icon:
|
|
1097
|
+
icon: import_zod16.z.string().optional(),
|
|
895
1098
|
/** Tooltip text. Falls back to `label` when omitted. */
|
|
896
|
-
tooltip:
|
|
1099
|
+
tooltip: import_zod16.z.string().optional(),
|
|
897
1100
|
condition: ActionConditionSchema.optional()
|
|
898
1101
|
});
|
|
899
1102
|
var JsonProcedureActionSchema = ActionSchema.extend({
|
|
900
|
-
type:
|
|
1103
|
+
type: import_zod16.z.literal("procedure").default("procedure"),
|
|
901
1104
|
/**
|
|
902
1105
|
* Filename (without extension) inside the resource's `actions/` directory
|
|
903
1106
|
* that exports the table-action procedure, e.g. `"syncZotero"`.
|
|
904
1107
|
*/
|
|
905
|
-
procedure:
|
|
1108
|
+
procedure: import_zod16.z.string(),
|
|
906
1109
|
/** HTTP method for the frontend. Defaults to `"post"`. */
|
|
907
|
-
method:
|
|
1110
|
+
method: import_zod16.z.string().optional().default("post"),
|
|
908
1111
|
/** Static query / body params passed to the endpoint. */
|
|
909
|
-
data:
|
|
1112
|
+
data: import_zod16.z.record(import_zod16.z.string(), import_zod16.z.unknown()).optional()
|
|
910
1113
|
});
|
|
911
1114
|
var JsonLinkActionSchema = ActionSchema.extend({
|
|
912
1115
|
/** Optional condition evaluated per row. Button is hidden when the condition is false. */
|
|
913
|
-
type:
|
|
1116
|
+
type: import_zod16.z.literal("link").default("link"),
|
|
914
1117
|
/** URL to open. May contain `{env.VAR}` placeholders. */
|
|
915
|
-
href:
|
|
916
|
-
blank:
|
|
1118
|
+
href: import_zod16.z.string(),
|
|
1119
|
+
blank: import_zod16.z.boolean().optional().default(true)
|
|
917
1120
|
});
|
|
918
1121
|
var transformAction = /* @__PURE__ */ __name((action) => {
|
|
919
1122
|
const label = action.label;
|
|
@@ -922,20 +1125,20 @@ var transformAction = /* @__PURE__ */ __name((action) => {
|
|
|
922
1125
|
...action
|
|
923
1126
|
};
|
|
924
1127
|
}, "transformAction");
|
|
925
|
-
var JsonActionSchema =
|
|
1128
|
+
var JsonActionSchema = import_zod16.z.union([
|
|
926
1129
|
JsonProcedureActionSchema,
|
|
927
1130
|
JsonLinkActionSchema
|
|
928
1131
|
]).transform(transformAction);
|
|
929
1132
|
|
|
930
1133
|
// ../crouton-core/src/lib/resource/include.schema.ts
|
|
931
|
-
var
|
|
932
|
-
var JsonIncludeEntrySchema =
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
relation:
|
|
936
|
-
include:
|
|
1134
|
+
var import_zod17 = require("zod");
|
|
1135
|
+
var JsonIncludeEntrySchema = import_zod17.z.lazy(() => import_zod17.z.union([
|
|
1136
|
+
import_zod17.z.string(),
|
|
1137
|
+
import_zod17.z.object({
|
|
1138
|
+
relation: import_zod17.z.string(),
|
|
1139
|
+
include: import_zod17.z.array(JsonIncludeEntrySchema).optional(),
|
|
937
1140
|
/** Prisma-format orderBy clause applied to the included records. */
|
|
938
|
-
orderBy:
|
|
1141
|
+
orderBy: import_zod17.z.record(import_zod17.z.string(), import_zod17.z.unknown()).optional()
|
|
939
1142
|
})
|
|
940
1143
|
]));
|
|
941
1144
|
|
|
@@ -944,16 +1147,16 @@ var BASELINE_RESOURCE_VERSION = 1;
|
|
|
944
1147
|
var CURRENT_RESOURCE_VERSION = 1;
|
|
945
1148
|
|
|
946
1149
|
// ../crouton-core/src/lib/resource/ResourceJson.schema.ts
|
|
947
|
-
var JsonColumnsMapSchema =
|
|
1150
|
+
var JsonColumnsMapSchema = import_zod18.z.record(import_zod18.z.string(), JsonColumnSchema.omit({
|
|
948
1151
|
id: true
|
|
949
|
-
}).catchall(
|
|
1152
|
+
}).catchall(import_zod18.z.unknown()));
|
|
950
1153
|
var ColumnsSchema = JsonColumnsMapSchema;
|
|
951
|
-
var JsonDisplaySchema =
|
|
952
|
-
mode:
|
|
1154
|
+
var JsonDisplaySchema = import_zod18.z.object({
|
|
1155
|
+
mode: import_zod18.z.enum([
|
|
953
1156
|
"page",
|
|
954
1157
|
"modal"
|
|
955
1158
|
]).default("modal"),
|
|
956
|
-
customComponent:
|
|
1159
|
+
customComponent: import_zod18.z.string().nullable().optional().default(null)
|
|
957
1160
|
});
|
|
958
1161
|
var normalizeColumns = /* @__PURE__ */ __name((columns) => {
|
|
959
1162
|
if (!columns) return void 0;
|
|
@@ -966,37 +1169,61 @@ var normalizeColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
966
1169
|
label: col.label ?? labelFromId(col.id)
|
|
967
1170
|
}));
|
|
968
1171
|
}, "normalizeColumns");
|
|
969
|
-
var ResourceJsonShape =
|
|
1172
|
+
var ResourceJsonShape = import_zod18.z.object({
|
|
970
1173
|
/**
|
|
971
1174
|
* URL of the generated JSON Schema, for editor autocomplete/validation. Declared so the
|
|
972
1175
|
* key is *allowed* (not stripped, and not flagged by the very schema it points at).
|
|
973
1176
|
* Ignored at runtime.
|
|
974
1177
|
*/
|
|
975
|
-
$schema:
|
|
1178
|
+
$schema: import_zod18.z.string().optional(),
|
|
976
1179
|
/**
|
|
977
1180
|
* resource.json shape version. Missing ⇒ baseline (see `./version`). Auto-migrated
|
|
978
1181
|
* toward `CURRENT_RESOURCE_VERSION` on load in the dev environment.
|
|
979
1182
|
*/
|
|
980
|
-
schemaVersion:
|
|
1183
|
+
schemaVersion: import_zod18.z.number().int().positive().optional(),
|
|
981
1184
|
/** When `true`, the resource lives in the repo but is NOT loaded/served (work in progress). */
|
|
982
|
-
draft:
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
model
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1185
|
+
draft: import_zod18.z.boolean().optional().default(false),
|
|
1186
|
+
/**
|
|
1187
|
+
* Where the data comes from. `prisma` (the default) is backed by a Prisma
|
|
1188
|
+
* model plus a `schema.ts`; `custom` is configuration only and the developer
|
|
1189
|
+
* supplies a `repository.ts`. See `./ResourceKind`.
|
|
1190
|
+
*/
|
|
1191
|
+
kind: ResourceKindSchema,
|
|
1192
|
+
name: import_zod18.z.string(),
|
|
1193
|
+
route: import_zod18.z.string(),
|
|
1194
|
+
/**
|
|
1195
|
+
* Prisma model name. Required when `kind` is `prisma` (enforced by the
|
|
1196
|
+
* refinement on `ResourceJsonSchema`), and must be absent when `kind` is
|
|
1197
|
+
* `custom` — there is no Prisma delegate to address.
|
|
1198
|
+
*/
|
|
1199
|
+
model: import_zod18.z.string().optional(),
|
|
1200
|
+
tag: import_zod18.z.string(),
|
|
1201
|
+
title: import_zod18.z.string().optional(),
|
|
1202
|
+
table: import_zod18.z.string().optional(),
|
|
1203
|
+
/**
|
|
1204
|
+
* Type of the resource's primary key, used to coerce `:id` route params.
|
|
1205
|
+
* Written by codegen from the Prisma model; defaults to `'string'`.
|
|
1206
|
+
*/
|
|
1207
|
+
idType: import_zod18.z.enum([
|
|
1208
|
+
"string",
|
|
1209
|
+
"number"
|
|
1210
|
+
]).optional(),
|
|
1211
|
+
database: import_zod18.z.string().optional(),
|
|
1212
|
+
/**
|
|
1213
|
+
* Mount this resource under a parent route instead of at the top level —
|
|
1214
|
+
* see `./ParentRef.schema`. Only valid on a `kind: "custom"` resource.
|
|
1215
|
+
*/
|
|
1216
|
+
parent: ParentRefSchema.optional(),
|
|
990
1217
|
sidebar: SidebarSchema.default(SidebarSchema.parse({})),
|
|
991
1218
|
display: JsonDisplaySchema.default(JsonDisplaySchema.parse({})),
|
|
992
1219
|
operations: JsonOperationsSchema,
|
|
993
1220
|
columns: ColumnsSchema.default(ColumnsSchema.parse({})),
|
|
994
|
-
calculatedColumns:
|
|
995
|
-
actions:
|
|
1221
|
+
calculatedColumns: import_zod18.z.array(CalculatedColumnSchema).default([]),
|
|
1222
|
+
actions: import_zod18.z.array(JsonActionSchema).default([]),
|
|
996
1223
|
/** Global table-level actions (no record id). Shown as toolbar buttons. */
|
|
997
|
-
tableActions:
|
|
1224
|
+
tableActions: import_zod18.z.array(JsonActionSchema).default([]),
|
|
998
1225
|
/** Modal width when opening a form for this resource. */
|
|
999
|
-
modalSize:
|
|
1226
|
+
modalSize: import_zod18.z.enum([
|
|
1000
1227
|
"xs",
|
|
1001
1228
|
"sm",
|
|
1002
1229
|
"lg",
|
|
@@ -1009,9 +1236,73 @@ var ResourceJsonShape = import_zod15.z.object({
|
|
|
1009
1236
|
* `{ "relation": "text_author", "include": ["author"] }` →
|
|
1010
1237
|
* `include: { text_author: { include: { author: true } } }`
|
|
1011
1238
|
*/
|
|
1012
|
-
include:
|
|
1239
|
+
include: import_zod18.z.array(JsonIncludeEntrySchema).default([])
|
|
1013
1240
|
});
|
|
1014
|
-
var
|
|
1241
|
+
var refineByKind = /* @__PURE__ */ __name((obj, ctx) => {
|
|
1242
|
+
if (obj.kind === "custom") {
|
|
1243
|
+
if (obj.model !== void 0) {
|
|
1244
|
+
ctx.addIssue({
|
|
1245
|
+
code: "custom",
|
|
1246
|
+
path: [
|
|
1247
|
+
"model"
|
|
1248
|
+
],
|
|
1249
|
+
message: 'A custom resource has no Prisma model. Remove "model" \u2014 data access comes from repository.ts.'
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
if (obj.calculatedColumns?.length) {
|
|
1253
|
+
ctx.addIssue({
|
|
1254
|
+
code: "custom",
|
|
1255
|
+
path: [
|
|
1256
|
+
"calculatedColumns"
|
|
1257
|
+
],
|
|
1258
|
+
message: "calculatedColumns run raw SQL against a database table and are not supported on a custom resource. Compute the value in repository.ts instead."
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
for (const [id, col] of Object.entries(obj.columns ?? {})) {
|
|
1262
|
+
if (col.fieldInput?.format === "relation" || col.fieldInput?.type === "autocomplete") {
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
if (col.type === void 0) {
|
|
1266
|
+
ctx.addIssue({
|
|
1267
|
+
code: "custom",
|
|
1268
|
+
path: [
|
|
1269
|
+
"columns",
|
|
1270
|
+
id,
|
|
1271
|
+
"type"
|
|
1272
|
+
],
|
|
1273
|
+
message: `Column "${id}" needs a "type": a custom resource has no schema.ts, so its json_schema is built from the column types.`
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
if (obj.parent && obj.parent.param === "id") {
|
|
1278
|
+
ctx.addIssue({
|
|
1279
|
+
code: "custom",
|
|
1280
|
+
path: [
|
|
1281
|
+
"parent",
|
|
1282
|
+
"param"
|
|
1283
|
+
],
|
|
1284
|
+
message: `parent.param cannot be "id" \u2014 that is the child's own id in /:id routes. Use something like "groupId".`
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
} else if (obj.parent !== void 0) {
|
|
1288
|
+
ctx.addIssue({
|
|
1289
|
+
code: "custom",
|
|
1290
|
+
path: [
|
|
1291
|
+
"parent"
|
|
1292
|
+
],
|
|
1293
|
+
message: '"parent" is only supported on a custom resource. A prisma resource is nested by declaring a relation column on its parent.'
|
|
1294
|
+
});
|
|
1295
|
+
} else if (obj.model === void 0) {
|
|
1296
|
+
ctx.addIssue({
|
|
1297
|
+
code: "custom",
|
|
1298
|
+
path: [
|
|
1299
|
+
"model"
|
|
1300
|
+
],
|
|
1301
|
+
message: '"model" is required for a prisma-backed resource. Set "kind": "custom" for a resource with no Prisma model.'
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
}, "refineByKind");
|
|
1305
|
+
var ResourceJsonSchema = ResourceJsonShape.superRefine(refineByKind).transform((obj) => {
|
|
1015
1306
|
const title = obj.title ?? labelFromId(obj.name);
|
|
1016
1307
|
const schemaVersion = obj.schemaVersion ?? BASELINE_RESOURCE_VERSION;
|
|
1017
1308
|
return {
|
|
@@ -1090,50 +1381,50 @@ var resolveViewField = /* @__PURE__ */ __name((c) => mergeFieldVariant(c.fieldIn
|
|
|
1090
1381
|
var resolveTableField = /* @__PURE__ */ __name((c) => mergeFieldVariant(resolveViewField(c), c.fieldTable), "resolveTableField");
|
|
1091
1382
|
|
|
1092
1383
|
// ../crouton-core/src/lib/config/CroutonConfig.schema.ts
|
|
1093
|
-
var
|
|
1094
|
-
var RulesetSchema =
|
|
1095
|
-
hideIdInTable:
|
|
1096
|
-
hideIdInForm:
|
|
1097
|
-
hideIdInView:
|
|
1098
|
-
hideTimestamps:
|
|
1099
|
-
hideForeignKeys:
|
|
1100
|
-
includeRelations:
|
|
1101
|
-
hideRelationsInTable:
|
|
1102
|
-
showRelationsInForm:
|
|
1103
|
-
enumValueLabel:
|
|
1104
|
-
sharedEnums:
|
|
1384
|
+
var import_zod19 = require("zod");
|
|
1385
|
+
var RulesetSchema = import_zod19.z.object({
|
|
1386
|
+
hideIdInTable: import_zod19.z.boolean().default(true),
|
|
1387
|
+
hideIdInForm: import_zod19.z.boolean().default(true),
|
|
1388
|
+
hideIdInView: import_zod19.z.boolean().default(true),
|
|
1389
|
+
hideTimestamps: import_zod19.z.boolean().default(true),
|
|
1390
|
+
hideForeignKeys: import_zod19.z.boolean().default(true),
|
|
1391
|
+
includeRelations: import_zod19.z.boolean().default(true),
|
|
1392
|
+
hideRelationsInTable: import_zod19.z.boolean().default(true),
|
|
1393
|
+
showRelationsInForm: import_zod19.z.boolean().default(true),
|
|
1394
|
+
enumValueLabel: import_zod19.z.boolean().default(true),
|
|
1395
|
+
sharedEnums: import_zod19.z.boolean().default(true),
|
|
1105
1396
|
defaultOperations: JsonOperationsSchema.default(JsonOperationsSchema.parse({}))
|
|
1106
1397
|
});
|
|
1107
|
-
var CroutonConfigSchema =
|
|
1398
|
+
var CroutonConfigSchema = import_zod19.z.object({
|
|
1108
1399
|
/**
|
|
1109
1400
|
* Application title served to the frontend via `GET /_app/layout`.
|
|
1110
1401
|
* Displayed in the admin sidebar header.
|
|
1111
1402
|
*/
|
|
1112
|
-
title:
|
|
1403
|
+
title: import_zod19.z.string(),
|
|
1113
1404
|
/** Where resource directories live, relative to the project root. */
|
|
1114
|
-
resourcesDir:
|
|
1405
|
+
resourcesDir: import_zod19.z.string(),
|
|
1115
1406
|
/** Where datasource folders live (each with a `data-source.json`). */
|
|
1116
|
-
dataSourcesDir:
|
|
1407
|
+
dataSourcesDir: import_zod19.z.string(),
|
|
1117
1408
|
/**
|
|
1118
1409
|
* Template for a model's Zod export name. `{Model}` → Prisma model name.
|
|
1119
1410
|
* Defaults to `{Model}WithRelationsSchema` (the relations-aware schema
|
|
1120
1411
|
* emitted by zod-prisma-types when `createRelationValuesTypes` is on).
|
|
1121
1412
|
*/
|
|
1122
|
-
schemaExportName:
|
|
1413
|
+
schemaExportName: import_zod19.z.string().default("{Model}WithRelationsSchema"),
|
|
1123
1414
|
/** Path to the shared enum registry, relative to project root. Default `crouton.enums.json`. */
|
|
1124
|
-
enumsFile:
|
|
1415
|
+
enumsFile: import_zod19.z.string().default("crouton.enums.json"),
|
|
1125
1416
|
/** Optional overrides of the default visibility ruleset. */
|
|
1126
1417
|
rules: RulesetSchema.default(RulesetSchema.parse({})),
|
|
1127
1418
|
/**
|
|
1128
1419
|
* Sidebar group definitions, keyed by group slug (e.g. `"metadata"`).
|
|
1129
1420
|
* Resources reference a group via `sidebar.group` in their `resource.json`.
|
|
1130
1421
|
*/
|
|
1131
|
-
sidebarGroups:
|
|
1422
|
+
sidebarGroups: import_zod19.z.record(import_zod19.z.string(), SidebarGroupSchema).default({}),
|
|
1132
1423
|
/**
|
|
1133
1424
|
* Whether form fields are saved automatically as the user edits them.
|
|
1134
1425
|
* @default true
|
|
1135
1426
|
*/
|
|
1136
|
-
autoSave:
|
|
1427
|
+
autoSave: import_zod19.z.boolean().default(true)
|
|
1137
1428
|
});
|
|
1138
1429
|
|
|
1139
1430
|
// ../crouton-core/src/lib/config/readConfig.ts
|
|
@@ -1149,6 +1440,9 @@ var isRelation = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "rel
|
|
|
1149
1440
|
var isAutocomplete = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "autocomplete", "isAutocomplete");
|
|
1150
1441
|
var isRecordCell = /* @__PURE__ */ __name((col) => isRelation(col) || isAutocomplete(col), "isRecordCell");
|
|
1151
1442
|
var isDateRange = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "date-range", "isDateRange");
|
|
1443
|
+
var isObjectColumn = /* @__PURE__ */ __name((col) => isObjectColumnType(col.type), "isObjectColumn");
|
|
1444
|
+
var isArrayColumn = /* @__PURE__ */ __name((col) => isArrayColumnType(col.type), "isArrayColumn");
|
|
1445
|
+
var isObjectCell = /* @__PURE__ */ __name((col) => isObjectColumn(col) && !!col.displayKey, "isObjectCell");
|
|
1152
1446
|
|
|
1153
1447
|
// ../crouton-core/src/lib/view/column.utils.ts
|
|
1154
1448
|
var colPosition = /* @__PURE__ */ __name((col, i) => col.fieldInput?.position ?? i, "colPosition");
|
|
@@ -1290,7 +1584,7 @@ var buildTableUiSchema = /* @__PURE__ */ __name((cols) => {
|
|
|
1290
1584
|
const id = el.scope?.replace("#/properties/", "");
|
|
1291
1585
|
const col = id ? colMap[id] : void 0;
|
|
1292
1586
|
if (!col) return el;
|
|
1293
|
-
const fieldInputOptions = isRecordCell(col) || isDateRange(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
|
|
1587
|
+
const fieldInputOptions = isRecordCell(col) || isDateRange(col) || isObjectCell(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
|
|
1294
1588
|
const dataPathOption = col.column ? {
|
|
1295
1589
|
dataPath: col.column
|
|
1296
1590
|
} : {};
|
|
@@ -1316,7 +1610,9 @@ var buildTableUiSchema = /* @__PURE__ */ __name((cols) => {
|
|
|
1316
1610
|
},
|
|
1317
1611
|
label: col.label
|
|
1318
1612
|
},
|
|
1319
|
-
|
|
1613
|
+
// An object column with a displayKey renders the same way as a relation:
|
|
1614
|
+
// one nested key out of an object value.
|
|
1615
|
+
...(isRecordCell(col) || isObjectCell(col)) && {
|
|
1320
1616
|
type: "RecordCell"
|
|
1321
1617
|
},
|
|
1322
1618
|
...isDateRange(col) && {
|
|
@@ -1374,6 +1670,11 @@ var buildDetailLayout = /* @__PURE__ */ __name((detail) => {
|
|
|
1374
1670
|
if (detail.titleKey) inner.titleKey(detail.titleKey);
|
|
1375
1671
|
return inner;
|
|
1376
1672
|
}, "buildDetailLayout");
|
|
1673
|
+
var defaultControlFormat = /* @__PURE__ */ __name((col) => {
|
|
1674
|
+
if (isObjectColumn(col)) return "object";
|
|
1675
|
+
if (isArrayColumn(col)) return "array";
|
|
1676
|
+
return "text";
|
|
1677
|
+
}, "defaultControlFormat");
|
|
1377
1678
|
var buildFormControl = /* @__PURE__ */ __name((col) => {
|
|
1378
1679
|
const control = ControlBuilder.properties(col.id);
|
|
1379
1680
|
const fieldInput = col.fieldInput;
|
|
@@ -1397,6 +1698,13 @@ var buildFormControl = /* @__PURE__ */ __name((col) => {
|
|
|
1397
1698
|
control.detailFixed(detailLayout, {
|
|
1398
1699
|
layout: fieldInput.detail.layout === "collapse" ? "row" : void 0
|
|
1399
1700
|
});
|
|
1701
|
+
} else if (fieldInput?.format !== "date-range" && (fieldInput?.type === "date" || fieldInput?.format === "dateTime")) {
|
|
1702
|
+
const options = {
|
|
1703
|
+
...fieldInput.options
|
|
1704
|
+
};
|
|
1705
|
+
if (!options.colspan) options.colspan = 12;
|
|
1706
|
+
const format = fieldInput.format === "dateTime" ? "dateTime" : "date";
|
|
1707
|
+
control.control(format, options).width("full");
|
|
1400
1708
|
} else if (fieldInput?.format === "date-range") {
|
|
1401
1709
|
const options = {
|
|
1402
1710
|
...fieldInput.options
|
|
@@ -1406,7 +1714,7 @@ var buildFormControl = /* @__PURE__ */ __name((col) => {
|
|
|
1406
1714
|
} else {
|
|
1407
1715
|
const options = fieldInput?.options ?? {};
|
|
1408
1716
|
if (!options.colspan) options.colspan = 12;
|
|
1409
|
-
const type = fieldInput?.type ??
|
|
1717
|
+
const type = fieldInput?.type ?? defaultControlFormat(col);
|
|
1410
1718
|
control.control(type, options).width("full");
|
|
1411
1719
|
}
|
|
1412
1720
|
if (fieldInput?.customRender) control.setCustomRender(fieldInput?.customRender);
|
|
@@ -1502,19 +1810,66 @@ var injectCalculatedColumns = /* @__PURE__ */ __name((tableView, calculated) =>
|
|
|
1502
1810
|
var injectCalculatedColumnsToView = /* @__PURE__ */ __name((viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view"), "injectCalculatedColumnsToView");
|
|
1503
1811
|
|
|
1504
1812
|
// ../crouton-core/src/lib/view/view.builder.ts
|
|
1505
|
-
var
|
|
1813
|
+
var import_zod21 = require("zod");
|
|
1814
|
+
|
|
1815
|
+
// ../crouton-core/src/lib/view/column-type-schema.source.ts
|
|
1816
|
+
var optionValues = /* @__PURE__ */ __name((col) => {
|
|
1817
|
+
const values = col.fieldInput?.options?.["values"];
|
|
1818
|
+
if (!Array.isArray(values)) return void 0;
|
|
1819
|
+
const unwrapped = values.map((v) => v && typeof v === "object" && "value" in v ? v.value : v);
|
|
1820
|
+
return unwrapped.length ? unwrapped : void 0;
|
|
1821
|
+
}, "optionValues");
|
|
1822
|
+
var columnToJsonSchemaProperty = /* @__PURE__ */ __name((col) => {
|
|
1823
|
+
const property = col.type === void 0 && isAutocomplete(col) ? {} : {
|
|
1824
|
+
...columnTypeToJsonSchema(col.type)
|
|
1825
|
+
};
|
|
1826
|
+
if (property.title === void 0) property.title = col.label ?? col.id;
|
|
1827
|
+
if (col.fieldInput?.defaultValue !== void 0 && property.default === void 0) {
|
|
1828
|
+
property.default = col.fieldInput.defaultValue;
|
|
1829
|
+
}
|
|
1830
|
+
if (property.enum === void 0) {
|
|
1831
|
+
const values = optionValues(col);
|
|
1832
|
+
if (values) property.enum = values;
|
|
1833
|
+
}
|
|
1834
|
+
return property;
|
|
1835
|
+
}, "columnToJsonSchemaProperty");
|
|
1836
|
+
var columnTypeSchemaSource = /* @__PURE__ */ __name((schemaCols) => {
|
|
1837
|
+
if (!schemaCols.length) return void 0;
|
|
1838
|
+
const properties = {};
|
|
1839
|
+
for (const col of schemaCols) {
|
|
1840
|
+
properties[col.id] = columnToJsonSchemaProperty(col);
|
|
1841
|
+
}
|
|
1842
|
+
return {
|
|
1843
|
+
type: "object",
|
|
1844
|
+
properties
|
|
1845
|
+
};
|
|
1846
|
+
}, "columnTypeSchemaSource");
|
|
1506
1847
|
|
|
1507
1848
|
// ../crouton-core/src/lib/view/json-schema.opts.ts
|
|
1508
|
-
var
|
|
1509
|
-
var
|
|
1510
|
-
if (zodSchema instanceof
|
|
1849
|
+
var import_zod20 = require("zod");
|
|
1850
|
+
var isInstanceOf = /* @__PURE__ */ __name((zodSchema, className) => {
|
|
1851
|
+
if (!(zodSchema instanceof import_zod20.z.ZodCustom)) return false;
|
|
1852
|
+
const result = zodSchema.safeParse(null);
|
|
1853
|
+
return !result.success && result.error.issues[0]?.expected === className;
|
|
1854
|
+
}, "isInstanceOf");
|
|
1855
|
+
var jsonSchemaOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
|
|
1856
|
+
if (zodSchema instanceof import_zod20.z.ZodDate) {
|
|
1511
1857
|
jsonSchema.type = "string";
|
|
1512
1858
|
jsonSchema.format = "date-time";
|
|
1513
1859
|
}
|
|
1514
|
-
|
|
1860
|
+
if (zodSchema instanceof import_zod20.z.ZodBigInt) {
|
|
1861
|
+
jsonSchema.type = "integer";
|
|
1862
|
+
}
|
|
1863
|
+
if (isInstanceOf(zodSchema, "Decimal")) {
|
|
1864
|
+
jsonSchema.type = "number";
|
|
1865
|
+
}
|
|
1866
|
+
if (isInstanceOf(zodSchema, "Buffer")) {
|
|
1867
|
+
jsonSchema.type = "string";
|
|
1868
|
+
}
|
|
1869
|
+
}, "jsonSchemaOverride");
|
|
1515
1870
|
var jsonSchemaOpts = {
|
|
1516
1871
|
unrepresentable: "any",
|
|
1517
|
-
override:
|
|
1872
|
+
override: jsonSchemaOverride
|
|
1518
1873
|
};
|
|
1519
1874
|
|
|
1520
1875
|
// ../crouton-core/src/lib/view/view.builder.ts
|
|
@@ -1567,11 +1922,7 @@ var injectFieldDefaults = /* @__PURE__ */ __name((jsonSchema, columns) => {
|
|
|
1567
1922
|
if (prop) prop.default = col.fieldInput.defaultValue;
|
|
1568
1923
|
}
|
|
1569
1924
|
}, "injectFieldDefaults");
|
|
1570
|
-
var
|
|
1571
|
-
if (!schema || !columns?.length) return void 0;
|
|
1572
|
-
const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
|
|
1573
|
-
if (!visibleCols.length) return void 0;
|
|
1574
|
-
const schemaCols = (schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols).filter((c) => !isRelation(c));
|
|
1925
|
+
var zodSchemaSource = /* @__PURE__ */ __name((schema) => (schemaCols) => {
|
|
1575
1926
|
const schemaKeys = new Set(Object.keys(schema.shape));
|
|
1576
1927
|
const schemaIds = schemaCols.map((c) => c.id).filter((id) => schemaKeys.has(id));
|
|
1577
1928
|
if (!schemaIds.length) return void 0;
|
|
@@ -1580,10 +1931,61 @@ var buildView = /* @__PURE__ */ __name((schema, columns, visible, buildUiSchema,
|
|
|
1580
1931
|
true
|
|
1581
1932
|
]));
|
|
1582
1933
|
const picked = schema.pick(mask);
|
|
1583
|
-
|
|
1934
|
+
return (0, import_zod21.toJSONSchema)(picked, {
|
|
1584
1935
|
target: "draft-07",
|
|
1585
1936
|
...jsonSchemaOpts
|
|
1586
1937
|
});
|
|
1938
|
+
}, "zodSchemaSource");
|
|
1939
|
+
var NON_NULL_TYPES = [
|
|
1940
|
+
"object",
|
|
1941
|
+
"array",
|
|
1942
|
+
"string",
|
|
1943
|
+
"number",
|
|
1944
|
+
"boolean"
|
|
1945
|
+
];
|
|
1946
|
+
var SHAPE_KEYS = [
|
|
1947
|
+
"type",
|
|
1948
|
+
"enum",
|
|
1949
|
+
"const",
|
|
1950
|
+
"anyOf",
|
|
1951
|
+
"oneOf",
|
|
1952
|
+
"allOf",
|
|
1953
|
+
"$ref"
|
|
1954
|
+
];
|
|
1955
|
+
var applyRequiredColumns = /* @__PURE__ */ __name((jsonSchema, columns) => {
|
|
1956
|
+
if (!columns?.length) return;
|
|
1957
|
+
const properties = jsonSchema["properties"];
|
|
1958
|
+
if (!properties) return;
|
|
1959
|
+
const required = new Set(Array.isArray(jsonSchema["required"]) ? jsonSchema["required"] : []);
|
|
1960
|
+
for (const col of columns) {
|
|
1961
|
+
if (col.required === void 0) continue;
|
|
1962
|
+
if (!(col.id in properties)) continue;
|
|
1963
|
+
if (!col.required) {
|
|
1964
|
+
required.delete(col.id);
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
if (col.idField) continue;
|
|
1968
|
+
if (col.createable === false && col.updateable === false) continue;
|
|
1969
|
+
required.add(col.id);
|
|
1970
|
+
const property = properties[col.id];
|
|
1971
|
+
if (property && typeof property === "object" && !SHAPE_KEYS.some((key) => key in property)) {
|
|
1972
|
+
property["type"] = [
|
|
1973
|
+
...NON_NULL_TYPES
|
|
1974
|
+
];
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
if (required.size) jsonSchema["required"] = [
|
|
1978
|
+
...required
|
|
1979
|
+
];
|
|
1980
|
+
else delete jsonSchema["required"];
|
|
1981
|
+
}, "applyRequiredColumns");
|
|
1982
|
+
var buildView = /* @__PURE__ */ __name((source, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
|
|
1983
|
+
if (!source || !columns?.length) return void 0;
|
|
1984
|
+
const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
|
|
1985
|
+
if (!visibleCols.length) return void 0;
|
|
1986
|
+
const schemaCols = (schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols).filter((c) => !isRelation(c));
|
|
1987
|
+
const jsonSchema = source(schemaCols);
|
|
1988
|
+
if (!jsonSchema) return void 0;
|
|
1587
1989
|
applySchemaTransforms(jsonSchema);
|
|
1588
1990
|
injectFieldDefaults(jsonSchema, visibleCols);
|
|
1589
1991
|
if (schemaVisible) {
|
|
@@ -1608,26 +2010,31 @@ var emptyTableView = /* @__PURE__ */ __name(() => ({
|
|
|
1608
2010
|
ui_schema: buildTableUiSchema([]),
|
|
1609
2011
|
columns: []
|
|
1610
2012
|
}), "emptyTableView");
|
|
1611
|
-
var
|
|
2013
|
+
var buildViewsWithSource = /* @__PURE__ */ __name((source, columns) => {
|
|
1612
2014
|
const views = {};
|
|
1613
|
-
const table = buildView(
|
|
2015
|
+
const table = buildView(source, columns?.map((c) => columnForContext(c, "table")), (c) => !c.hiddenInTable, buildTableUiSchema, true);
|
|
1614
2016
|
if (table) {
|
|
1615
2017
|
table.defaultSort = resolveDefaultSort(table.columns, columns);
|
|
1616
2018
|
views.table = table;
|
|
1617
2019
|
} else if (columns?.length) {
|
|
1618
2020
|
views.table = emptyTableView();
|
|
1619
2021
|
}
|
|
1620
|
-
const form = buildView(
|
|
1621
|
-
if (form)
|
|
1622
|
-
|
|
2022
|
+
const form = buildView(source, columns, (c) => !c.hiddenInForm, buildFormUiSchema, true, (c) => !c.idField && (c.createable === true || c.updateable === true));
|
|
2023
|
+
if (form) {
|
|
2024
|
+
applyRequiredColumns(form.json_schema, columns);
|
|
2025
|
+
views.form = form;
|
|
2026
|
+
}
|
|
2027
|
+
const filter = buildView(source, columns, (c) => !!c.filterable, buildFormUiSchema, true);
|
|
1623
2028
|
if (filter) {
|
|
1624
2029
|
patchFilterProperties(filter.json_schema, columns?.filter((c) => !!c.filterable));
|
|
1625
2030
|
views.filter = filter;
|
|
1626
2031
|
}
|
|
1627
|
-
const view = buildView(
|
|
2032
|
+
const view = buildView(source, columns?.map((c) => columnForContext(c, "view")), (c) => !c.hiddenInView, buildFormUiSchema, true);
|
|
1628
2033
|
if (view) views.view = view;
|
|
1629
2034
|
return Object.keys(views).length ? views : void 0;
|
|
1630
|
-
}, "
|
|
2035
|
+
}, "buildViewsWithSource");
|
|
2036
|
+
var buildViews = /* @__PURE__ */ __name((schema, columns) => buildViewsWithSource(schema ? zodSchemaSource(schema) : void 0, columns), "buildViews");
|
|
2037
|
+
var buildViewsFromColumnTypes = /* @__PURE__ */ __name((columns) => buildViewsWithSource(columnTypeSchemaSource, columns), "buildViewsFromColumnTypes");
|
|
1631
2038
|
var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
1632
2039
|
if (!columns?.length) return void 0;
|
|
1633
2040
|
const buildJsonSchema = /* @__PURE__ */ __name((cols) => {
|
|
@@ -1656,6 +2063,8 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
1656
2063
|
type: "string",
|
|
1657
2064
|
title: c.label ?? c.id
|
|
1658
2065
|
};
|
|
2066
|
+
} else if (c.type !== void 0) {
|
|
2067
|
+
properties[c.id] = columnToJsonSchemaProperty(c);
|
|
1659
2068
|
} else {
|
|
1660
2069
|
const opts = c.fieldInput?.options;
|
|
1661
2070
|
const isObject = opts?.emitObject === true || c.fieldInput?.type === "autocomplete";
|
|
@@ -1718,7 +2127,10 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
1718
2127
|
}
|
|
1719
2128
|
const formCols = sortByPosition(columns.filter((c) => !c.hiddenInForm));
|
|
1720
2129
|
const form = makeView(formCols, buildFormUiSchema);
|
|
1721
|
-
if (form)
|
|
2130
|
+
if (form) {
|
|
2131
|
+
applyRequiredColumns(form.json_schema, formCols);
|
|
2132
|
+
views.form = form;
|
|
2133
|
+
}
|
|
1722
2134
|
const viewCols = sortByPosition(columns.filter((c) => !c.hiddenInView).map((c) => columnForContext(c, "view")));
|
|
1723
2135
|
const viewView = makeView(viewCols, buildFormUiSchema);
|
|
1724
2136
|
if (viewView) views.view = viewView;
|
|
@@ -1726,41 +2138,41 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
1726
2138
|
}, "buildViewsFromColumns");
|
|
1727
2139
|
|
|
1728
2140
|
// ../crouton-core/src/lib/view/view.schema.ts
|
|
1729
|
-
var
|
|
1730
|
-
var ViewColumnConfigSchema =
|
|
1731
|
-
id:
|
|
1732
|
-
label:
|
|
1733
|
-
sortable:
|
|
1734
|
-
searchable:
|
|
2141
|
+
var import_zod22 = require("zod");
|
|
2142
|
+
var ViewColumnConfigSchema = import_zod22.z.object({
|
|
2143
|
+
id: import_zod22.z.string(),
|
|
2144
|
+
label: import_zod22.z.string().optional(),
|
|
2145
|
+
sortable: import_zod22.z.boolean().optional(),
|
|
2146
|
+
searchable: import_zod22.z.boolean().optional(),
|
|
1735
2147
|
fieldInput: FieldInputSchema.optional()
|
|
1736
2148
|
});
|
|
1737
|
-
var ViewConfigSchema =
|
|
1738
|
-
json_schema:
|
|
1739
|
-
ui_schema:
|
|
1740
|
-
columns:
|
|
1741
|
-
defaultSort:
|
|
2149
|
+
var ViewConfigSchema = import_zod22.z.object({
|
|
2150
|
+
json_schema: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.unknown()),
|
|
2151
|
+
ui_schema: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.unknown()),
|
|
2152
|
+
columns: import_zod22.z.array(ViewColumnConfigSchema),
|
|
2153
|
+
defaultSort: import_zod22.z.string().optional()
|
|
1742
2154
|
});
|
|
1743
2155
|
|
|
1744
2156
|
// src/lib/crouton-api.module.ts
|
|
1745
|
-
var
|
|
2157
|
+
var import_common21 = require("@nestjs/common");
|
|
1746
2158
|
var import_core = require("@nestjs/core");
|
|
1747
2159
|
|
|
1748
2160
|
// src/lib/crud/app-layout/app-layout.types.ts
|
|
1749
|
-
var
|
|
1750
|
-
var SidebarLeafSchema =
|
|
1751
|
-
kind:
|
|
1752
|
-
id:
|
|
1753
|
-
label:
|
|
1754
|
-
position:
|
|
2161
|
+
var import_zod23 = require("zod");
|
|
2162
|
+
var SidebarLeafSchema = import_zod23.z.object({
|
|
2163
|
+
kind: import_zod23.z.literal("item").default("item"),
|
|
2164
|
+
id: import_zod23.z.string(),
|
|
2165
|
+
label: import_zod23.z.string(),
|
|
2166
|
+
position: import_zod23.z.number().optional()
|
|
1755
2167
|
});
|
|
1756
|
-
var SidebarGroupSchema2 =
|
|
1757
|
-
kind:
|
|
1758
|
-
id:
|
|
1759
|
-
label:
|
|
1760
|
-
position:
|
|
1761
|
-
children:
|
|
2168
|
+
var SidebarGroupSchema2 = import_zod23.z.object({
|
|
2169
|
+
kind: import_zod23.z.literal("group").default("group"),
|
|
2170
|
+
id: import_zod23.z.string(),
|
|
2171
|
+
label: import_zod23.z.string(),
|
|
2172
|
+
position: import_zod23.z.number().optional(),
|
|
2173
|
+
children: import_zod23.z.array(SidebarLeafSchema).default([])
|
|
1762
2174
|
});
|
|
1763
|
-
var SidebarNodeSchema =
|
|
2175
|
+
var SidebarNodeSchema = import_zod23.z.discriminatedUnion("kind", [
|
|
1764
2176
|
SidebarLeafSchema,
|
|
1765
2177
|
SidebarGroupSchema2
|
|
1766
2178
|
]);
|
|
@@ -1773,7 +2185,10 @@ var byPosition = /* @__PURE__ */ __name((a, b) => {
|
|
|
1773
2185
|
return a.label.localeCompare(b.label);
|
|
1774
2186
|
}, "byPosition");
|
|
1775
2187
|
var buildLayoutPayload = /* @__PURE__ */ __name((configs, sidebarGroups = {}, title, autoSave = true, isDev = false) => {
|
|
1776
|
-
const visible = configs.filter((c) => c.sidebar?.hide !== true && c.views?.["table"]
|
|
2188
|
+
const visible = configs.filter((c) => c.sidebar?.hide !== true && c.views?.["table"] && // A resource nested under a parent has no standalone route, so a sidebar
|
|
2189
|
+
// entry for it would resolve to `<name>/schemas` and 404. It is reached
|
|
2190
|
+
// from its parent's detail view instead.
|
|
2191
|
+
!c.parent);
|
|
1777
2192
|
const topLevel = [];
|
|
1778
2193
|
const groupMap = new Map(Object.entries(sidebarGroups).map(([slug, cfg]) => [
|
|
1779
2194
|
slug,
|
|
@@ -2026,51 +2441,51 @@ CroutonValidationExceptionFilter = _ts_decorate3([
|
|
|
2026
2441
|
], CroutonValidationExceptionFilter);
|
|
2027
2442
|
|
|
2028
2443
|
// src/lib/crud/crud-controller.factory.ts
|
|
2029
|
-
var
|
|
2444
|
+
var import_common18 = require("@nestjs/common");
|
|
2030
2445
|
var import_swagger11 = require("@nestjs/swagger");
|
|
2031
2446
|
|
|
2032
2447
|
// src/lib/crud/action/action.types.ts
|
|
2033
|
-
var
|
|
2034
|
-
var ActionMetadataSchema =
|
|
2448
|
+
var import_zod24 = require("zod");
|
|
2449
|
+
var ActionMetadataSchema = import_zod24.z.object({
|
|
2035
2450
|
/** URL segment used in the endpoint. */
|
|
2036
|
-
id:
|
|
2451
|
+
id: import_zod24.z.string(),
|
|
2037
2452
|
/** Human-readable label shown as a button. */
|
|
2038
|
-
label:
|
|
2453
|
+
label: import_zod24.z.string().optional(),
|
|
2039
2454
|
/** MDI icon name, e.g. `"mdi:open-in-new"`. */
|
|
2040
|
-
icon:
|
|
2455
|
+
icon: import_zod24.z.string().optional(),
|
|
2041
2456
|
/** Tooltip text. Falls back to `label` when omitted. */
|
|
2042
|
-
tooltip:
|
|
2457
|
+
tooltip: import_zod24.z.string().optional(),
|
|
2043
2458
|
/** Per-row condition — button is hidden when false. */
|
|
2044
|
-
condition:
|
|
2459
|
+
condition: import_zod24.z.custom().optional()
|
|
2045
2460
|
});
|
|
2046
2461
|
var ResourceLinkActionSchema = ActionMetadataSchema.extend({
|
|
2047
|
-
type:
|
|
2462
|
+
type: import_zod24.z.literal("link"),
|
|
2048
2463
|
/** URL to open. May contain `{id}` or `{env.VAR}` placeholders. */
|
|
2049
|
-
href:
|
|
2464
|
+
href: import_zod24.z.string()
|
|
2050
2465
|
});
|
|
2051
2466
|
var ResourceRowProcedureActionSchema = ActionMetadataSchema.extend({
|
|
2052
|
-
type:
|
|
2467
|
+
type: import_zod24.z.literal("procedure").optional(),
|
|
2053
2468
|
/** HTTP method for the endpoint. Defaults to `"post"`. */
|
|
2054
|
-
method:
|
|
2469
|
+
method: import_zod24.z.string().optional(),
|
|
2055
2470
|
/** Static data payload merged into the request body by the frontend. */
|
|
2056
|
-
data:
|
|
2471
|
+
data: import_zod24.z.record(import_zod24.z.string(), import_zod24.z.unknown()).optional(),
|
|
2057
2472
|
/** Procedure called with `(prisma, recordId)`. */
|
|
2058
|
-
procedure:
|
|
2473
|
+
procedure: import_zod24.z.custom((v) => typeof v === "function")
|
|
2059
2474
|
});
|
|
2060
2475
|
var ResourceTableProcedureActionSchema = ActionMetadataSchema.extend({
|
|
2061
|
-
type:
|
|
2476
|
+
type: import_zod24.z.literal("procedure").optional(),
|
|
2062
2477
|
/** HTTP method for the endpoint. Defaults to `"post"`. */
|
|
2063
|
-
method:
|
|
2478
|
+
method: import_zod24.z.string().optional(),
|
|
2064
2479
|
/** Static data payload merged into the request body by the frontend. */
|
|
2065
|
-
data:
|
|
2480
|
+
data: import_zod24.z.record(import_zod24.z.string(), import_zod24.z.unknown()).optional(),
|
|
2066
2481
|
/** Procedure called with `(prisma)` — no record id. */
|
|
2067
|
-
procedure:
|
|
2482
|
+
procedure: import_zod24.z.custom((v) => typeof v === "function")
|
|
2068
2483
|
});
|
|
2069
|
-
var ResourceRowActionSchema =
|
|
2484
|
+
var ResourceRowActionSchema = import_zod24.z.union([
|
|
2070
2485
|
ResourceRowProcedureActionSchema,
|
|
2071
2486
|
ResourceLinkActionSchema
|
|
2072
2487
|
]);
|
|
2073
|
-
var ResourceTableActionSchema =
|
|
2488
|
+
var ResourceTableActionSchema = import_zod24.z.union([
|
|
2074
2489
|
ResourceTableProcedureActionSchema,
|
|
2075
2490
|
ResourceLinkActionSchema
|
|
2076
2491
|
]);
|
|
@@ -2093,7 +2508,7 @@ var findModule = /* @__PURE__ */ __name((dir, name) => {
|
|
|
2093
2508
|
return void 0;
|
|
2094
2509
|
}, "findModule");
|
|
2095
2510
|
var IS_VITE = typeof globalThis.__vite_ssr_import__ === "function";
|
|
2096
|
-
var importDefault = /* @__PURE__ */ __name(async (filePath) => {
|
|
2511
|
+
var importDefault = /* @__PURE__ */ __name(async (filePath, onError) => {
|
|
2097
2512
|
try {
|
|
2098
2513
|
if (IS_VITE) {
|
|
2099
2514
|
const importPath = IS_DEV ? `${filePath}?t=${Date.now()}` : filePath;
|
|
@@ -2103,7 +2518,9 @@ var importDefault = /* @__PURE__ */ __name(async (filePath) => {
|
|
|
2103
2518
|
const mod = _require(filePath);
|
|
2104
2519
|
return mod.default;
|
|
2105
2520
|
}
|
|
2106
|
-
} catch {
|
|
2521
|
+
} catch (error) {
|
|
2522
|
+
if (onError) onError(error, filePath);
|
|
2523
|
+
else console.error(`[crouton] Failed to import ${filePath}:`, error);
|
|
2107
2524
|
return void 0;
|
|
2108
2525
|
}
|
|
2109
2526
|
}, "importDefault");
|
|
@@ -2149,9 +2566,422 @@ var schemaFor = /* @__PURE__ */ __name((def2, op) => {
|
|
|
2149
2566
|
}, "schemaFor");
|
|
2150
2567
|
var upsertOnFor = /* @__PURE__ */ __name((def2) => def2.upsert?.upsertOn, "upsertOnFor");
|
|
2151
2568
|
|
|
2152
|
-
// src/lib/crud/
|
|
2569
|
+
// src/lib/crud/custom-repository/custom-repository.types.ts
|
|
2570
|
+
var import_zod25 = require("zod");
|
|
2571
|
+
var CUSTOM_OPS = [
|
|
2572
|
+
"findAll",
|
|
2573
|
+
"findOne",
|
|
2574
|
+
"create",
|
|
2575
|
+
"update",
|
|
2576
|
+
"patch",
|
|
2577
|
+
"delete"
|
|
2578
|
+
];
|
|
2579
|
+
var PARENT_METHOD = {
|
|
2580
|
+
findAll: "findAllByParent",
|
|
2581
|
+
findOne: "findOneByParent",
|
|
2582
|
+
create: "createByParent",
|
|
2583
|
+
update: "updateByParent",
|
|
2584
|
+
patch: "patchByParent",
|
|
2585
|
+
delete: "deleteByParent"
|
|
2586
|
+
};
|
|
2587
|
+
var CustomRepositorySchema = import_zod25.z.custom((value) => typeof value === "object" && value !== null);
|
|
2588
|
+
|
|
2589
|
+
// src/lib/crud/resource/resource-load-errors.registry.ts
|
|
2590
|
+
var ResourceLoadErrorsRegistry = class ResourceLoadErrorsRegistry2 {
|
|
2591
|
+
static {
|
|
2592
|
+
__name(this, "ResourceLoadErrorsRegistry");
|
|
2593
|
+
}
|
|
2594
|
+
errors = [];
|
|
2595
|
+
record(e) {
|
|
2596
|
+
this.errors.push(e);
|
|
2597
|
+
}
|
|
2598
|
+
getAll() {
|
|
2599
|
+
return [
|
|
2600
|
+
...this.errors
|
|
2601
|
+
];
|
|
2602
|
+
}
|
|
2603
|
+
clear() {
|
|
2604
|
+
this.errors = [];
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
var resourceLoadErrorsRegistry = new ResourceLoadErrorsRegistry();
|
|
2608
|
+
|
|
2609
|
+
// src/lib/crud/custom-repository/custom-repository.loader.ts
|
|
2610
|
+
var REPOSITORY_MODULE = "repository";
|
|
2611
|
+
var loadCustomRepository = /* @__PURE__ */ __name(async (basePath, resourceName) => {
|
|
2612
|
+
const file = findModule(basePath, REPOSITORY_MODULE);
|
|
2613
|
+
if (!file) return void 0;
|
|
2614
|
+
let failure;
|
|
2615
|
+
const repository = await importDefault(file, (error) => {
|
|
2616
|
+
failure = error;
|
|
2617
|
+
});
|
|
2618
|
+
if (failure !== void 0) {
|
|
2619
|
+
resourceLoadErrorsRegistry.record({
|
|
2620
|
+
name: resourceName,
|
|
2621
|
+
path: file,
|
|
2622
|
+
error: `Failed to import ${REPOSITORY_MODULE}: ${failure instanceof Error ? failure.message : String(failure)}`
|
|
2623
|
+
});
|
|
2624
|
+
return void 0;
|
|
2625
|
+
}
|
|
2626
|
+
if (repository && typeof repository !== "object") {
|
|
2627
|
+
resourceLoadErrorsRegistry.record({
|
|
2628
|
+
name: resourceName,
|
|
2629
|
+
path: file,
|
|
2630
|
+
error: `${REPOSITORY_MODULE} must default-export an object of operation functions.`
|
|
2631
|
+
});
|
|
2632
|
+
return void 0;
|
|
2633
|
+
}
|
|
2634
|
+
return repository;
|
|
2635
|
+
}, "loadCustomRepository");
|
|
2636
|
+
var loadSubResourceRepositories = /* @__PURE__ */ __name(async (subResources, parentName) => {
|
|
2637
|
+
for (const sub of subResources) {
|
|
2638
|
+
if (sub.childKind !== "custom") continue;
|
|
2639
|
+
if (!sub.childDir) {
|
|
2640
|
+
resourceLoadErrorsRegistry.record({
|
|
2641
|
+
name: parentName,
|
|
2642
|
+
path: sub.childRoute,
|
|
2643
|
+
error: `Sub-resource "${sub.childRoute}" is a custom resource but its directory could not be resolved, so its repository.ts cannot be loaded. Check the relation column's "resource" path.`
|
|
2644
|
+
});
|
|
2645
|
+
continue;
|
|
2646
|
+
}
|
|
2647
|
+
const repository = await loadCustomRepository(sub.childDir, `${parentName}.${sub.childRoute}`);
|
|
2648
|
+
if (repository) sub.repository = repository;
|
|
2649
|
+
}
|
|
2650
|
+
}, "loadSubResourceRepositories");
|
|
2651
|
+
|
|
2652
|
+
// src/lib/crud/custom-repository/custom-repository.adapter.ts
|
|
2153
2653
|
var import_common4 = require("@nestjs/common");
|
|
2154
2654
|
|
|
2655
|
+
// src/lib/crud/constants.ts
|
|
2656
|
+
var PRISMA_NOT_FOUND_CODE = "P2025";
|
|
2657
|
+
var DEFAULT_ID_TYPE = "string";
|
|
2658
|
+
var DEFAULT_ID_FIELD = "id";
|
|
2659
|
+
|
|
2660
|
+
// src/lib/crud/hooks/hooks.types.ts
|
|
2661
|
+
var import_zod26 = require("zod");
|
|
2662
|
+
var WriteOpSchema = import_zod26.z.enum([
|
|
2663
|
+
"create",
|
|
2664
|
+
"update",
|
|
2665
|
+
"patch",
|
|
2666
|
+
"upsert",
|
|
2667
|
+
"delete"
|
|
2668
|
+
]);
|
|
2669
|
+
var ReadOpSchema = import_zod26.z.enum([
|
|
2670
|
+
"findAll",
|
|
2671
|
+
"findOne"
|
|
2672
|
+
]);
|
|
2673
|
+
var ResourceHooksSchema = import_zod26.z.object({
|
|
2674
|
+
beforeWrite: import_zod26.z.custom().optional(),
|
|
2675
|
+
afterWrite: import_zod26.z.custom().optional(),
|
|
2676
|
+
afterRead: import_zod26.z.custom().optional()
|
|
2677
|
+
});
|
|
2678
|
+
|
|
2679
|
+
// src/lib/crud/hooks/hooks.loader.ts
|
|
2680
|
+
var import_node_path4 = require("path");
|
|
2681
|
+
var loadResourceHooks = /* @__PURE__ */ __name(async (basePath) => {
|
|
2682
|
+
const file = findModule(basePath, "hooks");
|
|
2683
|
+
return file ? importDefault(file) : void 0;
|
|
2684
|
+
}, "loadResourceHooks");
|
|
2685
|
+
var loadSubResourceHooks = /* @__PURE__ */ __name(async (subResources, basePath) => {
|
|
2686
|
+
for (const sub of subResources) {
|
|
2687
|
+
const file = (sub.name ? findModule((0, import_node_path4.join)(basePath, "hooks"), sub.name) : void 0) ?? (sub.childDir ? findModule(sub.childDir, "hooks") : void 0);
|
|
2688
|
+
if (!file) continue;
|
|
2689
|
+
const hooks = await importDefault(file);
|
|
2690
|
+
if (hooks) sub.hooks = hooks;
|
|
2691
|
+
}
|
|
2692
|
+
}, "loadSubResourceHooks");
|
|
2693
|
+
|
|
2694
|
+
// src/lib/crud/resource/valueLabel.apply.ts
|
|
2695
|
+
var applyValueLabelColumns = /* @__PURE__ */ __name((row, cols) => {
|
|
2696
|
+
if (!row || !cols?.length) return row;
|
|
2697
|
+
const out = {
|
|
2698
|
+
...row
|
|
2699
|
+
};
|
|
2700
|
+
for (const { field, values } of cols) {
|
|
2701
|
+
if (field in out) out[field] = toValueLabel(out[field], values);
|
|
2702
|
+
}
|
|
2703
|
+
return out;
|
|
2704
|
+
}, "applyValueLabelColumns");
|
|
2705
|
+
var normalizeValueLabels = /* @__PURE__ */ __name((data, cols) => {
|
|
2706
|
+
if (!data || typeof data !== "object" || Array.isArray(data) || !cols?.length) return data;
|
|
2707
|
+
const out = {
|
|
2708
|
+
...data
|
|
2709
|
+
};
|
|
2710
|
+
for (const { field } of cols) {
|
|
2711
|
+
if (field in out) out[field] = fromValueLabel(out[field]);
|
|
2712
|
+
}
|
|
2713
|
+
return out;
|
|
2714
|
+
}, "normalizeValueLabels");
|
|
2715
|
+
|
|
2716
|
+
// src/lib/crud/hooks/hooks.apply.ts
|
|
2717
|
+
var decorateRows = /* @__PURE__ */ __name(async (rows, op, target, prisma, request, parent) => {
|
|
2718
|
+
const hook = target.hooks?.afterRead;
|
|
2719
|
+
const hooked = hook ? await Promise.all(rows.map((row) => hook(row, {
|
|
2720
|
+
prisma,
|
|
2721
|
+
op,
|
|
2722
|
+
request,
|
|
2723
|
+
...parent && {
|
|
2724
|
+
parent
|
|
2725
|
+
}
|
|
2726
|
+
}))) : rows;
|
|
2727
|
+
const cols = target.valueLabelColumns;
|
|
2728
|
+
return cols?.length ? hooked.map((r) => applyValueLabelColumns(r, cols)) : hooked;
|
|
2729
|
+
}, "decorateRows");
|
|
2730
|
+
var decorateRow = /* @__PURE__ */ __name(async (row, op, target, prisma, request, parent) => {
|
|
2731
|
+
const hook = target.hooks?.afterRead;
|
|
2732
|
+
return hook ? hook(row, {
|
|
2733
|
+
prisma,
|
|
2734
|
+
op,
|
|
2735
|
+
request,
|
|
2736
|
+
...parent && {
|
|
2737
|
+
parent
|
|
2738
|
+
}
|
|
2739
|
+
}) : row;
|
|
2740
|
+
}, "decorateRow");
|
|
2741
|
+
var prepareWrite = /* @__PURE__ */ __name(async (data, op, target, prisma, id, request, parent) => {
|
|
2742
|
+
const normalized = normalizeValueLabels(data, target.valueLabelColumns);
|
|
2743
|
+
const hook = target.hooks?.beforeWrite;
|
|
2744
|
+
return hook ? hook(normalized, {
|
|
2745
|
+
prisma,
|
|
2746
|
+
op,
|
|
2747
|
+
id,
|
|
2748
|
+
request,
|
|
2749
|
+
...parent && {
|
|
2750
|
+
parent
|
|
2751
|
+
}
|
|
2752
|
+
}) : normalized;
|
|
2753
|
+
}, "prepareWrite");
|
|
2754
|
+
var postWrite = /* @__PURE__ */ __name(async (result, op, target, prisma, id, request, parent) => {
|
|
2755
|
+
const hook = target.hooks?.afterWrite;
|
|
2756
|
+
return hook ? hook(result, {
|
|
2757
|
+
prisma,
|
|
2758
|
+
op,
|
|
2759
|
+
id,
|
|
2760
|
+
request,
|
|
2761
|
+
...parent && {
|
|
2762
|
+
parent
|
|
2763
|
+
}
|
|
2764
|
+
}) : result;
|
|
2765
|
+
}, "postWrite");
|
|
2766
|
+
|
|
2767
|
+
// src/lib/crud/custom-repository/custom-repository.adapter.ts
|
|
2768
|
+
var unsupported = /* @__PURE__ */ __name((config, op) => {
|
|
2769
|
+
const method = config.parent ? PARENT_METHOD[op] : op;
|
|
2770
|
+
throw new import_common4.NotImplementedException(`Resource "${config.name}" enables "${op}" but its repository.ts does not implement "${method}".`);
|
|
2771
|
+
}, "unsupported");
|
|
2772
|
+
var createCustomRepository = /* @__PURE__ */ __name((prisma, config, dataSources, repository) => {
|
|
2773
|
+
const repo = repository ?? {};
|
|
2774
|
+
const idField = config.idField ?? DEFAULT_ID_FIELD;
|
|
2775
|
+
const toId = /* @__PURE__ */ __name((id) => (config.idType ?? DEFAULT_ID_TYPE) === "number" ? +id : String(id), "toId");
|
|
2776
|
+
const parentRef = config.parent;
|
|
2777
|
+
const parentIdFrom = /* @__PURE__ */ __name((request) => {
|
|
2778
|
+
const raw = request?.params?.[parentRef.param];
|
|
2779
|
+
if (raw === void 0 || raw === null || raw === "") {
|
|
2780
|
+
throw new import_common4.BadRequestException(`Resource "${config.name}" is nested under "${parentRef.route}" but no "${parentRef.param}" was supplied.`);
|
|
2781
|
+
}
|
|
2782
|
+
return (parentRef.idType ?? "string") === "number" ? +raw : String(raw);
|
|
2783
|
+
}, "parentIdFrom");
|
|
2784
|
+
const parentCtx = /* @__PURE__ */ __name((request, parentId2) => ({
|
|
2785
|
+
parent: {
|
|
2786
|
+
route: parentRef.route,
|
|
2787
|
+
param: parentRef.param,
|
|
2788
|
+
id: parentId2
|
|
2789
|
+
}
|
|
2790
|
+
}), "parentCtx");
|
|
2791
|
+
const parentHookCtx = /* @__PURE__ */ __name((request) => {
|
|
2792
|
+
if (!parentRef) return void 0;
|
|
2793
|
+
const raw = request?.params?.[parentRef.param];
|
|
2794
|
+
if (raw === void 0 || raw === null || raw === "") return void 0;
|
|
2795
|
+
return {
|
|
2796
|
+
route: parentRef.route,
|
|
2797
|
+
param: parentRef.param,
|
|
2798
|
+
id: (parentRef.idType ?? "string") === "number" ? +raw : String(raw)
|
|
2799
|
+
};
|
|
2800
|
+
}, "parentHookCtx");
|
|
2801
|
+
const ctx = /* @__PURE__ */ __name((op, params, id, request) => ({
|
|
2802
|
+
prisma,
|
|
2803
|
+
dataSources,
|
|
2804
|
+
config,
|
|
2805
|
+
op,
|
|
2806
|
+
offset: params ? offsetOf(params) : 0,
|
|
2807
|
+
...id !== void 0 && {
|
|
2808
|
+
id
|
|
2809
|
+
},
|
|
2810
|
+
...request !== void 0 && {
|
|
2811
|
+
request
|
|
2812
|
+
}
|
|
2813
|
+
}), "ctx");
|
|
2814
|
+
const findAllWithCount = /* @__PURE__ */ __name(async (params, request) => {
|
|
2815
|
+
let result;
|
|
2816
|
+
if (parentRef) {
|
|
2817
|
+
if (!repo.findAllByParent) unsupported(config, "findAll");
|
|
2818
|
+
const parentId2 = parentIdFrom(request);
|
|
2819
|
+
result = await repo.findAllByParent(parentId2, params, {
|
|
2820
|
+
...ctx("findAll", params, void 0, request),
|
|
2821
|
+
...parentCtx(request, parentId2)
|
|
2822
|
+
});
|
|
2823
|
+
} else {
|
|
2824
|
+
if (!repo.findAll) unsupported(config, "findAll");
|
|
2825
|
+
result = await repo.findAll(params, ctx("findAll", params, void 0, request));
|
|
2826
|
+
}
|
|
2827
|
+
const data = await decorateRows(result?.data ?? [], "findAll", config, prisma, request, parentHookCtx(request));
|
|
2828
|
+
return {
|
|
2829
|
+
data,
|
|
2830
|
+
count: result?.count ?? data.length
|
|
2831
|
+
};
|
|
2832
|
+
}, "findAllWithCount");
|
|
2833
|
+
const findOne = /* @__PURE__ */ __name(async (id, request) => {
|
|
2834
|
+
let row;
|
|
2835
|
+
if (parentRef) {
|
|
2836
|
+
if (!repo.findOneByParent) unsupported(config, "findOne");
|
|
2837
|
+
const parentId2 = parentIdFrom(request);
|
|
2838
|
+
row = await repo.findOneByParent(parentId2, toId(id), {
|
|
2839
|
+
...ctx("findOne", void 0, id, request),
|
|
2840
|
+
...parentCtx(request, parentId2)
|
|
2841
|
+
});
|
|
2842
|
+
} else {
|
|
2843
|
+
if (!repo.findOne) unsupported(config, "findOne");
|
|
2844
|
+
row = await repo.findOne(toId(id), ctx("findOne", void 0, id, request));
|
|
2845
|
+
}
|
|
2846
|
+
if (row === null || row === void 0) {
|
|
2847
|
+
throw new import_common4.NotFoundException(`${config.name} with id ${id} not found`);
|
|
2848
|
+
}
|
|
2849
|
+
return decorateRow(row, "findOne", config, prisma, request, parentHookCtx(request));
|
|
2850
|
+
}, "findOne");
|
|
2851
|
+
const write = /* @__PURE__ */ __name(async (op, data, id, request) => {
|
|
2852
|
+
const coercedId = id !== void 0 ? toId(id) : void 0;
|
|
2853
|
+
const prepared = /* @__PURE__ */ __name(() => prepareWrite(data, op, config, prisma, coercedId, request, parentHookCtx(request)), "prepared");
|
|
2854
|
+
let result;
|
|
2855
|
+
if (parentRef) {
|
|
2856
|
+
const fn = op === "create" ? repo.createByParent : op === "update" ? repo.updateByParent : repo.patchByParent ?? repo.updateByParent;
|
|
2857
|
+
if (!fn) unsupported(config, op);
|
|
2858
|
+
const parentId2 = parentIdFrom(request);
|
|
2859
|
+
const nestedCtx = {
|
|
2860
|
+
...ctx(op, void 0, coercedId, request),
|
|
2861
|
+
...parentCtx(request, parentId2)
|
|
2862
|
+
};
|
|
2863
|
+
const body = await prepared();
|
|
2864
|
+
result = op === "create" ? await fn(parentId2, body, nestedCtx) : await fn(parentId2, coercedId, body, nestedCtx);
|
|
2865
|
+
} else {
|
|
2866
|
+
const fn = op === "create" ? repo.create : op === "update" ? repo.update : repo.patch ?? repo.update;
|
|
2867
|
+
if (!fn) unsupported(config, op);
|
|
2868
|
+
const body = await prepared();
|
|
2869
|
+
result = op === "create" ? await fn(body, ctx("create", void 0, void 0, request)) : await fn(coercedId, body, ctx(op, void 0, coercedId, request));
|
|
2870
|
+
}
|
|
2871
|
+
return postWrite(result, op, config, prisma, coercedId, request, parentHookCtx(request));
|
|
2872
|
+
}, "write");
|
|
2873
|
+
const notAChildRepository = /* @__PURE__ */ __name(async () => {
|
|
2874
|
+
throw new import_common4.NotImplementedException(`Resource "${config.name}" is a custom resource; nested sub-resource routes are not supported. Expose the child collection as its own resource instead.`);
|
|
2875
|
+
}, "notAChildRepository");
|
|
2876
|
+
return {
|
|
2877
|
+
prisma,
|
|
2878
|
+
// Preferred by register-findall: one round trip returns rows and count.
|
|
2879
|
+
findAllWithCount,
|
|
2880
|
+
findAll: /* @__PURE__ */ __name(async (params, request) => (await findAllWithCount(params, request)).data, "findAll"),
|
|
2881
|
+
count: /* @__PURE__ */ __name(async (filter) => (await findAllWithCount({
|
|
2882
|
+
page: 1,
|
|
2883
|
+
pageSize: 1,
|
|
2884
|
+
sort: idField,
|
|
2885
|
+
sortDir: "asc",
|
|
2886
|
+
filter: filter ?? []
|
|
2887
|
+
})).count, "count"),
|
|
2888
|
+
findOne,
|
|
2889
|
+
create: /* @__PURE__ */ __name((data, request) => write("create", data, void 0, request), "create"),
|
|
2890
|
+
update: /* @__PURE__ */ __name((id, data, request) => write("update", data, id, request), "update"),
|
|
2891
|
+
patch: /* @__PURE__ */ __name((id, data, request) => write("patch", data, id, request), "patch"),
|
|
2892
|
+
delete: /* @__PURE__ */ __name(async (id, request) => {
|
|
2893
|
+
const coercedId = toId(id);
|
|
2894
|
+
let result;
|
|
2895
|
+
if (parentRef) {
|
|
2896
|
+
if (!repo.deleteByParent) unsupported(config, "delete");
|
|
2897
|
+
const parentId2 = parentIdFrom(request);
|
|
2898
|
+
result = await repo.deleteByParent(parentId2, coercedId, {
|
|
2899
|
+
...ctx("delete", void 0, coercedId, request),
|
|
2900
|
+
...parentCtx(request, parentId2)
|
|
2901
|
+
});
|
|
2902
|
+
} else {
|
|
2903
|
+
if (!repo.delete) unsupported(config, "delete");
|
|
2904
|
+
result = await repo.delete(coercedId, ctx("delete", void 0, coercedId, request));
|
|
2905
|
+
}
|
|
2906
|
+
return postWrite(result, "delete", config, prisma, coercedId, request, parentHookCtx(request));
|
|
2907
|
+
}, "delete"),
|
|
2908
|
+
upsert: /* @__PURE__ */ __name(async () => {
|
|
2909
|
+
throw new import_common4.NotImplementedException(`Resource "${config.name}" is a custom resource; upsert is not part of the repository contract.`);
|
|
2910
|
+
}, "upsert"),
|
|
2911
|
+
upsertMany: /* @__PURE__ */ __name(async () => {
|
|
2912
|
+
throw new import_common4.NotImplementedException(`Resource "${config.name}" is a custom resource; upsert is not part of the repository contract.`);
|
|
2913
|
+
}, "upsertMany"),
|
|
2914
|
+
findAllByParent: notAChildRepository,
|
|
2915
|
+
findOneChild: notAChildRepository,
|
|
2916
|
+
createChild: notAChildRepository,
|
|
2917
|
+
updateChild: notAChildRepository,
|
|
2918
|
+
deleteChild: notAChildRepository
|
|
2919
|
+
};
|
|
2920
|
+
}, "createCustomRepository");
|
|
2921
|
+
|
|
2922
|
+
// src/lib/crud/custom-repository/custom-repository.validate.ts
|
|
2923
|
+
var validateCustomRepository = /* @__PURE__ */ __name((config, repository) => {
|
|
2924
|
+
const definition = resolveDefinition(config);
|
|
2925
|
+
const enabled = CUSTOM_OPS.filter((op) => isOperationEnabled(definition, op));
|
|
2926
|
+
const nested = !!config.parent;
|
|
2927
|
+
const methodFor = /* @__PURE__ */ __name((op) => nested ? PARENT_METHOD[op] : op, "methodFor");
|
|
2928
|
+
if (!repository) {
|
|
2929
|
+
return `No repository.ts found. A custom resource implements its own data access; create ${config.name}/repository.ts with a default export implementing: ${enabled.map(methodFor).join(", ") || "no operations"}.`;
|
|
2930
|
+
}
|
|
2931
|
+
const implemented = /* @__PURE__ */ __name((name) => typeof repository[name] === "function", "implemented");
|
|
2932
|
+
const missing = enabled.filter((op) => op === "patch" ? !implemented(methodFor("patch")) && !implemented(methodFor("update")) : !implemented(methodFor(op)));
|
|
2933
|
+
if (missing.length) {
|
|
2934
|
+
const names = missing.map(methodFor);
|
|
2935
|
+
return `repository.ts does not implement ${names.join(", ")}` + (nested ? ` (this resource is nested under "${config.parent.route}", so it implements the parent-aware operations). ` : ". ") + `Either implement them or disable the operation in resource.json ("operations": { "${missing[0]}": false }).`;
|
|
2936
|
+
}
|
|
2937
|
+
return void 0;
|
|
2938
|
+
}, "validateCustomRepository");
|
|
2939
|
+
|
|
2940
|
+
// src/lib/crud/read.repository.ts
|
|
2941
|
+
var import_common6 = require("@nestjs/common");
|
|
2942
|
+
|
|
2943
|
+
// src/lib/crud/custom-repository/child-delegate.ts
|
|
2944
|
+
var import_common5 = require("@nestjs/common");
|
|
2945
|
+
var childRepositoryFn = /* @__PURE__ */ __name((sub, op, parentName) => {
|
|
2946
|
+
const repo = sub.repository;
|
|
2947
|
+
const method = PARENT_METHOD[op];
|
|
2948
|
+
if (!repo) {
|
|
2949
|
+
throw new import_common5.NotImplementedException(`Sub-resource "${sub.childRoute}" of "${parentName}" is a custom resource but no repository.ts was loaded for it.`);
|
|
2950
|
+
}
|
|
2951
|
+
const fn = repo[method] ?? (op === "patch" ? repo[PARENT_METHOD.update] : void 0);
|
|
2952
|
+
if (typeof fn !== "function") {
|
|
2953
|
+
throw new import_common5.NotImplementedException(`Sub-resource "${sub.childRoute}" of "${parentName}" does not implement "${method}" in its repository.ts.`);
|
|
2954
|
+
}
|
|
2955
|
+
return fn.bind(repo);
|
|
2956
|
+
}, "childRepositoryFn");
|
|
2957
|
+
var childCtx = /* @__PURE__ */ __name(({ parentConfig, prisma, op, parentId: parentId2, params, id, request }) => ({
|
|
2958
|
+
prisma,
|
|
2959
|
+
dataSources: {
|
|
2960
|
+
resolve: /* @__PURE__ */ __name(() => prisma, "resolve"),
|
|
2961
|
+
entries: /* @__PURE__ */ __name(() => [], "entries")
|
|
2962
|
+
},
|
|
2963
|
+
config: parentConfig,
|
|
2964
|
+
op,
|
|
2965
|
+
offset: params ? offsetOf(params) : 0,
|
|
2966
|
+
...id !== void 0 && {
|
|
2967
|
+
id
|
|
2968
|
+
},
|
|
2969
|
+
...request !== void 0 && {
|
|
2970
|
+
request
|
|
2971
|
+
},
|
|
2972
|
+
// Served under the parent's `:id`, so that is the param name here — unlike a
|
|
2973
|
+
// top-level nested resource, which names its own param.
|
|
2974
|
+
parent: {
|
|
2975
|
+
route: parentConfig.route,
|
|
2976
|
+
param: "id",
|
|
2977
|
+
id: parentId2
|
|
2978
|
+
}
|
|
2979
|
+
}), "childCtx");
|
|
2980
|
+
var parentIdFromRequest = /* @__PURE__ */ __name((request, fallback) => {
|
|
2981
|
+
const raw = request?.params?.id;
|
|
2982
|
+
return raw === void 0 || raw === null || raw === "" ? fallback : raw;
|
|
2983
|
+
}, "parentIdFromRequest");
|
|
2984
|
+
|
|
2155
2985
|
// src/lib/crud/sql.helpers.ts
|
|
2156
2986
|
var castExpression = /* @__PURE__ */ __name((col) => {
|
|
2157
2987
|
if (col.type === "string") return `(${col.sqlExpression})`;
|
|
@@ -2168,17 +2998,17 @@ var coerceColumnValue = /* @__PURE__ */ __name((col) => {
|
|
|
2168
2998
|
if (col.type === "string") return (v) => v === void 0 || v === null ? null : String(v);
|
|
2169
2999
|
return (v) => Number(v ?? 0);
|
|
2170
3000
|
}, "coerceColumnValue");
|
|
2171
|
-
var buildCalculatedColumnSql = /* @__PURE__ */ __name((col, tableName, ids) => {
|
|
3001
|
+
var buildCalculatedColumnSql = /* @__PURE__ */ __name((col, tableName, ids, idField) => {
|
|
2172
3002
|
const alias = col.alias ?? col.id;
|
|
2173
3003
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ");
|
|
2174
|
-
return `SELECT main.id, ${castExpression(col)} AS "${alias}" FROM "${tableName}" main WHERE main.
|
|
3004
|
+
return `SELECT main."${idField}" AS id, ${castExpression(col)} AS "${alias}" FROM "${tableName}" main WHERE main."${idField}" IN (${placeholders})`;
|
|
2175
3005
|
}, "buildCalculatedColumnSql");
|
|
2176
|
-
var mergeCalculatedColumnsForRows = /* @__PURE__ */ __name(async (rows, calcCols, tableName, prisma) => {
|
|
3006
|
+
var mergeCalculatedColumnsForRows = /* @__PURE__ */ __name(async (rows, calcCols, tableName, prisma, idField = "id") => {
|
|
2177
3007
|
if (!calcCols.length || !rows.length) return rows;
|
|
2178
|
-
const ids = rows.map((r) => r
|
|
3008
|
+
const ids = rows.map((r) => r[idField]);
|
|
2179
3009
|
const results = await Promise.all(calcCols.map(async (col) => {
|
|
2180
3010
|
const alias = col.alias ?? col.id;
|
|
2181
|
-
const sql = buildCalculatedColumnSql(col, tableName, ids);
|
|
3011
|
+
const sql = buildCalculatedColumnSql(col, tableName, ids, idField);
|
|
2182
3012
|
const defaultValue = defaultValueForType(col);
|
|
2183
3013
|
const coerce = coerceColumnValue(col);
|
|
2184
3014
|
try {
|
|
@@ -2203,7 +3033,7 @@ var mergeCalculatedColumnsForRows = /* @__PURE__ */ __name(async (rows, calcCols
|
|
|
2203
3033
|
return rows.map((row) => {
|
|
2204
3034
|
const extra = {};
|
|
2205
3035
|
for (const { id, defaultValue, map } of results) {
|
|
2206
|
-
extra[id] = map[String(row
|
|
3036
|
+
extra[id] = map[String(row[idField])] ?? defaultValue;
|
|
2207
3037
|
}
|
|
2208
3038
|
return {
|
|
2209
3039
|
...row,
|
|
@@ -2263,7 +3093,7 @@ var buildChildSortClause = /* @__PURE__ */ __name((sort, sortDir) => {
|
|
|
2263
3093
|
}, {});
|
|
2264
3094
|
}, "buildChildSortClause");
|
|
2265
3095
|
var buildFindOneIncludes = /* @__PURE__ */ __name((subResources, configInclude) => {
|
|
2266
|
-
const autoSubs = subResources.filter((s) => s.includeInFindOne);
|
|
3096
|
+
const autoSubs = subResources.filter((s) => s.includeInFindOne && s.childKind !== "custom");
|
|
2267
3097
|
const flatIncludes = autoSubs.length ? Object.fromEntries(autoSubs.map((s) => s.findOneOrderBy ? [
|
|
2268
3098
|
s.relation,
|
|
2269
3099
|
{
|
|
@@ -2408,16 +3238,6 @@ var orderableChildSort = /* @__PURE__ */ __name((sort, childModel, _sub) => {
|
|
|
2408
3238
|
if (sort.includes(".")) return sort;
|
|
2409
3239
|
return scalarFields.has(sort) ? sort : void 0;
|
|
2410
3240
|
}, "orderableChildSort");
|
|
2411
|
-
var applyValueLabelColumns = /* @__PURE__ */ __name((row, cols) => {
|
|
2412
|
-
if (!row || !cols?.length) return row;
|
|
2413
|
-
const out = {
|
|
2414
|
-
...row
|
|
2415
|
-
};
|
|
2416
|
-
for (const { field, values } of cols) {
|
|
2417
|
-
if (field in out) out[field] = toValueLabel(out[field], values);
|
|
2418
|
-
}
|
|
2419
|
-
return out;
|
|
2420
|
-
}, "applyValueLabelColumns");
|
|
2421
3241
|
var ReadRepository = class {
|
|
2422
3242
|
static {
|
|
2423
3243
|
__name(this, "ReadRepository");
|
|
@@ -2434,6 +3254,15 @@ var ReadRepository = class {
|
|
|
2434
3254
|
this.listSelect = listSelect;
|
|
2435
3255
|
this.oneSelect = oneSelect;
|
|
2436
3256
|
}
|
|
3257
|
+
/**
|
|
3258
|
+
* Physical table name for raw SQL (calculated columns).
|
|
3259
|
+
*
|
|
3260
|
+
* `table` overrides `model` for a `@@map`-ed Prisma model. Empty for a custom
|
|
3261
|
+
* resource, which cannot have calculated columns.
|
|
3262
|
+
*/
|
|
3263
|
+
get tableName() {
|
|
3264
|
+
return this.config.table ?? this.config.model ?? "";
|
|
3265
|
+
}
|
|
2437
3266
|
toId(id) {
|
|
2438
3267
|
return (this.config.idType ?? "string") === "number" ? +id : String(id);
|
|
2439
3268
|
}
|
|
@@ -2451,43 +3280,55 @@ var ReadRepository = class {
|
|
|
2451
3280
|
if (this.listSelect && !(sort in this.listSelect)) return void 0;
|
|
2452
3281
|
return buildSort(sort, sortDir);
|
|
2453
3282
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
return
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
3283
|
+
/**
|
|
3284
|
+
* The parent a *child* read is scoped to, for the hook context.
|
|
3285
|
+
*
|
|
3286
|
+
* A sub-resource is served by the parent's controller, so its parent id arrives
|
|
3287
|
+
* as the parent's own `:id` — hence `param: 'id'`. `undefined` when no parent id
|
|
3288
|
+
* was supplied, so a hook is never handed a fabricated one.
|
|
3289
|
+
*/
|
|
3290
|
+
parentHookContext(parentId2) {
|
|
3291
|
+
if (parentId2 === void 0 || parentId2 === null || parentId2 === "") {
|
|
3292
|
+
return void 0;
|
|
3293
|
+
}
|
|
3294
|
+
return {
|
|
3295
|
+
route: this.config.route,
|
|
3296
|
+
param: "id",
|
|
3297
|
+
id: this.toId(parentId2)
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
async decorate(rows, op, request) {
|
|
3301
|
+
return decorateRows(rows, op, this.config, this.prisma, request);
|
|
3302
|
+
}
|
|
3303
|
+
async decorateOne(row, op, request) {
|
|
3304
|
+
return decorateRow(row, op, this.config, this.prisma, request);
|
|
2469
3305
|
}
|
|
2470
3306
|
/**
|
|
2471
3307
|
* Fetch a paginated, sorted, and filtered list of records.
|
|
2472
3308
|
* Sub-resource counts are merged onto each row; calculated columns are resolved via raw SQL.
|
|
2473
3309
|
*/
|
|
2474
|
-
async findAll(params) {
|
|
3310
|
+
async findAll(params, request) {
|
|
2475
3311
|
const subResources = this.config.subResources ?? [];
|
|
2476
3312
|
const projection = this.projection("findAll");
|
|
2477
3313
|
const query = {
|
|
2478
3314
|
where: this.buildWhere(params.filter),
|
|
2479
3315
|
take: params.pageSize,
|
|
2480
|
-
skip:
|
|
3316
|
+
skip: offsetOf(params),
|
|
2481
3317
|
orderBy: this.safeSort(sanitizeValueLabelSort(params.sort, this.config.valueLabelColumns), params.sortDir)
|
|
2482
3318
|
};
|
|
2483
|
-
const
|
|
2484
|
-
const
|
|
3319
|
+
const prismaSubResources = subResources.filter((s) => s.childKind !== "custom");
|
|
3320
|
+
const oneToManySubResources = prismaSubResources.filter((s) => s.relationType !== "manyToOne");
|
|
3321
|
+
const countableSubResources = oneToManySubResources.filter((s) => !s.hiddenInTable);
|
|
3322
|
+
const manyToOneIncludes = prismaSubResources.filter((s) => s.relationType === "manyToOne").map((s) => s.relation);
|
|
2485
3323
|
const flatIncludes = manyToOneIncludes.length ? Object.fromEntries(manyToOneIncludes.map((r) => [
|
|
2486
3324
|
r,
|
|
2487
3325
|
true
|
|
2488
3326
|
])) : void 0;
|
|
2489
3327
|
const configInclude = buildIncludeClause(this.config.include);
|
|
2490
|
-
const countableRelations = new Set(
|
|
3328
|
+
const countableRelations = /* @__PURE__ */ new Set([
|
|
3329
|
+
...oneToManySubResources.map((s) => s.relation),
|
|
3330
|
+
...subResources.filter((s) => s.childKind === "custom").map((s) => s.relation)
|
|
3331
|
+
]);
|
|
2491
3332
|
const filteredConfigInclude = configInclude ? Object.fromEntries(Object.entries(configInclude).filter(([key]) => !countableRelations.has(key))) : void 0;
|
|
2492
3333
|
const safeConfigInclude = filteredConfigInclude && Object.keys(filteredConfigInclude).length ? filteredConfigInclude : void 0;
|
|
2493
3334
|
const mergedInclude = flatIncludes || safeConfigInclude ? {
|
|
@@ -2525,8 +3366,8 @@ var ReadRepository = class {
|
|
|
2525
3366
|
...counts
|
|
2526
3367
|
};
|
|
2527
3368
|
}) : rows;
|
|
2528
|
-
const withCalc = await mergeCalculatedColumnsForRows(mapped, this.config.calculatedColumns ?? [], this.
|
|
2529
|
-
return this.decorate(withCalc, "findAll");
|
|
3369
|
+
const withCalc = await mergeCalculatedColumnsForRows(mapped, this.config.calculatedColumns ?? [], this.tableName, this.prisma, this.config.idField ?? "id");
|
|
3370
|
+
return this.decorate(withCalc, "findAll", request);
|
|
2530
3371
|
}
|
|
2531
3372
|
/** Count records matching the given filter strings. */
|
|
2532
3373
|
count(filter) {
|
|
@@ -2540,7 +3381,7 @@ var ReadRepository = class {
|
|
|
2540
3381
|
* `config.include` entries are loaded with full nesting via `buildIncludeClause`.
|
|
2541
3382
|
* @throws {NotFoundException} When no record exists for the given id.
|
|
2542
3383
|
*/
|
|
2543
|
-
async findOne(id) {
|
|
3384
|
+
async findOne(id, request) {
|
|
2544
3385
|
const projection = this.projection("findOne");
|
|
2545
3386
|
const idField = this.config.idField ?? "id";
|
|
2546
3387
|
const query = {
|
|
@@ -2561,36 +3402,59 @@ var ReadRepository = class {
|
|
|
2561
3402
|
}
|
|
2562
3403
|
}
|
|
2563
3404
|
const record = await this.prismaModel.findUnique(query);
|
|
2564
|
-
if (!record) throw new
|
|
3405
|
+
if (!record) throw new import_common6.NotFoundException(`${this.config.name} with id ${id} not found`);
|
|
2565
3406
|
const [withCalc] = await mergeCalculatedColumnsForRows([
|
|
2566
3407
|
record
|
|
2567
|
-
], this.config.calculatedColumns ?? [], this.
|
|
3408
|
+
], this.config.calculatedColumns ?? [], this.tableName, this.prisma, this.config.idField ?? "id");
|
|
2568
3409
|
let enriched = withCalc ?? record;
|
|
2569
3410
|
for (const sub of this.config.subResources ?? []) {
|
|
2570
3411
|
if (!sub.calculatedColumns?.length) continue;
|
|
2571
3412
|
const nested = enriched[sub.relation];
|
|
2572
3413
|
if (!Array.isArray(nested) || !nested.length) continue;
|
|
2573
|
-
const enrichedNested = await mergeCalculatedColumnsForRows(nested, sub.calculatedColumns, sub.childModel, this.prisma);
|
|
3414
|
+
const enrichedNested = await mergeCalculatedColumnsForRows(nested, sub.calculatedColumns, sub.childModel, this.prisma, sub.idField ?? "id");
|
|
2574
3415
|
enriched = {
|
|
2575
3416
|
...enriched,
|
|
2576
3417
|
[sub.relation]: enrichedNested
|
|
2577
3418
|
};
|
|
2578
3419
|
}
|
|
2579
|
-
return this.decorateOne(enriched, "findOne");
|
|
3420
|
+
return this.decorateOne(enriched, "findOne", request);
|
|
2580
3421
|
}
|
|
2581
3422
|
/**
|
|
2582
3423
|
* Fetch a paginated list of child records belonging to the given parent.
|
|
2583
3424
|
* @param childRoute - Matches the `childRoute` key on a `SubResourceConfig`.
|
|
2584
3425
|
* @throws {Error} When no matching sub-resource config or Prisma model is found.
|
|
2585
3426
|
*/
|
|
2586
|
-
async findAllByParent(
|
|
3427
|
+
async findAllByParent(parentId2, childRoute, params, request) {
|
|
2587
3428
|
const sub = (this.config.subResources ?? []).find((s) => s.childRoute === childRoute);
|
|
2588
3429
|
if (!sub) throw new Error(`No sub-resource "${childRoute}" on "${this.config.name}"`);
|
|
3430
|
+
if (sub.childKind === "custom") {
|
|
3431
|
+
const findAll = childRepositoryFn(sub, "findAll", this.config.name);
|
|
3432
|
+
const result = await findAll(this.toId(parentId2), params, childCtx({
|
|
3433
|
+
parentConfig: this.config,
|
|
3434
|
+
prisma: this.prisma,
|
|
3435
|
+
op: "findAll",
|
|
3436
|
+
parentId: this.toId(parentId2),
|
|
3437
|
+
params,
|
|
3438
|
+
request
|
|
3439
|
+
}));
|
|
3440
|
+
const rows = result?.data ?? [];
|
|
3441
|
+
const decorated2 = sub.hooks?.afterRead ? await Promise.all(rows.map((row) => sub.hooks.afterRead(row, {
|
|
3442
|
+
prisma: this.prisma,
|
|
3443
|
+
op: "findAll",
|
|
3444
|
+
request,
|
|
3445
|
+
parent: this.parentHookContext(parentId2)
|
|
3446
|
+
}))) : rows;
|
|
3447
|
+
const labeled2 = sub.valueLabelColumns?.length ? decorated2.map((r) => applyValueLabelColumns(r, sub.valueLabelColumns)) : decorated2;
|
|
3448
|
+
return {
|
|
3449
|
+
data: labeled2,
|
|
3450
|
+
count: result?.count ?? labeled2.length
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
2589
3453
|
const childModel = this.prisma[sub.childModel];
|
|
2590
3454
|
if (!childModel) throw new Error(`Prisma model "${sub.childModel}" not found`);
|
|
2591
3455
|
const where = {
|
|
2592
3456
|
...this.buildWhere(params.filter),
|
|
2593
|
-
[sub.foreignKey]: this.toId(
|
|
3457
|
+
[sub.foreignKey]: this.toId(parentId2)
|
|
2594
3458
|
};
|
|
2595
3459
|
const includeClause = buildIncludeClause(sub.include);
|
|
2596
3460
|
const childSort = orderableChildSort(sanitizeValueLabelSort(params.sort, sub.valueLabelColumns), childModel, sub);
|
|
@@ -2598,7 +3462,7 @@ var ReadRepository = class {
|
|
|
2598
3462
|
childModel.findMany({
|
|
2599
3463
|
where,
|
|
2600
3464
|
take: params.pageSize,
|
|
2601
|
-
skip:
|
|
3465
|
+
skip: offsetOf(params),
|
|
2602
3466
|
orderBy: childSort ? buildChildSortClause(childSort, params.sortDir) : void 0,
|
|
2603
3467
|
...includeClause && {
|
|
2604
3468
|
include: includeClause
|
|
@@ -2608,10 +3472,12 @@ var ReadRepository = class {
|
|
|
2608
3472
|
where
|
|
2609
3473
|
})
|
|
2610
3474
|
]);
|
|
2611
|
-
const withCalc = sub.calculatedColumns?.length ? await mergeCalculatedColumnsForRows(data, sub.calculatedColumns, sub.childModel, this.prisma) : data;
|
|
3475
|
+
const withCalc = sub.calculatedColumns?.length ? await mergeCalculatedColumnsForRows(data, sub.calculatedColumns, sub.childModel, this.prisma, sub.idField ?? "id") : data;
|
|
2612
3476
|
const decorated = sub.hooks?.afterRead ? await Promise.all(withCalc.map((row) => sub.hooks.afterRead(row, {
|
|
2613
3477
|
prisma: this.prisma,
|
|
2614
|
-
op: "findAll"
|
|
3478
|
+
op: "findAll",
|
|
3479
|
+
request,
|
|
3480
|
+
parent: this.parentHookContext(parentId2)
|
|
2615
3481
|
}))) : withCalc;
|
|
2616
3482
|
const labeled = sub.valueLabelColumns?.length ? decorated.map((r) => applyValueLabelColumns(r, sub.valueLabelColumns)) : decorated;
|
|
2617
3483
|
return {
|
|
@@ -2623,7 +3489,30 @@ var ReadRepository = class {
|
|
|
2623
3489
|
* Fetch a single child record. When `parentId` is supplied the query also filters by the foreign key.
|
|
2624
3490
|
* @throws {NotFoundException} When no matching record is found.
|
|
2625
3491
|
*/
|
|
2626
|
-
async findOneChild(sub, childId,
|
|
3492
|
+
async findOneChild(sub, childId, parentId2, request) {
|
|
3493
|
+
if (sub.childKind === "custom") {
|
|
3494
|
+
if (parentId2 === void 0) {
|
|
3495
|
+
throw new import_common6.BadRequestException(`Sub-resource "${sub.childRoute}" of "${this.config.name}" requires a parent id.`);
|
|
3496
|
+
}
|
|
3497
|
+
const findOne = childRepositoryFn(sub, "findOne", this.config.name);
|
|
3498
|
+
const row = await findOne(this.toId(parentId2), (sub.idType ?? "string") === "number" ? +childId : String(childId), childCtx({
|
|
3499
|
+
parentConfig: this.config,
|
|
3500
|
+
prisma: this.prisma,
|
|
3501
|
+
op: "findOne",
|
|
3502
|
+
parentId: this.toId(parentId2),
|
|
3503
|
+
id: childId,
|
|
3504
|
+
request
|
|
3505
|
+
}));
|
|
3506
|
+
if (row === null || row === void 0) {
|
|
3507
|
+
throw new import_common6.NotFoundException(`${sub.childRoute} with id ${childId} not found`);
|
|
3508
|
+
}
|
|
3509
|
+
return sub.hooks?.afterRead ? sub.hooks.afterRead(row, {
|
|
3510
|
+
prisma: this.prisma,
|
|
3511
|
+
op: "findOne",
|
|
3512
|
+
request,
|
|
3513
|
+
parent: this.parentHookContext(parentId2)
|
|
3514
|
+
}) : row;
|
|
3515
|
+
}
|
|
2627
3516
|
const childModel = this.prisma[sub.childModel];
|
|
2628
3517
|
if (!childModel) throw new Error(`Prisma model "${sub.childModel}" not found`);
|
|
2629
3518
|
const id = (sub.idType ?? "string") === "number" ? +childId : String(childId);
|
|
@@ -2631,7 +3520,7 @@ var ReadRepository = class {
|
|
|
2631
3520
|
const where = {
|
|
2632
3521
|
[idField]: id
|
|
2633
3522
|
};
|
|
2634
|
-
if (
|
|
3523
|
+
if (parentId2 !== void 0) where[sub.foreignKey] = this.toId(parentId2);
|
|
2635
3524
|
const includeClause = buildIncludeClause(sub.include);
|
|
2636
3525
|
const record = await childModel.findFirst({
|
|
2637
3526
|
where,
|
|
@@ -2639,24 +3528,26 @@ var ReadRepository = class {
|
|
|
2639
3528
|
include: includeClause
|
|
2640
3529
|
}
|
|
2641
3530
|
});
|
|
2642
|
-
if (!record) throw new
|
|
3531
|
+
if (!record) throw new import_common6.NotFoundException(`${sub.childRoute} with id ${childId} not found`);
|
|
2643
3532
|
const [withCalc] = sub.calculatedColumns?.length ? await mergeCalculatedColumnsForRows([
|
|
2644
3533
|
record
|
|
2645
|
-
], sub.calculatedColumns, sub.childModel, this.prisma) : [
|
|
3534
|
+
], sub.calculatedColumns, sub.childModel, this.prisma, sub.idField ?? "id") : [
|
|
2646
3535
|
record
|
|
2647
3536
|
];
|
|
2648
3537
|
if (sub.hooks?.afterRead) return sub.hooks.afterRead(withCalc, {
|
|
2649
3538
|
prisma: this.prisma,
|
|
2650
|
-
op: "findOne"
|
|
3539
|
+
op: "findOne",
|
|
3540
|
+
request,
|
|
3541
|
+
parent: this.parentHookContext(parentId2)
|
|
2651
3542
|
});
|
|
2652
3543
|
return withCalc;
|
|
2653
3544
|
}
|
|
2654
3545
|
};
|
|
2655
3546
|
|
|
2656
3547
|
// src/lib/crud/schema.utils.ts
|
|
2657
|
-
var
|
|
3548
|
+
var import_zod27 = require("zod");
|
|
2658
3549
|
function isZodSchema(schema) {
|
|
2659
|
-
return schema instanceof
|
|
3550
|
+
return schema instanceof import_zod27.ZodObject;
|
|
2660
3551
|
}
|
|
2661
3552
|
__name(isZodSchema, "isZodSchema");
|
|
2662
3553
|
var isNullableProperty2 = /* @__PURE__ */ __name((property) => {
|
|
@@ -2670,7 +3561,7 @@ var dropNullableFromRequired2 = /* @__PURE__ */ __name((jsonSchema) => {
|
|
|
2670
3561
|
}, "dropNullableFromRequired");
|
|
2671
3562
|
function toJsonSchema(schema) {
|
|
2672
3563
|
if (isZodSchema(schema)) {
|
|
2673
|
-
const jsonSchema = (0,
|
|
3564
|
+
const jsonSchema = (0, import_zod27.toJSONSchema)(schema, {
|
|
2674
3565
|
target: "openApi3",
|
|
2675
3566
|
...jsonSchemaOpts
|
|
2676
3567
|
});
|
|
@@ -2723,24 +3614,25 @@ function toSelectFields(schema) {
|
|
|
2723
3614
|
__name(toSelectFields, "toSelectFields");
|
|
2724
3615
|
|
|
2725
3616
|
// src/lib/crud/write.repository.ts
|
|
2726
|
-
var
|
|
2727
|
-
|
|
2728
|
-
// src/lib/crud/constants.ts
|
|
2729
|
-
var PRISMA_NOT_FOUND_CODE = "P2025";
|
|
2730
|
-
var DEFAULT_ID_FIELD = "id";
|
|
2731
|
-
|
|
2732
|
-
// src/lib/crud/write.repository.ts
|
|
2733
|
-
var normalizeValueLabels = /* @__PURE__ */ __name((data, cols) => {
|
|
2734
|
-
if (!data || typeof data !== "object" || Array.isArray(data) || !cols?.length) return data;
|
|
2735
|
-
const out = {
|
|
2736
|
-
...data
|
|
2737
|
-
};
|
|
2738
|
-
for (const { field } of cols) {
|
|
2739
|
-
if (field in out) out[field] = fromValueLabel(out[field]);
|
|
2740
|
-
}
|
|
2741
|
-
return out;
|
|
2742
|
-
}, "normalizeValueLabels");
|
|
3617
|
+
var import_common7 = require("@nestjs/common");
|
|
2743
3618
|
var includeRelationNames = /* @__PURE__ */ __name((include) => new Set((include ?? []).map((e) => typeof e === "string" ? e : e.relation)), "includeRelationNames");
|
|
3619
|
+
var PRISMA_RELATION_WRITE_KEYS = /* @__PURE__ */ new Set([
|
|
3620
|
+
"connect",
|
|
3621
|
+
"connectOrCreate",
|
|
3622
|
+
"create",
|
|
3623
|
+
"createMany",
|
|
3624
|
+
"set",
|
|
3625
|
+
"disconnect",
|
|
3626
|
+
"update",
|
|
3627
|
+
"updateMany",
|
|
3628
|
+
"upsert",
|
|
3629
|
+
"delete",
|
|
3630
|
+
"deleteMany"
|
|
3631
|
+
]);
|
|
3632
|
+
var isPrismaRelationWrite = /* @__PURE__ */ __name((value) => {
|
|
3633
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
3634
|
+
return Object.keys(value).some((k) => PRISMA_RELATION_WRITE_KEYS.has(k));
|
|
3635
|
+
}, "isPrismaRelationWrite");
|
|
2744
3636
|
var WriteRepository = class {
|
|
2745
3637
|
static {
|
|
2746
3638
|
__name(this, "WriteRepository");
|
|
@@ -2757,7 +3649,7 @@ var WriteRepository = class {
|
|
|
2757
3649
|
return (this.config.idType ?? "string") === "number" ? +id : String(id);
|
|
2758
3650
|
}
|
|
2759
3651
|
notFound(id) {
|
|
2760
|
-
return new
|
|
3652
|
+
return new import_common7.NotFoundException(`${this.config.name} with id ${id} not found`);
|
|
2761
3653
|
}
|
|
2762
3654
|
stripSubResourceKeys(data) {
|
|
2763
3655
|
if (!data || typeof data !== "object" || Array.isArray(data)) return data;
|
|
@@ -2771,26 +3663,29 @@ var WriteRepository = class {
|
|
|
2771
3663
|
if (!nonCreateable.size) return data;
|
|
2772
3664
|
return Object.fromEntries(Object.entries(data).filter(([k]) => !nonCreateable.has(k)));
|
|
2773
3665
|
}
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
3666
|
+
/**
|
|
3667
|
+
* The parent a *child* write is scoped to, for the hook context.
|
|
3668
|
+
*
|
|
3669
|
+
* A sub-resource is served by the parent's controller, so its parent id arrives
|
|
3670
|
+
* as the parent's own `:id` — hence `param: 'id'`. A resource that declares
|
|
3671
|
+
* `parent` names its own param and goes through the custom adapter instead.
|
|
3672
|
+
*/
|
|
3673
|
+
parentHookContext(parentId1) {
|
|
3674
|
+
return {
|
|
3675
|
+
route: this.config.route,
|
|
3676
|
+
param: "id",
|
|
3677
|
+
id: this.toId(parentId1)
|
|
3678
|
+
};
|
|
2782
3679
|
}
|
|
2783
|
-
async
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
id
|
|
2789
|
-
}) : result;
|
|
3680
|
+
async prepare(data, op, id, request) {
|
|
3681
|
+
return prepareWrite(data, op, this.config, this.prisma, id, request);
|
|
3682
|
+
}
|
|
3683
|
+
async postWrite(result, op, id, request) {
|
|
3684
|
+
return postWrite(result, op, this.config, this.prisma, id, request);
|
|
2790
3685
|
}
|
|
2791
3686
|
upsertWhere(data) {
|
|
2792
3687
|
const keys = upsertOnFor(resolveDefinition(this.config));
|
|
2793
|
-
if (!keys) throw new
|
|
3688
|
+
if (!keys) throw new import_common7.BadRequestException(`${this.config.name} has no upsertOn configured`);
|
|
2794
3689
|
if (typeof keys === "string") return {
|
|
2795
3690
|
[keys]: data[keys]
|
|
2796
3691
|
};
|
|
@@ -2802,61 +3697,62 @@ var WriteRepository = class {
|
|
|
2802
3697
|
]))
|
|
2803
3698
|
};
|
|
2804
3699
|
}
|
|
2805
|
-
async create(data) {
|
|
3700
|
+
async create(data, request) {
|
|
2806
3701
|
const result = await this.prismaModel.create({
|
|
2807
|
-
data: await this.prepare(this.stripSubResourceKeys(data), "create")
|
|
3702
|
+
data: await this.prepare(this.stripSubResourceKeys(data), "create", void 0, request)
|
|
2808
3703
|
});
|
|
2809
|
-
return this.postWrite(result, "create");
|
|
3704
|
+
return this.postWrite(result, "create", void 0, request);
|
|
2810
3705
|
}
|
|
2811
|
-
async update(id, data) {
|
|
3706
|
+
async update(id, data, request) {
|
|
2812
3707
|
const idField = this.config.idField ?? "id";
|
|
2813
3708
|
try {
|
|
2814
3709
|
const result = await this.prismaModel.update({
|
|
2815
3710
|
where: {
|
|
2816
3711
|
[idField]: this.toId(id)
|
|
2817
3712
|
},
|
|
2818
|
-
data: await this.prepare(this.stripSubResourceKeys(data), "update", this.toId(id))
|
|
3713
|
+
data: await this.prepare(this.stripSubResourceKeys(data), "update", this.toId(id), request)
|
|
2819
3714
|
});
|
|
2820
|
-
return this.postWrite(result, "update", this.toId(id));
|
|
3715
|
+
return this.postWrite(result, "update", this.toId(id), request);
|
|
2821
3716
|
} catch (e) {
|
|
2822
3717
|
if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
|
|
2823
3718
|
throw e;
|
|
2824
3719
|
}
|
|
2825
3720
|
}
|
|
2826
|
-
async patch(id, data) {
|
|
3721
|
+
async patch(id, data, request) {
|
|
2827
3722
|
const idField = this.config.idField ?? "id";
|
|
2828
3723
|
try {
|
|
2829
3724
|
const result = await this.prismaModel.update({
|
|
2830
3725
|
where: {
|
|
2831
3726
|
[idField]: this.toId(id)
|
|
2832
3727
|
},
|
|
2833
|
-
data: await this.prepare(this.stripSubResourceKeys(data), "patch", this.toId(id))
|
|
3728
|
+
data: await this.prepare(this.stripSubResourceKeys(data), "patch", this.toId(id), request)
|
|
2834
3729
|
});
|
|
2835
|
-
return this.postWrite(result, "patch", this.toId(id));
|
|
3730
|
+
return this.postWrite(result, "patch", this.toId(id), request);
|
|
2836
3731
|
} catch (e) {
|
|
2837
3732
|
if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
|
|
2838
3733
|
throw e;
|
|
2839
3734
|
}
|
|
2840
3735
|
}
|
|
2841
|
-
async upsert(data) {
|
|
3736
|
+
async upsert(data, request) {
|
|
2842
3737
|
const where = this.upsertWhere(data);
|
|
2843
3738
|
const existing = await this.prismaModel.findFirst({
|
|
2844
3739
|
where
|
|
2845
3740
|
});
|
|
2846
3741
|
const op = existing ? "update" : "create";
|
|
2847
|
-
const
|
|
3742
|
+
const existingId = existing ? existing[this.config.idField ?? DEFAULT_ID_FIELD] : void 0;
|
|
3743
|
+
const prepared = await this.prepare(this.stripSubResourceKeys(data), op, existingId, request);
|
|
2848
3744
|
const result = await this.prismaModel.upsert({
|
|
2849
3745
|
where,
|
|
2850
3746
|
create: prepared,
|
|
2851
3747
|
update: prepared
|
|
2852
3748
|
});
|
|
2853
|
-
return this.postWrite(result, op,
|
|
3749
|
+
return this.postWrite(result, op, existingId, request);
|
|
2854
3750
|
}
|
|
2855
3751
|
/** Upsert multiple rows in parallel. */
|
|
2856
|
-
upsertMany(rows) {
|
|
2857
|
-
return Promise.all(rows.map((r) => this.upsert(r)));
|
|
3752
|
+
upsertMany(rows, request) {
|
|
3753
|
+
return Promise.all(rows.map((r) => this.upsert(r, request)));
|
|
2858
3754
|
}
|
|
2859
|
-
async delete(id) {
|
|
3755
|
+
async delete(id, request) {
|
|
2860
3756
|
const idField = this.config.idField ?? "id";
|
|
2861
3757
|
try {
|
|
2862
3758
|
const result = await this.prismaModel.delete({
|
|
@@ -2864,40 +3760,96 @@ var WriteRepository = class {
|
|
|
2864
3760
|
[idField]: this.toId(id)
|
|
2865
3761
|
}
|
|
2866
3762
|
});
|
|
2867
|
-
return this.postWrite(result, "delete", this.toId(id));
|
|
3763
|
+
return this.postWrite(result, "delete", this.toId(id), request);
|
|
2868
3764
|
} catch (e) {
|
|
2869
3765
|
if (e?.code === PRISMA_NOT_FOUND_CODE) throw this.notFound(id);
|
|
2870
3766
|
throw e;
|
|
2871
3767
|
}
|
|
2872
3768
|
}
|
|
2873
3769
|
/**
|
|
3770
|
+
* Write to a **custom** sub-resource by delegating to the child's own
|
|
3771
|
+
* repository.
|
|
3772
|
+
*
|
|
3773
|
+
* The child has no Prisma model, so `prisma[childModel]` is not an option.
|
|
3774
|
+
* Value-label normalisation and the child's `beforeWrite`/`afterWrite` hooks
|
|
3775
|
+
* still apply, so a custom child behaves like a Prisma one from the caller's
|
|
3776
|
+
* point of view.
|
|
3777
|
+
*/
|
|
3778
|
+
async delegateChildWrite(sub, op, parentId1, childId, data, request) {
|
|
3779
|
+
if (parentId1 === void 0) {
|
|
3780
|
+
throw new import_common7.BadRequestException(`Sub-resource "${sub.childRoute}" of "${this.config.name}" requires a parent id.`);
|
|
3781
|
+
}
|
|
3782
|
+
const fn = childRepositoryFn(sub, op, this.config.name);
|
|
3783
|
+
const id = childId === void 0 ? void 0 : (sub.idType ?? "string") === "number" ? +childId : String(childId);
|
|
3784
|
+
const ctx = childCtx({
|
|
3785
|
+
parentConfig: this.config,
|
|
3786
|
+
prisma: this.prisma,
|
|
3787
|
+
op,
|
|
3788
|
+
parentId: this.toId(parentId1),
|
|
3789
|
+
id,
|
|
3790
|
+
request
|
|
3791
|
+
});
|
|
3792
|
+
let payload;
|
|
3793
|
+
if (op !== "delete") {
|
|
3794
|
+
const stripped = op === "create" ? this.stripNonCreateableChildFields(data, sub) : data;
|
|
3795
|
+
const normalized = normalizeValueLabels(stripped, sub.valueLabelColumns);
|
|
3796
|
+
payload = sub.hooks?.beforeWrite ? await sub.hooks.beforeWrite(normalized, {
|
|
3797
|
+
prisma: this.prisma,
|
|
3798
|
+
op: op === "patch" ? "patch" : op,
|
|
3799
|
+
...id !== void 0 && {
|
|
3800
|
+
id
|
|
3801
|
+
},
|
|
3802
|
+
request,
|
|
3803
|
+
parent: ctx.parent
|
|
3804
|
+
}) : normalized;
|
|
3805
|
+
}
|
|
3806
|
+
const result = op === "create" ? await fn(this.toId(parentId1), payload, ctx) : op === "delete" ? await fn(this.toId(parentId1), id, ctx) : await fn(this.toId(parentId1), id, payload, ctx);
|
|
3807
|
+
return sub.hooks?.afterWrite ? sub.hooks.afterWrite(result, {
|
|
3808
|
+
prisma: this.prisma,
|
|
3809
|
+
op: op === "patch" ? "patch" : op,
|
|
3810
|
+
...id !== void 0 && {
|
|
3811
|
+
id
|
|
3812
|
+
},
|
|
3813
|
+
request
|
|
3814
|
+
}) : result;
|
|
3815
|
+
}
|
|
3816
|
+
/**
|
|
2874
3817
|
* Create a child record and attach it to the parent via the configured foreign key.
|
|
2875
3818
|
* Fields marked `createable: false` in the form view are stripped before writing.
|
|
2876
3819
|
*/
|
|
2877
|
-
async createChild(
|
|
3820
|
+
async createChild(parentId1, sub, data, request) {
|
|
3821
|
+
if (sub.childKind === "custom") {
|
|
3822
|
+
return this.delegateChildWrite(sub, "create", parentId1, void 0, data, request);
|
|
3823
|
+
}
|
|
2878
3824
|
const childModel = this.prisma[sub.childModel];
|
|
2879
3825
|
if (!childModel) throw new Error(`Prisma model "${sub.childModel}" not found`);
|
|
2880
3826
|
const stripped = this.stripNonCreateableChildFields(data, sub);
|
|
2881
3827
|
const normalized = normalizeValueLabels(stripped, sub.valueLabelColumns);
|
|
2882
3828
|
const payload = {
|
|
2883
3829
|
...normalized,
|
|
2884
|
-
[sub.foreignKey]: this.toId(
|
|
3830
|
+
[sub.foreignKey]: this.toId(parentId1)
|
|
2885
3831
|
};
|
|
2886
3832
|
const prepared = sub.hooks?.beforeWrite ? await sub.hooks.beforeWrite(payload, {
|
|
2887
3833
|
prisma: this.prisma,
|
|
2888
|
-
op: "create"
|
|
3834
|
+
op: "create",
|
|
3835
|
+
request,
|
|
3836
|
+
parent: this.parentHookContext(parentId1)
|
|
2889
3837
|
}) : payload;
|
|
2890
3838
|
const includeKeys = includeRelationNames(sub.include);
|
|
3839
|
+
const preparedEntries = prepared;
|
|
2891
3840
|
const prismaData = {
|
|
2892
|
-
...Object.fromEntries(Object.entries(
|
|
2893
|
-
|
|
3841
|
+
...Object.fromEntries(Object.entries(preparedEntries).filter(([k, v]) => !includeKeys.has(k) || isPrismaRelationWrite(v))),
|
|
3842
|
+
...sub.foreignKey in preparedEntries ? {
|
|
3843
|
+
[sub.foreignKey]: this.toId(parentId1)
|
|
3844
|
+
} : {}
|
|
2894
3845
|
};
|
|
2895
3846
|
const result = await childModel.create({
|
|
2896
3847
|
data: prismaData
|
|
2897
3848
|
});
|
|
2898
3849
|
return sub.hooks?.afterWrite ? sub.hooks.afterWrite(result, {
|
|
2899
3850
|
prisma: this.prisma,
|
|
2900
|
-
op: "create"
|
|
3851
|
+
op: "create",
|
|
3852
|
+
request
|
|
2901
3853
|
}) : result;
|
|
2902
3854
|
}
|
|
2903
3855
|
/**
|
|
@@ -2905,7 +3857,10 @@ var WriteRepository = class {
|
|
|
2905
3857
|
* receive non-scalar fields.
|
|
2906
3858
|
* @throws {NotFoundException} When the child record does not exist (Prisma P2025).
|
|
2907
3859
|
*/
|
|
2908
|
-
async updateChild(sub, childId, data) {
|
|
3860
|
+
async updateChild(sub, childId, data, request) {
|
|
3861
|
+
if (sub.childKind === "custom") {
|
|
3862
|
+
return this.delegateChildWrite(sub, "update", parentIdFromRequest(request), childId, data, request);
|
|
3863
|
+
}
|
|
2909
3864
|
const childModel = this.prisma[sub.childModel];
|
|
2910
3865
|
if (!childModel) throw new Error(`Prisma model "${sub.childModel}" not found`);
|
|
2911
3866
|
const id = (sub.idType ?? "string") === "number" ? +childId : String(childId);
|
|
@@ -2913,10 +3868,12 @@ var WriteRepository = class {
|
|
|
2913
3868
|
const afterHook = sub.hooks?.beforeWrite ? await sub.hooks.beforeWrite(normalized, {
|
|
2914
3869
|
prisma: this.prisma,
|
|
2915
3870
|
op: "update",
|
|
2916
|
-
id
|
|
3871
|
+
id,
|
|
3872
|
+
request,
|
|
3873
|
+
parent: this.parentHookContext(parentId)
|
|
2917
3874
|
}) : normalized;
|
|
2918
3875
|
const includeKeys = includeRelationNames(sub.include);
|
|
2919
|
-
const prepared = Object.fromEntries(Object.entries(afterHook).filter(([k]) => !includeKeys.has(k)));
|
|
3876
|
+
const prepared = Object.fromEntries(Object.entries(afterHook).filter(([k, v]) => !includeKeys.has(k) || isPrismaRelationWrite(v)));
|
|
2920
3877
|
try {
|
|
2921
3878
|
const result = await childModel.update({
|
|
2922
3879
|
where: {
|
|
@@ -2927,10 +3884,11 @@ var WriteRepository = class {
|
|
|
2927
3884
|
return sub.hooks?.afterWrite ? sub.hooks.afterWrite(result, {
|
|
2928
3885
|
prisma: this.prisma,
|
|
2929
3886
|
op: "update",
|
|
2930
|
-
id
|
|
3887
|
+
id,
|
|
3888
|
+
request
|
|
2931
3889
|
}) : result;
|
|
2932
3890
|
} catch (e) {
|
|
2933
|
-
if (e?.code === PRISMA_NOT_FOUND_CODE) throw new
|
|
3891
|
+
if (e?.code === PRISMA_NOT_FOUND_CODE) throw new import_common7.NotFoundException(`${sub.childRoute} with id ${childId} not found`);
|
|
2934
3892
|
throw e;
|
|
2935
3893
|
}
|
|
2936
3894
|
}
|
|
@@ -2939,7 +3897,10 @@ var WriteRepository = class {
|
|
|
2939
3897
|
* clause to prevent cross-parent deletions.
|
|
2940
3898
|
* @throws {NotFoundException} When no matching record is found.
|
|
2941
3899
|
*/
|
|
2942
|
-
async deleteChild(sub, childId,
|
|
3900
|
+
async deleteChild(sub, childId, parentId1, request) {
|
|
3901
|
+
if (sub.childKind === "custom") {
|
|
3902
|
+
return this.delegateChildWrite(sub, "delete", parentIdFromRequest(request, parentId1), childId, void 0, request);
|
|
3903
|
+
}
|
|
2943
3904
|
const childModel = this.prisma[sub.childModel];
|
|
2944
3905
|
if (!childModel) throw new Error(`Prisma model "${sub.childModel}" not found`);
|
|
2945
3906
|
const id = (sub.idType ?? "string") === "number" ? +childId : String(childId);
|
|
@@ -2947,26 +3908,36 @@ var WriteRepository = class {
|
|
|
2947
3908
|
const where = {
|
|
2948
3909
|
[idField]: id
|
|
2949
3910
|
};
|
|
2950
|
-
if (
|
|
3911
|
+
if (parentId1 !== void 0) where[sub.foreignKey] = this.toId(parentId1);
|
|
2951
3912
|
try {
|
|
2952
3913
|
const result = await childModel.deleteMany({
|
|
2953
3914
|
where
|
|
2954
3915
|
});
|
|
2955
|
-
if (result.count === 0) throw new
|
|
3916
|
+
if (result.count === 0) throw new import_common7.NotFoundException(`${sub.childRoute} with id ${childId} not found`);
|
|
2956
3917
|
return sub.hooks?.afterWrite ? sub.hooks.afterWrite(result, {
|
|
2957
3918
|
prisma: this.prisma,
|
|
2958
3919
|
op: "delete",
|
|
2959
|
-
id
|
|
3920
|
+
id,
|
|
3921
|
+
request
|
|
2960
3922
|
}) : result;
|
|
2961
3923
|
} catch (e) {
|
|
2962
|
-
if (e?.code === PRISMA_NOT_FOUND_CODE) throw new
|
|
3924
|
+
if (e?.code === PRISMA_NOT_FOUND_CODE) throw new import_common7.NotFoundException(`${sub.childRoute} with id ${childId} not found`);
|
|
2963
3925
|
throw e;
|
|
2964
3926
|
}
|
|
2965
3927
|
}
|
|
2966
3928
|
};
|
|
2967
3929
|
|
|
2968
3930
|
// src/lib/crud/crud-repository.factory.ts
|
|
2969
|
-
function createCrudRepository(prisma, config) {
|
|
3931
|
+
function createCrudRepository(prisma, config, dataSources) {
|
|
3932
|
+
if (config.kind === "custom") {
|
|
3933
|
+
return createCustomRepository(prisma, config, dataSources ?? {
|
|
3934
|
+
resolve: /* @__PURE__ */ __name(() => prisma, "resolve"),
|
|
3935
|
+
entries: /* @__PURE__ */ __name(() => [], "entries")
|
|
3936
|
+
}, config.repository);
|
|
3937
|
+
}
|
|
3938
|
+
if (!config.model) {
|
|
3939
|
+
throw new Error(`Resource "${config.name}" has no "model". A prisma-backed resource must name its Prisma model; set "kind": "custom" for a resource with no model.`);
|
|
3940
|
+
}
|
|
2970
3941
|
const model = prisma[config.model];
|
|
2971
3942
|
if (!model) {
|
|
2972
3943
|
throw new Error(`Model "${config.model}" not found on the provided PrismaClient. Check the resource config for "${config.name}".`);
|
|
@@ -2999,7 +3970,7 @@ function createCrudRepository(prisma, config) {
|
|
|
2999
3970
|
__name(createCrudRepository, "createCrudRepository");
|
|
3000
3971
|
|
|
3001
3972
|
// src/lib/crud/data-source/data-source.registry.ts
|
|
3002
|
-
var
|
|
3973
|
+
var import_common8 = require("@nestjs/common");
|
|
3003
3974
|
function _ts_decorate4(decorators, target, key, desc2) {
|
|
3004
3975
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
3005
3976
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -3070,36 +4041,16 @@ var DataSourceRegistry = class {
|
|
|
3070
4041
|
}
|
|
3071
4042
|
};
|
|
3072
4043
|
DataSourceRegistry = _ts_decorate4([
|
|
3073
|
-
(0,
|
|
4044
|
+
(0, import_common8.Injectable)(),
|
|
3074
4045
|
_ts_metadata3("design:type", Function),
|
|
3075
4046
|
_ts_metadata3("design:paramtypes", [
|
|
3076
4047
|
Array
|
|
3077
4048
|
])
|
|
3078
4049
|
], DataSourceRegistry);
|
|
3079
4050
|
|
|
3080
|
-
// src/lib/crud/resource/resource-load-errors.registry.ts
|
|
3081
|
-
var ResourceLoadErrorsRegistry = class ResourceLoadErrorsRegistry2 {
|
|
3082
|
-
static {
|
|
3083
|
-
__name(this, "ResourceLoadErrorsRegistry");
|
|
3084
|
-
}
|
|
3085
|
-
errors = [];
|
|
3086
|
-
record(e) {
|
|
3087
|
-
this.errors.push(e);
|
|
3088
|
-
}
|
|
3089
|
-
getAll() {
|
|
3090
|
-
return [
|
|
3091
|
-
...this.errors
|
|
3092
|
-
];
|
|
3093
|
-
}
|
|
3094
|
-
clear() {
|
|
3095
|
-
this.errors = [];
|
|
3096
|
-
}
|
|
3097
|
-
};
|
|
3098
|
-
var resourceLoadErrorsRegistry = new ResourceLoadErrorsRegistry();
|
|
3099
|
-
|
|
3100
4051
|
// src/lib/crud/data-source/data-source.loader.ts
|
|
3101
4052
|
var import_node_fs2 = require("fs");
|
|
3102
|
-
var
|
|
4053
|
+
var import_node_path5 = require("path");
|
|
3103
4054
|
var loadDataSourcesFromDir = /* @__PURE__ */ __name(async (dirPath) => {
|
|
3104
4055
|
if (!(0, import_node_fs2.existsSync)(dirPath)) return [];
|
|
3105
4056
|
const entries = (0, import_node_fs2.readdirSync)(dirPath, {
|
|
@@ -3108,8 +4059,8 @@ var loadDataSourcesFromDir = /* @__PURE__ */ __name(async (dirPath) => {
|
|
|
3108
4059
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3109
4060
|
const results = [];
|
|
3110
4061
|
for (const dir of dirs) {
|
|
3111
|
-
const basePath = (0,
|
|
3112
|
-
const jsonFile = (0,
|
|
4062
|
+
const basePath = (0, import_node_path5.join)(dirPath, dir);
|
|
4063
|
+
const jsonFile = (0, import_node_path5.join)(basePath, "data-source.json");
|
|
3113
4064
|
if (!(0, import_node_fs2.existsSync)(jsonFile)) continue;
|
|
3114
4065
|
let _config;
|
|
3115
4066
|
try {
|
|
@@ -3149,14 +4100,14 @@ var findModule2 = /* @__PURE__ */ __name((dir, name) => {
|
|
|
3149
4100
|
".ts",
|
|
3150
4101
|
".js"
|
|
3151
4102
|
]) {
|
|
3152
|
-
const p = (0,
|
|
4103
|
+
const p = (0, import_node_path5.join)(dir, `${name}${ext}`);
|
|
3153
4104
|
if ((0, import_node_fs2.existsSync)(p)) return p;
|
|
3154
4105
|
}
|
|
3155
4106
|
return void 0;
|
|
3156
4107
|
}, "findModule");
|
|
3157
4108
|
|
|
3158
4109
|
// src/lib/crud/operations/register-actions.ts
|
|
3159
|
-
var
|
|
4110
|
+
var import_common9 = require("@nestjs/common");
|
|
3160
4111
|
var import_swagger2 = require("@nestjs/swagger");
|
|
3161
4112
|
|
|
3162
4113
|
// src/lib/crud/operations/decorator.utils.ts
|
|
@@ -3180,8 +4131,8 @@ var registerActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
|
3180
4131
|
return action.procedure(this.repo.prisma, recordId);
|
|
3181
4132
|
});
|
|
3182
4133
|
const d = desc(cls, methodName);
|
|
3183
|
-
(0,
|
|
3184
|
-
(0,
|
|
4134
|
+
(0, import_common9.Post)(`procedure/${action.id}/:recordId`)(cls.prototype, methodName, d);
|
|
4135
|
+
(0, import_common9.Param)("recordId")(cls.prototype, methodName, 0);
|
|
3185
4136
|
(0, import_swagger2.ApiOperation)({
|
|
3186
4137
|
summary: `Execute action "${action.label}" on a ${name}`
|
|
3187
4138
|
})(cls.prototype, methodName, d);
|
|
@@ -3205,7 +4156,7 @@ var registerTableActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
|
3205
4156
|
return action.procedure(this.repo.prisma);
|
|
3206
4157
|
});
|
|
3207
4158
|
const d = desc(cls, methodName);
|
|
3208
|
-
(0,
|
|
4159
|
+
(0, import_common9.Post)(`table-action/${action.id}`)(cls.prototype, methodName, d);
|
|
3209
4160
|
(0, import_swagger2.ApiOperation)({
|
|
3210
4161
|
summary: `Execute table action "${action.label ?? action.id}" on ${name}`
|
|
3211
4162
|
})(cls.prototype, methodName, d);
|
|
@@ -3217,7 +4168,7 @@ var registerTableActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
|
3217
4168
|
}, "registerTableActionRoutes");
|
|
3218
4169
|
|
|
3219
4170
|
// src/lib/crud/operations/register-create.ts
|
|
3220
|
-
var
|
|
4171
|
+
var import_common10 = require("@nestjs/common");
|
|
3221
4172
|
var import_swagger3 = require("@nestjs/swagger");
|
|
3222
4173
|
var defaultCreate = /* @__PURE__ */ __name((ctx) => {
|
|
3223
4174
|
if (!isOperationEnabled(ctx.definition, "create")) return null;
|
|
@@ -3227,13 +4178,14 @@ var defaultCreate = /* @__PURE__ */ __name((ctx) => {
|
|
|
3227
4178
|
route: "",
|
|
3228
4179
|
methodName,
|
|
3229
4180
|
name: config.name,
|
|
3230
|
-
createFn: /* @__PURE__ */ __name(function(body) {
|
|
3231
|
-
return this.repo.create(body);
|
|
4181
|
+
createFn: /* @__PURE__ */ __name(function(body, req) {
|
|
4182
|
+
return this.repo.create(body, req);
|
|
3232
4183
|
}, "createFn"),
|
|
3233
4184
|
decorators: /* @__PURE__ */ __name(() => {
|
|
3234
4185
|
bodyDecorator(createSchema, {
|
|
3235
4186
|
coerceNullableUndefinedToNull: true
|
|
3236
4187
|
})(cls.prototype, methodName, 0);
|
|
4188
|
+
(0, import_common10.Req)()(cls.prototype, methodName, 1);
|
|
3237
4189
|
}, "decorators")
|
|
3238
4190
|
};
|
|
3239
4191
|
}, "defaultCreate");
|
|
@@ -3241,12 +4193,13 @@ var childCreate = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
3241
4193
|
if (!isOperationEnabled(sub.operations, "create")) return null;
|
|
3242
4194
|
const { cls } = ctx;
|
|
3243
4195
|
const methodName = `createChild_${sub.childRoute}`;
|
|
3244
|
-
const createFn = /* @__PURE__ */ __name(async function(id, body) {
|
|
3245
|
-
return this.repo.createChild(id, sub, body);
|
|
4196
|
+
const createFn = /* @__PURE__ */ __name(async function(id, body, req) {
|
|
4197
|
+
return this.repo.createChild(id, sub, body, req);
|
|
3246
4198
|
}, "createFn");
|
|
3247
4199
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
3248
|
-
(0,
|
|
3249
|
-
(0,
|
|
4200
|
+
(0, import_common10.Param)("id")(cls.prototype, methodName, 0);
|
|
4201
|
+
(0, import_common10.Body)()(cls.prototype, methodName, 1);
|
|
4202
|
+
(0, import_common10.Req)()(cls.prototype, methodName, 2);
|
|
3250
4203
|
}, "decorators");
|
|
3251
4204
|
return {
|
|
3252
4205
|
route: `:id/${sub.childRoute}`,
|
|
@@ -3264,7 +4217,7 @@ var registerCreate = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3264
4217
|
const { cls } = ctx;
|
|
3265
4218
|
def(cls, methodName, properties.createFn);
|
|
3266
4219
|
const d = desc(cls, methodName);
|
|
3267
|
-
(0,
|
|
4220
|
+
(0, import_common10.Post)(route)(cls.prototype, methodName, d);
|
|
3268
4221
|
(0, import_swagger3.ApiOperation)({
|
|
3269
4222
|
summary: `Create a ${name}`
|
|
3270
4223
|
})(cls.prototype, methodName, d);
|
|
@@ -3276,7 +4229,7 @@ var registerCreate = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3276
4229
|
}, "registerCreate");
|
|
3277
4230
|
|
|
3278
4231
|
// src/lib/crud/operations/register-delete.ts
|
|
3279
|
-
var
|
|
4232
|
+
var import_common11 = require("@nestjs/common");
|
|
3280
4233
|
var import_swagger4 = require("@nestjs/swagger");
|
|
3281
4234
|
var defaultDelete = /* @__PURE__ */ __name((ctx) => {
|
|
3282
4235
|
if (!isOperationEnabled(ctx.definition, "delete")) return null;
|
|
@@ -3289,8 +4242,8 @@ var defaultDelete = /* @__PURE__ */ __name((ctx) => {
|
|
|
3289
4242
|
name,
|
|
3290
4243
|
decorators: /* @__PURE__ */ __name(() => {
|
|
3291
4244
|
}, "decorators"),
|
|
3292
|
-
deleteFn: /* @__PURE__ */ __name(function(id) {
|
|
3293
|
-
return this.repo.delete(id);
|
|
4245
|
+
deleteFn: /* @__PURE__ */ __name(function(id, req) {
|
|
4246
|
+
return this.repo.delete(id, req);
|
|
3294
4247
|
}, "deleteFn")
|
|
3295
4248
|
};
|
|
3296
4249
|
}, "defaultDelete");
|
|
@@ -3298,11 +4251,13 @@ var deleteChild = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
3298
4251
|
if (!isOperationEnabled(sub.operations, "delete")) return null;
|
|
3299
4252
|
const { cls } = ctx;
|
|
3300
4253
|
const methodName = `deleteChild_${sub.childRoute}`;
|
|
3301
|
-
const deleteFn = /* @__PURE__ */ __name(async function(childId,
|
|
3302
|
-
return this.repo.deleteChild(sub, childId,
|
|
4254
|
+
const deleteFn = /* @__PURE__ */ __name(async function(childId, parentId2, req) {
|
|
4255
|
+
return this.repo.deleteChild(sub, childId, parentId2, req);
|
|
3303
4256
|
}, "deleteFn");
|
|
3304
4257
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
3305
|
-
(0,
|
|
4258
|
+
(0, import_common11.Param)("childId")(cls.prototype, methodName, 0);
|
|
4259
|
+
(0, import_common11.Param)("id")(cls.prototype, methodName, 1);
|
|
4260
|
+
(0, import_common11.Req)()(cls.prototype, methodName, 2);
|
|
3306
4261
|
}, "decorators");
|
|
3307
4262
|
return {
|
|
3308
4263
|
route: `:id/${sub.childRoute}/:childId`,
|
|
@@ -3320,8 +4275,8 @@ var registerDelete = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3320
4275
|
const { methodName, route, name } = properties;
|
|
3321
4276
|
def(cls, methodName, properties.deleteFn);
|
|
3322
4277
|
const d = desc(cls, methodName);
|
|
3323
|
-
(0,
|
|
3324
|
-
(0,
|
|
4278
|
+
(0, import_common11.Delete)(route)(cls.prototype, methodName, d);
|
|
4279
|
+
(0, import_common11.Param)("id")(cls.prototype, methodName, 0);
|
|
3325
4280
|
(0, import_swagger4.ApiOperation)({
|
|
3326
4281
|
summary: `Delete ${name} record`
|
|
3327
4282
|
})(cls.prototype, methodName, d);
|
|
@@ -3330,10 +4285,11 @@ var registerDelete = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3330
4285
|
status: 200
|
|
3331
4286
|
})(cls.prototype, methodName, d);
|
|
3332
4287
|
properties.decorators();
|
|
4288
|
+
if (!sub) (0, import_common11.Req)()(cls.prototype, methodName, 1);
|
|
3333
4289
|
}, "registerDelete");
|
|
3334
4290
|
|
|
3335
4291
|
// src/lib/crud/operations/register-findall.ts
|
|
3336
|
-
var
|
|
4292
|
+
var import_common12 = require("@nestjs/common");
|
|
3337
4293
|
var import_swagger5 = require("@nestjs/swagger");
|
|
3338
4294
|
|
|
3339
4295
|
// src/lib/crud/request.dto.ts
|
|
@@ -3416,7 +4372,7 @@ var ZodValidationPipe = class {
|
|
|
3416
4372
|
};
|
|
3417
4373
|
|
|
3418
4374
|
// src/lib/crud/operations/register-findall.ts
|
|
3419
|
-
var _findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
|
|
4375
|
+
var _findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel, request) => {
|
|
3420
4376
|
const effectiveParams = {
|
|
3421
4377
|
...params
|
|
3422
4378
|
};
|
|
@@ -3426,10 +4382,13 @@ var _findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
|
|
|
3426
4382
|
`${lookupLabel}:${q}`
|
|
3427
4383
|
];
|
|
3428
4384
|
}
|
|
3429
|
-
const
|
|
3430
|
-
repo.findAll(effectiveParams),
|
|
4385
|
+
const { data, count } = repo.findAllWithCount ? await repo.findAllWithCount(effectiveParams, request) : await Promise.all([
|
|
4386
|
+
repo.findAll(effectiveParams, request),
|
|
3431
4387
|
repo.count(effectiveParams.filter)
|
|
3432
|
-
])
|
|
4388
|
+
]).then(([data2, count2]) => ({
|
|
4389
|
+
data: data2,
|
|
4390
|
+
count: count2
|
|
4391
|
+
}));
|
|
3433
4392
|
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
3434
4393
|
return {
|
|
3435
4394
|
data,
|
|
@@ -3444,8 +4403,8 @@ var _findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
|
|
|
3444
4403
|
}
|
|
3445
4404
|
};
|
|
3446
4405
|
}, "_findAll");
|
|
3447
|
-
var findAllByParent = /* @__PURE__ */ __name(async (repo, id, childRoute, params) => {
|
|
3448
|
-
const { data, count } = await repo.findAllByParent(id, childRoute, params);
|
|
4406
|
+
var findAllByParent = /* @__PURE__ */ __name(async (repo, id, childRoute, params, request) => {
|
|
4407
|
+
const { data, count } = await repo.findAllByParent(id, childRoute, params, request);
|
|
3449
4408
|
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
3450
4409
|
return {
|
|
3451
4410
|
data,
|
|
@@ -3464,8 +4423,8 @@ var defaultFindAll = /* @__PURE__ */ __name((ctx) => {
|
|
|
3464
4423
|
if (!isOperationEnabled(ctx.definition, "findAll")) return;
|
|
3465
4424
|
const { config } = ctx;
|
|
3466
4425
|
const lookupLabel = config.lookup?.label;
|
|
3467
|
-
const findAll = /* @__PURE__ */ __name(async function(params, q) {
|
|
3468
|
-
return _findAll(this.repo, params, q, lookupLabel);
|
|
4426
|
+
const findAll = /* @__PURE__ */ __name(async function(params, q, req) {
|
|
4427
|
+
return _findAll(this.repo, params, q, lookupLabel, req);
|
|
3469
4428
|
}, "findAll");
|
|
3470
4429
|
return {
|
|
3471
4430
|
route: "",
|
|
@@ -3481,11 +4440,12 @@ var childFindAll = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
3481
4440
|
if (!isOperationEnabled(sub.operations, "findAll")) return;
|
|
3482
4441
|
const { cls } = ctx;
|
|
3483
4442
|
const methodName = `findAllBy_${sub.childRoute}`;
|
|
3484
|
-
const findAll = /* @__PURE__ */ __name(async function(params, q, id) {
|
|
3485
|
-
return findAllByParent(this.repo, id, sub.childRoute, params);
|
|
4443
|
+
const findAll = /* @__PURE__ */ __name(async function(params, q, id, req) {
|
|
4444
|
+
return findAllByParent(this.repo, id, sub.childRoute, params, req);
|
|
3486
4445
|
}, "findAll");
|
|
3487
4446
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
3488
|
-
(0,
|
|
4447
|
+
(0, import_common12.Param)("id")(cls.prototype, methodName, 2);
|
|
4448
|
+
(0, import_common12.Req)()(cls.prototype, methodName, 3);
|
|
3489
4449
|
}, "decorators");
|
|
3490
4450
|
return {
|
|
3491
4451
|
name: sub.childRoute,
|
|
@@ -3504,9 +4464,10 @@ var registerFindAll = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3504
4464
|
const { cls } = ctx;
|
|
3505
4465
|
def(cls, methodName, properties.findAll);
|
|
3506
4466
|
const d = desc(cls, methodName);
|
|
3507
|
-
(0,
|
|
3508
|
-
(0,
|
|
3509
|
-
(0,
|
|
4467
|
+
(0, import_common12.Get)(route)(cls.prototype, methodName, d);
|
|
4468
|
+
(0, import_common12.Query)(new ZodValidationPipe(RequestDtoNoOffset.zodSchema))(cls.prototype, methodName, 0);
|
|
4469
|
+
(0, import_common12.Query)("q")(cls.prototype, methodName, 1);
|
|
4470
|
+
if (!sub) (0, import_common12.Req)()(cls.prototype, methodName, 2);
|
|
3510
4471
|
(0, import_swagger5.ApiOperation)({
|
|
3511
4472
|
summary: `List all ${name}s`
|
|
3512
4473
|
})(cls.prototype, methodName, d);
|
|
@@ -3524,7 +4485,7 @@ var registerFindAll = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3524
4485
|
}, "registerFindAll");
|
|
3525
4486
|
|
|
3526
4487
|
// src/lib/crud/operations/register-findone.ts
|
|
3527
|
-
var
|
|
4488
|
+
var import_common13 = require("@nestjs/common");
|
|
3528
4489
|
var import_swagger6 = require("@nestjs/swagger");
|
|
3529
4490
|
var defaultFindOne = /* @__PURE__ */ __name((ctx) => {
|
|
3530
4491
|
if (!isOperationEnabled(ctx.definition, "findOne")) return null;
|
|
@@ -3534,11 +4495,12 @@ var defaultFindOne = /* @__PURE__ */ __name((ctx) => {
|
|
|
3534
4495
|
route: ":id",
|
|
3535
4496
|
methodName,
|
|
3536
4497
|
name: config.name,
|
|
3537
|
-
findOneFn: /* @__PURE__ */ __name(function(id) {
|
|
3538
|
-
return this.repo.findOne(id);
|
|
4498
|
+
findOneFn: /* @__PURE__ */ __name(function(id, req) {
|
|
4499
|
+
return this.repo.findOne(id, req);
|
|
3539
4500
|
}, "findOneFn"),
|
|
3540
4501
|
decorators: /* @__PURE__ */ __name(() => {
|
|
3541
|
-
(0,
|
|
4502
|
+
(0, import_common13.Param)("id")(cls.prototype, methodName, 0);
|
|
4503
|
+
(0, import_common13.Req)()(cls.prototype, methodName, 1);
|
|
3542
4504
|
}, "decorators")
|
|
3543
4505
|
};
|
|
3544
4506
|
}, "defaultFindOne");
|
|
@@ -3546,12 +4508,13 @@ var childFindOne = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
3546
4508
|
if (!isOperationEnabled(sub.operations, "findOne")) return null;
|
|
3547
4509
|
const { cls } = ctx;
|
|
3548
4510
|
const methodName = `findOneChild_${sub.childRoute}`;
|
|
3549
|
-
const findOneFn = /* @__PURE__ */ __name(async function(
|
|
3550
|
-
return this.repo.findOneChild(sub, childId,
|
|
4511
|
+
const findOneFn = /* @__PURE__ */ __name(async function(parentId2, childId, req) {
|
|
4512
|
+
return this.repo.findOneChild(sub, childId, parentId2, req);
|
|
3551
4513
|
}, "findOneFn");
|
|
3552
4514
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
3553
|
-
(0,
|
|
3554
|
-
(0,
|
|
4515
|
+
(0, import_common13.Param)("id")(cls.prototype, methodName, 0);
|
|
4516
|
+
(0, import_common13.Param)("childId")(cls.prototype, methodName, 1);
|
|
4517
|
+
(0, import_common13.Req)()(cls.prototype, methodName, 2);
|
|
3555
4518
|
}, "decorators");
|
|
3556
4519
|
return {
|
|
3557
4520
|
route: `:id/${sub.childRoute}/:childId`,
|
|
@@ -3569,7 +4532,7 @@ var registerFindOne = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3569
4532
|
const { cls } = ctx;
|
|
3570
4533
|
def(cls, methodName, properties.findOneFn);
|
|
3571
4534
|
const d = desc(cls, methodName);
|
|
3572
|
-
(0,
|
|
4535
|
+
(0, import_common13.Get)(route)(cls.prototype, methodName, d);
|
|
3573
4536
|
(0, import_swagger6.ApiOperation)({
|
|
3574
4537
|
summary: `Get one ${name} by id`
|
|
3575
4538
|
})(cls.prototype, methodName, d);
|
|
@@ -3588,7 +4551,7 @@ var registerFindOne = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3588
4551
|
}, "registerFindOne");
|
|
3589
4552
|
|
|
3590
4553
|
// src/lib/crud/operations/register-patch.ts
|
|
3591
|
-
var
|
|
4554
|
+
var import_common14 = require("@nestjs/common");
|
|
3592
4555
|
var import_swagger7 = require("@nestjs/swagger");
|
|
3593
4556
|
var defaultPatch = /* @__PURE__ */ __name((ctx) => {
|
|
3594
4557
|
if (!isOperationEnabled(ctx.definition, "patch")) return null;
|
|
@@ -3598,12 +4561,13 @@ var defaultPatch = /* @__PURE__ */ __name((ctx) => {
|
|
|
3598
4561
|
route: ":id",
|
|
3599
4562
|
methodName,
|
|
3600
4563
|
name: config.name,
|
|
3601
|
-
patchFn: /* @__PURE__ */ __name(function(id, body) {
|
|
3602
|
-
return this.repo.patch(id, body);
|
|
4564
|
+
patchFn: /* @__PURE__ */ __name(function(id, body, req) {
|
|
4565
|
+
return this.repo.patch(id, body, req);
|
|
3603
4566
|
}, "patchFn"),
|
|
3604
4567
|
decorators: /* @__PURE__ */ __name(() => {
|
|
3605
|
-
(0,
|
|
4568
|
+
(0, import_common14.Param)("id")(cls.prototype, methodName, 0);
|
|
3606
4569
|
bodyDecorator(patchSchema)(cls.prototype, methodName, 1);
|
|
4570
|
+
(0, import_common14.Req)()(cls.prototype, methodName, 2);
|
|
3607
4571
|
}, "decorators")
|
|
3608
4572
|
};
|
|
3609
4573
|
}, "defaultPatch");
|
|
@@ -3611,13 +4575,14 @@ var childPatch = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
3611
4575
|
if (!isOperationEnabled(sub.operations, "patch")) return null;
|
|
3612
4576
|
const { cls } = ctx;
|
|
3613
4577
|
const methodName = `patchChild_${sub.childRoute}`;
|
|
3614
|
-
const patchFn = /* @__PURE__ */ __name(async function(_id, childId, body) {
|
|
3615
|
-
return this.repo.updateChild(sub, childId, body);
|
|
4578
|
+
const patchFn = /* @__PURE__ */ __name(async function(_id, childId, body, req) {
|
|
4579
|
+
return this.repo.updateChild(sub, childId, body, req);
|
|
3616
4580
|
}, "patchFn");
|
|
3617
4581
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
3618
|
-
(0,
|
|
3619
|
-
(0,
|
|
3620
|
-
(0,
|
|
4582
|
+
(0, import_common14.Param)("id")(cls.prototype, methodName, 0);
|
|
4583
|
+
(0, import_common14.Param)("childId")(cls.prototype, methodName, 1);
|
|
4584
|
+
(0, import_common14.Body)()(cls.prototype, methodName, 2);
|
|
4585
|
+
(0, import_common14.Req)()(cls.prototype, methodName, 3);
|
|
3621
4586
|
}, "decorators");
|
|
3622
4587
|
return {
|
|
3623
4588
|
route: `:id/${sub.childRoute}/:childId`,
|
|
@@ -3635,7 +4600,7 @@ var registerPatch = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3635
4600
|
const { cls } = ctx;
|
|
3636
4601
|
def(cls, methodName, properties.patchFn);
|
|
3637
4602
|
const d = desc(cls, methodName);
|
|
3638
|
-
(0,
|
|
4603
|
+
(0, import_common14.Patch)(route)(cls.prototype, methodName, d);
|
|
3639
4604
|
(0, import_swagger7.ApiOperation)({
|
|
3640
4605
|
summary: `Update a ${name}`
|
|
3641
4606
|
})(cls.prototype, methodName, d);
|
|
@@ -3650,30 +4615,30 @@ var registerPatch = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
3650
4615
|
}, "registerPatch");
|
|
3651
4616
|
|
|
3652
4617
|
// src/lib/crud/operations/register-schema-endpoints.ts
|
|
3653
|
-
var
|
|
4618
|
+
var import_common15 = require("@nestjs/common");
|
|
3654
4619
|
var import_swagger8 = require("@nestjs/swagger");
|
|
3655
4620
|
|
|
3656
4621
|
// src/lib/crud/resource/PatchResourceJson.schema.ts
|
|
3657
|
-
var
|
|
3658
|
-
var FieldVariantPatchSchema =
|
|
3659
|
-
type:
|
|
3660
|
-
format:
|
|
3661
|
-
resource:
|
|
3662
|
-
position:
|
|
3663
|
-
options:
|
|
4622
|
+
var import_zod28 = require("zod");
|
|
4623
|
+
var FieldVariantPatchSchema = import_zod28.z.object({
|
|
4624
|
+
type: import_zod28.z.string().nullable().optional(),
|
|
4625
|
+
format: import_zod28.z.string().nullable().optional(),
|
|
4626
|
+
resource: import_zod28.z.string().nullable().optional(),
|
|
4627
|
+
position: import_zod28.z.number().nullable().optional(),
|
|
4628
|
+
options: import_zod28.z.record(import_zod28.z.string(), import_zod28.z.unknown().nullable()).optional()
|
|
3664
4629
|
}).partial();
|
|
3665
|
-
var PatchColumnSchema =
|
|
3666
|
-
label:
|
|
3667
|
-
column:
|
|
3668
|
-
hiddenInTable:
|
|
3669
|
-
hiddenInForm:
|
|
3670
|
-
hiddenInView:
|
|
4630
|
+
var PatchColumnSchema = import_zod28.z.object({
|
|
4631
|
+
label: import_zod28.z.string().optional(),
|
|
4632
|
+
column: import_zod28.z.string().optional(),
|
|
4633
|
+
hiddenInTable: import_zod28.z.boolean().optional(),
|
|
4634
|
+
hiddenInForm: import_zod28.z.boolean().optional(),
|
|
4635
|
+
hiddenInView: import_zod28.z.boolean().optional(),
|
|
3671
4636
|
fieldInput: FieldVariantPatchSchema.optional(),
|
|
3672
4637
|
fieldView: FieldVariantPatchSchema.optional(),
|
|
3673
4638
|
fieldTable: FieldVariantPatchSchema.optional()
|
|
3674
4639
|
}).partial();
|
|
3675
|
-
var PatchResourceJsonSchema =
|
|
3676
|
-
columns:
|
|
4640
|
+
var PatchResourceJsonSchema = import_zod28.z.object({
|
|
4641
|
+
columns: import_zod28.z.record(import_zod28.z.string(), PatchColumnSchema)
|
|
3677
4642
|
});
|
|
3678
4643
|
|
|
3679
4644
|
// src/lib/crud/resource/WriteResourceJson.ts
|
|
@@ -3906,7 +4871,7 @@ var resolveActions = /* @__PURE__ */ __name((baseUrl, actions) => {
|
|
|
3906
4871
|
var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
3907
4872
|
if (!config.views || !Object.keys(config.views).length) return void 0;
|
|
3908
4873
|
const definition = resolveDefinition(config);
|
|
3909
|
-
const baseUri = `${baseUrl}/${config.route}`;
|
|
4874
|
+
const baseUri = config.parent ? `${baseUrl}/${config.parent.route}/{${config.parent.param}}/${config.route}` : `${baseUrl}/${config.route}`;
|
|
3910
4875
|
const operations = buildResourceOperations(definition, baseUri);
|
|
3911
4876
|
if (isOperationEnabled(definition, "findAll")) {
|
|
3912
4877
|
operations["lookup"] = `${baseUri}?q={text}`;
|
|
@@ -3926,7 +4891,7 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
|
3926
4891
|
id: config.name,
|
|
3927
4892
|
name: config.name,
|
|
3928
4893
|
route: config.route,
|
|
3929
|
-
uri:
|
|
4894
|
+
uri: baseUri,
|
|
3930
4895
|
title: config.title ?? config.tag,
|
|
3931
4896
|
idField: config.lookup?.key ?? "id",
|
|
3932
4897
|
idType: config.idType ?? "string",
|
|
@@ -3995,7 +4960,7 @@ var buildEditableColumnsPayload = /* @__PURE__ */ __name((config) => {
|
|
|
3995
4960
|
}, "buildEditableColumnsPayload");
|
|
3996
4961
|
|
|
3997
4962
|
// src/lib/crud/operations/register-schema-endpoints.ts
|
|
3998
|
-
var
|
|
4963
|
+
var import_node_path6 = require("path");
|
|
3999
4964
|
var registerDefinitionEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
4000
4965
|
const { cls, config } = ctx;
|
|
4001
4966
|
const { route, name } = config;
|
|
@@ -4008,7 +4973,7 @@ var registerDefinitionEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4008
4973
|
return definitionPayload;
|
|
4009
4974
|
});
|
|
4010
4975
|
const d = desc(cls, "getDefinition");
|
|
4011
|
-
(0,
|
|
4976
|
+
(0, import_common15.Get)("definition")(cls.prototype, "getDefinition", d);
|
|
4012
4977
|
(0, import_swagger8.ApiOperation)({
|
|
4013
4978
|
summary: `Get the resource definition for ${name}`
|
|
4014
4979
|
})(cls.prototype, "getDefinition", d);
|
|
@@ -4029,7 +4994,7 @@ var registerResourceJsonEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4029
4994
|
return resourceJsonPayload;
|
|
4030
4995
|
});
|
|
4031
4996
|
const d = desc(cls, "getResourceJson");
|
|
4032
|
-
(0,
|
|
4997
|
+
(0, import_common15.Get)("resource.json")(cls.prototype, "getResourceJson", d);
|
|
4033
4998
|
(0, import_swagger8.ApiOperation)({
|
|
4034
4999
|
summary: `Get resource descriptor for ${name}`
|
|
4035
5000
|
})(cls.prototype, "getResourceJson", d);
|
|
@@ -4043,13 +5008,13 @@ var registerResourceColumnsEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4043
5008
|
const { route, name } = config;
|
|
4044
5009
|
def(cls, "getResourceColumns", async function() {
|
|
4045
5010
|
if (!IS_DEV) {
|
|
4046
|
-
throw new
|
|
5011
|
+
throw new import_common15.ForbiddenException("The resource schema editor is only available when the backend is running in local dev mode.");
|
|
4047
5012
|
}
|
|
4048
5013
|
const fresh = await this.configRegistry.getByRoute(route);
|
|
4049
5014
|
return buildEditableColumnsPayload(fresh ?? config);
|
|
4050
5015
|
});
|
|
4051
5016
|
const d = desc(cls, "getResourceColumns");
|
|
4052
|
-
(0,
|
|
5017
|
+
(0, import_common15.Get)("resource-columns")(cls.prototype, "getResourceColumns", d);
|
|
4053
5018
|
(0, import_swagger8.ApiOperation)({
|
|
4054
5019
|
summary: `Dev-only: get the editable column list for ${name}`
|
|
4055
5020
|
})(cls.prototype, "getResourceColumns", d);
|
|
@@ -4063,30 +5028,30 @@ var registerResourceJsonPatchEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4063
5028
|
const { route, name } = config;
|
|
4064
5029
|
def(cls, "patchResourceJson", async function(body) {
|
|
4065
5030
|
if (!IS_DEV) {
|
|
4066
|
-
throw new
|
|
5031
|
+
throw new import_common15.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
|
|
4067
5032
|
}
|
|
4068
5033
|
await this.configRegistry.getByRoute(route);
|
|
4069
5034
|
const dir = this.configRegistry.getResourceDir(route);
|
|
4070
5035
|
if (!dir) {
|
|
4071
|
-
throw new
|
|
5036
|
+
throw new import_common15.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
|
|
4072
5037
|
}
|
|
4073
|
-
const jsonPath = (0,
|
|
5038
|
+
const jsonPath = (0, import_node_path6.join)(dir, "resource.json");
|
|
4074
5039
|
const raw = readRawResourceJson(jsonPath);
|
|
4075
5040
|
if (!raw) {
|
|
4076
|
-
throw new
|
|
5041
|
+
throw new import_common15.NotFoundException(`resource.json not found at ${jsonPath}`);
|
|
4077
5042
|
}
|
|
4078
5043
|
const merged = applyColumnPatch(raw, body);
|
|
4079
5044
|
const validated = validateResourceJson(merged);
|
|
4080
5045
|
if (!validated.success) {
|
|
4081
|
-
throw new
|
|
5046
|
+
throw new import_common15.BadRequestException(validated.error.issues);
|
|
4082
5047
|
}
|
|
4083
5048
|
writeRawResourceJson(jsonPath, merged);
|
|
4084
5049
|
const fresh = await this.configRegistry.getByRoute(route);
|
|
4085
5050
|
return buildEditableColumnsPayload(fresh ?? config);
|
|
4086
5051
|
});
|
|
4087
5052
|
const d = desc(cls, "patchResourceJson");
|
|
4088
|
-
(0,
|
|
4089
|
-
(0,
|
|
5053
|
+
(0, import_common15.Patch)("resource.json")(cls.prototype, "patchResourceJson", d);
|
|
5054
|
+
(0, import_common15.Body)(new ZodValidationPipe(PatchResourceJsonSchema))(cls.prototype, "patchResourceJson", 0);
|
|
4090
5055
|
(0, import_swagger8.ApiOperation)({
|
|
4091
5056
|
summary: `Dev-only: patch column layout in resource.json for ${name}`
|
|
4092
5057
|
})(cls.prototype, "patchResourceJson", d);
|
|
@@ -4100,22 +5065,22 @@ var registerResourceJsonRawGetEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4100
5065
|
const { route, name } = config;
|
|
4101
5066
|
def(cls, "getResourceJsonRaw", async function() {
|
|
4102
5067
|
if (!IS_DEV) {
|
|
4103
|
-
throw new
|
|
5068
|
+
throw new import_common15.ForbiddenException("The resource JSON editor is only available when the backend is running in local dev mode.");
|
|
4104
5069
|
}
|
|
4105
5070
|
await this.configRegistry.getByRoute(route);
|
|
4106
5071
|
const dir = this.configRegistry.getResourceDir(route);
|
|
4107
5072
|
if (!dir) {
|
|
4108
|
-
throw new
|
|
5073
|
+
throw new import_common15.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
|
|
4109
5074
|
}
|
|
4110
|
-
const jsonPath = (0,
|
|
5075
|
+
const jsonPath = (0, import_node_path6.join)(dir, "resource.json");
|
|
4111
5076
|
const raw = readRawResourceJson(jsonPath);
|
|
4112
5077
|
if (!raw) {
|
|
4113
|
-
throw new
|
|
5078
|
+
throw new import_common15.NotFoundException(`resource.json not found at ${jsonPath}`);
|
|
4114
5079
|
}
|
|
4115
5080
|
return raw;
|
|
4116
5081
|
});
|
|
4117
5082
|
const d = desc(cls, "getResourceJsonRaw");
|
|
4118
|
-
(0,
|
|
5083
|
+
(0, import_common15.Get)("resource-json-raw")(cls.prototype, "getResourceJsonRaw", d);
|
|
4119
5084
|
(0, import_swagger8.ApiOperation)({
|
|
4120
5085
|
summary: `Dev-only: get the raw resource.json for ${name}`
|
|
4121
5086
|
})(cls.prototype, "getResourceJsonRaw", d);
|
|
@@ -4129,24 +5094,24 @@ var registerResourceJsonRawPutEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4129
5094
|
const { route, name } = config;
|
|
4130
5095
|
def(cls, "putResourceJsonRaw", async function(body) {
|
|
4131
5096
|
if (!IS_DEV) {
|
|
4132
|
-
throw new
|
|
5097
|
+
throw new import_common15.ForbiddenException("Editing resource.json is only available when the backend is running in local dev mode.");
|
|
4133
5098
|
}
|
|
4134
5099
|
await this.configRegistry.getByRoute(route);
|
|
4135
5100
|
const dir = this.configRegistry.getResourceDir(route);
|
|
4136
5101
|
if (!dir) {
|
|
4137
|
-
throw new
|
|
5102
|
+
throw new import_common15.NotFoundException(`No resource.json on disk for "${name}" (route "${route}").`);
|
|
4138
5103
|
}
|
|
4139
5104
|
const validated = validateResourceJson(body);
|
|
4140
5105
|
if (!validated.success) {
|
|
4141
|
-
throw new
|
|
5106
|
+
throw new import_common15.BadRequestException(validated.error.issues);
|
|
4142
5107
|
}
|
|
4143
|
-
const jsonPath = (0,
|
|
5108
|
+
const jsonPath = (0, import_node_path6.join)(dir, "resource.json");
|
|
4144
5109
|
writeRawResourceJson(jsonPath, body);
|
|
4145
5110
|
return body;
|
|
4146
5111
|
});
|
|
4147
5112
|
const d = desc(cls, "putResourceJsonRaw");
|
|
4148
|
-
(0,
|
|
4149
|
-
(0,
|
|
5113
|
+
(0, import_common15.Put)("resource-json-raw")(cls.prototype, "putResourceJsonRaw", d);
|
|
5114
|
+
(0, import_common15.Body)()(cls.prototype, "putResourceJsonRaw", 0);
|
|
4150
5115
|
(0, import_swagger8.ApiOperation)({
|
|
4151
5116
|
summary: `Dev-only: replace the full resource.json for ${name}`
|
|
4152
5117
|
})(cls.prototype, "putResourceJsonRaw", d);
|
|
@@ -4157,7 +5122,7 @@ var registerResourceJsonRawPutEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
4157
5122
|
}, "registerResourceJsonRawPutEndpoint");
|
|
4158
5123
|
|
|
4159
5124
|
// src/lib/crud/operations/register-schemas.ts
|
|
4160
|
-
var
|
|
5125
|
+
var import_common16 = require("@nestjs/common");
|
|
4161
5126
|
var import_swagger9 = require("@nestjs/swagger");
|
|
4162
5127
|
var defaultSchemas = /* @__PURE__ */ __name((ctx) => {
|
|
4163
5128
|
const { config, baseUrl } = ctx;
|
|
@@ -4201,7 +5166,7 @@ var registerSchemas = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
4201
5166
|
const { cls } = ctx;
|
|
4202
5167
|
def(cls, methodName, properties.schemasFn);
|
|
4203
5168
|
const d = desc(cls, methodName);
|
|
4204
|
-
(0,
|
|
5169
|
+
(0, import_common16.Get)(route)(cls.prototype, methodName, d);
|
|
4205
5170
|
(0, import_swagger9.ApiOperation)({
|
|
4206
5171
|
summary: `Get view schemas for ${name}`
|
|
4207
5172
|
})(cls.prototype, methodName, d);
|
|
@@ -4213,7 +5178,7 @@ var registerSchemas = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
4213
5178
|
}, "registerSchemas");
|
|
4214
5179
|
|
|
4215
5180
|
// src/lib/crud/operations/register-update.ts
|
|
4216
|
-
var
|
|
5181
|
+
var import_common17 = require("@nestjs/common");
|
|
4217
5182
|
var import_swagger10 = require("@nestjs/swagger");
|
|
4218
5183
|
var defaultUpdate = /* @__PURE__ */ __name((ctx) => {
|
|
4219
5184
|
if (!isOperationEnabled(ctx.definition, "update")) return null;
|
|
@@ -4223,12 +5188,13 @@ var defaultUpdate = /* @__PURE__ */ __name((ctx) => {
|
|
|
4223
5188
|
route: ":id",
|
|
4224
5189
|
methodName,
|
|
4225
5190
|
name: config.name,
|
|
4226
|
-
updateFn: /* @__PURE__ */ __name(function(id, body) {
|
|
4227
|
-
return this.repo.update(id, body);
|
|
5191
|
+
updateFn: /* @__PURE__ */ __name(function(id, body, req) {
|
|
5192
|
+
return this.repo.update(id, body, req);
|
|
4228
5193
|
}, "updateFn"),
|
|
4229
5194
|
decorators: /* @__PURE__ */ __name(() => {
|
|
4230
|
-
(0,
|
|
5195
|
+
(0, import_common17.Param)("id")(cls.prototype, methodName, 0);
|
|
4231
5196
|
bodyDecorator(updateSchema)(cls.prototype, methodName, 1);
|
|
5197
|
+
(0, import_common17.Req)()(cls.prototype, methodName, 2);
|
|
4232
5198
|
}, "decorators")
|
|
4233
5199
|
};
|
|
4234
5200
|
}, "defaultUpdate");
|
|
@@ -4236,13 +5202,14 @@ var childUpdate = /* @__PURE__ */ __name((sub) => (ctx) => {
|
|
|
4236
5202
|
if (!isOperationEnabled(sub.operations, "update")) return null;
|
|
4237
5203
|
const { cls } = ctx;
|
|
4238
5204
|
const methodName = `updateChild_${sub.childRoute}`;
|
|
4239
|
-
const updateFn = /* @__PURE__ */ __name(async function(_id, childId, body) {
|
|
4240
|
-
return this.repo.updateChild(sub, childId, body);
|
|
5205
|
+
const updateFn = /* @__PURE__ */ __name(async function(_id, childId, body, req) {
|
|
5206
|
+
return this.repo.updateChild(sub, childId, body, req);
|
|
4241
5207
|
}, "updateFn");
|
|
4242
5208
|
const decorators = /* @__PURE__ */ __name(() => {
|
|
4243
|
-
(0,
|
|
4244
|
-
(0,
|
|
4245
|
-
(0,
|
|
5209
|
+
(0, import_common17.Param)("id")(cls.prototype, methodName, 0);
|
|
5210
|
+
(0, import_common17.Param)("childId")(cls.prototype, methodName, 1);
|
|
5211
|
+
(0, import_common17.Body)()(cls.prototype, methodName, 2);
|
|
5212
|
+
(0, import_common17.Req)()(cls.prototype, methodName, 3);
|
|
4246
5213
|
}, "decorators");
|
|
4247
5214
|
return {
|
|
4248
5215
|
route: `:id/${sub.childRoute}/:childId`,
|
|
@@ -4260,7 +5227,7 @@ var registerUpdate = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
4260
5227
|
const { cls } = ctx;
|
|
4261
5228
|
def(cls, methodName, properties.updateFn);
|
|
4262
5229
|
const d = desc(cls, methodName);
|
|
4263
|
-
(0,
|
|
5230
|
+
(0, import_common17.Put)(route)(cls.prototype, methodName, d);
|
|
4264
5231
|
(0, import_swagger10.ApiOperation)({
|
|
4265
5232
|
summary: `Replace a ${name}`
|
|
4266
5233
|
})(cls.prototype, methodName, d);
|
|
@@ -4318,10 +5285,18 @@ function createCrudController(config, baseUrl) {
|
|
|
4318
5285
|
throw new Error(`Resource "${name}" declares 'upsert' but no upsertOn`);
|
|
4319
5286
|
}
|
|
4320
5287
|
const bodyDecorator = /* @__PURE__ */ __name((schema, options) => {
|
|
4321
|
-
if (!schema) return (0,
|
|
4322
|
-
if (isZodSchema(schema)) return (0,
|
|
4323
|
-
return (0,
|
|
5288
|
+
if (!schema) return (0, import_common18.Body)();
|
|
5289
|
+
if (isZodSchema(schema)) return (0, import_common18.Body)(new ZodValidationPipe(schema, options));
|
|
5290
|
+
return (0, import_common18.Body)();
|
|
4324
5291
|
}, "bodyDecorator");
|
|
5292
|
+
const resolveClient = /* @__PURE__ */ __name((registry, resource) => {
|
|
5293
|
+
try {
|
|
5294
|
+
return registry.resolve(resource.database);
|
|
5295
|
+
} catch (e) {
|
|
5296
|
+
if (resource.kind === "custom") return void 0;
|
|
5297
|
+
throw e;
|
|
5298
|
+
}
|
|
5299
|
+
}, "resolveClient");
|
|
4325
5300
|
let CrudControllerBase = class CrudControllerBase {
|
|
4326
5301
|
static {
|
|
4327
5302
|
__name(this, "CrudControllerBase");
|
|
@@ -4329,8 +5304,8 @@ function createCrudController(config, baseUrl) {
|
|
|
4329
5304
|
repo;
|
|
4330
5305
|
configRegistry;
|
|
4331
5306
|
constructor(registry, configRegistry) {
|
|
4332
|
-
const prisma = registry
|
|
4333
|
-
this.repo = createCrudRepository(prisma, config);
|
|
5307
|
+
const prisma = resolveClient(registry, config);
|
|
5308
|
+
this.repo = createCrudRepository(prisma, config, registry);
|
|
4334
5309
|
this.configRegistry = configRegistry;
|
|
4335
5310
|
}
|
|
4336
5311
|
};
|
|
@@ -4352,7 +5327,7 @@ function createCrudController(config, baseUrl) {
|
|
|
4352
5327
|
baseUrl
|
|
4353
5328
|
};
|
|
4354
5329
|
registerEndpoints(ctx);
|
|
4355
|
-
(0,
|
|
5330
|
+
(0, import_common18.Controller)(resourceControllerPath(route, config.parent))(CrudControllerBase);
|
|
4356
5331
|
(0, import_swagger11.ApiTags)(tag)(CrudControllerBase);
|
|
4357
5332
|
Object.defineProperty(CrudControllerBase, "name", {
|
|
4358
5333
|
value: `${name.charAt(0).toUpperCase() + name.slice(1)}Controller`
|
|
@@ -4366,7 +5341,7 @@ function createCrudController(config, baseUrl) {
|
|
|
4366
5341
|
__name(createCrudController, "createCrudController");
|
|
4367
5342
|
|
|
4368
5343
|
// src/lib/crud/dev-tools/dev-resources.controller.ts
|
|
4369
|
-
var
|
|
5344
|
+
var import_common19 = require("@nestjs/common");
|
|
4370
5345
|
var import_swagger12 = require("@nestjs/swagger");
|
|
4371
5346
|
|
|
4372
5347
|
// ../crouton-codegen/src/naming.ts
|
|
@@ -4595,9 +5570,14 @@ var classify = /* @__PURE__ */ __name((model, ctx = {}) => {
|
|
|
4595
5570
|
};
|
|
4596
5571
|
} else if (field.kind === "relation") {
|
|
4597
5572
|
const target = ctx.resolveRelationResource?.(field.relationModel ?? "");
|
|
5573
|
+
const isCollectionSide = field.relationType === "oneToMany" || field.relationType === "manyToMany";
|
|
4598
5574
|
if (ruleset.showRelationsInForm && target) {
|
|
4599
5575
|
col = {
|
|
4600
5576
|
hiddenInTable: ruleset.hideRelationsInTable,
|
|
5577
|
+
...isCollectionSide ? {
|
|
5578
|
+
hiddenInForm: true,
|
|
5579
|
+
hiddenInView: true
|
|
5580
|
+
} : {},
|
|
4601
5581
|
fieldInput: {
|
|
4602
5582
|
format: "relation",
|
|
4603
5583
|
resource: target,
|
|
@@ -4793,7 +5773,8 @@ var diff = /* @__PURE__ */ __name(({ draft, existing, hasSchemaFile = false }) =
|
|
|
4793
5773
|
}
|
|
4794
5774
|
return {
|
|
4795
5775
|
name: draft.name,
|
|
4796
|
-
model
|
|
5776
|
+
// Codegen only ever drafts prisma-backed resources, so `model` is set.
|
|
5777
|
+
model: draft.config.model ?? draft.name,
|
|
4797
5778
|
isNew,
|
|
4798
5779
|
decisions,
|
|
4799
5780
|
draft,
|
|
@@ -4825,7 +5806,7 @@ var resolve2 = /* @__PURE__ */ __name(async (diff2, resolver = recommendedResolv
|
|
|
4825
5806
|
// ../crouton-codegen/src/serialize.ts
|
|
4826
5807
|
var RESOURCE_SCHEMA_URL = `https://ghentcdh.github.io/crouton/schema/v${CURRENT_RESOURCE_VERSION}/resource.schema.json`;
|
|
4827
5808
|
var withResourceHeader = /* @__PURE__ */ __name((config, opts = {}) => {
|
|
4828
|
-
const { $schema: _schema, schemaVersion: _version, draft: existingDraft, ...rest } = config;
|
|
5809
|
+
const { $schema: _schema, schemaVersion: _version, draft: existingDraft, kind: existingKind, ...rest } = config;
|
|
4829
5810
|
const draft = opts.draft !== void 0 ? opts.draft : existingDraft;
|
|
4830
5811
|
return {
|
|
4831
5812
|
$schema: RESOURCE_SCHEMA_URL,
|
|
@@ -4833,6 +5814,9 @@ var withResourceHeader = /* @__PURE__ */ __name((config, opts = {}) => {
|
|
|
4833
5814
|
...draft !== void 0 ? {
|
|
4834
5815
|
draft
|
|
4835
5816
|
} : {},
|
|
5817
|
+
...existingKind !== void 0 ? {
|
|
5818
|
+
kind: existingKind
|
|
5819
|
+
} : {},
|
|
4836
5820
|
...rest
|
|
4837
5821
|
};
|
|
4838
5822
|
}, "withResourceHeader");
|
|
@@ -4933,7 +5917,7 @@ var apply = /* @__PURE__ */ __name((resolved, ctx) => {
|
|
|
4933
5917
|
|
|
4934
5918
|
// ../crouton-codegen/src/commit.ts
|
|
4935
5919
|
var import_promises3 = require("fs/promises");
|
|
4936
|
-
var
|
|
5920
|
+
var import_node_path7 = require("path");
|
|
4937
5921
|
var commit = /* @__PURE__ */ __name(async (plan, opts = {}) => {
|
|
4938
5922
|
const written = [];
|
|
4939
5923
|
const skipped = [];
|
|
@@ -4943,7 +5927,7 @@ var commit = /* @__PURE__ */ __name(async (plan, opts = {}) => {
|
|
|
4943
5927
|
continue;
|
|
4944
5928
|
}
|
|
4945
5929
|
if (!opts.dryRun) {
|
|
4946
|
-
await (0, import_promises3.mkdir)((0,
|
|
5930
|
+
await (0, import_promises3.mkdir)((0, import_node_path7.dirname)(file.path), {
|
|
4947
5931
|
recursive: true
|
|
4948
5932
|
});
|
|
4949
5933
|
await (0, import_promises3.writeFile)(file.path, file.contents, "utf-8");
|
|
@@ -4958,15 +5942,15 @@ var commit = /* @__PURE__ */ __name(async (plan, opts = {}) => {
|
|
|
4958
5942
|
|
|
4959
5943
|
// ../crouton-codegen/src/config.ts
|
|
4960
5944
|
var import_promises4 = require("fs/promises");
|
|
4961
|
-
var
|
|
5945
|
+
var import_node_path8 = require("path");
|
|
4962
5946
|
var findConfigPath2 = /* @__PURE__ */ __name(async (cwd) => {
|
|
4963
|
-
let dir = (0,
|
|
5947
|
+
let dir = (0, import_node_path8.resolve)(cwd);
|
|
4964
5948
|
while (true) {
|
|
4965
5949
|
for (const name of CONFIG_FILES) {
|
|
4966
|
-
const candidate = (0,
|
|
5950
|
+
const candidate = (0, import_node_path8.join)(dir, name);
|
|
4967
5951
|
if (await fileExists2(candidate)) return candidate;
|
|
4968
5952
|
}
|
|
4969
|
-
const parent = (0,
|
|
5953
|
+
const parent = (0, import_node_path8.dirname)(dir);
|
|
4970
5954
|
if (parent === dir) return void 0;
|
|
4971
5955
|
dir = parent;
|
|
4972
5956
|
}
|
|
@@ -4987,7 +5971,7 @@ var loadConfig2 = /* @__PURE__ */ __name(async (cwd) => {
|
|
|
4987
5971
|
return {
|
|
4988
5972
|
config,
|
|
4989
5973
|
path,
|
|
4990
|
-
root: (0,
|
|
5974
|
+
root: (0, import_node_path8.dirname)(path)
|
|
4991
5975
|
};
|
|
4992
5976
|
}, "loadConfig");
|
|
4993
5977
|
var validateConfig = /* @__PURE__ */ __name((config, path = "<config>") => {
|
|
@@ -5004,7 +5988,7 @@ var loadDatasources = /* @__PURE__ */ __name(async (loaded) => {
|
|
|
5004
5988
|
const datasources = [];
|
|
5005
5989
|
for (const e of entries) {
|
|
5006
5990
|
if (!e.isDirectory()) continue;
|
|
5007
|
-
const jsonPath = (0,
|
|
5991
|
+
const jsonPath = (0, import_node_path8.join)(base, e.name, "data-source.json");
|
|
5008
5992
|
if (!await fileExists2(jsonPath)) continue;
|
|
5009
5993
|
const ds_json = JSON.parse(await (0, import_promises4.readFile)(jsonPath, "utf-8"));
|
|
5010
5994
|
const name = ds_json.name ?? e.name;
|
|
@@ -5050,34 +6034,42 @@ var makeSchemaExportName = /* @__PURE__ */ __name((config) => {
|
|
|
5050
6034
|
const template = config.schemaExportName ?? "{Model}WithRelationsSchema";
|
|
5051
6035
|
return (prismaName) => template.replace("{Model}", prismaName);
|
|
5052
6036
|
}, "makeSchemaExportName");
|
|
5053
|
-
var resolveFromRoot = /* @__PURE__ */ __name((root, p) => (0,
|
|
6037
|
+
var resolveFromRoot = /* @__PURE__ */ __name((root, p) => (0, import_node_path8.isAbsolute)(p) ? p : (0, import_node_path8.join)(root, p), "resolveFromRoot");
|
|
5054
6038
|
|
|
5055
6039
|
// ../crouton-codegen/src/scaffold.ts
|
|
5056
|
-
var
|
|
6040
|
+
var import_zod29 = require("zod");
|
|
5057
6041
|
var import_promises5 = require("fs/promises");
|
|
5058
|
-
var
|
|
6042
|
+
var import_node_path9 = require("path");
|
|
5059
6043
|
var ScallfoldDatasourceSchema = DataSourceShape.extend({
|
|
5060
6044
|
/** Folder name under `dataSourcesDir`. */
|
|
5061
|
-
folder:
|
|
6045
|
+
folder: import_zod29.z.string().default("default")
|
|
5062
6046
|
}).transform(transformDataSource);
|
|
5063
6047
|
|
|
5064
6048
|
// ../crouton-codegen/src/datasource-scaffold.ts
|
|
5065
|
-
var
|
|
6049
|
+
var import_node_path10 = require("path");
|
|
5066
6050
|
|
|
5067
6051
|
// ../crouton-codegen/src/project.ts
|
|
5068
6052
|
var import_promises6 = require("fs/promises");
|
|
5069
|
-
var
|
|
5070
|
-
var resourceDir = /* @__PURE__ */ __name((loaded, name) => (0,
|
|
6053
|
+
var import_node_path11 = require("path");
|
|
6054
|
+
var resourceDir = /* @__PURE__ */ __name((loaded, name) => (0, import_node_path11.join)(resolveFromRoot(loaded.root, loaded.config.resourcesDir), name), "resourceDir");
|
|
5071
6055
|
var readExistingResource = /* @__PURE__ */ __name(async (loaded, name) => {
|
|
5072
6056
|
const dir = resourceDir(loaded, name);
|
|
5073
|
-
const jsonPath = (0,
|
|
6057
|
+
const jsonPath = (0, import_node_path11.join)(dir, "resource.json");
|
|
5074
6058
|
const config = await fileExists2(jsonPath) ? JSON.parse(await (0, import_promises6.readFile)(jsonPath, "utf-8")) : null;
|
|
5075
|
-
const hasSchemaFile = await fileExists2((0,
|
|
6059
|
+
const hasSchemaFile = await fileExists2((0, import_node_path11.join)(dir, "schema.ts")) || await fileExists2((0, import_node_path11.join)(dir, "schema.js"));
|
|
5076
6060
|
return {
|
|
5077
6061
|
config,
|
|
5078
6062
|
hasSchemaFile
|
|
5079
6063
|
};
|
|
5080
6064
|
}, "readExistingResource");
|
|
6065
|
+
var isCustomResourceFile = /* @__PURE__ */ __name(async (jsonPath) => {
|
|
6066
|
+
try {
|
|
6067
|
+
const parsed = JSON.parse(await (0, import_promises6.readFile)(jsonPath, "utf-8"));
|
|
6068
|
+
return parsed?.kind === "custom";
|
|
6069
|
+
} catch {
|
|
6070
|
+
return false;
|
|
6071
|
+
}
|
|
6072
|
+
}, "isCustomResourceFile");
|
|
5081
6073
|
var listResourceNames = /* @__PURE__ */ __name(async (loaded) => {
|
|
5082
6074
|
const base = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
|
|
5083
6075
|
if (!await fileExists2(base)) return [];
|
|
@@ -5086,9 +6078,11 @@ var listResourceNames = /* @__PURE__ */ __name(async (loaded) => {
|
|
|
5086
6078
|
});
|
|
5087
6079
|
const names = [];
|
|
5088
6080
|
for (const e of entries) {
|
|
5089
|
-
if (e.isDirectory()
|
|
5090
|
-
|
|
5091
|
-
|
|
6081
|
+
if (!e.isDirectory()) continue;
|
|
6082
|
+
const jsonPath = (0, import_node_path11.join)(base, e.name, "resource.json");
|
|
6083
|
+
if (!await fileExists2(jsonPath)) continue;
|
|
6084
|
+
if (await isCustomResourceFile(jsonPath)) continue;
|
|
6085
|
+
names.push(e.name);
|
|
5092
6086
|
}
|
|
5093
6087
|
return names;
|
|
5094
6088
|
}, "listResourceNames");
|
|
@@ -5126,7 +6120,7 @@ var buildResourceDiffs = /* @__PURE__ */ __name(async (models, deps) => {
|
|
|
5126
6120
|
var import_node_child_process = require("child_process");
|
|
5127
6121
|
var import_node_fs4 = require("fs");
|
|
5128
6122
|
var import_promises7 = require("fs/promises");
|
|
5129
|
-
var
|
|
6123
|
+
var import_node_path12 = require("path");
|
|
5130
6124
|
var run = /* @__PURE__ */ __name((cmd, args, cwd) => new Promise((resolve7) => {
|
|
5131
6125
|
const child = (0, import_node_child_process.spawn)(cmd, args, {
|
|
5132
6126
|
cwd,
|
|
@@ -5213,7 +6207,7 @@ var fixZodImports = /* @__PURE__ */ __name(async (zodOutputDir) => {
|
|
|
5213
6207
|
return;
|
|
5214
6208
|
}
|
|
5215
6209
|
for (const entry of entries) {
|
|
5216
|
-
const full = (0,
|
|
6210
|
+
const full = (0, import_node_path12.join)(dir, entry.name);
|
|
5217
6211
|
if (entry.isDirectory()) {
|
|
5218
6212
|
await walk(full);
|
|
5219
6213
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -5262,8 +6256,8 @@ var pullAndGenerate = /* @__PURE__ */ __name(async (input) => {
|
|
|
5262
6256
|
};
|
|
5263
6257
|
}, "pullAndGenerate");
|
|
5264
6258
|
var normalizeSchema = /* @__PURE__ */ __name(async (schemaPath, configDir) => {
|
|
5265
|
-
const dir = configDir ?? (0,
|
|
5266
|
-
const configPath = (0,
|
|
6259
|
+
const dir = configDir ?? (0, import_node_path12.dirname)(schemaPath);
|
|
6260
|
+
const configPath = (0, import_node_path12.join)(dir, "normalize-schema.json");
|
|
5267
6261
|
if (!(0, import_node_fs4.existsSync)(configPath)) return {
|
|
5268
6262
|
renamed: 0
|
|
5269
6263
|
};
|
|
@@ -5293,7 +6287,7 @@ var normalizeSchema = /* @__PURE__ */ __name(async (schemaPath, configDir) => {
|
|
|
5293
6287
|
}, "normalizeSchema");
|
|
5294
6288
|
|
|
5295
6289
|
// src/lib/crud/resource/ResourceFlags.ts
|
|
5296
|
-
var
|
|
6290
|
+
var import_node_path13 = require("path");
|
|
5297
6291
|
var applyResourceFlagPatch = /* @__PURE__ */ __name((raw, patch) => {
|
|
5298
6292
|
const result = {
|
|
5299
6293
|
...raw
|
|
@@ -5334,8 +6328,8 @@ var applyResourceFlagPatch = /* @__PURE__ */ __name((raw, patch) => {
|
|
|
5334
6328
|
return result;
|
|
5335
6329
|
}, "applyResourceFlagPatch");
|
|
5336
6330
|
var resolveResourcePath = /* @__PURE__ */ __name((resourcesDir, name) => {
|
|
5337
|
-
const resolved = (0,
|
|
5338
|
-
if (!resolved.startsWith((0,
|
|
6331
|
+
const resolved = (0, import_node_path13.resolve)(resourcesDir, name, "resource.json");
|
|
6332
|
+
if (!resolved.startsWith((0, import_node_path13.resolve)(resourcesDir) + "/")) {
|
|
5339
6333
|
throw new Error(`Invalid resource name: "${name}"`);
|
|
5340
6334
|
}
|
|
5341
6335
|
return resolved;
|
|
@@ -5394,7 +6388,7 @@ var DevResourcesController = class {
|
|
|
5394
6388
|
}
|
|
5395
6389
|
assertDev() {
|
|
5396
6390
|
if (!IS_DEV) {
|
|
5397
|
-
throw new
|
|
6391
|
+
throw new import_common19.ForbiddenException("The database sync tools are only available when CROUTON_SCHEMA_EDITOR is enabled.");
|
|
5398
6392
|
}
|
|
5399
6393
|
}
|
|
5400
6394
|
/** Loads project config + resolves the datasource + Prisma schema path. Throws 404/400 with a clear message on misconfiguration. */
|
|
@@ -5403,14 +6397,14 @@ var DevResourcesController = class {
|
|
|
5403
6397
|
try {
|
|
5404
6398
|
loaded = await loadConfig2(process.cwd());
|
|
5405
6399
|
} catch (e) {
|
|
5406
|
-
throw new
|
|
6400
|
+
throw new import_common19.NotFoundException(e.message ?? "No crouton.json config found.");
|
|
5407
6401
|
}
|
|
5408
6402
|
const datasources = await loadDatasources(loaded);
|
|
5409
6403
|
let ds;
|
|
5410
6404
|
try {
|
|
5411
6405
|
ds = resolveDatasource(datasources, datasourceName);
|
|
5412
6406
|
} catch (e) {
|
|
5413
|
-
throw new
|
|
6407
|
+
throw new import_common19.BadRequestException(e.message);
|
|
5414
6408
|
}
|
|
5415
6409
|
const schemaPath = resolveFromRoot(loaded.root, ds.prismaSchema);
|
|
5416
6410
|
return {
|
|
@@ -5425,7 +6419,7 @@ var DevResourcesController = class {
|
|
|
5425
6419
|
schemaPath
|
|
5426
6420
|
});
|
|
5427
6421
|
} catch (e) {
|
|
5428
|
-
throw new
|
|
6422
|
+
throw new import_common19.BadRequestException(`Failed to read Prisma schema at ${schemaPath}: ${e.message}`);
|
|
5429
6423
|
}
|
|
5430
6424
|
}
|
|
5431
6425
|
buildApplyContext(loaded, ds) {
|
|
@@ -5495,7 +6489,7 @@ var DevResourcesController = class {
|
|
|
5495
6489
|
zodOutputDir: zodDir
|
|
5496
6490
|
});
|
|
5497
6491
|
if (!result.ok) {
|
|
5498
|
-
throw new
|
|
6492
|
+
throw new import_common19.BadRequestException(`prisma db pull failed:
|
|
5499
6493
|
${result.dbPull.output}`);
|
|
5500
6494
|
}
|
|
5501
6495
|
return {
|
|
@@ -5512,13 +6506,13 @@ ${result.dbPull.output}`);
|
|
|
5512
6506
|
async sync(body) {
|
|
5513
6507
|
this.assertDev();
|
|
5514
6508
|
if (!body?.model) {
|
|
5515
|
-
throw new
|
|
6509
|
+
throw new import_common19.BadRequestException('"model" is required.');
|
|
5516
6510
|
}
|
|
5517
6511
|
const { loaded, ds, schemaPath } = await this.loadProject(body.datasource);
|
|
5518
6512
|
const models = await this.introspectModels(schemaPath);
|
|
5519
6513
|
const model = models.find((m) => m.prismaName === body.model || m.clientAccessor === body.model);
|
|
5520
6514
|
if (!model) {
|
|
5521
|
-
throw new
|
|
6515
|
+
throw new import_common19.NotFoundException(`Model "${body.model}" not found in ${schemaPath}.`);
|
|
5522
6516
|
}
|
|
5523
6517
|
const resolveRelationResource = await makeRelationResolver(loaded);
|
|
5524
6518
|
const diff2 = await buildResourceDiff(model, {
|
|
@@ -5640,14 +6634,14 @@ ${result.dbPull.output}`);
|
|
|
5640
6634
|
const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
|
|
5641
6635
|
const jsonPath = resolveResourcePath(resourcesDir, name);
|
|
5642
6636
|
const raw = readRawResourceJson(jsonPath);
|
|
5643
|
-
if (!raw) throw new
|
|
5644
|
-
if (jsonPath.endsWith(".ts")) throw new
|
|
6637
|
+
if (!raw) throw new import_common19.NotFoundException(`Resource "${name}" not found.`);
|
|
6638
|
+
if (jsonPath.endsWith(".ts")) throw new import_common19.ForbiddenException("TypeScript resources cannot be edited.");
|
|
5645
6639
|
const patched = applyResourceFlagPatch(raw, {
|
|
5646
6640
|
draft: false
|
|
5647
6641
|
});
|
|
5648
6642
|
const result = validateResourceJson(patched);
|
|
5649
6643
|
if (!result.success) {
|
|
5650
|
-
throw new
|
|
6644
|
+
throw new import_common19.BadRequestException(`Validation failed: ${result.error.message}`);
|
|
5651
6645
|
}
|
|
5652
6646
|
writeRawResourceJson(jsonPath, patched);
|
|
5653
6647
|
return {
|
|
@@ -5660,8 +6654,8 @@ ${result.dbPull.output}`);
|
|
|
5660
6654
|
const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
|
|
5661
6655
|
const jsonPath = resolveResourcePath(resourcesDir, name);
|
|
5662
6656
|
const raw = readRawResourceJson(jsonPath);
|
|
5663
|
-
if (!raw) throw new
|
|
5664
|
-
if (jsonPath.endsWith(".ts")) throw new
|
|
6657
|
+
if (!raw) throw new import_common19.NotFoundException(`Resource "${name}" not found.`);
|
|
6658
|
+
if (jsonPath.endsWith(".ts")) throw new import_common19.ForbiddenException("TypeScript resources cannot be edited.");
|
|
5665
6659
|
const patched = applyResourceFlagPatch(raw, {
|
|
5666
6660
|
sidebar: {
|
|
5667
6661
|
hide: true
|
|
@@ -5669,7 +6663,7 @@ ${result.dbPull.output}`);
|
|
|
5669
6663
|
});
|
|
5670
6664
|
const result = validateResourceJson(patched);
|
|
5671
6665
|
if (!result.success) {
|
|
5672
|
-
throw new
|
|
6666
|
+
throw new import_common19.BadRequestException(`Validation failed: ${result.error.message}`);
|
|
5673
6667
|
}
|
|
5674
6668
|
writeRawResourceJson(jsonPath, patched);
|
|
5675
6669
|
return {
|
|
@@ -5682,8 +6676,8 @@ ${result.dbPull.output}`);
|
|
|
5682
6676
|
const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
|
|
5683
6677
|
const jsonPath = resolveResourcePath(resourcesDir, name);
|
|
5684
6678
|
const raw = readRawResourceJson(jsonPath);
|
|
5685
|
-
if (!raw) throw new
|
|
5686
|
-
if (jsonPath.endsWith(".ts")) throw new
|
|
6679
|
+
if (!raw) throw new import_common19.NotFoundException(`Resource "${name}" not found.`);
|
|
6680
|
+
if (jsonPath.endsWith(".ts")) throw new import_common19.ForbiddenException("TypeScript resources cannot be edited.");
|
|
5687
6681
|
const sidebarPatch = {
|
|
5688
6682
|
hide: false
|
|
5689
6683
|
};
|
|
@@ -5696,7 +6690,7 @@ ${result.dbPull.output}`);
|
|
|
5696
6690
|
});
|
|
5697
6691
|
const result = validateResourceJson(patched);
|
|
5698
6692
|
if (!result.success) {
|
|
5699
|
-
throw new
|
|
6693
|
+
throw new import_common19.BadRequestException(`Validation failed: ${result.error.message}`);
|
|
5700
6694
|
}
|
|
5701
6695
|
writeRawResourceJson(jsonPath, patched);
|
|
5702
6696
|
return {
|
|
@@ -5709,12 +6703,12 @@ ${result.dbPull.output}`);
|
|
|
5709
6703
|
const resourcesDir = resolveFromRoot(loaded.root, loaded.config.resourcesDir);
|
|
5710
6704
|
const jsonPath = resolveResourcePath(resourcesDir, name);
|
|
5711
6705
|
const raw = readRawResourceJson(jsonPath);
|
|
5712
|
-
if (!raw) throw new
|
|
5713
|
-
if (jsonPath.endsWith(".ts")) throw new
|
|
6706
|
+
if (!raw) throw new import_common19.NotFoundException(`Resource "${name}" not found.`);
|
|
6707
|
+
if (jsonPath.endsWith(".ts")) throw new import_common19.ForbiddenException("TypeScript resources cannot be edited.");
|
|
5714
6708
|
const patched = applyResourceFlagPatch(raw, body);
|
|
5715
6709
|
const result = validateResourceJson(patched);
|
|
5716
6710
|
if (!result.success) {
|
|
5717
|
-
throw new
|
|
6711
|
+
throw new import_common19.BadRequestException(`Validation failed: ${result.error.message}`);
|
|
5718
6712
|
}
|
|
5719
6713
|
writeRawResourceJson(jsonPath, patched);
|
|
5720
6714
|
return {
|
|
@@ -5723,7 +6717,7 @@ ${result.dbPull.output}`);
|
|
|
5723
6717
|
}
|
|
5724
6718
|
};
|
|
5725
6719
|
_ts_decorate5([
|
|
5726
|
-
(0,
|
|
6720
|
+
(0, import_common19.Get)("models"),
|
|
5727
6721
|
(0, import_swagger12.ApiOperation)({
|
|
5728
6722
|
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"
|
|
5729
6723
|
}),
|
|
@@ -5736,7 +6730,7 @@ _ts_decorate5([
|
|
|
5736
6730
|
_ts_metadata4("design:returntype", Promise)
|
|
5737
6731
|
], DevResourcesController.prototype, "listModels", null);
|
|
5738
6732
|
_ts_decorate5([
|
|
5739
|
-
(0,
|
|
6733
|
+
(0, import_common19.Post)("restart"),
|
|
5740
6734
|
(0, import_swagger12.ApiOperation)({
|
|
5741
6735
|
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."
|
|
5742
6736
|
}),
|
|
@@ -5749,7 +6743,7 @@ _ts_decorate5([
|
|
|
5749
6743
|
_ts_metadata4("design:returntype", Object)
|
|
5750
6744
|
], DevResourcesController.prototype, "restart", null);
|
|
5751
6745
|
_ts_decorate5([
|
|
5752
|
-
(0,
|
|
6746
|
+
(0, import_common19.Post)("pull"),
|
|
5753
6747
|
(0, import_swagger12.ApiOperation)({
|
|
5754
6748
|
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"
|
|
5755
6749
|
}),
|
|
@@ -5757,7 +6751,7 @@ _ts_decorate5([
|
|
|
5757
6751
|
status: 200,
|
|
5758
6752
|
description: "Result of each step, or requiresConfirmation if schema.prisma has uncommitted changes"
|
|
5759
6753
|
}),
|
|
5760
|
-
_ts_param(0, (0,
|
|
6754
|
+
_ts_param(0, (0, import_common19.Body)()),
|
|
5761
6755
|
_ts_metadata4("design:type", Function),
|
|
5762
6756
|
_ts_metadata4("design:paramtypes", [
|
|
5763
6757
|
Object
|
|
@@ -5765,7 +6759,7 @@ _ts_decorate5([
|
|
|
5765
6759
|
_ts_metadata4("design:returntype", Promise)
|
|
5766
6760
|
], DevResourcesController.prototype, "pull", null);
|
|
5767
6761
|
_ts_decorate5([
|
|
5768
|
-
(0,
|
|
6762
|
+
(0, import_common19.Post)("sync"),
|
|
5769
6763
|
(0, import_swagger12.ApiOperation)({
|
|
5770
6764
|
summary: "Dev-only: generate or update a single resource.json from its DB model, using recommended defaults (non-interactive)"
|
|
5771
6765
|
}),
|
|
@@ -5773,7 +6767,7 @@ _ts_decorate5([
|
|
|
5773
6767
|
status: 200,
|
|
5774
6768
|
description: "Files written for this resource"
|
|
5775
6769
|
}),
|
|
5776
|
-
_ts_param(0, (0,
|
|
6770
|
+
_ts_param(0, (0, import_common19.Body)()),
|
|
5777
6771
|
_ts_metadata4("design:type", Function),
|
|
5778
6772
|
_ts_metadata4("design:paramtypes", [
|
|
5779
6773
|
Object
|
|
@@ -5781,7 +6775,7 @@ _ts_decorate5([
|
|
|
5781
6775
|
_ts_metadata4("design:returntype", Promise)
|
|
5782
6776
|
], DevResourcesController.prototype, "sync", null);
|
|
5783
6777
|
_ts_decorate5([
|
|
5784
|
-
(0,
|
|
6778
|
+
(0, import_common19.Post)("plan"),
|
|
5785
6779
|
(0, import_swagger12.ApiOperation)({
|
|
5786
6780
|
summary: "Dev-only: dry-run introspect + diff across all (or selected) DB models using recommended defaults \u2014 computes what would change, writes nothing"
|
|
5787
6781
|
}),
|
|
@@ -5789,7 +6783,7 @@ _ts_decorate5([
|
|
|
5789
6783
|
status: 200,
|
|
5790
6784
|
description: "Proposed per-resource changes"
|
|
5791
6785
|
}),
|
|
5792
|
-
_ts_param(0, (0,
|
|
6786
|
+
_ts_param(0, (0, import_common19.Body)()),
|
|
5793
6787
|
_ts_metadata4("design:type", Function),
|
|
5794
6788
|
_ts_metadata4("design:paramtypes", [
|
|
5795
6789
|
Object
|
|
@@ -5797,7 +6791,7 @@ _ts_decorate5([
|
|
|
5797
6791
|
_ts_metadata4("design:returntype", Promise)
|
|
5798
6792
|
], DevResourcesController.prototype, "plan", null);
|
|
5799
6793
|
_ts_decorate5([
|
|
5800
|
-
(0,
|
|
6794
|
+
(0, import_common19.Post)("apply"),
|
|
5801
6795
|
(0, import_swagger12.ApiOperation)({
|
|
5802
6796
|
summary: "Dev-only: commit resource.json/schema.ts changes to disk for the given resources (or all, if omitted), using recommended defaults"
|
|
5803
6797
|
}),
|
|
@@ -5805,7 +6799,7 @@ _ts_decorate5([
|
|
|
5805
6799
|
status: 200,
|
|
5806
6800
|
description: "Per-resource commit results"
|
|
5807
6801
|
}),
|
|
5808
|
-
_ts_param(0, (0,
|
|
6802
|
+
_ts_param(0, (0, import_common19.Body)()),
|
|
5809
6803
|
_ts_metadata4("design:type", Function),
|
|
5810
6804
|
_ts_metadata4("design:paramtypes", [
|
|
5811
6805
|
Object
|
|
@@ -5813,7 +6807,7 @@ _ts_decorate5([
|
|
|
5813
6807
|
_ts_metadata4("design:returntype", Promise)
|
|
5814
6808
|
], DevResourcesController.prototype, "apply", null);
|
|
5815
6809
|
_ts_decorate5([
|
|
5816
|
-
(0,
|
|
6810
|
+
(0, import_common19.Get)("visibility"),
|
|
5817
6811
|
(0, import_swagger12.ApiOperation)({
|
|
5818
6812
|
summary: "Dev-only: list all resources with their menu visibility state (in-menu, hidden, draft, error)"
|
|
5819
6813
|
}),
|
|
@@ -5826,7 +6820,7 @@ _ts_decorate5([
|
|
|
5826
6820
|
_ts_metadata4("design:returntype", Promise)
|
|
5827
6821
|
], DevResourcesController.prototype, "visibility", null);
|
|
5828
6822
|
_ts_decorate5([
|
|
5829
|
-
(0,
|
|
6823
|
+
(0, import_common19.Post)(":name/publish"),
|
|
5830
6824
|
(0, import_swagger12.ApiOperation)({
|
|
5831
6825
|
summary: "Dev-only: publish a draft resource (removes `draft: true` from resource.json)"
|
|
5832
6826
|
}),
|
|
@@ -5834,7 +6828,7 @@ _ts_decorate5([
|
|
|
5834
6828
|
status: 200,
|
|
5835
6829
|
description: "Resource published"
|
|
5836
6830
|
}),
|
|
5837
|
-
_ts_param(0, (0,
|
|
6831
|
+
_ts_param(0, (0, import_common19.Param)("name")),
|
|
5838
6832
|
_ts_metadata4("design:type", Function),
|
|
5839
6833
|
_ts_metadata4("design:paramtypes", [
|
|
5840
6834
|
String
|
|
@@ -5842,7 +6836,7 @@ _ts_decorate5([
|
|
|
5842
6836
|
_ts_metadata4("design:returntype", Promise)
|
|
5843
6837
|
], DevResourcesController.prototype, "publish", null);
|
|
5844
6838
|
_ts_decorate5([
|
|
5845
|
-
(0,
|
|
6839
|
+
(0, import_common19.Post)(":name/remove-from-menu"),
|
|
5846
6840
|
(0, import_swagger12.ApiOperation)({
|
|
5847
6841
|
summary: "Dev-only: hide a resource from the sidebar menu (sets `sidebar.hide: true` in resource.json)"
|
|
5848
6842
|
}),
|
|
@@ -5850,7 +6844,7 @@ _ts_decorate5([
|
|
|
5850
6844
|
status: 200,
|
|
5851
6845
|
description: "Resource removed from menu"
|
|
5852
6846
|
}),
|
|
5853
|
-
_ts_param(0, (0,
|
|
6847
|
+
_ts_param(0, (0, import_common19.Param)("name")),
|
|
5854
6848
|
_ts_metadata4("design:type", Function),
|
|
5855
6849
|
_ts_metadata4("design:paramtypes", [
|
|
5856
6850
|
String
|
|
@@ -5858,7 +6852,7 @@ _ts_decorate5([
|
|
|
5858
6852
|
_ts_metadata4("design:returntype", Promise)
|
|
5859
6853
|
], DevResourcesController.prototype, "removeFromMenu", null);
|
|
5860
6854
|
_ts_decorate5([
|
|
5861
|
-
(0,
|
|
6855
|
+
(0, import_common19.Post)(":name/add-to-menu"),
|
|
5862
6856
|
(0, import_swagger12.ApiOperation)({
|
|
5863
6857
|
summary: "Dev-only: publish + un-hide a resource and optionally set sidebar group/position/label"
|
|
5864
6858
|
}),
|
|
@@ -5866,8 +6860,8 @@ _ts_decorate5([
|
|
|
5866
6860
|
status: 200,
|
|
5867
6861
|
description: "Resource added to menu"
|
|
5868
6862
|
}),
|
|
5869
|
-
_ts_param(0, (0,
|
|
5870
|
-
_ts_param(1, (0,
|
|
6863
|
+
_ts_param(0, (0, import_common19.Param)("name")),
|
|
6864
|
+
_ts_param(1, (0, import_common19.Body)()),
|
|
5871
6865
|
_ts_metadata4("design:type", Function),
|
|
5872
6866
|
_ts_metadata4("design:paramtypes", [
|
|
5873
6867
|
String,
|
|
@@ -5876,7 +6870,7 @@ _ts_decorate5([
|
|
|
5876
6870
|
_ts_metadata4("design:returntype", Promise)
|
|
5877
6871
|
], DevResourcesController.prototype, "addToMenu", null);
|
|
5878
6872
|
_ts_decorate5([
|
|
5879
|
-
(0,
|
|
6873
|
+
(0, import_common19.Patch)(":name/flags"),
|
|
5880
6874
|
(0, import_swagger12.ApiOperation)({
|
|
5881
6875
|
summary: "Dev-only: set arbitrary resource flags (draft, sidebar.hide, group, position, label)"
|
|
5882
6876
|
}),
|
|
@@ -5884,8 +6878,8 @@ _ts_decorate5([
|
|
|
5884
6878
|
status: 200,
|
|
5885
6879
|
description: "Flags updated"
|
|
5886
6880
|
}),
|
|
5887
|
-
_ts_param(0, (0,
|
|
5888
|
-
_ts_param(1, (0,
|
|
6881
|
+
_ts_param(0, (0, import_common19.Param)("name")),
|
|
6882
|
+
_ts_param(1, (0, import_common19.Body)()),
|
|
5889
6883
|
_ts_metadata4("design:type", Function),
|
|
5890
6884
|
_ts_metadata4("design:paramtypes", [
|
|
5891
6885
|
String,
|
|
@@ -5894,7 +6888,7 @@ _ts_decorate5([
|
|
|
5894
6888
|
_ts_metadata4("design:returntype", Promise)
|
|
5895
6889
|
], DevResourcesController.prototype, "updateFlags", null);
|
|
5896
6890
|
DevResourcesController = _ts_decorate5([
|
|
5897
|
-
(0,
|
|
6891
|
+
(0, import_common19.Controller)("_app/resources"),
|
|
5898
6892
|
(0, import_swagger12.ApiTags)("Dev tools"),
|
|
5899
6893
|
_ts_metadata4("design:type", Function),
|
|
5900
6894
|
_ts_metadata4("design:paramtypes", [
|
|
@@ -5904,28 +6898,28 @@ DevResourcesController = _ts_decorate5([
|
|
|
5904
6898
|
], DevResourcesController);
|
|
5905
6899
|
|
|
5906
6900
|
// src/lib/crud/enum-registry/enum-registry.types.ts
|
|
5907
|
-
var
|
|
5908
|
-
var EnumEntrySchema =
|
|
5909
|
-
value:
|
|
5910
|
-
label:
|
|
6901
|
+
var import_zod30 = require("zod");
|
|
6902
|
+
var EnumEntrySchema = import_zod30.z.object({
|
|
6903
|
+
value: import_zod30.z.unknown(),
|
|
6904
|
+
label: import_zod30.z.string()
|
|
5911
6905
|
});
|
|
5912
|
-
var EnumRegistrySchema =
|
|
6906
|
+
var EnumRegistrySchema = import_zod30.z.record(import_zod30.z.string(), import_zod30.z.array(EnumEntrySchema)).default({});
|
|
5913
6907
|
|
|
5914
6908
|
// src/lib/crud/enum-registry/enum-registry.loader.ts
|
|
5915
6909
|
var import_node_fs5 = require("fs");
|
|
5916
|
-
var
|
|
6910
|
+
var import_node_path14 = require("path");
|
|
5917
6911
|
var ENUMS_FILE = "crouton.enums.json";
|
|
5918
6912
|
var loadEnumRegistry = /* @__PURE__ */ __name((startDir, enumsFile) => {
|
|
5919
6913
|
let file = enumsFile;
|
|
5920
6914
|
if (!file) {
|
|
5921
6915
|
let dir = startDir;
|
|
5922
6916
|
while (true) {
|
|
5923
|
-
const candidate = (0,
|
|
6917
|
+
const candidate = (0, import_node_path14.join)(dir, ENUMS_FILE);
|
|
5924
6918
|
if ((0, import_node_fs5.existsSync)(candidate)) {
|
|
5925
6919
|
file = candidate;
|
|
5926
6920
|
break;
|
|
5927
6921
|
}
|
|
5928
|
-
const parent = (0,
|
|
6922
|
+
const parent = (0, import_node_path14.dirname)(dir);
|
|
5929
6923
|
if (parent === dir) break;
|
|
5930
6924
|
dir = parent;
|
|
5931
6925
|
}
|
|
@@ -5991,9 +6985,9 @@ var upsertOp = /* @__PURE__ */ __name((entry, schema) => {
|
|
|
5991
6985
|
}, "upsertOp");
|
|
5992
6986
|
|
|
5993
6987
|
// src/lib/crud/adapter/relation-type.ts
|
|
5994
|
-
var
|
|
6988
|
+
var import_zod31 = require("zod");
|
|
5995
6989
|
var unwrapZodType = /* @__PURE__ */ __name((type) => {
|
|
5996
|
-
if (type instanceof
|
|
6990
|
+
if (type instanceof import_zod31.ZodOptional || type instanceof import_zod31.ZodNullable) {
|
|
5997
6991
|
return unwrapZodType(type.unwrap());
|
|
5998
6992
|
}
|
|
5999
6993
|
return type;
|
|
@@ -6003,7 +6997,7 @@ var deriveRelationType = /* @__PURE__ */ __name((schema, columnId) => {
|
|
|
6003
6997
|
const field = schema.shape[columnId];
|
|
6004
6998
|
if (!field) return void 0;
|
|
6005
6999
|
const inner = unwrapZodType(field);
|
|
6006
|
-
return inner instanceof
|
|
7000
|
+
return inner instanceof import_zod31.ZodArray ? "oneToMany" : "manyToOne";
|
|
6007
7001
|
}, "deriveRelationType");
|
|
6008
7002
|
var deriveRelationTypeFromColumns = /* @__PURE__ */ __name((col, cols) => {
|
|
6009
7003
|
const base = col.column ?? col.id;
|
|
@@ -6035,7 +7029,7 @@ var enrichRelationTypes = /* @__PURE__ */ __name((columns, schema) => {
|
|
|
6035
7029
|
|
|
6036
7030
|
// src/lib/crud/resource/ReadResourceJson.ts
|
|
6037
7031
|
var import_node_fs6 = require("fs");
|
|
6038
|
-
var
|
|
7032
|
+
var import_node_path15 = require("path");
|
|
6039
7033
|
var readResourceJson = /* @__PURE__ */ __name((jsonPath) => {
|
|
6040
7034
|
if (!(0, import_node_fs6.existsSync)(jsonPath)) return void 0;
|
|
6041
7035
|
let fileContent;
|
|
@@ -6058,30 +7052,62 @@ var readResourceJson = /* @__PURE__ */ __name((jsonPath) => {
|
|
|
6058
7052
|
success: true,
|
|
6059
7053
|
data: {
|
|
6060
7054
|
json: resource.data,
|
|
6061
|
-
dir: (0,
|
|
7055
|
+
dir: (0, import_node_path15.dirname)(jsonPath)
|
|
6062
7056
|
}
|
|
6063
7057
|
};
|
|
6064
7058
|
}, "readResourceJson");
|
|
6065
7059
|
|
|
6066
7060
|
// src/lib/crud/adapter/resource-resolver.ts
|
|
6067
7061
|
var import_node_fs7 = require("fs");
|
|
6068
|
-
var
|
|
6069
|
-
var
|
|
6070
|
-
|
|
6071
|
-
return void 0;
|
|
6072
|
-
}, "unwrap");
|
|
6073
|
-
var resolveChildResource = /* @__PURE__ */ __name((resourcePath, parentDir) => {
|
|
6074
|
-
const directPath = (0, import_node_path15.resolve)(parentDir, resourcePath);
|
|
7062
|
+
var import_node_path16 = require("path");
|
|
7063
|
+
var resolveChildResourceDetailed = /* @__PURE__ */ __name((resourcePath, parentDir) => {
|
|
7064
|
+
const attempted = [];
|
|
6075
7065
|
try {
|
|
6076
|
-
if (resourcePath.endsWith(".json")
|
|
6077
|
-
|
|
7066
|
+
if (resourcePath.endsWith(".json")) {
|
|
7067
|
+
const directPath = (0, import_node_path16.resolve)(parentDir, resourcePath);
|
|
7068
|
+
attempted.push(directPath);
|
|
7069
|
+
if ((0, import_node_fs7.existsSync)(directPath)) {
|
|
7070
|
+
const result2 = readResourceJson(directPath);
|
|
7071
|
+
if (result2?.success) return {
|
|
7072
|
+
ok: true,
|
|
7073
|
+
value: result2.data
|
|
7074
|
+
};
|
|
7075
|
+
return {
|
|
7076
|
+
ok: false,
|
|
7077
|
+
reason: "invalid",
|
|
7078
|
+
error: result2?.error ?? `Could not read ${directPath}`
|
|
7079
|
+
};
|
|
7080
|
+
}
|
|
6078
7081
|
}
|
|
6079
7082
|
const childName = resourcePath.replace(/^\.\//, "").replace(/\.resource$/, "");
|
|
6080
|
-
const childJsonPath = (0,
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
return
|
|
7083
|
+
const childJsonPath = (0, import_node_path16.resolve)((0, import_node_path16.dirname)(parentDir), childName, "resource.json");
|
|
7084
|
+
attempted.push(childJsonPath);
|
|
7085
|
+
const result = readResourceJson(childJsonPath);
|
|
7086
|
+
if (result?.success) return {
|
|
7087
|
+
ok: true,
|
|
7088
|
+
value: result.data
|
|
7089
|
+
};
|
|
7090
|
+
if (result) return {
|
|
7091
|
+
ok: false,
|
|
7092
|
+
reason: "invalid",
|
|
7093
|
+
error: result.error
|
|
7094
|
+
};
|
|
7095
|
+
return {
|
|
7096
|
+
ok: false,
|
|
7097
|
+
reason: "missing",
|
|
7098
|
+
attempted
|
|
7099
|
+
};
|
|
7100
|
+
} catch (err) {
|
|
7101
|
+
return {
|
|
7102
|
+
ok: false,
|
|
7103
|
+
reason: "invalid",
|
|
7104
|
+
error: err.message
|
|
7105
|
+
};
|
|
6084
7106
|
}
|
|
7107
|
+
}, "resolveChildResourceDetailed");
|
|
7108
|
+
var resolveChildResource = /* @__PURE__ */ __name((resourcePath, parentDir) => {
|
|
7109
|
+
const resolution = resolveChildResourceDetailed(resourcePath, parentDir);
|
|
7110
|
+
return resolution.ok ? resolution.value : void 0;
|
|
6085
7111
|
}, "resolveChildResource");
|
|
6086
7112
|
|
|
6087
7113
|
// src/lib/crud/adapter/column-enrichment.ts
|
|
@@ -6261,18 +7287,44 @@ var resolveColumnFieldVariants = /* @__PURE__ */ __name((cols) => cols?.map((col
|
|
|
6261
7287
|
}), "resolveColumnFieldVariants");
|
|
6262
7288
|
|
|
6263
7289
|
// src/lib/crud/adapter/sub-resource.builder.ts
|
|
7290
|
+
var REMOTE_RESOURCE = /^https?:\/\//i;
|
|
6264
7291
|
var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentModel, parentDir, enums = {}, baseUrl) => {
|
|
6265
7292
|
if (!columns || !parentDir) return [];
|
|
6266
|
-
return columns.filter((c) => c.fieldInput?.format === "relation" && c.fieldInput?.resource).
|
|
6267
|
-
const
|
|
6268
|
-
|
|
6269
|
-
const
|
|
6270
|
-
|
|
7293
|
+
return columns.filter((c) => c.fieldInput?.format === "relation" && c.fieldInput?.resource).flatMap((c) => {
|
|
7294
|
+
const resourcePath = c.fieldInput.resource;
|
|
7295
|
+
if (REMOTE_RESOURCE.test(resourcePath)) return [];
|
|
7296
|
+
const resolution = resolveChildResourceDetailed(resourcePath, parentDir);
|
|
7297
|
+
if (!resolution.ok) {
|
|
7298
|
+
resourceLoadErrorsRegistry.record({
|
|
7299
|
+
name: parentRoute,
|
|
7300
|
+
path: resourcePath,
|
|
7301
|
+
error: resolution.reason === "missing" ? `Relation column "${c.id}" points at "${resourcePath}", but no resource.json was found there (looked in: ${resolution.attempted.join(", ")}). Sub-resource routes for it were not registered.` : `Relation column "${c.id}" points at "${resourcePath}", which could not be read: ${resolution.error} Sub-resource routes for it were not registered.`
|
|
7302
|
+
});
|
|
7303
|
+
return [];
|
|
7304
|
+
}
|
|
7305
|
+
const childJson = resolution.value.json;
|
|
7306
|
+
const childDir = resolution.value.dir;
|
|
7307
|
+
if (childJson.parent) {
|
|
7308
|
+
resourceLoadErrorsRegistry.record({
|
|
7309
|
+
name: parentRoute,
|
|
7310
|
+
path: resourcePath,
|
|
7311
|
+
error: `Relation column "${c.id}" declares "${childJson.name}" as a sub-resource, but that resource also declares "parent": { "route": "${childJson.parent.route}" }. The two ways of nesting are mutually exclusive \u2014 remove the "parent" block to embed it in this resource, or remove this relation column to keep its own nested controller. Sub-resource routes for it were not registered.`
|
|
7312
|
+
});
|
|
7313
|
+
return [];
|
|
7314
|
+
}
|
|
7315
|
+
const childRoute = childJson.route ?? resourcePath.replace(/^\.\.?\//, "").replace(/\/resource\.json$/, "").replace(/\.resource$/, "");
|
|
6271
7316
|
const rawChildColumns = childJson?.columns;
|
|
6272
7317
|
const expandedChildColumns = rawChildColumns ? expandExtendColumns(rawChildColumns, childDir) : void 0;
|
|
6273
7318
|
const childColumns = applyRelationFormatDefault(expandedChildColumns) ?? expandedChildColumns;
|
|
6274
7319
|
injectEnumValues(childColumns, enums);
|
|
6275
7320
|
const enrichedChildColumns = enrichNestedRelationColumns(childColumns, childDir, baseUrl);
|
|
7321
|
+
const autoIncludes = (enrichedChildColumns ?? []).filter((col) => col.fieldInput?.format === "relation" && (col.fieldInput.relationType ?? deriveRelationTypeFromColumns(col, enrichedChildColumns)) === "manyToOne").map((col) => col.fieldInput?.relation ?? col.id);
|
|
7322
|
+
const explicitIncludes = childJson?.include ?? [];
|
|
7323
|
+
const explicitNames = new Set(explicitIncludes.map((e) => typeof e === "string" ? e : e.relation));
|
|
7324
|
+
const mergedIncludes = [
|
|
7325
|
+
...explicitIncludes,
|
|
7326
|
+
...autoIncludes.filter((name) => !explicitNames.has(name))
|
|
7327
|
+
];
|
|
6276
7328
|
const childLookupKey = childColumns?.find((col) => col.idField)?.id ?? "id";
|
|
6277
7329
|
const childCalculatedColumns = childJson?.calculatedColumns ?? [];
|
|
6278
7330
|
let childViews = childJson ? buildViewsFromColumns(enrichedChildColumns) : void 0;
|
|
@@ -6289,16 +7341,27 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
|
|
|
6289
7341
|
}
|
|
6290
7342
|
}
|
|
6291
7343
|
const childOps = childJson?.operations ?? {};
|
|
7344
|
+
const childKind = childJson?.kind === "custom" ? "custom" : "prisma";
|
|
6292
7345
|
return {
|
|
6293
7346
|
column: c.id,
|
|
6294
7347
|
relation: c.fieldInput?.relation ?? c.id,
|
|
6295
7348
|
childRoute,
|
|
6296
|
-
|
|
7349
|
+
childKind,
|
|
7350
|
+
...childDir && {
|
|
7351
|
+
childDir
|
|
7352
|
+
},
|
|
7353
|
+
// A custom child has no Prisma model. Leave it empty rather than
|
|
7354
|
+
// falling back to the column id, which would produce a bogus
|
|
7355
|
+
// `prisma[<column>]` lookup at query time.
|
|
7356
|
+
childModel: childKind === "custom" ? "" : c.id,
|
|
6297
7357
|
foreignKey: c.fieldInput?.foreignKey ?? `${parentModel}Id`,
|
|
6298
7358
|
name: childJson?.name ?? childRoute,
|
|
6299
7359
|
title: childJson?.title ?? childJson?.tag ?? childRoute,
|
|
6300
7360
|
idField: childLookupKey,
|
|
6301
7361
|
idType: childJson?.idType ?? "string",
|
|
7362
|
+
...c.hiddenInTable && {
|
|
7363
|
+
hiddenInTable: true
|
|
7364
|
+
},
|
|
6302
7365
|
...childViews && {
|
|
6303
7366
|
views: childViews
|
|
6304
7367
|
},
|
|
@@ -6317,8 +7380,8 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
|
|
|
6317
7380
|
...childJson?.modalSize && {
|
|
6318
7381
|
modalSize: childJson.modalSize
|
|
6319
7382
|
},
|
|
6320
|
-
...
|
|
6321
|
-
include:
|
|
7383
|
+
...mergedIncludes.length && {
|
|
7384
|
+
include: mergedIncludes
|
|
6322
7385
|
},
|
|
6323
7386
|
...childJson?.calculatedColumns?.length && {
|
|
6324
7387
|
calculatedColumns: childJson.calculatedColumns
|
|
@@ -6342,17 +7405,18 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
|
|
|
6342
7405
|
}, "buildSubResources");
|
|
6343
7406
|
|
|
6344
7407
|
// src/lib/crud/adapter/json-adapter.ts
|
|
6345
|
-
var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, actions, tableActions, enums = {}) => {
|
|
7408
|
+
var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, actions, tableActions, enums = {}, repository) => {
|
|
7409
|
+
const isCustom = json.kind === "custom";
|
|
6346
7410
|
const rawColumns = expandExtendColumns(json.columns, dirPath);
|
|
6347
7411
|
const columns = enrichRelationTypes(applyRelationFormatDefault(rawColumns) ?? rawColumns, schema);
|
|
6348
7412
|
injectEnumValues(columns, enums);
|
|
6349
|
-
const subResources = buildSubResources(columns, json.route, json.model, dirPath, enums, baseUrl);
|
|
7413
|
+
const subResources = isCustom ? [] : buildSubResources(columns, json.route, json.model ?? "", dirPath, enums, baseUrl);
|
|
6350
7414
|
const enrichedColumns = resolveColumnFieldVariants(enrichResourceRefColumns(enrichActionColumns(columns, json.route, subResources, baseUrl), dirPath, baseUrl) ?? columns);
|
|
6351
7415
|
const calculatedColumns = json.calculatedColumns ?? [];
|
|
6352
7416
|
const picked = pickByColumns(schema, enrichedColumns);
|
|
6353
7417
|
const createSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.createable !== false);
|
|
6354
7418
|
const updateSchema = pickByColumns(schema, enrichedColumns, (c) => !c.idField && c.updateable !== false);
|
|
6355
|
-
let views = buildViews(schema, enrichedColumns);
|
|
7419
|
+
let views = isCustom ? buildViewsFromColumnTypes(enrichedColumns) : buildViews(schema, enrichedColumns);
|
|
6356
7420
|
if (views && calculatedColumns.length) {
|
|
6357
7421
|
views = {
|
|
6358
7422
|
...views,
|
|
@@ -6410,6 +7474,9 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
|
|
|
6410
7474
|
...hooks && {
|
|
6411
7475
|
hooks
|
|
6412
7476
|
},
|
|
7477
|
+
...repository && {
|
|
7478
|
+
repository
|
|
7479
|
+
},
|
|
6413
7480
|
definition,
|
|
6414
7481
|
...views && {
|
|
6415
7482
|
views
|
|
@@ -6453,40 +7520,6 @@ var buildLookup = /* @__PURE__ */ __name((columns) => {
|
|
|
6453
7520
|
};
|
|
6454
7521
|
}, "buildLookup");
|
|
6455
7522
|
|
|
6456
|
-
// src/lib/crud/hooks/hooks.types.ts
|
|
6457
|
-
var import_zod27 = require("zod");
|
|
6458
|
-
var WriteOpSchema = import_zod27.z.enum([
|
|
6459
|
-
"create",
|
|
6460
|
-
"update",
|
|
6461
|
-
"patch",
|
|
6462
|
-
"upsert",
|
|
6463
|
-
"delete"
|
|
6464
|
-
]);
|
|
6465
|
-
var ReadOpSchema = import_zod27.z.enum([
|
|
6466
|
-
"findAll",
|
|
6467
|
-
"findOne"
|
|
6468
|
-
]);
|
|
6469
|
-
var ResourceHooksSchema = import_zod27.z.object({
|
|
6470
|
-
beforeWrite: import_zod27.z.custom().optional(),
|
|
6471
|
-
afterWrite: import_zod27.z.custom().optional(),
|
|
6472
|
-
afterRead: import_zod27.z.custom().optional()
|
|
6473
|
-
});
|
|
6474
|
-
|
|
6475
|
-
// src/lib/crud/hooks/hooks.loader.ts
|
|
6476
|
-
var import_node_path16 = require("path");
|
|
6477
|
-
var loadResourceHooks = /* @__PURE__ */ __name(async (basePath) => {
|
|
6478
|
-
const file = findModule(basePath, "hooks");
|
|
6479
|
-
return file ? importDefault(file) : void 0;
|
|
6480
|
-
}, "loadResourceHooks");
|
|
6481
|
-
var loadSubResourceHooks = /* @__PURE__ */ __name(async (subResources, basePath) => {
|
|
6482
|
-
for (const sub of subResources) {
|
|
6483
|
-
const file = sub.name ? findModule((0, import_node_path16.join)(basePath, "hooks"), sub.name) : void 0;
|
|
6484
|
-
if (!file) continue;
|
|
6485
|
-
const hooks = await importDefault(file);
|
|
6486
|
-
if (hooks) sub.hooks = hooks;
|
|
6487
|
-
}
|
|
6488
|
-
}, "loadSubResourceHooks");
|
|
6489
|
-
|
|
6490
7523
|
// src/lib/crud/resource/MigrateResourceJson.ts
|
|
6491
7524
|
var import_node_fs8 = require("fs");
|
|
6492
7525
|
var migrateResourceJsonFile = /* @__PURE__ */ __name((jsonPath, opts) => {
|
|
@@ -6609,10 +7642,12 @@ var loadResourceConfigsFromDir = /* @__PURE__ */ __name(async (dirPath, baseUrl,
|
|
|
6609
7642
|
});
|
|
6610
7643
|
continue;
|
|
6611
7644
|
}
|
|
7645
|
+
const repository = json.kind === "custom" ? await loadCustomRepository(basePath, json.name) : void 0;
|
|
6612
7646
|
const actions = await loadActions(json.actions ?? [], basePath, "row");
|
|
6613
7647
|
const tableActions = await loadActions(json.tableActions ?? [], basePath, "table");
|
|
6614
|
-
const config = fromJson(json, schema, hooks, basePath, baseUrl, actions, tableActions, enums);
|
|
7648
|
+
const config = fromJson(json, schema, hooks, basePath, baseUrl, actions, tableActions, enums, repository);
|
|
6615
7649
|
await loadSubResourceHooks(config.subResources ?? [], basePath);
|
|
7650
|
+
await loadSubResourceRepositories(config.subResources ?? [], config.name);
|
|
6616
7651
|
onResourceDir?.(config.route, basePath);
|
|
6617
7652
|
configs.push(config);
|
|
6618
7653
|
continue;
|
|
@@ -6736,6 +7771,13 @@ var getResourceStatus = /* @__PURE__ */ __name((loadedConfigs) => {
|
|
|
6736
7771
|
path: c.route,
|
|
6737
7772
|
valid: true,
|
|
6738
7773
|
version: c.schemaVersion ?? CURRENT_RESOURCE_VERSION,
|
|
7774
|
+
kind: c.kind ?? "prisma",
|
|
7775
|
+
// Which operations the user's repository.ts actually implements. A resource
|
|
7776
|
+
// only reaches this list after validateCustomRepository passed, so this is
|
|
7777
|
+
// informational rather than a warning.
|
|
7778
|
+
...c.kind === "custom" && c.repository ? {
|
|
7779
|
+
customOperations: CUSTOM_OPS.filter((op) => typeof c.repository?.[op] === "function")
|
|
7780
|
+
} : {},
|
|
6739
7781
|
...c.sidebar?.hide ? {
|
|
6740
7782
|
hidden: true
|
|
6741
7783
|
} : {}
|
|
@@ -6893,7 +7935,7 @@ var buildStatus = /* @__PURE__ */ __name(async (registry, loadedConfigs, enumReg
|
|
|
6893
7935
|
}, "buildStatus");
|
|
6894
7936
|
|
|
6895
7937
|
// src/lib/crud/status/status.controller.ts
|
|
6896
|
-
var
|
|
7938
|
+
var import_common20 = require("@nestjs/common");
|
|
6897
7939
|
var import_swagger13 = require("@nestjs/swagger");
|
|
6898
7940
|
function _ts_decorate6(decorators, target, key, desc2) {
|
|
6899
7941
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
@@ -6923,7 +7965,7 @@ var createStatusController = /* @__PURE__ */ __name((enumRegistry) => {
|
|
|
6923
7965
|
}
|
|
6924
7966
|
};
|
|
6925
7967
|
_ts_decorate6([
|
|
6926
|
-
(0,
|
|
7968
|
+
(0, import_common20.Get)("status.json"),
|
|
6927
7969
|
(0, import_swagger13.ApiOperation)({
|
|
6928
7970
|
summary: "Crouton system status (db, resources, version)"
|
|
6929
7971
|
}),
|
|
@@ -6936,7 +7978,7 @@ var createStatusController = /* @__PURE__ */ __name((enumRegistry) => {
|
|
|
6936
7978
|
_ts_metadata5("design:returntype", Promise)
|
|
6937
7979
|
], StatusController.prototype, "getStatus", null);
|
|
6938
7980
|
StatusController = _ts_decorate6([
|
|
6939
|
-
(0,
|
|
7981
|
+
(0, import_common20.Controller)("crouton"),
|
|
6940
7982
|
(0, import_swagger13.ApiTags)("Status"),
|
|
6941
7983
|
_ts_metadata5("design:type", Function),
|
|
6942
7984
|
_ts_metadata5("design:paramtypes", [
|
|
@@ -6978,9 +8020,34 @@ var CroutonApiModule = class _CroutonApiModule {
|
|
|
6978
8020
|
const enumRegistry = loadEnumRegistry(startDir, config.enumsFile);
|
|
6979
8021
|
const validConfigs = [];
|
|
6980
8022
|
for (const c of configs) {
|
|
8023
|
+
if (c.kind === "custom") {
|
|
8024
|
+
if (c.database) {
|
|
8025
|
+
try {
|
|
8026
|
+
dataSourceRegistry.resolve(c.database);
|
|
8027
|
+
} catch (e) {
|
|
8028
|
+
resourceLoadErrorsRegistry.record({
|
|
8029
|
+
name: c.name,
|
|
8030
|
+
path: c.route,
|
|
8031
|
+
error: e.message ?? String(e)
|
|
8032
|
+
});
|
|
8033
|
+
continue;
|
|
8034
|
+
}
|
|
8035
|
+
}
|
|
8036
|
+
const problem = validateCustomRepository(c, c.repository);
|
|
8037
|
+
if (problem) {
|
|
8038
|
+
resourceLoadErrorsRegistry.record({
|
|
8039
|
+
name: c.name,
|
|
8040
|
+
path: c.route,
|
|
8041
|
+
error: problem
|
|
8042
|
+
});
|
|
8043
|
+
continue;
|
|
8044
|
+
}
|
|
8045
|
+
validConfigs.push(c);
|
|
8046
|
+
continue;
|
|
8047
|
+
}
|
|
6981
8048
|
try {
|
|
6982
8049
|
const prisma = dataSourceRegistry.resolve(c.database);
|
|
6983
|
-
if (!prisma[c.model]) {
|
|
8050
|
+
if (!c.model || !prisma[c.model]) {
|
|
6984
8051
|
resourceLoadErrorsRegistry.record({
|
|
6985
8052
|
name: c.name,
|
|
6986
8053
|
path: c.route,
|
|
@@ -7043,7 +8110,7 @@ var CroutonApiModule = class _CroutonApiModule {
|
|
|
7043
8110
|
}
|
|
7044
8111
|
};
|
|
7045
8112
|
CroutonApiModule = _ts_decorate7([
|
|
7046
|
-
(0,
|
|
8113
|
+
(0, import_common21.Module)({
|
|
7047
8114
|
controllers: [],
|
|
7048
8115
|
providers: [],
|
|
7049
8116
|
exports: []
|
|
@@ -7054,14 +8121,25 @@ CroutonApiModule = _ts_decorate7([
|
|
|
7054
8121
|
CroutonApiModule,
|
|
7055
8122
|
DataSourceRegistry,
|
|
7056
8123
|
FileSystemResourceConfigLoader,
|
|
8124
|
+
ReadOpSchema,
|
|
7057
8125
|
ResourceConfigLoader,
|
|
7058
8126
|
ResourceConfigRegistry,
|
|
8127
|
+
ResourceHooksSchema,
|
|
8128
|
+
WriteOpSchema,
|
|
8129
|
+
buildFilterWhere,
|
|
7059
8130
|
buildViews,
|
|
8131
|
+
decorateRow,
|
|
8132
|
+
decorateRows,
|
|
7060
8133
|
isOperationEnabled,
|
|
7061
8134
|
isRowProcedureAction,
|
|
7062
8135
|
isTableProcedureAction,
|
|
7063
8136
|
loadDataSourcesFromDir,
|
|
7064
8137
|
loadResourceConfigsFromDir,
|
|
8138
|
+
loadResourceHooks,
|
|
8139
|
+
loadSubResourceHooks,
|
|
8140
|
+
parseFilterString,
|
|
8141
|
+
postWrite,
|
|
8142
|
+
prepareWrite,
|
|
7065
8143
|
resolveDefinition,
|
|
7066
8144
|
schemaFor,
|
|
7067
8145
|
upsertOnFor
|