@ghentcdh/crouton-api 0.0.1-alpha.3 → 0.0.1-alpha.31
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 +1401 -741
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1069 -276
- package/dist/index.d.ts +1069 -276
- package/dist/index.js +1386 -735
- package/dist/index.js.map +1 -1
- package/package.json +13 -8
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
var __export = (target, all) => {
|
|
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc2) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/index.ts
|
|
@@ -27,6 +37,8 @@ __export(index_exports, {
|
|
|
27
37
|
ResourceConfigLoader: () => ResourceConfigLoader2,
|
|
28
38
|
ResourceConfigRegistry: () => ResourceConfigRegistry,
|
|
29
39
|
isOperationEnabled: () => isOperationEnabled,
|
|
40
|
+
isRowProcedureAction: () => isRowProcedureAction,
|
|
41
|
+
isTableProcedureAction: () => isTableProcedureAction,
|
|
30
42
|
loadDataSourcesFromDir: () => loadDataSourcesFromDir,
|
|
31
43
|
loadResourceConfigsFromDir: () => loadResourceConfigsFromDir,
|
|
32
44
|
resolveDefinition: () => resolveDefinition,
|
|
@@ -35,21 +47,41 @@ __export(index_exports, {
|
|
|
35
47
|
});
|
|
36
48
|
module.exports = __toCommonJS(index_exports);
|
|
37
49
|
|
|
50
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.58.8_@types+node@26.0.1__@swc+core@1.15.43_@swc+h_ec0dcc88aa461a763e37ec5746d3e7df/node_modules/tsup/assets/cjs_shims.js
|
|
51
|
+
var getImportMetaUrl = /* @__PURE__ */ __name(() => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href, "getImportMetaUrl");
|
|
52
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
53
|
+
|
|
38
54
|
// src/lib/crouton-api.module.ts
|
|
39
|
-
var
|
|
55
|
+
var import_common12 = require("@nestjs/common");
|
|
40
56
|
var import_core = require("@nestjs/core");
|
|
41
57
|
|
|
42
|
-
// src/lib/crud/app-layout.
|
|
43
|
-
var
|
|
44
|
-
var
|
|
58
|
+
// src/lib/crud/app-layout/app-layout.types.ts
|
|
59
|
+
var import_zod = require("zod");
|
|
60
|
+
var SidebarLeafSchema = import_zod.z.object({
|
|
61
|
+
kind: import_zod.z.literal("item").default("item"),
|
|
62
|
+
id: import_zod.z.string(),
|
|
63
|
+
label: import_zod.z.string(),
|
|
64
|
+
position: import_zod.z.number().optional()
|
|
65
|
+
});
|
|
66
|
+
var SidebarGroupSchema = import_zod.z.object({
|
|
67
|
+
kind: import_zod.z.literal("group").default("group"),
|
|
68
|
+
id: import_zod.z.string(),
|
|
69
|
+
label: import_zod.z.string(),
|
|
70
|
+
position: import_zod.z.number().optional(),
|
|
71
|
+
children: import_zod.z.array(SidebarLeafSchema).default([])
|
|
72
|
+
});
|
|
73
|
+
var SidebarNodeSchema = import_zod.z.discriminatedUnion("kind", [
|
|
74
|
+
SidebarLeafSchema,
|
|
75
|
+
SidebarGroupSchema
|
|
76
|
+
]);
|
|
45
77
|
|
|
46
78
|
// ../crouton-core/src/lib/request.model.ts
|
|
47
|
-
var
|
|
79
|
+
var import_zod3 = require("zod");
|
|
48
80
|
|
|
49
81
|
// ../crouton-core/src/lib/zod.types.ts
|
|
50
|
-
var
|
|
51
|
-
var PositiveRequestNumber = /* @__PURE__ */ __name(() =>
|
|
52
|
-
var StringOrArray = /* @__PURE__ */ __name(() =>
|
|
82
|
+
var import_zod2 = require("zod");
|
|
83
|
+
var PositiveRequestNumber = /* @__PURE__ */ __name(() => import_zod2.z.coerce.number().int().positive().nonnegative(), "PositiveRequestNumber");
|
|
84
|
+
var StringOrArray = /* @__PURE__ */ __name(() => import_zod2.z.string().or(import_zod2.z.array(import_zod2.z.string())).transform((val) => {
|
|
53
85
|
if (Array.isArray(val)) return val;
|
|
54
86
|
return [
|
|
55
87
|
val
|
|
@@ -57,14 +89,14 @@ var StringOrArray = /* @__PURE__ */ __name(() => import_zod.z.string().or(import
|
|
|
57
89
|
}), "StringOrArray");
|
|
58
90
|
|
|
59
91
|
// ../crouton-core/src/lib/request.model.ts
|
|
60
|
-
var SortDirEnum =
|
|
92
|
+
var SortDirEnum = import_zod3.z.enum([
|
|
61
93
|
"asc",
|
|
62
94
|
"desc"
|
|
63
95
|
]);
|
|
64
|
-
var RequestSchema =
|
|
96
|
+
var RequestSchema = import_zod3.z.object({
|
|
65
97
|
page: PositiveRequestNumber().optional().default(1),
|
|
66
98
|
pageSize: PositiveRequestNumber().optional().default(20),
|
|
67
|
-
sort:
|
|
99
|
+
sort: import_zod3.z.string().optional().default("id"),
|
|
68
100
|
sortDir: SortDirEnum.optional().default("asc"),
|
|
69
101
|
// Filter is of the format key:value:operator (e.g. name:john:eq) operator is optional
|
|
70
102
|
filter: StringOrArray().optional().default([])
|
|
@@ -115,18 +147,18 @@ var buildSortKey = /* @__PURE__ */ __name((keys, sortDir) => {
|
|
|
115
147
|
}, "buildSortKey");
|
|
116
148
|
|
|
117
149
|
// ../crouton-core/src/lib/response.model.ts
|
|
118
|
-
var
|
|
150
|
+
var import_zod5 = require("zod");
|
|
119
151
|
var ResponseRequestSchema = RequestSchema.extend({
|
|
120
152
|
count: PositiveRequestNumber(),
|
|
121
153
|
totalPages: PositiveRequestNumber()
|
|
122
154
|
});
|
|
123
|
-
var ResponseSchema =
|
|
124
|
-
data:
|
|
155
|
+
var ResponseSchema = import_zod5.z.object({
|
|
156
|
+
data: import_zod5.z.array(import_zod5.z.unknown()),
|
|
125
157
|
request: ResponseRequestSchema
|
|
126
158
|
});
|
|
127
159
|
|
|
128
160
|
// ../crouton-core/src/lib/create-schema.ts
|
|
129
|
-
var
|
|
161
|
+
var import_zod7 = require("zod");
|
|
130
162
|
|
|
131
163
|
// ../crouton-core/src/lib/layout/base.builder.ts
|
|
132
164
|
var Builder = class {
|
|
@@ -384,23 +416,6 @@ var LayoutBuilder = class _LayoutBuilder extends BuilderWithElements {
|
|
|
384
416
|
}
|
|
385
417
|
};
|
|
386
418
|
|
|
387
|
-
// ../crouton-core/src/lib/json-config.types.ts
|
|
388
|
-
var labelFromId = /* @__PURE__ */ __name((id) => {
|
|
389
|
-
const words = id.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
390
|
-
return words.charAt(0).toUpperCase() + words.slice(1).toLowerCase();
|
|
391
|
-
}, "labelFromId");
|
|
392
|
-
var normalizeColumns = /* @__PURE__ */ __name((columns) => {
|
|
393
|
-
if (!columns) return void 0;
|
|
394
|
-
const raw = Array.isArray(columns) ? columns : Object.entries(columns).map(([id, col]) => ({
|
|
395
|
-
id,
|
|
396
|
-
...col
|
|
397
|
-
}));
|
|
398
|
-
return raw.map((col) => ({
|
|
399
|
-
...col,
|
|
400
|
-
label: col.label ?? labelFromId(col.id)
|
|
401
|
-
}));
|
|
402
|
-
}, "normalizeColumns");
|
|
403
|
-
|
|
404
419
|
// ../crouton-core/src/lib/value-label.ts
|
|
405
420
|
var wrapOne = /* @__PURE__ */ __name((value, values) => {
|
|
406
421
|
const match = values.find((o) => o.value === value);
|
|
@@ -494,6 +509,515 @@ var TableBuilder = class _TableBuilder {
|
|
|
494
509
|
}
|
|
495
510
|
};
|
|
496
511
|
|
|
512
|
+
// ../crouton-core/src/lib/data-source/DataSourceSchema.ts
|
|
513
|
+
var import_zod8 = __toESM(require("zod"), 1);
|
|
514
|
+
var DataSourceShape = import_zod8.default.object({
|
|
515
|
+
/** Datasource name (folder + `name` in data-source.json). */
|
|
516
|
+
name: import_zod8.default.string(),
|
|
517
|
+
/** Env var holding the connection URL. */
|
|
518
|
+
urlEnv: import_zod8.default.string(),
|
|
519
|
+
/** Import path for this datasource's generated Zod types (for resource `schema.ts`). */
|
|
520
|
+
generatedTypesImport: import_zod8.default.string().optional(),
|
|
521
|
+
/** Datasource type tag. Default `postgres`. */
|
|
522
|
+
type: import_zod8.default.string().default("postgres"),
|
|
523
|
+
/** Mark this as the default datasource. Default `false`. */
|
|
524
|
+
default: import_zod8.default.boolean().default(false),
|
|
525
|
+
/** Prisma schema path, relative to project root. Default `prisma/<name>/schema.prisma`. */
|
|
526
|
+
prismaSchema: import_zod8.default.string().optional(),
|
|
527
|
+
/** Prisma config path, relative to project root. Default `prisma/<name>/prisma.config.ts`. */
|
|
528
|
+
prismaConfig: import_zod8.default.string().optional(),
|
|
529
|
+
/** zod-prisma-types output dir, relative to project root. Default `generated/<name>/src`. */
|
|
530
|
+
zodOutput: import_zod8.default.string().optional(),
|
|
531
|
+
/** Prisma client output dir, relative to project root. Default `generated/<name>/client`. */
|
|
532
|
+
clientOutput: import_zod8.default.string().optional()
|
|
533
|
+
});
|
|
534
|
+
var transformDataSource = /* @__PURE__ */ __name((data) => {
|
|
535
|
+
const name = data.name ?? "name";
|
|
536
|
+
return {
|
|
537
|
+
...data,
|
|
538
|
+
generatedTypesImport: data.generatedTypesImport ?? `@your-scope/generated/${name}`,
|
|
539
|
+
prismaSchema: data.prismaSchema ?? `prisma/${name}/schema.prisma`,
|
|
540
|
+
prismaConfig: data.prismaConfig ?? `prisma/${name}/prisma.config.ts`,
|
|
541
|
+
zodOutput: data.zodOutput ?? `generated/${name}/src`,
|
|
542
|
+
clientOutput: data.clientOutput ?? `generated/${name}/client`
|
|
543
|
+
};
|
|
544
|
+
}, "transformDataSource");
|
|
545
|
+
var DataSourceSchema = DataSourceShape.transform(transformDataSource);
|
|
546
|
+
|
|
547
|
+
// ../crouton-core/src/lib/data-source/Operations.schema.ts
|
|
548
|
+
var import_zod9 = require("zod");
|
|
549
|
+
var BoolOrUpsertSchema = import_zod9.z.union([
|
|
550
|
+
import_zod9.z.boolean(),
|
|
551
|
+
import_zod9.z.object({
|
|
552
|
+
upsertOn: import_zod9.z.union([
|
|
553
|
+
import_zod9.z.string(),
|
|
554
|
+
import_zod9.z.array(import_zod9.z.string())
|
|
555
|
+
])
|
|
556
|
+
})
|
|
557
|
+
]);
|
|
558
|
+
var JsonOperationsSchema = import_zod9.z.object({
|
|
559
|
+
findAll: import_zod9.z.boolean().default(true),
|
|
560
|
+
findOne: import_zod9.z.boolean().default(true),
|
|
561
|
+
create: import_zod9.z.boolean().default(true),
|
|
562
|
+
update: import_zod9.z.boolean().default(true),
|
|
563
|
+
upsert: BoolOrUpsertSchema.default(false),
|
|
564
|
+
delete: import_zod9.z.boolean().default(true)
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// ../crouton-core/src/lib/resource/ResourceJson.schema.ts
|
|
568
|
+
var import_zod16 = require("zod");
|
|
569
|
+
|
|
570
|
+
// ../crouton-core/src/lib/resource/CalculatedColumn.schema.ts
|
|
571
|
+
var import_zod11 = require("zod");
|
|
572
|
+
|
|
573
|
+
// ../crouton-core/src/lib/resource/FieldInput.schema.ts
|
|
574
|
+
var import_zod10 = require("zod");
|
|
575
|
+
var RelationType = import_zod10.z.enum([
|
|
576
|
+
"oneToOne",
|
|
577
|
+
"manyToOne",
|
|
578
|
+
"oneToMany",
|
|
579
|
+
"manyToMany"
|
|
580
|
+
]);
|
|
581
|
+
var DetailControlSchema = import_zod10.z.object({
|
|
582
|
+
property: import_zod10.z.string(),
|
|
583
|
+
type: import_zod10.z.string().optional(),
|
|
584
|
+
options: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.unknown()).optional(),
|
|
585
|
+
hideLabel: import_zod10.z.boolean().optional(),
|
|
586
|
+
width: import_zod10.z.string().optional()
|
|
587
|
+
});
|
|
588
|
+
var DetailConfigSchema = import_zod10.z.object({
|
|
589
|
+
layout: import_zod10.z.enum([
|
|
590
|
+
"collapse",
|
|
591
|
+
"row"
|
|
592
|
+
]),
|
|
593
|
+
titleKey: import_zod10.z.string().optional(),
|
|
594
|
+
controls: import_zod10.z.array(DetailControlSchema)
|
|
595
|
+
});
|
|
596
|
+
var RelationFieldInputOptionsSchema = import_zod10.z.object({
|
|
597
|
+
/** Field to sort related records by, e.g. `"title"` or `"author.name"`. */
|
|
598
|
+
sort: import_zod10.z.string().optional(),
|
|
599
|
+
/** Sort direction. Defaults to `"asc"` when omitted. */
|
|
600
|
+
sortDir: import_zod10.z.enum([
|
|
601
|
+
"asc",
|
|
602
|
+
"desc"
|
|
603
|
+
]).optional(),
|
|
604
|
+
/** CSS flex direction for the relation control button layout. */
|
|
605
|
+
direction: import_zod10.z.string().optional(),
|
|
606
|
+
/** Key used as the display label in the relation control. */
|
|
607
|
+
displayKey: import_zod10.z.string().optional(),
|
|
608
|
+
/** Path to the related resource (resolved to a URI at load time). */
|
|
609
|
+
resource: import_zod10.z.string().optional()
|
|
610
|
+
}).catchall(import_zod10.z.unknown());
|
|
611
|
+
var FieldInputSchema = import_zod10.z.object({
|
|
612
|
+
type: import_zod10.z.string().optional(),
|
|
613
|
+
customRender: import_zod10.z.string().optional(),
|
|
614
|
+
/**
|
|
615
|
+
* Render format hint for the frontend (e.g. `"relation"` for sub-resource
|
|
616
|
+
* relations). When `format` is `"relation"`, `resource` must also be set.
|
|
617
|
+
*/
|
|
618
|
+
format: import_zod10.z.string().optional(),
|
|
619
|
+
/**
|
|
620
|
+
* Relative path to the child resource definition, e.g. `"./author.resource"`.
|
|
621
|
+
* Only used when `format` is `"relation"`. Resolved at load time to inject
|
|
622
|
+
* sub-resource URIs into `options`.
|
|
623
|
+
*/
|
|
624
|
+
resource: import_zod10.z.string().optional(),
|
|
625
|
+
/**
|
|
626
|
+
* Cardinality of the relation. Used by the frontend to determine the correct
|
|
627
|
+
* renderer and behaviour (e.g. single-select vs multi-select).
|
|
628
|
+
* - `manyToOne` / `oneToOne` — single FK reference (autocomplete / relation control)
|
|
629
|
+
* - `oneToMany` / `manyToMany` — collection (sub-resource table / multi-select)
|
|
630
|
+
*/
|
|
631
|
+
relationType: RelationType.optional(),
|
|
632
|
+
/** Override the display order in form views. Lower values come first. */
|
|
633
|
+
position: import_zod10.z.number().optional(),
|
|
634
|
+
options: import_zod10.z.union([
|
|
635
|
+
RelationFieldInputOptionsSchema,
|
|
636
|
+
import_zod10.z.unknown()
|
|
637
|
+
]).optional(),
|
|
638
|
+
/** Nested array detail layout (renders via `detailFixed`). */
|
|
639
|
+
detail: DetailConfigSchema.optional()
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// ../crouton-core/src/lib/schema/label.helper.ts
|
|
643
|
+
var labelFromId = /* @__PURE__ */ __name((id) => {
|
|
644
|
+
const words = id.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
|
|
645
|
+
return words.charAt(0).toUpperCase() + words.slice(1).toLowerCase();
|
|
646
|
+
}, "labelFromId");
|
|
647
|
+
var normalizeLabel = /* @__PURE__ */ __name((obj) => {
|
|
648
|
+
return {
|
|
649
|
+
label: labelFromId(obj.id),
|
|
650
|
+
...obj
|
|
651
|
+
};
|
|
652
|
+
}, "normalizeLabel");
|
|
653
|
+
|
|
654
|
+
// ../crouton-core/src/lib/resource/CalculatedColumn.schema.ts
|
|
655
|
+
var CalculatedColumnSchema = import_zod11.z.object({
|
|
656
|
+
/** Column identifier used in the response payload and UI schema. */
|
|
657
|
+
id: import_zod11.z.string(),
|
|
658
|
+
/** SQL alias — must match `id`. */
|
|
659
|
+
alias: import_zod11.z.string(),
|
|
660
|
+
/** Human-readable label shown in the table header. */
|
|
661
|
+
label: import_zod11.z.string().optional(),
|
|
662
|
+
sqlExpression: import_zod11.z.string(),
|
|
663
|
+
/** JSON schema / cast type for the computed value. Defaults to `"number"`. `"string"` skips CAST. */
|
|
664
|
+
type: import_zod11.z.enum([
|
|
665
|
+
"number",
|
|
666
|
+
"boolean",
|
|
667
|
+
"string"
|
|
668
|
+
]).optional(),
|
|
669
|
+
/**
|
|
670
|
+
* Insertion position relative to the other visible columns.
|
|
671
|
+
* Uses the same semantics as `fieldInput.position` on regular columns:
|
|
672
|
+
* `position: N` places this column before the element currently at index N.
|
|
673
|
+
* Can also be set via `fieldInput.position` (takes precedence).
|
|
674
|
+
*/
|
|
675
|
+
position: import_zod11.z.number().optional(),
|
|
676
|
+
hiddenInTable: import_zod11.z.boolean().optional(),
|
|
677
|
+
hiddenInView: import_zod11.z.boolean().optional(),
|
|
678
|
+
/**
|
|
679
|
+
* Field input options for rendering in form / view schemas.
|
|
680
|
+
* Supports `colspan`, `format`, and any other options passed to the Control renderer.
|
|
681
|
+
* `position` here takes precedence over the top-level `position`.
|
|
682
|
+
*/
|
|
683
|
+
fieldInput: FieldInputSchema.optional()
|
|
684
|
+
}).transform(normalizeLabel);
|
|
685
|
+
|
|
686
|
+
// ../crouton-core/src/lib/resource/Column.ts
|
|
687
|
+
var import_zod12 = require("zod");
|
|
688
|
+
var WhenConditionSchema = import_zod12.z.object({
|
|
689
|
+
field: import_zod12.z.string(),
|
|
690
|
+
eq: import_zod12.z.unknown().optional(),
|
|
691
|
+
neq: import_zod12.z.unknown().optional(),
|
|
692
|
+
exists: import_zod12.z.boolean().optional(),
|
|
693
|
+
notExists: import_zod12.z.boolean().optional()
|
|
694
|
+
});
|
|
695
|
+
var JsonColumnSchema = import_zod12.z.object({
|
|
696
|
+
id: import_zod12.z.string(),
|
|
697
|
+
column: import_zod12.z.string().optional(),
|
|
698
|
+
label: import_zod12.z.string().optional(),
|
|
699
|
+
hiddenInTable: import_zod12.z.boolean().default(false),
|
|
700
|
+
hiddenInForm: import_zod12.z.boolean().default(false),
|
|
701
|
+
hiddenInView: import_zod12.z.boolean().default(false),
|
|
702
|
+
sortable: import_zod12.z.boolean().default(false),
|
|
703
|
+
defaultSort: import_zod12.z.boolean().default(false),
|
|
704
|
+
searchable: import_zod12.z.boolean().default(false),
|
|
705
|
+
filterable: import_zod12.z.boolean().default(false),
|
|
706
|
+
createable: import_zod12.z.boolean().default(true),
|
|
707
|
+
updateable: import_zod12.z.boolean().default(true),
|
|
708
|
+
hideLabel: import_zod12.z.boolean().default(false),
|
|
709
|
+
showWhen: WhenConditionSchema.optional(),
|
|
710
|
+
hideWhen: WhenConditionSchema.optional(),
|
|
711
|
+
disabledWhen: WhenConditionSchema.optional(),
|
|
712
|
+
displayKey: import_zod12.z.string().optional(),
|
|
713
|
+
sortId: import_zod12.z.string().optional(),
|
|
714
|
+
/**
|
|
715
|
+
* Name of a shared enum in the project enum registry (`crouton.enums.json`).
|
|
716
|
+
* At load time the loader injects that enum's `{ value, label }[]` into
|
|
717
|
+
* `fieldInput.options.values`, so columns don't duplicate the option list.
|
|
718
|
+
*/
|
|
719
|
+
enum: import_zod12.z.string().optional(),
|
|
720
|
+
idField: import_zod12.z.boolean().default(false),
|
|
721
|
+
showInLookup: import_zod12.z.boolean().default(false),
|
|
722
|
+
columnType: import_zod12.z.string().default("string"),
|
|
723
|
+
fieldInput: FieldInputSchema.optional(),
|
|
724
|
+
/**
|
|
725
|
+
* Path to another `resource.json` whose columns are expanded as nested sub-columns
|
|
726
|
+
* under this column's object key.
|
|
727
|
+
*
|
|
728
|
+
* Example: `"extend": "../internalAuthor/resource.json"` on a column `author`
|
|
729
|
+
* auto-generates virtual sub-columns like `author_name` (column: "author", displayKey: "name").
|
|
730
|
+
*
|
|
731
|
+
* Visibility (`hiddenInTable/Form/View`) on this column is inherited by all sub-columns as a
|
|
732
|
+
* default; the referenced resource's own column visibility further restricts it.
|
|
733
|
+
*/
|
|
734
|
+
extend: import_zod12.z.string().optional(),
|
|
735
|
+
/**
|
|
736
|
+
* Per-sub-column overrides when using `extend`.
|
|
737
|
+
* Key: the virtual column id (`"{extendId}_{refColId}"`) or just the referenced column id.
|
|
738
|
+
* Value: any `JsonColumn` fields to merge over the generated virtual column.
|
|
739
|
+
*/
|
|
740
|
+
columns: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.record(import_zod12.z.string(), import_zod12.z.unknown())).optional()
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// ../crouton-core/src/lib/resource/Sidebar.schema.ts
|
|
744
|
+
var import_zod13 = __toESM(require("zod"), 1);
|
|
745
|
+
var SidebarGroupSchema2 = import_zod13.default.object({
|
|
746
|
+
/** Human-readable heading shown in the sidebar. Defaults to a title-cased version of the slug. */
|
|
747
|
+
label: import_zod13.default.string().optional(),
|
|
748
|
+
/** Controls the order of this group among top-level sidebar items. */
|
|
749
|
+
position: import_zod13.default.number().optional()
|
|
750
|
+
});
|
|
751
|
+
var SidebarSchema = import_zod13.default.object({
|
|
752
|
+
hide: import_zod13.default.boolean().default(false),
|
|
753
|
+
position: import_zod13.default.number().optional(),
|
|
754
|
+
label: import_zod13.default.string().optional(),
|
|
755
|
+
/**
|
|
756
|
+
* Slug of the group this resource belongs to.
|
|
757
|
+
* Must match a key in `sidebarGroups` in `crouton.json`.
|
|
758
|
+
* Resources with the same `group` are nested under a shared collapsible section.
|
|
759
|
+
*/
|
|
760
|
+
group: import_zod13.default.string().optional()
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// ../crouton-core/src/lib/resource/TableAction.schema.ts
|
|
764
|
+
var import_zod14 = require("zod");
|
|
765
|
+
var ActionConditionSchema = import_zod14.z.object({
|
|
766
|
+
/** Row field to evaluate. */
|
|
767
|
+
field: import_zod14.z.string(),
|
|
768
|
+
/**
|
|
769
|
+
* Comparison operator. Defaults to `"eq"`.
|
|
770
|
+
* - `eq` / `neq` — strict equality
|
|
771
|
+
* - `gt` / `gte` / `lt` / `lte` — numeric or date comparison
|
|
772
|
+
* - `exists` — field is not null / undefined / empty string
|
|
773
|
+
* - `notExists` — field is null / undefined / empty string
|
|
774
|
+
*/
|
|
775
|
+
op: import_zod14.z.enum([
|
|
776
|
+
"eq",
|
|
777
|
+
"neq",
|
|
778
|
+
"gt",
|
|
779
|
+
"gte",
|
|
780
|
+
"lt",
|
|
781
|
+
"lte",
|
|
782
|
+
"exists",
|
|
783
|
+
"notExists"
|
|
784
|
+
]).default("eq"),
|
|
785
|
+
/** Comparison value. Not required for `exists` / `notExists`. */
|
|
786
|
+
value: import_zod14.z.unknown().optional()
|
|
787
|
+
});
|
|
788
|
+
var ActionSchema = import_zod14.z.object({
|
|
789
|
+
type: import_zod14.z.string(),
|
|
790
|
+
/** Unique identifier for the action. */
|
|
791
|
+
id: import_zod14.z.string(),
|
|
792
|
+
/** Human-readable button label. */
|
|
793
|
+
label: import_zod14.z.string(),
|
|
794
|
+
/** MDI icon name, e.g. `"mdi:open-in-new"`. */
|
|
795
|
+
icon: import_zod14.z.string().optional(),
|
|
796
|
+
/** Tooltip text. Falls back to `label` when omitted. */
|
|
797
|
+
tooltip: import_zod14.z.string().optional(),
|
|
798
|
+
condition: ActionConditionSchema.optional()
|
|
799
|
+
});
|
|
800
|
+
var JsonProcedureActionSchema = ActionSchema.extend({
|
|
801
|
+
type: import_zod14.z.literal("procedure").default("procedure"),
|
|
802
|
+
/**
|
|
803
|
+
* Filename (without extension) inside the resource's `actions/` directory
|
|
804
|
+
* that exports the table-action procedure, e.g. `"syncZotero"`.
|
|
805
|
+
*/
|
|
806
|
+
procedure: import_zod14.z.string(),
|
|
807
|
+
/** HTTP method for the frontend. Defaults to `"post"`. */
|
|
808
|
+
method: import_zod14.z.string().optional().default("post"),
|
|
809
|
+
/** Static query / body params passed to the endpoint. */
|
|
810
|
+
data: import_zod14.z.record(import_zod14.z.string(), import_zod14.z.unknown()).optional()
|
|
811
|
+
});
|
|
812
|
+
var JsonLinkActionSchema = ActionSchema.extend({
|
|
813
|
+
/** Optional condition evaluated per row. Button is hidden when the condition is false. */
|
|
814
|
+
type: import_zod14.z.literal("link").default("link"),
|
|
815
|
+
/** URL to open. May contain `{env.VAR}` placeholders. */
|
|
816
|
+
href: import_zod14.z.string()
|
|
817
|
+
});
|
|
818
|
+
var transformAction = /* @__PURE__ */ __name((action) => {
|
|
819
|
+
const label = action.label;
|
|
820
|
+
return {
|
|
821
|
+
tooltip: label,
|
|
822
|
+
...action
|
|
823
|
+
};
|
|
824
|
+
}, "transformAction");
|
|
825
|
+
var JsonActionSchema = import_zod14.z.union([
|
|
826
|
+
JsonProcedureActionSchema,
|
|
827
|
+
JsonLinkActionSchema
|
|
828
|
+
]).transform(transformAction);
|
|
829
|
+
|
|
830
|
+
// ../crouton-core/src/lib/resource/include.schema.ts
|
|
831
|
+
var import_zod15 = require("zod");
|
|
832
|
+
var JsonIncludeEntrySchema = import_zod15.z.lazy(() => import_zod15.z.union([
|
|
833
|
+
import_zod15.z.string(),
|
|
834
|
+
import_zod15.z.object({
|
|
835
|
+
relation: import_zod15.z.string(),
|
|
836
|
+
include: import_zod15.z.array(JsonIncludeEntrySchema).optional(),
|
|
837
|
+
/** Prisma-format orderBy clause applied to the included records. */
|
|
838
|
+
orderBy: import_zod15.z.record(import_zod15.z.string(), import_zod15.z.unknown()).optional()
|
|
839
|
+
})
|
|
840
|
+
]));
|
|
841
|
+
|
|
842
|
+
// ../crouton-core/src/lib/resource/ResourceJson.schema.ts
|
|
843
|
+
var JsonColumnsMapSchema = import_zod16.z.record(import_zod16.z.string(), JsonColumnSchema.omit({
|
|
844
|
+
id: true
|
|
845
|
+
}).catchall(import_zod16.z.unknown()));
|
|
846
|
+
var ColumnsSchema = JsonColumnsMapSchema;
|
|
847
|
+
var JsonDisplaySchema = import_zod16.z.object({
|
|
848
|
+
mode: import_zod16.z.enum([
|
|
849
|
+
"page",
|
|
850
|
+
"modal"
|
|
851
|
+
]).default("modal"),
|
|
852
|
+
customComponent: import_zod16.z.string().nullable().optional().default(null)
|
|
853
|
+
});
|
|
854
|
+
var normalizeColumns = /* @__PURE__ */ __name((columns) => {
|
|
855
|
+
if (!columns) return void 0;
|
|
856
|
+
const raw = Array.isArray(columns) ? columns : Object.entries(columns).map(([id, col]) => ({
|
|
857
|
+
id,
|
|
858
|
+
...col
|
|
859
|
+
}));
|
|
860
|
+
return raw.map((col) => ({
|
|
861
|
+
...col,
|
|
862
|
+
label: col.label ?? labelFromId(col.id)
|
|
863
|
+
}));
|
|
864
|
+
}, "normalizeColumns");
|
|
865
|
+
var ResourceJsonShape = import_zod16.z.object({
|
|
866
|
+
name: import_zod16.z.string(),
|
|
867
|
+
route: import_zod16.z.string(),
|
|
868
|
+
model: import_zod16.z.string(),
|
|
869
|
+
tag: import_zod16.z.string(),
|
|
870
|
+
title: import_zod16.z.string().optional(),
|
|
871
|
+
table: import_zod16.z.string().optional(),
|
|
872
|
+
database: import_zod16.z.string().optional(),
|
|
873
|
+
sidebar: SidebarSchema.default(SidebarSchema.parse({})),
|
|
874
|
+
display: JsonDisplaySchema.default(JsonDisplaySchema.parse({})),
|
|
875
|
+
operations: JsonOperationsSchema,
|
|
876
|
+
columns: ColumnsSchema.default(ColumnsSchema.parse({})),
|
|
877
|
+
calculatedColumns: import_zod16.z.array(CalculatedColumnSchema).default([]),
|
|
878
|
+
actions: import_zod16.z.array(JsonActionSchema).default([]),
|
|
879
|
+
/** Global table-level actions (no record id). Shown as toolbar buttons. */
|
|
880
|
+
tableActions: import_zod16.z.array(JsonActionSchema).default([]),
|
|
881
|
+
/** Modal width when opening a form for this resource. */
|
|
882
|
+
modalSize: import_zod16.z.enum([
|
|
883
|
+
"xs",
|
|
884
|
+
"sm",
|
|
885
|
+
"lg",
|
|
886
|
+
"xl"
|
|
887
|
+
]).default("sm"),
|
|
888
|
+
/**
|
|
889
|
+
* Relations to include when querying this resource.
|
|
890
|
+
* Each entry is either a plain relation name (`"author"` → `include: { author: true }`)
|
|
891
|
+
* or an object for nested includes:
|
|
892
|
+
* `{ "relation": "text_author", "include": ["author"] }` →
|
|
893
|
+
* `include: { text_author: { include: { author: true } } }`
|
|
894
|
+
*/
|
|
895
|
+
include: import_zod16.z.array(JsonIncludeEntrySchema).default([])
|
|
896
|
+
});
|
|
897
|
+
var ResourceJsonSchema = ResourceJsonShape.transform((obj) => {
|
|
898
|
+
const title = obj.title ?? labelFromId(obj.name);
|
|
899
|
+
return {
|
|
900
|
+
title,
|
|
901
|
+
...obj,
|
|
902
|
+
columns: normalizeColumns(obj.columns)
|
|
903
|
+
};
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// ../crouton-core/src/lib/config/CroutonConfig.schema.ts
|
|
907
|
+
var import_zod17 = require("zod");
|
|
908
|
+
var RulesetSchema = import_zod17.z.object({
|
|
909
|
+
hideIdInTable: import_zod17.z.boolean().default(true),
|
|
910
|
+
hideIdInForm: import_zod17.z.boolean().default(true),
|
|
911
|
+
hideIdInView: import_zod17.z.boolean().default(true),
|
|
912
|
+
hideTimestamps: import_zod17.z.boolean().default(true),
|
|
913
|
+
hideForeignKeys: import_zod17.z.boolean().default(true),
|
|
914
|
+
includeRelations: import_zod17.z.boolean().default(false),
|
|
915
|
+
hideRelationsInTable: import_zod17.z.boolean().default(true),
|
|
916
|
+
showRelationsInForm: import_zod17.z.boolean().default(true),
|
|
917
|
+
enumValueLabel: import_zod17.z.boolean().default(true),
|
|
918
|
+
sharedEnums: import_zod17.z.boolean().default(true),
|
|
919
|
+
defaultOperations: JsonOperationsSchema.default(JsonOperationsSchema.parse({}))
|
|
920
|
+
});
|
|
921
|
+
var CroutonConfigSchema = import_zod17.z.object({
|
|
922
|
+
/**
|
|
923
|
+
* Application title served to the frontend via `GET /_app/layout`.
|
|
924
|
+
* Displayed in the admin sidebar header.
|
|
925
|
+
*/
|
|
926
|
+
title: import_zod17.z.string(),
|
|
927
|
+
/** Where resource directories live, relative to the project root. */
|
|
928
|
+
resourcesDir: import_zod17.z.string(),
|
|
929
|
+
/** Where datasource folders live (each with a `data-source.json`). */
|
|
930
|
+
dataSourcesDir: import_zod17.z.string(),
|
|
931
|
+
/**
|
|
932
|
+
* Template for a model's Zod export name. `{Model}` → Prisma model name.
|
|
933
|
+
* Defaults to `{Model}WithRelationsSchema` (the relations-aware schema
|
|
934
|
+
* emitted by zod-prisma-types when `createRelationValuesTypes` is on).
|
|
935
|
+
*/
|
|
936
|
+
schemaExportName: import_zod17.z.string().default("{Model}WithRelationsSchema"),
|
|
937
|
+
/** Path to the shared enum registry, relative to project root. Default `crouton.enums.json`. */
|
|
938
|
+
enumsFile: import_zod17.z.string().default("crouton.enums.json"),
|
|
939
|
+
/** Optional overrides of the default visibility ruleset. */
|
|
940
|
+
rules: RulesetSchema.default(RulesetSchema.parse({})),
|
|
941
|
+
/**
|
|
942
|
+
* Sidebar group definitions, keyed by group slug (e.g. `"metadata"`).
|
|
943
|
+
* Resources reference a group via `sidebar.group` in their `resource.json`.
|
|
944
|
+
*/
|
|
945
|
+
sidebarGroups: import_zod17.z.record(import_zod17.z.string(), SidebarGroupSchema2).default({}),
|
|
946
|
+
/**
|
|
947
|
+
* Whether form fields are saved automatically as the user edits them.
|
|
948
|
+
* @default true
|
|
949
|
+
*/
|
|
950
|
+
autoSave: import_zod17.z.boolean().default(true)
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
// ../crouton-core/src/lib/config/readConfig.ts
|
|
954
|
+
var CONFIG_FILES = [
|
|
955
|
+
"crouton.json",
|
|
956
|
+
"crouton.mjs",
|
|
957
|
+
"crouton.js"
|
|
958
|
+
];
|
|
959
|
+
|
|
960
|
+
// src/lib/crud/app-layout/app-layout.builder.ts
|
|
961
|
+
var byPosition = /* @__PURE__ */ __name((a, b) => {
|
|
962
|
+
if (a.position != null && b.position != null) return a.position - b.position;
|
|
963
|
+
if (a.position != null) return -1;
|
|
964
|
+
if (b.position != null) return 1;
|
|
965
|
+
return a.label.localeCompare(b.label);
|
|
966
|
+
}, "byPosition");
|
|
967
|
+
var buildLayoutPayload = /* @__PURE__ */ __name((configs, sidebarGroups = {}, title, autoSave = true) => {
|
|
968
|
+
const visible = configs.filter((c) => c.sidebar?.hide !== true && c.views?.["table"]);
|
|
969
|
+
const topLevel = [];
|
|
970
|
+
const groupMap = new Map(Object.entries(sidebarGroups).map(([slug, cfg]) => [
|
|
971
|
+
slug,
|
|
972
|
+
{
|
|
973
|
+
label: cfg.label ?? labelFromId(slug),
|
|
974
|
+
position: cfg.position,
|
|
975
|
+
children: []
|
|
976
|
+
}
|
|
977
|
+
]));
|
|
978
|
+
for (const c of visible) {
|
|
979
|
+
const leaf = SidebarLeafSchema.parse({
|
|
980
|
+
id: c.name,
|
|
981
|
+
label: c.sidebar?.label ?? c.title ?? c.tag,
|
|
982
|
+
position: c.sidebar?.position
|
|
983
|
+
});
|
|
984
|
+
const groupSlug = c.sidebar?.group;
|
|
985
|
+
if (groupSlug) {
|
|
986
|
+
if (!groupMap.has(groupSlug)) {
|
|
987
|
+
groupMap.set(groupSlug, {
|
|
988
|
+
label: labelFromId(groupSlug),
|
|
989
|
+
children: []
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
groupMap.get(groupSlug).children.push(leaf);
|
|
993
|
+
} else {
|
|
994
|
+
topLevel.push(leaf);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
const groups = [
|
|
998
|
+
...groupMap.entries()
|
|
999
|
+
].filter(([, g]) => g.children.length > 0).map(([id, g]) => ({
|
|
1000
|
+
kind: "group",
|
|
1001
|
+
id,
|
|
1002
|
+
label: g.label,
|
|
1003
|
+
position: g.position,
|
|
1004
|
+
children: g.children.sort(byPosition)
|
|
1005
|
+
}));
|
|
1006
|
+
const sidebar = [
|
|
1007
|
+
...topLevel,
|
|
1008
|
+
...groups
|
|
1009
|
+
].sort(byPosition);
|
|
1010
|
+
return {
|
|
1011
|
+
sidebar,
|
|
1012
|
+
title,
|
|
1013
|
+
autoSave
|
|
1014
|
+
};
|
|
1015
|
+
}, "buildLayoutPayload");
|
|
1016
|
+
|
|
1017
|
+
// src/lib/crud/app-layout/app-layout.controller.ts
|
|
1018
|
+
var import_common2 = require("@nestjs/common");
|
|
1019
|
+
var import_swagger = require("@nestjs/swagger");
|
|
1020
|
+
|
|
497
1021
|
// src/lib/crud/dev-mode.ts
|
|
498
1022
|
var IS_DEV = process.env["NODE_ENV"] !== "production";
|
|
499
1023
|
|
|
@@ -542,7 +1066,7 @@ ResourceConfigRegistry = _ts_decorate([
|
|
|
542
1066
|
])
|
|
543
1067
|
], ResourceConfigRegistry);
|
|
544
1068
|
|
|
545
|
-
// src/lib/crud/app-layout.controller.ts
|
|
1069
|
+
// src/lib/crud/app-layout/app-layout.controller.ts
|
|
546
1070
|
function _ts_decorate2(decorators, target, key, desc2) {
|
|
547
1071
|
var c = arguments.length, r = c < 3 ? target : desc2 === null ? desc2 = Object.getOwnPropertyDescriptor(target, key) : desc2, d;
|
|
548
1072
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc2);
|
|
@@ -554,62 +1078,6 @@ function _ts_metadata2(k, v) {
|
|
|
554
1078
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
555
1079
|
}
|
|
556
1080
|
__name(_ts_metadata2, "_ts_metadata");
|
|
557
|
-
var byPosition = /* @__PURE__ */ __name((a, b) => {
|
|
558
|
-
if (a.position != null && b.position != null) return a.position - b.position;
|
|
559
|
-
if (a.position != null) return -1;
|
|
560
|
-
if (b.position != null) return 1;
|
|
561
|
-
return a.label.localeCompare(b.label);
|
|
562
|
-
}, "byPosition");
|
|
563
|
-
var buildLayoutPayload = /* @__PURE__ */ __name((configs, sidebarGroups = {}, title, autoSave = true) => {
|
|
564
|
-
const visible = configs.filter((c) => c.sidebar?.hide !== true && c.views?.["table"]);
|
|
565
|
-
const topLevel = [];
|
|
566
|
-
const groupMap = new Map(Object.entries(sidebarGroups).map(([slug, cfg]) => [
|
|
567
|
-
slug,
|
|
568
|
-
{
|
|
569
|
-
label: cfg.label ?? labelFromId(slug),
|
|
570
|
-
position: cfg.position,
|
|
571
|
-
children: []
|
|
572
|
-
}
|
|
573
|
-
]));
|
|
574
|
-
for (const c of visible) {
|
|
575
|
-
const leaf = {
|
|
576
|
-
kind: "item",
|
|
577
|
-
id: c.name,
|
|
578
|
-
label: c.sidebar?.label ?? c.title ?? c.tag,
|
|
579
|
-
position: c.sidebar?.position
|
|
580
|
-
};
|
|
581
|
-
const groupSlug = c.sidebar?.group;
|
|
582
|
-
if (groupSlug) {
|
|
583
|
-
if (!groupMap.has(groupSlug)) {
|
|
584
|
-
groupMap.set(groupSlug, {
|
|
585
|
-
label: labelFromId(groupSlug),
|
|
586
|
-
children: []
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
groupMap.get(groupSlug).children.push(leaf);
|
|
590
|
-
} else {
|
|
591
|
-
topLevel.push(leaf);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
const groups = [
|
|
595
|
-
...groupMap.entries()
|
|
596
|
-
].filter(([, g]) => g.children.length > 0).map(([id, g]) => ({
|
|
597
|
-
kind: "group",
|
|
598
|
-
id,
|
|
599
|
-
label: g.label,
|
|
600
|
-
position: g.position,
|
|
601
|
-
children: g.children.sort(byPosition)
|
|
602
|
-
}));
|
|
603
|
-
const sidebar = [
|
|
604
|
-
...topLevel,
|
|
605
|
-
...groups
|
|
606
|
-
].sort(byPosition);
|
|
607
|
-
return {
|
|
608
|
-
sidebar,
|
|
609
|
-
title,
|
|
610
|
-
autoSave
|
|
611
|
-
};
|
|
612
|
-
}, "buildLayoutPayload");
|
|
613
1081
|
var createAppLayoutController = /* @__PURE__ */ __name((configs, sidebarGroups = {}, title, autoSave = true) => {
|
|
614
1082
|
const layoutPayload = buildLayoutPayload(configs, sidebarGroups, title, autoSave);
|
|
615
1083
|
let AppLayoutController = class AppLayoutController {
|
|
@@ -655,6 +1123,45 @@ var createAppLayoutController = /* @__PURE__ */ __name((configs, sidebarGroups =
|
|
|
655
1123
|
return AppLayoutController;
|
|
656
1124
|
}, "createAppLayoutController");
|
|
657
1125
|
|
|
1126
|
+
// src/lib/crud/config/read.ts
|
|
1127
|
+
var import_promises = require("fs/promises");
|
|
1128
|
+
var import_node_path = require("path");
|
|
1129
|
+
var fileExists = /* @__PURE__ */ __name(async (p) => {
|
|
1130
|
+
try {
|
|
1131
|
+
await (0, import_promises.access)(p);
|
|
1132
|
+
return true;
|
|
1133
|
+
} catch {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
}, "fileExists");
|
|
1137
|
+
var findConfigPath = /* @__PURE__ */ __name(async (cwd) => {
|
|
1138
|
+
let dir = (0, import_node_path.resolve)(cwd);
|
|
1139
|
+
while (true) {
|
|
1140
|
+
for (const name of CONFIG_FILES) {
|
|
1141
|
+
const candidate = (0, import_node_path.join)(dir, name);
|
|
1142
|
+
if (await fileExists(candidate)) return candidate;
|
|
1143
|
+
}
|
|
1144
|
+
const parent = (0, import_node_path.dirname)(dir);
|
|
1145
|
+
if (parent === dir) return void 0;
|
|
1146
|
+
dir = parent;
|
|
1147
|
+
}
|
|
1148
|
+
}, "findConfigPath");
|
|
1149
|
+
var loadConfig = /* @__PURE__ */ __name(async () => {
|
|
1150
|
+
const cwd = (0, import_node_path.resolve)(process.cwd());
|
|
1151
|
+
const path = await findConfigPath(cwd);
|
|
1152
|
+
if (!path) {
|
|
1153
|
+
throw new Error(`No crouton config found (looked for ${CONFIG_FILES.join(", ")} up from ${cwd}).`);
|
|
1154
|
+
}
|
|
1155
|
+
let config;
|
|
1156
|
+
if (path.endsWith(".json")) {
|
|
1157
|
+
config = JSON.parse(await (0, import_promises.readFile)(path, "utf-8"));
|
|
1158
|
+
} else {
|
|
1159
|
+
const mod = await import(path);
|
|
1160
|
+
config = mod.default ?? mod;
|
|
1161
|
+
}
|
|
1162
|
+
return CroutonConfigSchema.parse(config);
|
|
1163
|
+
}, "loadConfig");
|
|
1164
|
+
|
|
658
1165
|
// src/lib/crud/crouton-validation.filter.ts
|
|
659
1166
|
var import_common3 = require("@nestjs/common");
|
|
660
1167
|
|
|
@@ -697,24 +1204,156 @@ CroutonValidationExceptionFilter = _ts_decorate3([
|
|
|
697
1204
|
|
|
698
1205
|
// src/lib/crud/crud-controller.factory.ts
|
|
699
1206
|
var import_common11 = require("@nestjs/common");
|
|
700
|
-
var import_common12 = require("@nestjs/common");
|
|
701
1207
|
var import_swagger6 = require("@nestjs/swagger");
|
|
702
1208
|
|
|
703
|
-
// src/lib/crud/
|
|
704
|
-
var
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
var
|
|
1209
|
+
// src/lib/crud/action/action.types.ts
|
|
1210
|
+
var import_zod18 = require("zod");
|
|
1211
|
+
var ActionMetadataSchema = import_zod18.z.object({
|
|
1212
|
+
/** URL segment used in the endpoint. */
|
|
1213
|
+
id: import_zod18.z.string(),
|
|
1214
|
+
/** Human-readable label shown as a button. */
|
|
1215
|
+
label: import_zod18.z.string().optional(),
|
|
1216
|
+
/** MDI icon name, e.g. `"mdi:open-in-new"`. */
|
|
1217
|
+
icon: import_zod18.z.string().optional(),
|
|
1218
|
+
/** Tooltip text. Falls back to `label` when omitted. */
|
|
1219
|
+
tooltip: import_zod18.z.string().optional(),
|
|
1220
|
+
/** Per-row condition — button is hidden when false. */
|
|
1221
|
+
condition: import_zod18.z.custom().optional()
|
|
1222
|
+
});
|
|
1223
|
+
var ResourceLinkActionSchema = ActionMetadataSchema.extend({
|
|
1224
|
+
type: import_zod18.z.literal("link"),
|
|
1225
|
+
/** URL to open. May contain `{id}` or `{env.VAR}` placeholders. */
|
|
1226
|
+
href: import_zod18.z.string()
|
|
1227
|
+
});
|
|
1228
|
+
var ResourceRowProcedureActionSchema = ActionMetadataSchema.extend({
|
|
1229
|
+
type: import_zod18.z.literal("procedure").optional(),
|
|
1230
|
+
/** HTTP method for the endpoint. Defaults to `"post"`. */
|
|
1231
|
+
method: import_zod18.z.string().optional(),
|
|
1232
|
+
/** Static data payload merged into the request body by the frontend. */
|
|
1233
|
+
data: import_zod18.z.record(import_zod18.z.string(), import_zod18.z.unknown()).optional(),
|
|
1234
|
+
/** Procedure called with `(prisma, recordId)`. */
|
|
1235
|
+
procedure: import_zod18.z.custom((v) => typeof v === "function")
|
|
1236
|
+
});
|
|
1237
|
+
var ResourceTableProcedureActionSchema = ActionMetadataSchema.extend({
|
|
1238
|
+
type: import_zod18.z.literal("procedure").optional(),
|
|
1239
|
+
/** HTTP method for the endpoint. Defaults to `"post"`. */
|
|
1240
|
+
method: import_zod18.z.string().optional(),
|
|
1241
|
+
/** Static data payload merged into the request body by the frontend. */
|
|
1242
|
+
data: import_zod18.z.record(import_zod18.z.string(), import_zod18.z.unknown()).optional(),
|
|
1243
|
+
/** Procedure called with `(prisma)` — no record id. */
|
|
1244
|
+
procedure: import_zod18.z.custom((v) => typeof v === "function")
|
|
1245
|
+
});
|
|
1246
|
+
var ResourceRowActionSchema = import_zod18.z.union([
|
|
1247
|
+
ResourceRowProcedureActionSchema,
|
|
1248
|
+
ResourceLinkActionSchema
|
|
1249
|
+
]);
|
|
1250
|
+
var ResourceTableActionSchema = import_zod18.z.union([
|
|
1251
|
+
ResourceTableProcedureActionSchema,
|
|
1252
|
+
ResourceLinkActionSchema
|
|
1253
|
+
]);
|
|
1254
|
+
var isRowProcedureAction = /* @__PURE__ */ __name((action) => action.type !== "link", "isRowProcedureAction");
|
|
1255
|
+
var isTableProcedureAction = /* @__PURE__ */ __name((action) => action.type !== "link", "isTableProcedureAction");
|
|
1256
|
+
|
|
1257
|
+
// src/lib/crud/loader/module.loader.ts
|
|
1258
|
+
var import_node_fs = require("fs");
|
|
1259
|
+
var import_node_module = require("module");
|
|
1260
|
+
var import_node_path2 = require("path");
|
|
1261
|
+
var _require = (0, import_node_module.createRequire)(importMetaUrl ?? __filename);
|
|
1262
|
+
var findModule = /* @__PURE__ */ __name((dir, name) => {
|
|
1263
|
+
for (const ext of [
|
|
1264
|
+
".ts",
|
|
1265
|
+
".js"
|
|
1266
|
+
]) {
|
|
1267
|
+
const p = (0, import_node_path2.join)(dir, `${name}${ext}`);
|
|
1268
|
+
if ((0, import_node_fs.existsSync)(p)) return p;
|
|
1269
|
+
}
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}, "findModule");
|
|
1272
|
+
var IS_VITE = typeof globalThis.__vite_ssr_import__ === "function";
|
|
1273
|
+
var importDefault = /* @__PURE__ */ __name(async (filePath) => {
|
|
1274
|
+
try {
|
|
1275
|
+
if (IS_VITE) {
|
|
1276
|
+
const importPath = IS_DEV ? `${filePath}?t=${Date.now()}` : filePath;
|
|
1277
|
+
const mod = await import(importPath);
|
|
1278
|
+
return mod.default ?? mod;
|
|
1279
|
+
} else {
|
|
1280
|
+
const mod = _require(filePath);
|
|
1281
|
+
return mod.default;
|
|
1282
|
+
}
|
|
1283
|
+
} catch {
|
|
1284
|
+
return void 0;
|
|
1285
|
+
}
|
|
1286
|
+
}, "importDefault");
|
|
1287
|
+
|
|
1288
|
+
// src/lib/crud/action/action.loader.ts
|
|
1289
|
+
var import_node_path3 = require("path");
|
|
1290
|
+
var loadActions = /* @__PURE__ */ __name(async (jsonActions, basePath, scope) => {
|
|
1291
|
+
const tag = scope === "row" ? "actions" : "tableActions";
|
|
1292
|
+
const results = [];
|
|
1293
|
+
for (const action of jsonActions) {
|
|
1294
|
+
if (action.type === "link") {
|
|
1295
|
+
results.push({
|
|
1296
|
+
type: "link",
|
|
1297
|
+
id: action.id,
|
|
1298
|
+
label: action.label,
|
|
1299
|
+
href: action.href,
|
|
1300
|
+
...action.icon && {
|
|
1301
|
+
icon: action.icon
|
|
1302
|
+
},
|
|
1303
|
+
...action.tooltip && {
|
|
1304
|
+
tooltip: action.tooltip
|
|
1305
|
+
},
|
|
1306
|
+
...action.condition && {
|
|
1307
|
+
condition: action.condition
|
|
1308
|
+
}
|
|
1309
|
+
});
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
const file = findModule((0, import_node_path3.join)(basePath, "actions"), action.procedure);
|
|
1313
|
+
if (!file) {
|
|
1314
|
+
console.warn(`[${tag}] Procedure file not found for "${action.id}" in ${basePath}/actions/`);
|
|
1315
|
+
continue;
|
|
1316
|
+
}
|
|
1317
|
+
const mod = await importDefault(file);
|
|
1318
|
+
if (!mod) {
|
|
1319
|
+
console.warn(`[${tag}] No default export in ${file}`);
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
results.push({
|
|
1323
|
+
id: action.id,
|
|
1324
|
+
label: action.label,
|
|
1325
|
+
method: action.method,
|
|
1326
|
+
data: action.data,
|
|
1327
|
+
...action.icon && {
|
|
1328
|
+
icon: action.icon
|
|
1329
|
+
},
|
|
1330
|
+
...action.tooltip && {
|
|
1331
|
+
tooltip: action.tooltip
|
|
1332
|
+
},
|
|
1333
|
+
...action.condition && {
|
|
1334
|
+
condition: action.condition
|
|
1335
|
+
},
|
|
1336
|
+
procedure: mod
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
return results;
|
|
1340
|
+
}, "loadActions");
|
|
1341
|
+
|
|
1342
|
+
// src/lib/crud/crud.config.ts
|
|
1343
|
+
var resolveDefinition = /* @__PURE__ */ __name((config) => {
|
|
1344
|
+
const def2 = config.definition;
|
|
1345
|
+
return typeof def2 === "function" ? def2() : def2;
|
|
1346
|
+
}, "resolveDefinition");
|
|
1347
|
+
var isOperationEnabled = /* @__PURE__ */ __name((def2, op) => def2[op] != null, "isOperationEnabled");
|
|
1348
|
+
var schemaFor = /* @__PURE__ */ __name((def2, op) => {
|
|
1349
|
+
const entry = def2[op];
|
|
1350
|
+
if (!entry || entry === true) return void 0;
|
|
1351
|
+
return entry.schema;
|
|
1352
|
+
}, "schemaFor");
|
|
1353
|
+
var upsertOnFor = /* @__PURE__ */ __name((def2) => def2.upsert?.upsertOn, "upsertOnFor");
|
|
1354
|
+
|
|
1355
|
+
// src/lib/crud/read.repository.ts
|
|
1356
|
+
var import_common4 = require("@nestjs/common");
|
|
718
1357
|
|
|
719
1358
|
// src/lib/crud/sql.helpers.ts
|
|
720
1359
|
var castExpression = /* @__PURE__ */ __name((col) => {
|
|
@@ -1199,9 +1838,9 @@ var ReadRepository = class {
|
|
|
1199
1838
|
};
|
|
1200
1839
|
|
|
1201
1840
|
// src/lib/crud/schema.utils.ts
|
|
1202
|
-
var
|
|
1841
|
+
var import_zod19 = require("zod");
|
|
1203
1842
|
var dateOverride = /* @__PURE__ */ __name(({ zodSchema, jsonSchema }) => {
|
|
1204
|
-
if (zodSchema instanceof
|
|
1843
|
+
if (zodSchema instanceof import_zod19.z.ZodDate) {
|
|
1205
1844
|
jsonSchema.type = "string";
|
|
1206
1845
|
jsonSchema.format = "date-time";
|
|
1207
1846
|
}
|
|
@@ -1211,7 +1850,7 @@ var jsonSchemaOpts = {
|
|
|
1211
1850
|
override: dateOverride
|
|
1212
1851
|
};
|
|
1213
1852
|
function isZodSchema(schema) {
|
|
1214
|
-
return schema instanceof
|
|
1853
|
+
return schema instanceof import_zod19.ZodObject;
|
|
1215
1854
|
}
|
|
1216
1855
|
__name(isZodSchema, "isZodSchema");
|
|
1217
1856
|
var isNullableProperty = /* @__PURE__ */ __name((property) => {
|
|
@@ -1225,7 +1864,7 @@ var dropNullableFromRequired = /* @__PURE__ */ __name((jsonSchema) => {
|
|
|
1225
1864
|
}, "dropNullableFromRequired");
|
|
1226
1865
|
function toJsonSchema(schema) {
|
|
1227
1866
|
if (isZodSchema(schema)) {
|
|
1228
|
-
const jsonSchema = (0,
|
|
1867
|
+
const jsonSchema = (0, import_zod19.toJSONSchema)(schema, {
|
|
1229
1868
|
target: "openApi3",
|
|
1230
1869
|
...jsonSchemaOpts
|
|
1231
1870
|
});
|
|
@@ -1600,21 +2239,27 @@ DataSourceRegistry = _ts_decorate4([
|
|
|
1600
2239
|
], DataSourceRegistry);
|
|
1601
2240
|
|
|
1602
2241
|
// src/lib/crud/data-source/data-source.loader.ts
|
|
1603
|
-
var
|
|
1604
|
-
var
|
|
2242
|
+
var import_node_fs2 = require("fs");
|
|
2243
|
+
var import_node_path4 = require("path");
|
|
1605
2244
|
var loadDataSourcesFromDir = /* @__PURE__ */ __name(async (dirPath) => {
|
|
1606
|
-
if (!(0,
|
|
1607
|
-
const entries = (0,
|
|
2245
|
+
if (!(0, import_node_fs2.existsSync)(dirPath)) return [];
|
|
2246
|
+
const entries = (0, import_node_fs2.readdirSync)(dirPath, {
|
|
1608
2247
|
withFileTypes: true
|
|
1609
2248
|
});
|
|
1610
2249
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1611
2250
|
const results = [];
|
|
1612
2251
|
for (const dir of dirs) {
|
|
1613
|
-
const basePath = (0,
|
|
1614
|
-
const jsonFile = (0,
|
|
1615
|
-
if (!(0,
|
|
1616
|
-
const
|
|
1617
|
-
const
|
|
2252
|
+
const basePath = (0, import_node_path4.join)(dirPath, dir);
|
|
2253
|
+
const jsonFile = (0, import_node_path4.join)(basePath, "data-source.json");
|
|
2254
|
+
if (!(0, import_node_fs2.existsSync)(jsonFile)) continue;
|
|
2255
|
+
const _config = JSON.parse((0, import_node_fs2.readFileSync)(jsonFile, "utf-8"));
|
|
2256
|
+
const datasource = DataSourceSchema.safeParse(_config);
|
|
2257
|
+
if (!datasource.success) {
|
|
2258
|
+
console.error(datasource.error);
|
|
2259
|
+
throw new Error(`Invalid datasource schema: ${jsonFile}`);
|
|
2260
|
+
}
|
|
2261
|
+
const config = datasource.data;
|
|
2262
|
+
const indexFile = findModule2(basePath, "index");
|
|
1618
2263
|
if (!indexFile) continue;
|
|
1619
2264
|
const mod = await import(indexFile);
|
|
1620
2265
|
const client = mod.default;
|
|
@@ -1626,13 +2271,13 @@ var loadDataSourcesFromDir = /* @__PURE__ */ __name(async (dirPath) => {
|
|
|
1626
2271
|
}
|
|
1627
2272
|
return results;
|
|
1628
2273
|
}, "loadDataSourcesFromDir");
|
|
1629
|
-
var
|
|
2274
|
+
var findModule2 = /* @__PURE__ */ __name((dir, name) => {
|
|
1630
2275
|
for (const ext of [
|
|
1631
2276
|
".ts",
|
|
1632
2277
|
".js"
|
|
1633
2278
|
]) {
|
|
1634
|
-
const p = (0,
|
|
1635
|
-
if ((0,
|
|
2279
|
+
const p = (0, import_node_path4.join)(dir, `${name}${ext}`);
|
|
2280
|
+
if ((0, import_node_fs2.existsSync)(p)) return p;
|
|
1636
2281
|
}
|
|
1637
2282
|
return void 0;
|
|
1638
2283
|
}, "findModule");
|
|
@@ -1655,7 +2300,7 @@ var desc = /* @__PURE__ */ __name((cls, method) => Object.getOwnPropertyDescript
|
|
|
1655
2300
|
var registerActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
1656
2301
|
const { cls, config } = ctx;
|
|
1657
2302
|
const { name } = config;
|
|
1658
|
-
const procedureActions = (config.actions ?? []).filter(
|
|
2303
|
+
const procedureActions = (config.actions ?? []).filter(isRowProcedureAction);
|
|
1659
2304
|
for (const action of procedureActions) {
|
|
1660
2305
|
const methodName = `procedure_${action.id}`;
|
|
1661
2306
|
def(cls, methodName, async function(recordId) {
|
|
@@ -1680,7 +2325,7 @@ var registerActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
|
1680
2325
|
var registerTableActionRoutes = /* @__PURE__ */ __name((ctx) => {
|
|
1681
2326
|
const { cls, config } = ctx;
|
|
1682
2327
|
const { name } = config;
|
|
1683
|
-
const procedureActions = (config.tableActions ?? []).filter(
|
|
2328
|
+
const procedureActions = (config.tableActions ?? []).filter(isTableProcedureAction);
|
|
1684
2329
|
for (const action of procedureActions) {
|
|
1685
2330
|
const methodName = `tableAction_${action.id}`;
|
|
1686
2331
|
def(cls, methodName, async function() {
|
|
@@ -1782,38 +2427,56 @@ var ZodValidationPipe = class {
|
|
|
1782
2427
|
};
|
|
1783
2428
|
|
|
1784
2429
|
// src/lib/crud/operations/register-crud.ts
|
|
2430
|
+
var findAll = /* @__PURE__ */ __name(async (repo, params, q, lookupLabel) => {
|
|
2431
|
+
const effectiveParams = {
|
|
2432
|
+
...params
|
|
2433
|
+
};
|
|
2434
|
+
if (q && lookupLabel) {
|
|
2435
|
+
effectiveParams.filter = [
|
|
2436
|
+
...params.filter ?? [],
|
|
2437
|
+
`${lookupLabel}:${q}`
|
|
2438
|
+
];
|
|
2439
|
+
}
|
|
2440
|
+
const [data, count] = await Promise.all([
|
|
2441
|
+
repo.findAll(effectiveParams),
|
|
2442
|
+
repo.count(effectiveParams.filter)
|
|
2443
|
+
]);
|
|
2444
|
+
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
2445
|
+
return {
|
|
2446
|
+
data,
|
|
2447
|
+
request: {
|
|
2448
|
+
count,
|
|
2449
|
+
page: params.page,
|
|
2450
|
+
pageSize: params.pageSize,
|
|
2451
|
+
totalPages,
|
|
2452
|
+
sort: params.sort,
|
|
2453
|
+
sortDir: params.sortDir,
|
|
2454
|
+
filter: params.filter
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}, "findAll");
|
|
2458
|
+
var findOne = /* @__PURE__ */ __name(async (repo, id) => {
|
|
2459
|
+
return repo.findOne(id);
|
|
2460
|
+
}, "findOne");
|
|
2461
|
+
var create = /* @__PURE__ */ __name(async (repo, body) => {
|
|
2462
|
+
return repo.create(body);
|
|
2463
|
+
}, "create");
|
|
2464
|
+
var update = /* @__PURE__ */ __name(async (repo, id, body) => {
|
|
2465
|
+
return repo.update(id, body);
|
|
2466
|
+
}, "update");
|
|
2467
|
+
var upsert = /* @__PURE__ */ __name(async (repo, body) => {
|
|
2468
|
+
return repo.upsert(body);
|
|
2469
|
+
}, "upsert");
|
|
2470
|
+
var del = /* @__PURE__ */ __name(async (repo, id) => {
|
|
2471
|
+
return repo.delete(id);
|
|
2472
|
+
}, "del");
|
|
1785
2473
|
var registerFindAll = /* @__PURE__ */ __name((ctx) => {
|
|
1786
2474
|
if (!isOperationEnabled(ctx.definition, "findAll")) return;
|
|
1787
2475
|
const { cls, config, listSchema } = ctx;
|
|
1788
2476
|
const { name } = config;
|
|
1789
2477
|
const lookupLabel = config.lookup?.label;
|
|
1790
2478
|
def(cls, "findAll", async function(params, q) {
|
|
1791
|
-
|
|
1792
|
-
...params
|
|
1793
|
-
};
|
|
1794
|
-
if (q && lookupLabel) {
|
|
1795
|
-
effectiveParams.filter = [
|
|
1796
|
-
...params.filter ?? [],
|
|
1797
|
-
`${lookupLabel}:${q}`
|
|
1798
|
-
];
|
|
1799
|
-
}
|
|
1800
|
-
const [data, count] = await Promise.all([
|
|
1801
|
-
this.repo.findAll(effectiveParams),
|
|
1802
|
-
this.repo.count(effectiveParams.filter)
|
|
1803
|
-
]);
|
|
1804
|
-
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
1805
|
-
return {
|
|
1806
|
-
data,
|
|
1807
|
-
request: {
|
|
1808
|
-
count,
|
|
1809
|
-
page: params.page,
|
|
1810
|
-
pageSize: params.pageSize,
|
|
1811
|
-
totalPages,
|
|
1812
|
-
sort: params.sort,
|
|
1813
|
-
sortDir: params.sortDir,
|
|
1814
|
-
filter: params.filter
|
|
1815
|
-
}
|
|
1816
|
-
};
|
|
2479
|
+
return findAll(this.repo, params, q, lookupLabel);
|
|
1817
2480
|
});
|
|
1818
2481
|
const d = desc(cls, "findAll");
|
|
1819
2482
|
(0, import_common8.Get)()(cls.prototype, "findAll", d);
|
|
@@ -1838,7 +2501,7 @@ var registerFindOne = /* @__PURE__ */ __name((ctx) => {
|
|
|
1838
2501
|
const { cls, config, oneSchema, idParamMeta } = ctx;
|
|
1839
2502
|
const { name } = config;
|
|
1840
2503
|
def(cls, "findOne", function(id) {
|
|
1841
|
-
return this.repo
|
|
2504
|
+
return findOne(this.repo, id);
|
|
1842
2505
|
});
|
|
1843
2506
|
const d = desc(cls, "findOne");
|
|
1844
2507
|
(0, import_common8.Get)(":id")(cls.prototype, "findOne", d);
|
|
@@ -1863,7 +2526,7 @@ var registerCreate = /* @__PURE__ */ __name((ctx) => {
|
|
|
1863
2526
|
const { cls, config, createSchema, bodyDecorator } = ctx;
|
|
1864
2527
|
const { name } = config;
|
|
1865
2528
|
def(cls, "create", function(body) {
|
|
1866
|
-
return this.repo
|
|
2529
|
+
return create(this.repo, body);
|
|
1867
2530
|
});
|
|
1868
2531
|
const d = desc(cls, "create");
|
|
1869
2532
|
(0, import_common8.Post)()(cls.prototype, "create", d);
|
|
@@ -1886,7 +2549,7 @@ var registerUpdate = /* @__PURE__ */ __name((ctx) => {
|
|
|
1886
2549
|
const { cls, config, updateSchema, idParamMeta, bodyDecorator } = ctx;
|
|
1887
2550
|
const { name } = config;
|
|
1888
2551
|
def(cls, "update", function(id, body) {
|
|
1889
|
-
return this.repo
|
|
2552
|
+
return update(this.repo, id, body);
|
|
1890
2553
|
});
|
|
1891
2554
|
const d = desc(cls, "update");
|
|
1892
2555
|
(0, import_common8.Patch)(":id")(cls.prototype, "update", d);
|
|
@@ -1912,7 +2575,7 @@ var registerUpsert = /* @__PURE__ */ __name((ctx) => {
|
|
|
1912
2575
|
const { cls, config, upsertSchema, bodyDecorator } = ctx;
|
|
1913
2576
|
const { name } = config;
|
|
1914
2577
|
def(cls, "upsert", function(body) {
|
|
1915
|
-
return this.repo
|
|
2578
|
+
return upsert(this.repo, body);
|
|
1916
2579
|
});
|
|
1917
2580
|
const d = desc(cls, "upsert");
|
|
1918
2581
|
(0, import_common8.Put)()(cls.prototype, "upsert", d);
|
|
@@ -1935,7 +2598,7 @@ var registerDelete = /* @__PURE__ */ __name((ctx) => {
|
|
|
1935
2598
|
const { cls, config, idParamMeta } = ctx;
|
|
1936
2599
|
const { name } = config;
|
|
1937
2600
|
def(cls, "delete", function(id) {
|
|
1938
|
-
return this.repo
|
|
2601
|
+
return del(this.repo, id);
|
|
1939
2602
|
});
|
|
1940
2603
|
const d = desc(cls, "delete");
|
|
1941
2604
|
(0, import_common8.Delete)(":id")(cls.prototype, "delete", d);
|
|
@@ -2046,6 +2709,7 @@ var buildDefinitionPayload = /* @__PURE__ */ __name((config) => {
|
|
|
2046
2709
|
tag,
|
|
2047
2710
|
operations,
|
|
2048
2711
|
upsertOn: upsertOnFor(definition),
|
|
2712
|
+
display: config.display,
|
|
2049
2713
|
schemas: {
|
|
2050
2714
|
...listSchema && {
|
|
2051
2715
|
findAll: toJsonSchema(listSchema)
|
|
@@ -2124,6 +2788,12 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
|
2124
2788
|
id: a.id,
|
|
2125
2789
|
label: a.label,
|
|
2126
2790
|
href: resolveEnvPlaceholders(a.href),
|
|
2791
|
+
...a.icon && {
|
|
2792
|
+
icon: a.icon
|
|
2793
|
+
},
|
|
2794
|
+
...a.tooltip && {
|
|
2795
|
+
tooltip: a.tooltip
|
|
2796
|
+
},
|
|
2127
2797
|
...a.condition && {
|
|
2128
2798
|
condition: a.condition
|
|
2129
2799
|
}
|
|
@@ -2135,6 +2805,12 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
|
2135
2805
|
...a.data && {
|
|
2136
2806
|
data: a.data
|
|
2137
2807
|
},
|
|
2808
|
+
...a.icon && {
|
|
2809
|
+
icon: a.icon
|
|
2810
|
+
},
|
|
2811
|
+
...a.tooltip && {
|
|
2812
|
+
tooltip: a.tooltip
|
|
2813
|
+
},
|
|
2138
2814
|
...a.condition && {
|
|
2139
2815
|
condition: a.condition
|
|
2140
2816
|
}
|
|
@@ -2147,7 +2823,10 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
|
2147
2823
|
label: a.label,
|
|
2148
2824
|
icon: a.icon,
|
|
2149
2825
|
tooltip: a.tooltip,
|
|
2150
|
-
href: resolveEnvPlaceholders(a.href)
|
|
2826
|
+
href: resolveEnvPlaceholders(a.href),
|
|
2827
|
+
...a.condition && {
|
|
2828
|
+
condition: a.condition
|
|
2829
|
+
}
|
|
2151
2830
|
} : {
|
|
2152
2831
|
id: a.id,
|
|
2153
2832
|
label: a.label,
|
|
@@ -2157,6 +2836,9 @@ var buildViewsPayload = /* @__PURE__ */ __name((config, baseUrl) => {
|
|
|
2157
2836
|
method: a.method ?? "post",
|
|
2158
2837
|
...a.data && {
|
|
2159
2838
|
data: a.data
|
|
2839
|
+
},
|
|
2840
|
+
...a.condition && {
|
|
2841
|
+
condition: a.condition
|
|
2160
2842
|
}
|
|
2161
2843
|
})
|
|
2162
2844
|
}
|
|
@@ -2231,6 +2913,34 @@ var registerResourceJsonEndpoint = /* @__PURE__ */ __name((ctx) => {
|
|
|
2231
2913
|
// src/lib/crud/operations/register-sub-resources.ts
|
|
2232
2914
|
var import_common10 = require("@nestjs/common");
|
|
2233
2915
|
var import_swagger5 = require("@nestjs/swagger");
|
|
2916
|
+
var findAllByParent = /* @__PURE__ */ __name(async (repo, id, childRoute, params) => {
|
|
2917
|
+
const { data, count } = await repo.findAllByParent(id, childRoute, params);
|
|
2918
|
+
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
2919
|
+
return {
|
|
2920
|
+
data,
|
|
2921
|
+
request: {
|
|
2922
|
+
count,
|
|
2923
|
+
page: params.page,
|
|
2924
|
+
pageSize: params.pageSize,
|
|
2925
|
+
totalPages,
|
|
2926
|
+
sort: params.sort,
|
|
2927
|
+
sortDir: params.sortDir,
|
|
2928
|
+
filter: params.filter
|
|
2929
|
+
}
|
|
2930
|
+
};
|
|
2931
|
+
}, "findAllByParent");
|
|
2932
|
+
var createChild = /* @__PURE__ */ __name(async (repo, id, sub, body) => {
|
|
2933
|
+
return repo.createChild(id, sub, body);
|
|
2934
|
+
}, "createChild");
|
|
2935
|
+
var findOneChild = /* @__PURE__ */ __name(async (repo, sub, childId, parentId) => {
|
|
2936
|
+
return repo.findOneChild(sub, childId, parentId);
|
|
2937
|
+
}, "findOneChild");
|
|
2938
|
+
var updateChild = /* @__PURE__ */ __name(async (repo, sub, childId, body) => {
|
|
2939
|
+
return repo.updateChild(sub, childId, body);
|
|
2940
|
+
}, "updateChild");
|
|
2941
|
+
var deleteChild = /* @__PURE__ */ __name(async (repo, sub, childId, parentId) => {
|
|
2942
|
+
return repo.deleteChild(sub, childId, parentId);
|
|
2943
|
+
}, "deleteChild");
|
|
2234
2944
|
var registerSubResourceSchemas = /* @__PURE__ */ __name((ctx, sub) => {
|
|
2235
2945
|
if (!sub.views) return;
|
|
2236
2946
|
const { cls, config, baseUrl } = ctx;
|
|
@@ -2302,20 +3012,7 @@ var registerSubResourceFindAll = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
2302
3012
|
const idParamMeta = ctx.idParamMeta;
|
|
2303
3013
|
const methodName = `findAllBy_${sub.childRoute}`;
|
|
2304
3014
|
def(cls, methodName, async function(id, params) {
|
|
2305
|
-
|
|
2306
|
-
const totalPages = Math.max(1, Math.ceil(count / params.pageSize));
|
|
2307
|
-
return {
|
|
2308
|
-
data,
|
|
2309
|
-
request: {
|
|
2310
|
-
count,
|
|
2311
|
-
page: params.page,
|
|
2312
|
-
pageSize: params.pageSize,
|
|
2313
|
-
totalPages,
|
|
2314
|
-
sort: params.sort,
|
|
2315
|
-
sortDir: params.sortDir,
|
|
2316
|
-
filter: params.filter
|
|
2317
|
-
}
|
|
2318
|
-
};
|
|
3015
|
+
return findAllByParent(this.repo, id, sub.childRoute, params);
|
|
2319
3016
|
});
|
|
2320
3017
|
const d = desc(cls, methodName);
|
|
2321
3018
|
(0, import_common10.Get)(`:id/${sub.childRoute}`)(cls.prototype, methodName, d);
|
|
@@ -2336,7 +3033,7 @@ var registerSubResourceCreate = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
2336
3033
|
const { name } = config;
|
|
2337
3034
|
const methodName = `createChild_${sub.childRoute}`;
|
|
2338
3035
|
def(cls, methodName, async function(id, body) {
|
|
2339
|
-
return this.repo
|
|
3036
|
+
return createChild(this.repo, id, sub, body);
|
|
2340
3037
|
});
|
|
2341
3038
|
const d = desc(cls, methodName);
|
|
2342
3039
|
(0, import_common10.Post)(`:id/${sub.childRoute}`)(cls.prototype, methodName, d);
|
|
@@ -2356,7 +3053,7 @@ var registerSubResourceFindOne = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
2356
3053
|
const { cls } = ctx;
|
|
2357
3054
|
const methodName = `findOneChild_${sub.childRoute}`;
|
|
2358
3055
|
def(cls, methodName, async function(parentId, childId) {
|
|
2359
|
-
return this.repo
|
|
3056
|
+
return findOneChild(this.repo, sub, childId, parentId);
|
|
2360
3057
|
});
|
|
2361
3058
|
const d = desc(cls, methodName);
|
|
2362
3059
|
(0, import_common10.Get)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
|
|
@@ -2376,7 +3073,7 @@ var registerSubResourceUpdate = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
2376
3073
|
const { cls } = ctx;
|
|
2377
3074
|
const methodName = `updateChild_${sub.childRoute}`;
|
|
2378
3075
|
def(cls, methodName, async function(_id, childId, body) {
|
|
2379
|
-
return this.repo
|
|
3076
|
+
return updateChild(this.repo, sub, childId, body);
|
|
2380
3077
|
});
|
|
2381
3078
|
const d = desc(cls, methodName);
|
|
2382
3079
|
(0, import_common10.Patch)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
|
|
@@ -2397,7 +3094,7 @@ var registerSubResourceDelete = /* @__PURE__ */ __name((ctx, sub) => {
|
|
|
2397
3094
|
const { cls } = ctx;
|
|
2398
3095
|
const methodName = `deleteChild_${sub.childRoute}`;
|
|
2399
3096
|
def(cls, methodName, async function(parentId, childId) {
|
|
2400
|
-
return this.repo
|
|
3097
|
+
return deleteChild(this.repo, sub, childId, parentId);
|
|
2401
3098
|
});
|
|
2402
3099
|
const d = desc(cls, methodName);
|
|
2403
3100
|
(0, import_common10.Delete)(`:id/${sub.childRoute}/:childId`)(cls.prototype, methodName, d);
|
|
@@ -2436,9 +3133,9 @@ function createCrudController(config, baseUrl) {
|
|
|
2436
3133
|
throw new Error(`Resource "${name}" declares 'upsert' but no upsertOn`);
|
|
2437
3134
|
}
|
|
2438
3135
|
const bodyDecorator = /* @__PURE__ */ __name((schema, options) => {
|
|
2439
|
-
if (!schema) return (0,
|
|
2440
|
-
if (isZodSchema(schema)) return (0,
|
|
2441
|
-
return (0,
|
|
3136
|
+
if (!schema) return (0, import_common11.Body)();
|
|
3137
|
+
if (isZodSchema(schema)) return (0, import_common11.Body)(new ZodValidationPipe(schema, options));
|
|
3138
|
+
return (0, import_common11.Body)();
|
|
2442
3139
|
}, "bodyDecorator");
|
|
2443
3140
|
let CrudControllerBase = class CrudControllerBase {
|
|
2444
3141
|
static {
|
|
@@ -2493,165 +3190,115 @@ function createCrudController(config, baseUrl) {
|
|
|
2493
3190
|
}
|
|
2494
3191
|
__name(createCrudController, "createCrudController");
|
|
2495
3192
|
|
|
2496
|
-
// src/lib/crud/
|
|
2497
|
-
var
|
|
2498
|
-
var
|
|
2499
|
-
var
|
|
2500
|
-
var
|
|
2501
|
-
var
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
3193
|
+
// src/lib/crud/builder/column-predicates.ts
|
|
3194
|
+
var isBoolean = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "boolean" || col.columnType === "boolean", "isBoolean");
|
|
3195
|
+
var isRelation = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "relation", "isRelation");
|
|
3196
|
+
var isAutocomplete = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "autocomplete", "isAutocomplete");
|
|
3197
|
+
var isRecordCell = /* @__PURE__ */ __name((col) => isRelation(col) || isAutocomplete(col), "isRecordCell");
|
|
3198
|
+
var isDateRange = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "date-range", "isDateRange");
|
|
3199
|
+
|
|
3200
|
+
// src/lib/crud/builder/column.utils.ts
|
|
3201
|
+
var colPosition = /* @__PURE__ */ __name((col, i) => col.fieldInput?.position ?? i, "colPosition");
|
|
3202
|
+
var sortByPosition = /* @__PURE__ */ __name((cols) => cols.map((col, i) => ({
|
|
3203
|
+
col,
|
|
3204
|
+
i
|
|
3205
|
+
})).sort((a, b) => colPosition(a.col, a.i) - colPosition(b.col, b.i)).map(({ col }) => col), "sortByPosition");
|
|
3206
|
+
var toViewColumn = /* @__PURE__ */ __name((col) => ({
|
|
3207
|
+
id: col.id,
|
|
3208
|
+
...col.label && {
|
|
3209
|
+
label: col.label
|
|
3210
|
+
},
|
|
3211
|
+
...col.sortable != null && {
|
|
3212
|
+
sortable: col.sortable
|
|
3213
|
+
},
|
|
3214
|
+
...col.searchable != null && {
|
|
3215
|
+
searchable: col.searchable
|
|
3216
|
+
},
|
|
3217
|
+
...col.fieldInput && {
|
|
3218
|
+
fieldInput: col.fieldInput
|
|
2509
3219
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
var
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
return
|
|
3220
|
+
}), "toViewColumn");
|
|
3221
|
+
|
|
3222
|
+
// src/lib/crud/builder/sort.helpers.ts
|
|
3223
|
+
var deriveSortId = /* @__PURE__ */ __name((col) => {
|
|
3224
|
+
if (col.sortable === false) return null;
|
|
3225
|
+
if (col.sortId) return col.sortId;
|
|
3226
|
+
const base = col.column ?? col.id;
|
|
3227
|
+
const fi = col.fieldInput;
|
|
3228
|
+
const opts = fi?.options ?? {};
|
|
3229
|
+
const isRelation2 = fi?.format === "relation" || !!fi?.relationType || fi?.type === "autocomplete" || typeof opts.resource === "string";
|
|
3230
|
+
if (isRelation2) {
|
|
3231
|
+
const key = (typeof col.displayKey === "string" ? col.displayKey : void 0) ?? (typeof opts.displayKey === "string" ? opts.displayKey : void 0) ?? (typeof opts.labelKey === "string" ? opts.labelKey : void 0);
|
|
3232
|
+
if (!key) return null;
|
|
3233
|
+
const path2 = key.includes(".") ? key : `${base}.${key}`;
|
|
3234
|
+
return path2.endsWith(".label") ? path2.slice(0, -".label".length) : path2;
|
|
2525
3235
|
}
|
|
2526
|
-
|
|
3236
|
+
if (!col.displayKey) return base;
|
|
3237
|
+
const path = `${base}.${col.displayKey}`;
|
|
3238
|
+
const isValueLabel = !!col.enum || opts.emitObject === true;
|
|
3239
|
+
if (isValueLabel && path.endsWith(".label")) return path.slice(0, -".label".length);
|
|
3240
|
+
return path;
|
|
3241
|
+
}, "deriveSortId");
|
|
3242
|
+
var resolveDefaultSort = /* @__PURE__ */ __name((tableCols, allColumns) => {
|
|
3243
|
+
const isSortable = /* @__PURE__ */ __name((c) => c.sortable !== false, "isSortable");
|
|
3244
|
+
const sortCol = tableCols.find((c) => c.defaultSort && isSortable(c)) ?? tableCols.find(isSortable) ?? allColumns?.find((c) => c.idField);
|
|
3245
|
+
return sortCol ? deriveSortId(sortCol) ?? sortCol.id : void 0;
|
|
3246
|
+
}, "resolveDefaultSort");
|
|
2527
3247
|
|
|
2528
|
-
// src/lib/crud/
|
|
2529
|
-
var
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
if (
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
label: action.label,
|
|
2538
|
-
href: action.href,
|
|
2539
|
-
...action.condition && {
|
|
2540
|
-
condition: action.condition
|
|
2541
|
-
}
|
|
2542
|
-
});
|
|
2543
|
-
continue;
|
|
2544
|
-
}
|
|
2545
|
-
const file = findModule2((0, import_node_path3.join)(basePath, "actions"), action.procedure);
|
|
2546
|
-
if (!file) {
|
|
2547
|
-
console.warn(`[actions] Procedure file not found for "${action.id}" in ${basePath}/actions/`);
|
|
2548
|
-
continue;
|
|
2549
|
-
}
|
|
2550
|
-
const mod = await importDefault(file);
|
|
2551
|
-
if (!mod) {
|
|
2552
|
-
console.warn(`[actions] No default export in ${file}`);
|
|
2553
|
-
continue;
|
|
3248
|
+
// src/lib/crud/builder/schema-transforms.ts
|
|
3249
|
+
var allowAdditionalProperties = /* @__PURE__ */ __name((schema) => {
|
|
3250
|
+
if (schema["type"] === "object") {
|
|
3251
|
+
schema["additionalProperties"] = true;
|
|
3252
|
+
const props = schema["properties"];
|
|
3253
|
+
if (props) {
|
|
3254
|
+
for (const value of Object.values(props)) {
|
|
3255
|
+
allowAdditionalProperties(value);
|
|
3256
|
+
}
|
|
2554
3257
|
}
|
|
2555
|
-
results.push({
|
|
2556
|
-
id: action.id,
|
|
2557
|
-
label: action.label,
|
|
2558
|
-
method: action.method,
|
|
2559
|
-
data: action.data,
|
|
2560
|
-
...action.condition && {
|
|
2561
|
-
condition: action.condition
|
|
2562
|
-
},
|
|
2563
|
-
procedure: mod
|
|
2564
|
-
});
|
|
2565
3258
|
}
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
var loadTableActions = /* @__PURE__ */ __name(async (jsonActions, basePath) => {
|
|
2569
|
-
const results = [];
|
|
2570
|
-
for (const action of jsonActions) {
|
|
2571
|
-
if (action.type === "link") {
|
|
2572
|
-
results.push({
|
|
2573
|
-
type: "link",
|
|
2574
|
-
id: action.id,
|
|
2575
|
-
label: action.label,
|
|
2576
|
-
icon: action.icon,
|
|
2577
|
-
tooltip: action.tooltip,
|
|
2578
|
-
href: action.href
|
|
2579
|
-
});
|
|
2580
|
-
continue;
|
|
2581
|
-
}
|
|
2582
|
-
const file = findModule2((0, import_node_path3.join)(basePath, "actions"), action.procedure);
|
|
2583
|
-
if (!file) {
|
|
2584
|
-
console.warn(`[tableActions] Procedure file not found for "${action.id}" in ${basePath}/actions/`);
|
|
2585
|
-
continue;
|
|
2586
|
-
}
|
|
2587
|
-
const mod = await importDefault(file);
|
|
2588
|
-
if (!mod) {
|
|
2589
|
-
console.warn(`[tableActions] No default export in ${file}`);
|
|
2590
|
-
continue;
|
|
2591
|
-
}
|
|
2592
|
-
results.push({
|
|
2593
|
-
id: action.id,
|
|
2594
|
-
label: action.label,
|
|
2595
|
-
icon: action.icon,
|
|
2596
|
-
tooltip: action.tooltip,
|
|
2597
|
-
method: action.method,
|
|
2598
|
-
data: action.data,
|
|
2599
|
-
procedure: mod
|
|
2600
|
-
});
|
|
3259
|
+
if (schema["type"] === "array" && schema["items"]) {
|
|
3260
|
+
allowAdditionalProperties(schema["items"]);
|
|
2601
3261
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
var
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
const candidate = (0, import_node_path4.join)(dir, ENUMS_FILE);
|
|
2615
|
-
if ((0, import_node_fs3.existsSync)(candidate)) {
|
|
2616
|
-
file = candidate;
|
|
2617
|
-
break;
|
|
2618
|
-
}
|
|
2619
|
-
const parent = (0, import_node_path4.dirname)(dir);
|
|
2620
|
-
if (parent === dir) break;
|
|
2621
|
-
dir = parent;
|
|
2622
|
-
}
|
|
3262
|
+
}, "allowAdditionalProperties");
|
|
3263
|
+
var isNullableProperty2 = /* @__PURE__ */ __name((prop) => {
|
|
3264
|
+
const anyOf = prop?.["anyOf"];
|
|
3265
|
+
return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
|
|
3266
|
+
}, "isNullableProperty");
|
|
3267
|
+
var dropNullableFromRequired2 = /* @__PURE__ */ __name((schema) => {
|
|
3268
|
+
if (schema["type"] !== "object") return;
|
|
3269
|
+
const props = schema["properties"];
|
|
3270
|
+
if (!props) return;
|
|
3271
|
+
const required = schema["required"];
|
|
3272
|
+
if (Array.isArray(required)) {
|
|
3273
|
+
schema["required"] = required.filter((key) => !isNullableProperty2(props[key]));
|
|
2623
3274
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
return JSON.parse((0, import_node_fs3.readFileSync)(file, "utf-8"));
|
|
2627
|
-
} catch {
|
|
2628
|
-
return {};
|
|
3275
|
+
for (const value of Object.values(props)) {
|
|
3276
|
+
dropNullableFromRequired2(value);
|
|
2629
3277
|
}
|
|
2630
|
-
}, "
|
|
2631
|
-
var
|
|
2632
|
-
if (
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
if (!
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
if (!("values" in options)) options.values = values;
|
|
2642
|
-
col.fieldInput.options = options;
|
|
3278
|
+
}, "dropNullableFromRequired");
|
|
3279
|
+
var enforceRequiredMinLength = /* @__PURE__ */ __name((schema) => {
|
|
3280
|
+
if (schema["type"] !== "object") return;
|
|
3281
|
+
const props = schema["properties"];
|
|
3282
|
+
if (!props) return;
|
|
3283
|
+
for (const prop of Object.values(props)) {
|
|
3284
|
+
if (prop?.["type"] === "string" && !("minLength" in prop)) {
|
|
3285
|
+
prop["minLength"] = 1;
|
|
3286
|
+
} else if (prop?.["type"] === "object") {
|
|
3287
|
+
enforceRequiredMinLength(prop);
|
|
3288
|
+
}
|
|
2643
3289
|
}
|
|
2644
|
-
}, "
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
3290
|
+
}, "enforceRequiredMinLength");
|
|
3291
|
+
var applySchemaTransforms = /* @__PURE__ */ __name((schema) => {
|
|
3292
|
+
allowAdditionalProperties(schema);
|
|
3293
|
+
dropNullableFromRequired2(schema);
|
|
3294
|
+
enforceRequiredMinLength(schema);
|
|
3295
|
+
}, "applySchemaTransforms");
|
|
2648
3296
|
|
|
2649
|
-
// src/lib/crud/
|
|
3297
|
+
// src/lib/crud/builder/schema.helpers.ts
|
|
2650
3298
|
var pickByColumns = /* @__PURE__ */ __name((schema, columns, filter) => {
|
|
2651
3299
|
if (!schema) return void 0;
|
|
2652
3300
|
if (!columns?.length) return schema;
|
|
2653
|
-
const
|
|
2654
|
-
const baseFilter = /* @__PURE__ */ __name((c) => !isRelation2(c) && (filter ? filter(c) : true), "baseFilter");
|
|
3301
|
+
const baseFilter = /* @__PURE__ */ __name((c) => !isRelation(c) && (filter ? filter(c) : true), "baseFilter");
|
|
2655
3302
|
const filtered = columns.filter(baseFilter);
|
|
2656
3303
|
if (!filtered.length) return void 0;
|
|
2657
3304
|
const mask = Object.fromEntries(filtered.map((c) => [
|
|
@@ -2682,10 +3329,75 @@ var upsertOp = /* @__PURE__ */ __name((entry, schema) => {
|
|
|
2682
3329
|
return void 0;
|
|
2683
3330
|
}, "upsertOp");
|
|
2684
3331
|
|
|
2685
|
-
// src/lib/crud/
|
|
2686
|
-
var
|
|
3332
|
+
// src/lib/crud/builder/table-schema.builder.ts
|
|
3333
|
+
var SHARED_CELL_OPTION_KEYS = [
|
|
3334
|
+
"values",
|
|
3335
|
+
"storeValue",
|
|
3336
|
+
"uri",
|
|
3337
|
+
"resourceUri",
|
|
3338
|
+
"schemasUri"
|
|
3339
|
+
];
|
|
3340
|
+
var pickSharedCellOptions = /* @__PURE__ */ __name((col) => {
|
|
3341
|
+
const options = col.fieldInput?.options ?? {};
|
|
3342
|
+
return Object.fromEntries(SHARED_CELL_OPTION_KEYS.filter((key) => options[key] !== void 0).map((key) => [
|
|
3343
|
+
key,
|
|
3344
|
+
options[key]
|
|
3345
|
+
]));
|
|
3346
|
+
}, "pickSharedCellOptions");
|
|
3347
|
+
var buildTableUiSchema = /* @__PURE__ */ __name((cols) => {
|
|
3348
|
+
const layout = TableBuilder.init().addControls(...cols.map((col) => {
|
|
3349
|
+
const cellBuilder = isBoolean(col) ? BooleanCellBuilder : TextCellBuilder;
|
|
3350
|
+
let builder = cellBuilder.properties(col.id);
|
|
3351
|
+
if (col.displayKey) builder = builder.key(col.displayKey);
|
|
3352
|
+
if (col.sortId) builder = builder.setSortId(col.sortId);
|
|
3353
|
+
return builder;
|
|
3354
|
+
})).build();
|
|
3355
|
+
const colMap = Object.fromEntries(cols.map((c) => [
|
|
3356
|
+
c.id,
|
|
3357
|
+
c
|
|
3358
|
+
]));
|
|
3359
|
+
layout.elements = layout.elements.map((el) => {
|
|
3360
|
+
const id = el.scope?.replace("#/properties/", "");
|
|
3361
|
+
const col = id ? colMap[id] : void 0;
|
|
3362
|
+
if (!col) return el;
|
|
3363
|
+
const fieldInputOptions = isRecordCell(col) || isDateRange(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
|
|
3364
|
+
const dataPathOption = col.column ? {
|
|
3365
|
+
dataPath: col.column
|
|
3366
|
+
} : {};
|
|
3367
|
+
const derivedSortId = isRecordCell(col) || isDateRange(col) ? null : deriveSortId(col);
|
|
3368
|
+
const sortOptions = derivedSortId ? {
|
|
3369
|
+
sortId: derivedSortId
|
|
3370
|
+
} : {
|
|
3371
|
+
sortable: false
|
|
3372
|
+
};
|
|
3373
|
+
const relationTypeOption = col.fieldInput?.relationType ? {
|
|
3374
|
+
relationType: col.fieldInput.relationType
|
|
3375
|
+
} : {};
|
|
3376
|
+
return {
|
|
3377
|
+
...el,
|
|
3378
|
+
options: {
|
|
3379
|
+
...el.options ?? {},
|
|
3380
|
+
...fieldInputOptions,
|
|
3381
|
+
...dataPathOption,
|
|
3382
|
+
...sortOptions,
|
|
3383
|
+
...relationTypeOption,
|
|
3384
|
+
...isDateRange(col) && {
|
|
3385
|
+
format: "date-range"
|
|
3386
|
+
},
|
|
3387
|
+
label: col.label
|
|
3388
|
+
},
|
|
3389
|
+
...isRecordCell(col) && {
|
|
3390
|
+
type: "RecordCell"
|
|
3391
|
+
},
|
|
3392
|
+
...isDateRange(col) && {
|
|
3393
|
+
type: "Control"
|
|
3394
|
+
}
|
|
3395
|
+
};
|
|
3396
|
+
});
|
|
3397
|
+
return layout;
|
|
3398
|
+
}, "buildTableUiSchema");
|
|
2687
3399
|
|
|
2688
|
-
// src/lib/crud/
|
|
3400
|
+
// src/lib/crud/builder/form-schema.builder.ts
|
|
2689
3401
|
var buildConditionSchema = /* @__PURE__ */ __name((when) => {
|
|
2690
3402
|
if (when.notExists) return {
|
|
2691
3403
|
not: {
|
|
@@ -2805,232 +3517,7 @@ var buildFormUiSchema = /* @__PURE__ */ __name((cols) => {
|
|
|
2805
3517
|
return layout;
|
|
2806
3518
|
}, "buildFormUiSchema");
|
|
2807
3519
|
|
|
2808
|
-
// src/lib/crud/
|
|
2809
|
-
var allowAdditionalProperties = /* @__PURE__ */ __name((schema) => {
|
|
2810
|
-
if (schema["type"] === "object") {
|
|
2811
|
-
schema["additionalProperties"] = true;
|
|
2812
|
-
const props = schema["properties"];
|
|
2813
|
-
if (props) {
|
|
2814
|
-
for (const value of Object.values(props)) {
|
|
2815
|
-
allowAdditionalProperties(value);
|
|
2816
|
-
}
|
|
2817
|
-
}
|
|
2818
|
-
}
|
|
2819
|
-
if (schema["type"] === "array" && schema["items"]) {
|
|
2820
|
-
allowAdditionalProperties(schema["items"]);
|
|
2821
|
-
}
|
|
2822
|
-
}, "allowAdditionalProperties");
|
|
2823
|
-
var isNullableProperty2 = /* @__PURE__ */ __name((prop) => {
|
|
2824
|
-
const anyOf = prop?.["anyOf"];
|
|
2825
|
-
return Array.isArray(anyOf) && anyOf.some((s) => s?.["type"] === "null");
|
|
2826
|
-
}, "isNullableProperty");
|
|
2827
|
-
var dropNullableFromRequired2 = /* @__PURE__ */ __name((schema) => {
|
|
2828
|
-
if (schema["type"] !== "object") return;
|
|
2829
|
-
const props = schema["properties"];
|
|
2830
|
-
if (!props) return;
|
|
2831
|
-
const required = schema["required"];
|
|
2832
|
-
if (Array.isArray(required)) {
|
|
2833
|
-
schema["required"] = required.filter((key) => !isNullableProperty2(props[key]));
|
|
2834
|
-
}
|
|
2835
|
-
for (const value of Object.values(props)) {
|
|
2836
|
-
dropNullableFromRequired2(value);
|
|
2837
|
-
}
|
|
2838
|
-
}, "dropNullableFromRequired");
|
|
2839
|
-
var enforceRequiredMinLength = /* @__PURE__ */ __name((schema) => {
|
|
2840
|
-
if (schema["type"] !== "object") return;
|
|
2841
|
-
const props = schema["properties"];
|
|
2842
|
-
if (!props) return;
|
|
2843
|
-
for (const prop of Object.values(props)) {
|
|
2844
|
-
if (prop?.["type"] === "string" && !("minLength" in prop)) {
|
|
2845
|
-
prop["minLength"] = 1;
|
|
2846
|
-
} else if (prop?.["type"] === "object") {
|
|
2847
|
-
enforceRequiredMinLength(prop);
|
|
2848
|
-
}
|
|
2849
|
-
}
|
|
2850
|
-
}, "enforceRequiredMinLength");
|
|
2851
|
-
|
|
2852
|
-
// src/lib/crud/loader/table-schema.builder.ts
|
|
2853
|
-
var isBoolean = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "boolean" || col.columnType === "boolean", "isBoolean");
|
|
2854
|
-
var isRelation = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "relation", "isRelation");
|
|
2855
|
-
var isAutocomplete = /* @__PURE__ */ __name((col) => col.fieldInput?.type === "autocomplete", "isAutocomplete");
|
|
2856
|
-
var isRecordCell = /* @__PURE__ */ __name((col) => isRelation(col) || isAutocomplete(col), "isRecordCell");
|
|
2857
|
-
var isDateRange = /* @__PURE__ */ __name((col) => col.fieldInput?.format === "date-range", "isDateRange");
|
|
2858
|
-
var deriveSortId = /* @__PURE__ */ __name((col) => {
|
|
2859
|
-
if (col.sortable === false) return null;
|
|
2860
|
-
if (col.sortId) return col.sortId;
|
|
2861
|
-
const base = col.column ?? col.id;
|
|
2862
|
-
const fi = col.fieldInput;
|
|
2863
|
-
const opts = fi?.options ?? {};
|
|
2864
|
-
const isRelation2 = fi?.format === "relation" || !!fi?.relationType || fi?.type === "autocomplete" || typeof opts.resource === "string";
|
|
2865
|
-
if (isRelation2) {
|
|
2866
|
-
const key = (typeof col.displayKey === "string" ? col.displayKey : void 0) ?? (typeof opts.displayKey === "string" ? opts.displayKey : void 0) ?? (typeof opts.labelKey === "string" ? opts.labelKey : void 0);
|
|
2867
|
-
if (!key) return null;
|
|
2868
|
-
const path2 = key.includes(".") ? key : `${base}.${key}`;
|
|
2869
|
-
return path2.endsWith(".label") ? path2.slice(0, -".label".length) : path2;
|
|
2870
|
-
}
|
|
2871
|
-
if (!col.displayKey) return base;
|
|
2872
|
-
const path = `${base}.${col.displayKey}`;
|
|
2873
|
-
const isValueLabel = !!col.enum || opts.emitObject === true;
|
|
2874
|
-
if (isValueLabel && path.endsWith(".label")) return path.slice(0, -".label".length);
|
|
2875
|
-
return path;
|
|
2876
|
-
}, "deriveSortId");
|
|
2877
|
-
var resolveDefaultSort = /* @__PURE__ */ __name((tableCols, allColumns) => {
|
|
2878
|
-
const isSortable = /* @__PURE__ */ __name((c) => c.sortable !== false, "isSortable");
|
|
2879
|
-
const sortCol = tableCols.find((c) => c.defaultSort && isSortable(c)) ?? tableCols.find(isSortable) ?? allColumns?.find((c) => c.idField);
|
|
2880
|
-
return sortCol ? deriveSortId(sortCol) ?? sortCol.id : void 0;
|
|
2881
|
-
}, "resolveDefaultSort");
|
|
2882
|
-
var SHARED_CELL_OPTION_KEYS = [
|
|
2883
|
-
"values",
|
|
2884
|
-
"storeValue",
|
|
2885
|
-
"uri",
|
|
2886
|
-
"resourceUri",
|
|
2887
|
-
"schemasUri"
|
|
2888
|
-
];
|
|
2889
|
-
var pickSharedCellOptions = /* @__PURE__ */ __name((col) => {
|
|
2890
|
-
const options = col.fieldInput?.options ?? {};
|
|
2891
|
-
return Object.fromEntries(SHARED_CELL_OPTION_KEYS.filter((key) => options[key] !== void 0).map((key) => [
|
|
2892
|
-
key,
|
|
2893
|
-
options[key]
|
|
2894
|
-
]));
|
|
2895
|
-
}, "pickSharedCellOptions");
|
|
2896
|
-
var buildTableUiSchema = /* @__PURE__ */ __name((cols) => {
|
|
2897
|
-
const layout = TableBuilder.init().addControls(...cols.map((col) => {
|
|
2898
|
-
const cellBuilder = isBoolean(col) ? BooleanCellBuilder : TextCellBuilder;
|
|
2899
|
-
let builder = cellBuilder.properties(col.id);
|
|
2900
|
-
if (col.displayKey) builder = builder.key(col.displayKey);
|
|
2901
|
-
if (col.sortId) builder = builder.setSortId(col.sortId);
|
|
2902
|
-
return builder;
|
|
2903
|
-
})).build();
|
|
2904
|
-
const colMap = Object.fromEntries(cols.map((c) => [
|
|
2905
|
-
c.id,
|
|
2906
|
-
c
|
|
2907
|
-
]));
|
|
2908
|
-
layout.elements = layout.elements.map((el) => {
|
|
2909
|
-
const id = el.scope?.replace("#/properties/", "");
|
|
2910
|
-
const col = id ? colMap[id] : void 0;
|
|
2911
|
-
if (!col) return el;
|
|
2912
|
-
const fieldInputOptions = isRecordCell(col) || isDateRange(col) ? col.fieldInput?.options ?? {} : pickSharedCellOptions(col);
|
|
2913
|
-
const dataPathOption = col.column ? {
|
|
2914
|
-
dataPath: col.column
|
|
2915
|
-
} : {};
|
|
2916
|
-
const derivedSortId = isRecordCell(col) || isDateRange(col) ? null : deriveSortId(col);
|
|
2917
|
-
const sortOptions = derivedSortId ? {
|
|
2918
|
-
sortId: derivedSortId
|
|
2919
|
-
} : {
|
|
2920
|
-
sortable: false
|
|
2921
|
-
};
|
|
2922
|
-
const relationTypeOption = col.fieldInput?.relationType ? {
|
|
2923
|
-
relationType: col.fieldInput.relationType
|
|
2924
|
-
} : {};
|
|
2925
|
-
return {
|
|
2926
|
-
...el,
|
|
2927
|
-
options: {
|
|
2928
|
-
...el.options ?? {},
|
|
2929
|
-
...fieldInputOptions,
|
|
2930
|
-
...dataPathOption,
|
|
2931
|
-
...sortOptions,
|
|
2932
|
-
...relationTypeOption,
|
|
2933
|
-
...isDateRange(col) && {
|
|
2934
|
-
format: "date-range"
|
|
2935
|
-
},
|
|
2936
|
-
label: col.label
|
|
2937
|
-
},
|
|
2938
|
-
...isRecordCell(col) && {
|
|
2939
|
-
type: "RecordCell"
|
|
2940
|
-
},
|
|
2941
|
-
...isDateRange(col) && {
|
|
2942
|
-
type: "Control"
|
|
2943
|
-
}
|
|
2944
|
-
};
|
|
2945
|
-
});
|
|
2946
|
-
return layout;
|
|
2947
|
-
}, "buildTableUiSchema");
|
|
2948
|
-
|
|
2949
|
-
// src/lib/crud/loader/view.builders.ts
|
|
2950
|
-
var patchFilterProperties = /* @__PURE__ */ __name((jsonSchema, columns) => {
|
|
2951
|
-
if (!columns?.length) return;
|
|
2952
|
-
const properties = jsonSchema.properties;
|
|
2953
|
-
if (!properties) return;
|
|
2954
|
-
for (const col of columns) {
|
|
2955
|
-
if (col.fieldInput?.format === "date-range") {
|
|
2956
|
-
delete properties[col.id];
|
|
2957
|
-
const opts = col.fieldInput?.options ?? {};
|
|
2958
|
-
const base = col.label ?? col.id;
|
|
2959
|
-
const fromField = opts["fromField"] ?? "from";
|
|
2960
|
-
const toField = opts["toField"] ?? "to";
|
|
2961
|
-
const dateProp = /* @__PURE__ */ __name((title) => ({
|
|
2962
|
-
type: "string",
|
|
2963
|
-
format: "date",
|
|
2964
|
-
title,
|
|
2965
|
-
"x-field-type": "date"
|
|
2966
|
-
}), "dateProp");
|
|
2967
|
-
properties[`${col.id}->${fromField}`] = dateProp(opts["fromLabel"] ?? `${base} (from)`);
|
|
2968
|
-
properties[`${col.id}->${toField}`] = dateProp(opts["toLabel"] ?? `${base} (to)`);
|
|
2969
|
-
continue;
|
|
2970
|
-
}
|
|
2971
|
-
const prop = properties[col.id];
|
|
2972
|
-
if (!prop) continue;
|
|
2973
|
-
prop.title = col.label ?? col.id;
|
|
2974
|
-
const values = col.fieldInput?.options?.["values"];
|
|
2975
|
-
if (values) {
|
|
2976
|
-
prop["x-values"] = values;
|
|
2977
|
-
}
|
|
2978
|
-
const ft = col.fieldInput?.type;
|
|
2979
|
-
if (ft === "select" || col.enum) {
|
|
2980
|
-
prop["x-field-type"] = "enum";
|
|
2981
|
-
} else if (ft === "number" || prop.type === "number" || prop.type === "integer") {
|
|
2982
|
-
prop["x-field-type"] = "number";
|
|
2983
|
-
} else if (prop.format === "date-time" || prop.format === "date") {
|
|
2984
|
-
prop["x-field-type"] = "date";
|
|
2985
|
-
} else if (prop.type === "boolean") {
|
|
2986
|
-
prop["x-field-type"] = "boolean";
|
|
2987
|
-
}
|
|
2988
|
-
}
|
|
2989
|
-
}, "patchFilterProperties");
|
|
2990
|
-
var colPosition = /* @__PURE__ */ __name((col, i) => col.fieldInput?.position ?? i, "colPosition");
|
|
2991
|
-
var sortByPosition = /* @__PURE__ */ __name((cols) => cols.map((col, i) => ({
|
|
2992
|
-
col,
|
|
2993
|
-
i
|
|
2994
|
-
})).sort((a, b) => colPosition(a.col, a.i) - colPosition(b.col, b.i)).map(({ col }) => col), "sortByPosition");
|
|
2995
|
-
var toViewColumn = /* @__PURE__ */ __name((col) => ({
|
|
2996
|
-
id: col.id,
|
|
2997
|
-
...col.label && {
|
|
2998
|
-
label: col.label
|
|
2999
|
-
},
|
|
3000
|
-
...col.sortable != null && {
|
|
3001
|
-
sortable: col.sortable
|
|
3002
|
-
},
|
|
3003
|
-
...col.searchable != null && {
|
|
3004
|
-
searchable: col.searchable
|
|
3005
|
-
},
|
|
3006
|
-
...col.fieldInput && {
|
|
3007
|
-
fieldInput: col.fieldInput
|
|
3008
|
-
}
|
|
3009
|
-
}), "toViewColumn");
|
|
3010
|
-
var buildView = /* @__PURE__ */ __name((schema, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
|
|
3011
|
-
if (!schema || !columns?.length) return void 0;
|
|
3012
|
-
const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
|
|
3013
|
-
if (!visibleCols.length) return void 0;
|
|
3014
|
-
const schemaCols = schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols;
|
|
3015
|
-
const schemaIds = schemaCols.map((c) => c.id);
|
|
3016
|
-
const mask = Object.fromEntries(schemaIds.map((id) => [
|
|
3017
|
-
id,
|
|
3018
|
-
true
|
|
3019
|
-
]));
|
|
3020
|
-
const picked = schema.pick(mask);
|
|
3021
|
-
const jsonSchema = (0, import_zod8.toJSONSchema)(picked, {
|
|
3022
|
-
target: "draft-07",
|
|
3023
|
-
...jsonSchemaOpts
|
|
3024
|
-
});
|
|
3025
|
-
allowAdditionalProperties(jsonSchema);
|
|
3026
|
-
dropNullableFromRequired2(jsonSchema);
|
|
3027
|
-
enforceRequiredMinLength(jsonSchema);
|
|
3028
|
-
return {
|
|
3029
|
-
json_schema: jsonSchema,
|
|
3030
|
-
ui_schema: buildUiSchema(visibleCols),
|
|
3031
|
-
columns: visibleCols.map(toViewColumn)
|
|
3032
|
-
};
|
|
3033
|
-
}, "buildView");
|
|
3520
|
+
// src/lib/crud/builder/calculated-columns.builder.ts
|
|
3034
3521
|
var isVisibleInMode = /* @__PURE__ */ __name((c, mode) => mode === "table" ? !c.hiddenInTable : !c.hiddenInView, "isVisibleInMode");
|
|
3035
3522
|
var buildCalculatedElement = /* @__PURE__ */ __name((c, mode) => mode === "table" ? {
|
|
3036
3523
|
type: c.type === "boolean" ? "BooleanCell" : "TextCell",
|
|
@@ -3109,6 +3596,78 @@ var injectCalculatedColumnsIntoView = /* @__PURE__ */ __name((viewConfig, calcul
|
|
|
3109
3596
|
}, "injectCalculatedColumnsIntoView");
|
|
3110
3597
|
var injectCalculatedColumns = /* @__PURE__ */ __name((tableView, calculated) => injectCalculatedColumnsIntoView(tableView, calculated, "table"), "injectCalculatedColumns");
|
|
3111
3598
|
var injectCalculatedColumnsToView = /* @__PURE__ */ __name((viewConfig, calculated) => injectCalculatedColumnsIntoView(viewConfig, calculated, "view"), "injectCalculatedColumnsToView");
|
|
3599
|
+
|
|
3600
|
+
// src/lib/crud/builder/view.builder.ts
|
|
3601
|
+
var import_zod20 = require("zod");
|
|
3602
|
+
var patchFilterProperties = /* @__PURE__ */ __name((jsonSchema, columns) => {
|
|
3603
|
+
if (!columns?.length) return;
|
|
3604
|
+
const properties = jsonSchema.properties;
|
|
3605
|
+
if (!properties) return;
|
|
3606
|
+
for (const col of columns) {
|
|
3607
|
+
if (col.fieldInput?.format === "date-range") {
|
|
3608
|
+
delete properties[col.id];
|
|
3609
|
+
const opts = col.fieldInput?.options ?? {};
|
|
3610
|
+
const base = col.label ?? col.id;
|
|
3611
|
+
const fromField = opts["fromField"] ?? "from";
|
|
3612
|
+
const toField = opts["toField"] ?? "to";
|
|
3613
|
+
const dateProp = /* @__PURE__ */ __name((title) => ({
|
|
3614
|
+
type: "string",
|
|
3615
|
+
format: "date",
|
|
3616
|
+
title,
|
|
3617
|
+
"x-field-type": "date"
|
|
3618
|
+
}), "dateProp");
|
|
3619
|
+
properties[`${col.id}->${fromField}`] = dateProp(opts["fromLabel"] ?? `${base} (from)`);
|
|
3620
|
+
properties[`${col.id}->${toField}`] = dateProp(opts["toLabel"] ?? `${base} (to)`);
|
|
3621
|
+
continue;
|
|
3622
|
+
}
|
|
3623
|
+
const prop = properties[col.id];
|
|
3624
|
+
if (!prop) continue;
|
|
3625
|
+
prop.title = col.label ?? col.id;
|
|
3626
|
+
const values = col.fieldInput?.options?.["values"];
|
|
3627
|
+
if (values) {
|
|
3628
|
+
prop["x-values"] = values;
|
|
3629
|
+
}
|
|
3630
|
+
const ft = col.fieldInput?.type;
|
|
3631
|
+
if (ft === "select" || col.enum) {
|
|
3632
|
+
prop["x-field-type"] = "enum";
|
|
3633
|
+
} else if (ft === "number" || prop.type === "number" || prop.type === "integer") {
|
|
3634
|
+
prop["x-field-type"] = "number";
|
|
3635
|
+
} else if (prop.format === "date-time" || prop.format === "date") {
|
|
3636
|
+
prop["x-field-type"] = "date";
|
|
3637
|
+
} else if (prop.type === "boolean") {
|
|
3638
|
+
prop["x-field-type"] = "boolean";
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
}, "patchFilterProperties");
|
|
3642
|
+
var buildView = /* @__PURE__ */ __name((schema, columns, visible, buildUiSchema, sort = false, schemaVisible) => {
|
|
3643
|
+
if (!schema || !columns?.length) return void 0;
|
|
3644
|
+
const visibleCols = sort ? sortByPosition(columns.filter(visible)) : columns.filter(visible);
|
|
3645
|
+
if (!visibleCols.length) return void 0;
|
|
3646
|
+
const schemaCols = (schemaVisible ? columns.filter((c) => visible(c) || schemaVisible(c)) : visibleCols).filter((c) => !isRelation(c));
|
|
3647
|
+
const schemaIds = schemaCols.map((c) => c.id);
|
|
3648
|
+
const mask = Object.fromEntries(schemaIds.map((id) => [
|
|
3649
|
+
id,
|
|
3650
|
+
true
|
|
3651
|
+
]));
|
|
3652
|
+
const picked = schema.pick(mask);
|
|
3653
|
+
const jsonSchema = (0, import_zod20.toJSONSchema)(picked, {
|
|
3654
|
+
target: "draft-07",
|
|
3655
|
+
...jsonSchemaOpts
|
|
3656
|
+
});
|
|
3657
|
+
applySchemaTransforms(jsonSchema);
|
|
3658
|
+
if (schemaVisible) {
|
|
3659
|
+
const nonEditableIds = new Set(schemaCols.filter((c) => c.idField || c.createable === false && c.updateable === false).map((c) => c.id));
|
|
3660
|
+
const required = jsonSchema["required"];
|
|
3661
|
+
if (Array.isArray(required) && nonEditableIds.size) {
|
|
3662
|
+
jsonSchema["required"] = required.filter((k) => !nonEditableIds.has(k));
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
return {
|
|
3666
|
+
json_schema: jsonSchema,
|
|
3667
|
+
ui_schema: buildUiSchema(visibleCols),
|
|
3668
|
+
columns: visibleCols.map(toViewColumn)
|
|
3669
|
+
};
|
|
3670
|
+
}, "buildView");
|
|
3112
3671
|
var emptyTableView = /* @__PURE__ */ __name(() => ({
|
|
3113
3672
|
json_schema: {
|
|
3114
3673
|
type: "object",
|
|
@@ -3127,7 +3686,7 @@ var buildViews = /* @__PURE__ */ __name((schema, columns) => {
|
|
|
3127
3686
|
} else if (columns?.length) {
|
|
3128
3687
|
views.table = emptyTableView();
|
|
3129
3688
|
}
|
|
3130
|
-
const form = buildView(schema, columns, (c) => !c.hiddenInForm, buildFormUiSchema, true, (c) => c.createable === true || c.updateable === true);
|
|
3689
|
+
const form = buildView(schema, columns, (c) => !c.hiddenInForm, buildFormUiSchema, true, (c) => !c.idField && (c.createable === true || c.updateable === true));
|
|
3131
3690
|
if (form) views.form = form;
|
|
3132
3691
|
const filter = buildView(schema, columns, (c) => !!c.filterable, buildFormUiSchema, true);
|
|
3133
3692
|
if (filter) {
|
|
@@ -3147,7 +3706,6 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
3147
3706
|
if (!properties[c.column]) {
|
|
3148
3707
|
properties[c.column] = {
|
|
3149
3708
|
type: "object",
|
|
3150
|
-
additionalProperties: true,
|
|
3151
3709
|
properties: {}
|
|
3152
3710
|
};
|
|
3153
3711
|
}
|
|
@@ -3158,7 +3716,6 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
3158
3716
|
if (!target[segment]) {
|
|
3159
3717
|
target[segment] = {
|
|
3160
3718
|
type: "object",
|
|
3161
|
-
additionalProperties: true,
|
|
3162
3719
|
properties: {}
|
|
3163
3720
|
};
|
|
3164
3721
|
}
|
|
@@ -3169,17 +3726,22 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
3169
3726
|
title: c.label ?? c.id
|
|
3170
3727
|
};
|
|
3171
3728
|
} else {
|
|
3729
|
+
const opts = c.fieldInput?.options;
|
|
3730
|
+
const isObject = opts?.emitObject === true || c.fieldInput?.type === "autocomplete";
|
|
3172
3731
|
properties[c.id] = {
|
|
3173
|
-
|
|
3732
|
+
...isObject ? {} : {
|
|
3733
|
+
type: c.columnType ?? "string"
|
|
3734
|
+
},
|
|
3174
3735
|
title: c.label ?? c.id
|
|
3175
3736
|
};
|
|
3176
3737
|
}
|
|
3177
3738
|
}
|
|
3178
|
-
|
|
3739
|
+
const schema = {
|
|
3179
3740
|
type: "object",
|
|
3180
|
-
additionalProperties: true,
|
|
3181
3741
|
properties
|
|
3182
3742
|
};
|
|
3743
|
+
applySchemaTransforms(schema);
|
|
3744
|
+
return schema;
|
|
3183
3745
|
}, "buildJsonSchema");
|
|
3184
3746
|
const fixNestedScopes = /* @__PURE__ */ __name((uiSchema, cols) => {
|
|
3185
3747
|
const colMap = Object.fromEntries(cols.map((c) => [
|
|
@@ -3229,33 +3791,223 @@ var buildViewsFromColumns = /* @__PURE__ */ __name((columns) => {
|
|
|
3229
3791
|
return Object.keys(views).length ? views : void 0;
|
|
3230
3792
|
}, "buildViewsFromColumns");
|
|
3231
3793
|
|
|
3232
|
-
// src/lib/crud/
|
|
3794
|
+
// src/lib/crud/enum-registry/enum-registry.types.ts
|
|
3795
|
+
var import_zod21 = require("zod");
|
|
3796
|
+
var EnumEntrySchema = import_zod21.z.object({
|
|
3797
|
+
value: import_zod21.z.unknown(),
|
|
3798
|
+
label: import_zod21.z.string()
|
|
3799
|
+
});
|
|
3800
|
+
var EnumRegistrySchema = import_zod21.z.record(import_zod21.z.string(), import_zod21.z.array(EnumEntrySchema)).default({});
|
|
3801
|
+
|
|
3802
|
+
// src/lib/crud/enum-registry/enum-registry.loader.ts
|
|
3803
|
+
var import_node_fs3 = require("fs");
|
|
3804
|
+
var import_node_path5 = require("path");
|
|
3805
|
+
var ENUMS_FILE = "crouton.enums.json";
|
|
3806
|
+
var loadEnumRegistry = /* @__PURE__ */ __name((startDir, enumsFile) => {
|
|
3807
|
+
let file = enumsFile;
|
|
3808
|
+
if (!file) {
|
|
3809
|
+
let dir = startDir;
|
|
3810
|
+
while (true) {
|
|
3811
|
+
const candidate = (0, import_node_path5.join)(dir, ENUMS_FILE);
|
|
3812
|
+
if ((0, import_node_fs3.existsSync)(candidate)) {
|
|
3813
|
+
file = candidate;
|
|
3814
|
+
break;
|
|
3815
|
+
}
|
|
3816
|
+
const parent = (0, import_node_path5.dirname)(dir);
|
|
3817
|
+
if (parent === dir) break;
|
|
3818
|
+
dir = parent;
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3821
|
+
if (!file || !(0, import_node_fs3.existsSync)(file)) return EnumRegistrySchema.parse(void 0);
|
|
3822
|
+
try {
|
|
3823
|
+
return EnumRegistrySchema.parse(JSON.parse((0, import_node_fs3.readFileSync)(file, "utf-8")));
|
|
3824
|
+
} catch {
|
|
3825
|
+
return EnumRegistrySchema.parse(void 0);
|
|
3826
|
+
}
|
|
3827
|
+
}, "loadEnumRegistry");
|
|
3828
|
+
|
|
3829
|
+
// src/lib/crud/enum-registry/enum-registry.injector.ts
|
|
3830
|
+
var injectEnumValues = /* @__PURE__ */ __name((columns, enums) => {
|
|
3831
|
+
if (!columns) return;
|
|
3832
|
+
for (const col of columns) {
|
|
3833
|
+
if (!col.enum) continue;
|
|
3834
|
+
const values = enums[col.enum];
|
|
3835
|
+
if (!values) continue;
|
|
3836
|
+
col.fieldInput = col.fieldInput ?? {
|
|
3837
|
+
type: "select"
|
|
3838
|
+
};
|
|
3839
|
+
const options = col.fieldInput.options ?? {};
|
|
3840
|
+
if (!("values" in options)) options.values = values;
|
|
3841
|
+
col.fieldInput.options = options;
|
|
3842
|
+
}
|
|
3843
|
+
}, "injectEnumValues");
|
|
3844
|
+
|
|
3845
|
+
// src/lib/crud/adapter/relation-type.ts
|
|
3846
|
+
var import_zod22 = require("zod");
|
|
3847
|
+
var unwrapZodType = /* @__PURE__ */ __name((type) => {
|
|
3848
|
+
if (type instanceof import_zod22.ZodOptional || type instanceof import_zod22.ZodNullable) {
|
|
3849
|
+
return unwrapZodType(type.unwrap());
|
|
3850
|
+
}
|
|
3851
|
+
return type;
|
|
3852
|
+
}, "unwrapZodType");
|
|
3853
|
+
var deriveRelationType = /* @__PURE__ */ __name((schema, columnId) => {
|
|
3854
|
+
if (!schema) return void 0;
|
|
3855
|
+
const field = schema.shape[columnId];
|
|
3856
|
+
if (!field) return void 0;
|
|
3857
|
+
const inner = unwrapZodType(field);
|
|
3858
|
+
return inner instanceof import_zod22.ZodArray ? "oneToMany" : "manyToOne";
|
|
3859
|
+
}, "deriveRelationType");
|
|
3860
|
+
var deriveRelationTypeFromColumns = /* @__PURE__ */ __name((col, cols) => {
|
|
3861
|
+
const base = col.column ?? col.id;
|
|
3862
|
+
const fkNames = /* @__PURE__ */ new Set([
|
|
3863
|
+
`${base}_id`,
|
|
3864
|
+
`${col.id}_id`
|
|
3865
|
+
]);
|
|
3866
|
+
return cols.some((c) => fkNames.has(c.id)) ? "manyToOne" : "oneToMany";
|
|
3867
|
+
}, "deriveRelationTypeFromColumns");
|
|
3868
|
+
var enrichRelationTypes = /* @__PURE__ */ __name((columns, schema) => {
|
|
3869
|
+
if (!schema) return columns;
|
|
3870
|
+
return columns.map((col) => {
|
|
3871
|
+
const fi = col.fieldInput;
|
|
3872
|
+
if (!fi) return col;
|
|
3873
|
+
const isRelationField = fi.type === "autocomplete" || fi.format === "relation";
|
|
3874
|
+
if (!isRelationField) return col;
|
|
3875
|
+
if (fi.relationType) return col;
|
|
3876
|
+
const derived = deriveRelationType(schema, col.id) ?? deriveRelationTypeFromColumns(col, columns);
|
|
3877
|
+
if (!derived) return col;
|
|
3878
|
+
return {
|
|
3879
|
+
...col,
|
|
3880
|
+
fieldInput: {
|
|
3881
|
+
...fi,
|
|
3882
|
+
relationType: derived
|
|
3883
|
+
}
|
|
3884
|
+
};
|
|
3885
|
+
});
|
|
3886
|
+
}, "enrichRelationTypes");
|
|
3887
|
+
|
|
3888
|
+
// src/lib/crud/resource/ReadResourceJson.ts
|
|
3233
3889
|
var import_node_fs4 = require("fs");
|
|
3234
|
-
var
|
|
3235
|
-
var
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
};
|
|
3243
|
-
} catch {
|
|
3244
|
-
return void 0;
|
|
3245
|
-
}
|
|
3890
|
+
var import_node_path6 = require("path");
|
|
3891
|
+
var readResourceJson = /* @__PURE__ */ __name((jsonPath) => {
|
|
3892
|
+
if (!(0, import_node_fs4.existsSync)(jsonPath)) return void 0;
|
|
3893
|
+
const fileContent = JSON.parse((0, import_node_fs4.readFileSync)(jsonPath, "utf-8"));
|
|
3894
|
+
const resource = ResourceJsonSchema.safeParse(fileContent);
|
|
3895
|
+
if (resource.error) {
|
|
3896
|
+
console.error(resource.error);
|
|
3897
|
+
throw new Error(`Resource cannot be parsed ${jsonPath}`);
|
|
3246
3898
|
}
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3899
|
+
return {
|
|
3900
|
+
json: resource.data,
|
|
3901
|
+
dir: (0, import_node_path6.dirname)(jsonPath)
|
|
3902
|
+
};
|
|
3903
|
+
}, "readResourceJson");
|
|
3904
|
+
|
|
3905
|
+
// src/lib/crud/adapter/resource-resolver.ts
|
|
3906
|
+
var import_node_fs5 = require("fs");
|
|
3907
|
+
var import_node_path7 = require("path");
|
|
3908
|
+
var resolveChildResource = /* @__PURE__ */ __name((resourcePath, parentDir) => {
|
|
3909
|
+
const directPath = (0, import_node_path7.resolve)(parentDir, resourcePath);
|
|
3250
3910
|
try {
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3911
|
+
if (resourcePath.endsWith(".json") && (0, import_node_fs5.existsSync)(directPath)) {
|
|
3912
|
+
return readResourceJson(directPath);
|
|
3913
|
+
}
|
|
3914
|
+
const childName = resourcePath.replace(/^\.\//, "").replace(/\.resource$/, "");
|
|
3915
|
+
const childJsonPath = (0, import_node_path7.resolve)((0, import_node_path7.dirname)(parentDir), childName, "resource.json");
|
|
3916
|
+
return readResourceJson(childJsonPath);
|
|
3255
3917
|
} catch {
|
|
3256
3918
|
return void 0;
|
|
3257
3919
|
}
|
|
3258
3920
|
}, "resolveChildResource");
|
|
3921
|
+
|
|
3922
|
+
// src/lib/crud/adapter/column-enrichment.ts
|
|
3923
|
+
var enrichActionColumns = /* @__PURE__ */ __name((columns, parentRoute, subResources, baseUrl) => {
|
|
3924
|
+
if (!columns) return columns;
|
|
3925
|
+
const base = baseUrl ?? "";
|
|
3926
|
+
return columns.map((col) => {
|
|
3927
|
+
if (col.fieldInput?.format !== "relation") return col;
|
|
3928
|
+
const sub = subResources.find((s) => s.column === col.id);
|
|
3929
|
+
if (!sub) return col;
|
|
3930
|
+
return {
|
|
3931
|
+
...col,
|
|
3932
|
+
fieldInput: {
|
|
3933
|
+
...col.fieldInput,
|
|
3934
|
+
options: {
|
|
3935
|
+
...col.fieldInput.options,
|
|
3936
|
+
uri: `${base}/${parentRoute}/{id}/${sub.childRoute}`,
|
|
3937
|
+
resourceUri: `${base}/${sub.childRoute}`,
|
|
3938
|
+
...sub.views && {
|
|
3939
|
+
resource: `${base}/${parentRoute}/${sub.childRoute}/schemas`
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
};
|
|
3944
|
+
});
|
|
3945
|
+
}, "enrichActionColumns");
|
|
3946
|
+
var enrichResourceRefColumns = /* @__PURE__ */ __name((columns, parentDir, baseUrl) => {
|
|
3947
|
+
if (!columns || !parentDir) return columns;
|
|
3948
|
+
const base = baseUrl ?? "";
|
|
3949
|
+
return columns.map((col) => {
|
|
3950
|
+
if (!col.fieldInput?.resource || col.fieldInput.format === "relation") return col;
|
|
3951
|
+
const childResolved = resolveChildResource(col.fieldInput.resource, parentDir);
|
|
3952
|
+
const childRoute = childResolved?.json?.route ?? col.fieldInput.resource.replace(/^\.\//, "").replace(/\.resource$/, "");
|
|
3953
|
+
return {
|
|
3954
|
+
...col,
|
|
3955
|
+
fieldInput: {
|
|
3956
|
+
...col.fieldInput,
|
|
3957
|
+
options: {
|
|
3958
|
+
...col.fieldInput.options,
|
|
3959
|
+
resourceUri: `${base}/${childRoute}`,
|
|
3960
|
+
schemasUri: `${base}/${childRoute}/schemas`
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
};
|
|
3964
|
+
});
|
|
3965
|
+
}, "enrichResourceRefColumns");
|
|
3966
|
+
var enrichNestedRelationColumns = /* @__PURE__ */ __name((cols, dir, baseUrl) => {
|
|
3967
|
+
if (!cols || !dir) return cols;
|
|
3968
|
+
const base = baseUrl ?? "";
|
|
3969
|
+
return cols.map((col) => {
|
|
3970
|
+
if (col.fieldInput?.format !== "relation" || !col.fieldInput.resource) return col;
|
|
3971
|
+
const resolved = resolveChildResource(col.fieldInput.resource, dir);
|
|
3972
|
+
const targetRoute = resolved?.json?.route ?? col.fieldInput.resource.replace(/^\.\//, "").replace(/\/resource\.json$/, "").replace(/\.resource$/, "");
|
|
3973
|
+
const relationType = col.fieldInput.relationType ?? deriveRelationTypeFromColumns(col, cols);
|
|
3974
|
+
return {
|
|
3975
|
+
...col,
|
|
3976
|
+
fieldInput: {
|
|
3977
|
+
...col.fieldInput,
|
|
3978
|
+
relationType,
|
|
3979
|
+
options: {
|
|
3980
|
+
...col.fieldInput.options,
|
|
3981
|
+
uri: `${base}/${targetRoute}`,
|
|
3982
|
+
resourceUri: `${base}/${targetRoute}`,
|
|
3983
|
+
resource: `${base}/${targetRoute}/schemas`
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
});
|
|
3988
|
+
}, "enrichNestedRelationColumns");
|
|
3989
|
+
var enrichIncludeWithSort = /* @__PURE__ */ __name((include, columns) => {
|
|
3990
|
+
if (!include?.length) return include;
|
|
3991
|
+
return include.map((entry) => {
|
|
3992
|
+
const relationName = typeof entry === "string" ? entry : entry.relation;
|
|
3993
|
+
const col = columns.find((c) => {
|
|
3994
|
+
const opts2 = c.fieldInput?.options;
|
|
3995
|
+
return (c.column ?? c.id) === relationName && opts2?.sort;
|
|
3996
|
+
});
|
|
3997
|
+
if (!col) return entry;
|
|
3998
|
+
const opts = col.fieldInput.options;
|
|
3999
|
+
const orderBy = buildChildSortClause(opts.sort, opts.sortDir ?? "asc");
|
|
4000
|
+
return typeof entry === "string" ? {
|
|
4001
|
+
relation: entry,
|
|
4002
|
+
orderBy
|
|
4003
|
+
} : {
|
|
4004
|
+
...entry,
|
|
4005
|
+
orderBy
|
|
4006
|
+
};
|
|
4007
|
+
});
|
|
4008
|
+
}, "enrichIncludeWithSort");
|
|
4009
|
+
|
|
4010
|
+
// src/lib/crud/adapter/column-transforms.ts
|
|
3259
4011
|
var expandExtendColumns = /* @__PURE__ */ __name((columns, dirPath) => {
|
|
3260
4012
|
if (!dirPath) return columns;
|
|
3261
4013
|
const result = [];
|
|
@@ -3270,7 +4022,7 @@ var expandExtendColumns = /* @__PURE__ */ __name((columns, dirPath) => {
|
|
|
3270
4022
|
result.push(col);
|
|
3271
4023
|
continue;
|
|
3272
4024
|
}
|
|
3273
|
-
const refColumns =
|
|
4025
|
+
const refColumns = resolved.json.columns;
|
|
3274
4026
|
const parentColumnKey = col.column ?? col.id;
|
|
3275
4027
|
for (const refCol of refColumns) {
|
|
3276
4028
|
if (refCol.idField) continue;
|
|
@@ -3331,37 +4083,8 @@ var applyRelationFormatDefault = /* @__PURE__ */ __name((cols) => cols?.map((col
|
|
|
3331
4083
|
}
|
|
3332
4084
|
return col;
|
|
3333
4085
|
}), "applyRelationFormatDefault");
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
const fkNames = /* @__PURE__ */ new Set([
|
|
3337
|
-
`${base}_id`,
|
|
3338
|
-
`${col.id}_id`
|
|
3339
|
-
]);
|
|
3340
|
-
return cols.some((c) => fkNames.has(c.id)) ? "manyToOne" : "oneToMany";
|
|
3341
|
-
}, "deriveRelationTypeFromColumns");
|
|
3342
|
-
var enrichNestedRelationColumns = /* @__PURE__ */ __name((cols, dir, baseUrl) => {
|
|
3343
|
-
if (!cols || !dir) return cols;
|
|
3344
|
-
const base = baseUrl ?? "";
|
|
3345
|
-
return cols.map((col) => {
|
|
3346
|
-
if (col.fieldInput?.format !== "relation" || !col.fieldInput.resource) return col;
|
|
3347
|
-
const resolved = resolveChildResource(col.fieldInput.resource, dir);
|
|
3348
|
-
const targetRoute = resolved?.json?.route ?? col.fieldInput.resource.replace(/^\.\//, "").replace(/\/resource\.json$/, "").replace(/\.resource$/, "");
|
|
3349
|
-
const relationType = col.fieldInput.relationType ?? deriveRelationTypeFromColumns(col, cols);
|
|
3350
|
-
return {
|
|
3351
|
-
...col,
|
|
3352
|
-
fieldInput: {
|
|
3353
|
-
...col.fieldInput,
|
|
3354
|
-
relationType,
|
|
3355
|
-
options: {
|
|
3356
|
-
...col.fieldInput.options,
|
|
3357
|
-
uri: `${base}/${targetRoute}`,
|
|
3358
|
-
resourceUri: `${base}/${targetRoute}`,
|
|
3359
|
-
resource: `${base}/${targetRoute}/schemas`
|
|
3360
|
-
}
|
|
3361
|
-
}
|
|
3362
|
-
};
|
|
3363
|
-
});
|
|
3364
|
-
}, "enrichNestedRelationColumns");
|
|
4086
|
+
|
|
4087
|
+
// src/lib/crud/adapter/sub-resource.builder.ts
|
|
3365
4088
|
var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentModel, parentDir, enums = {}, baseUrl) => {
|
|
3366
4089
|
if (!columns || !parentDir) return [];
|
|
3367
4090
|
return columns.filter((c) => c.fieldInput?.format === "relation" && c.fieldInput?.resource).map((c) => {
|
|
@@ -3369,7 +4092,7 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
|
|
|
3369
4092
|
const childJson = childResolved?.json;
|
|
3370
4093
|
const childDir = childResolved?.dir;
|
|
3371
4094
|
const childRoute = childJson?.route ?? c.fieldInput.resource.replace(/^\.\//, "").replace(/\.resource$/, "");
|
|
3372
|
-
const rawChildColumns = childJson
|
|
4095
|
+
const rawChildColumns = childJson?.columns;
|
|
3373
4096
|
const expandedChildColumns = rawChildColumns ? expandExtendColumns(rawChildColumns, childDir) : void 0;
|
|
3374
4097
|
const childColumns = applyRelationFormatDefault(expandedChildColumns) ?? expandedChildColumns;
|
|
3375
4098
|
injectEnumValues(childColumns, enums);
|
|
@@ -3431,71 +4154,10 @@ var buildSubResources = /* @__PURE__ */ __name((columns, parentRoute, parentMode
|
|
|
3431
4154
|
};
|
|
3432
4155
|
});
|
|
3433
4156
|
}, "buildSubResources");
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
const base = baseUrl ?? "";
|
|
3437
|
-
return columns.map((col) => {
|
|
3438
|
-
if (col.fieldInput?.format !== "relation") return col;
|
|
3439
|
-
const sub = subResources.find((s) => s.column === col.id);
|
|
3440
|
-
if (!sub) return col;
|
|
3441
|
-
return {
|
|
3442
|
-
...col,
|
|
3443
|
-
fieldInput: {
|
|
3444
|
-
...col.fieldInput,
|
|
3445
|
-
options: {
|
|
3446
|
-
...col.fieldInput.options,
|
|
3447
|
-
uri: `${base}/${parentRoute}/{id}/${sub.childRoute}`,
|
|
3448
|
-
resourceUri: `${base}/${sub.childRoute}`,
|
|
3449
|
-
...sub.views && {
|
|
3450
|
-
resource: `${base}/${parentRoute}/${sub.childRoute}/schemas`
|
|
3451
|
-
}
|
|
3452
|
-
}
|
|
3453
|
-
}
|
|
3454
|
-
};
|
|
3455
|
-
});
|
|
3456
|
-
}, "enrichActionColumns");
|
|
3457
|
-
var enrichResourceRefColumns = /* @__PURE__ */ __name((columns, parentDir, baseUrl) => {
|
|
3458
|
-
if (!columns || !parentDir) return columns;
|
|
3459
|
-
const base = baseUrl ?? "";
|
|
3460
|
-
return columns.map((col) => {
|
|
3461
|
-
if (!col.fieldInput?.resource || col.fieldInput.format === "relation") return col;
|
|
3462
|
-
const childResolved = resolveChildResource(col.fieldInput.resource, parentDir);
|
|
3463
|
-
const childRoute = childResolved?.json?.route ?? col.fieldInput.resource.replace(/^\.\//, "").replace(/\.resource$/, "");
|
|
3464
|
-
return {
|
|
3465
|
-
...col,
|
|
3466
|
-
fieldInput: {
|
|
3467
|
-
...col.fieldInput,
|
|
3468
|
-
options: {
|
|
3469
|
-
...col.fieldInput.options,
|
|
3470
|
-
resourceUri: `${base}/${childRoute}`,
|
|
3471
|
-
schemasUri: `${base}/${childRoute}/schemas`
|
|
3472
|
-
}
|
|
3473
|
-
}
|
|
3474
|
-
};
|
|
3475
|
-
});
|
|
3476
|
-
}, "enrichResourceRefColumns");
|
|
3477
|
-
var enrichIncludeWithSort = /* @__PURE__ */ __name((include, columns) => {
|
|
3478
|
-
if (!include?.length) return include;
|
|
3479
|
-
return include.map((entry) => {
|
|
3480
|
-
const relationName = typeof entry === "string" ? entry : entry.relation;
|
|
3481
|
-
const col = columns.find((c) => {
|
|
3482
|
-
const opts2 = c.fieldInput?.options;
|
|
3483
|
-
return (c.column ?? c.id) === relationName && opts2?.sort;
|
|
3484
|
-
});
|
|
3485
|
-
if (!col) return entry;
|
|
3486
|
-
const opts = col.fieldInput.options;
|
|
3487
|
-
const orderBy = buildChildSortClause(opts.sort, opts.sortDir ?? "asc");
|
|
3488
|
-
return typeof entry === "string" ? {
|
|
3489
|
-
relation: entry,
|
|
3490
|
-
orderBy
|
|
3491
|
-
} : {
|
|
3492
|
-
...entry,
|
|
3493
|
-
orderBy
|
|
3494
|
-
};
|
|
3495
|
-
});
|
|
3496
|
-
}, "enrichIncludeWithSort");
|
|
4157
|
+
|
|
4158
|
+
// src/lib/crud/adapter/json-adapter.ts
|
|
3497
4159
|
var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, actions, tableActions, enums = {}) => {
|
|
3498
|
-
const rawColumns = expandExtendColumns(
|
|
4160
|
+
const rawColumns = expandExtendColumns(json.columns, dirPath);
|
|
3499
4161
|
const columns = enrichRelationTypes(applyRelationFormatDefault(rawColumns) ?? rawColumns, schema);
|
|
3500
4162
|
injectEnumValues(columns, enums);
|
|
3501
4163
|
const subResources = buildSubResources(columns, json.route, json.model, dirPath, enums, baseUrl);
|
|
@@ -3539,22 +4201,14 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
|
|
|
3539
4201
|
delete: true
|
|
3540
4202
|
}
|
|
3541
4203
|
};
|
|
3542
|
-
const display = {
|
|
3543
|
-
mode: json.display?.mode === "page" ? "page" : "modal",
|
|
3544
|
-
customComponent: json.display?.customComponent ?? null
|
|
3545
|
-
};
|
|
3546
4204
|
return {
|
|
4205
|
+
...json,
|
|
3547
4206
|
name: json.name,
|
|
3548
4207
|
route: json.route,
|
|
3549
4208
|
model: json.model,
|
|
3550
4209
|
tag: json.tag,
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
title: json.title
|
|
3554
|
-
},
|
|
3555
|
-
...json.sidebar && {
|
|
3556
|
-
sidebar: json.sidebar
|
|
3557
|
-
},
|
|
4210
|
+
sidebar: json.sidebar,
|
|
4211
|
+
display: json.display,
|
|
3558
4212
|
...json.idType && {
|
|
3559
4213
|
idType: json.idType
|
|
3560
4214
|
},
|
|
@@ -3597,38 +4251,6 @@ var fromJson = /* @__PURE__ */ __name((json, schema, hooks, dirPath, baseUrl, ac
|
|
|
3597
4251
|
}
|
|
3598
4252
|
};
|
|
3599
4253
|
}, "fromJson");
|
|
3600
|
-
var unwrapZodType = /* @__PURE__ */ __name((type) => {
|
|
3601
|
-
if (type instanceof import_zod9.ZodOptional || type instanceof import_zod9.ZodNullable) {
|
|
3602
|
-
return unwrapZodType(type.unwrap());
|
|
3603
|
-
}
|
|
3604
|
-
return type;
|
|
3605
|
-
}, "unwrapZodType");
|
|
3606
|
-
var deriveRelationType = /* @__PURE__ */ __name((schema, columnId) => {
|
|
3607
|
-
if (!schema) return void 0;
|
|
3608
|
-
const field = schema.shape[columnId];
|
|
3609
|
-
if (!field) return void 0;
|
|
3610
|
-
const inner = unwrapZodType(field);
|
|
3611
|
-
return inner instanceof import_zod9.ZodArray ? "oneToMany" : "manyToOne";
|
|
3612
|
-
}, "deriveRelationType");
|
|
3613
|
-
var enrichRelationTypes = /* @__PURE__ */ __name((columns, schema) => {
|
|
3614
|
-
if (!schema) return columns;
|
|
3615
|
-
return columns.map((col) => {
|
|
3616
|
-
const fi = col.fieldInput;
|
|
3617
|
-
if (!fi) return col;
|
|
3618
|
-
const isRelationField = fi.type === "autocomplete" || fi.format === "relation";
|
|
3619
|
-
if (!isRelationField) return col;
|
|
3620
|
-
if (fi.relationType) return col;
|
|
3621
|
-
const derived = deriveRelationType(schema, col.id) ?? deriveRelationTypeFromColumns(col, columns);
|
|
3622
|
-
if (!derived) return col;
|
|
3623
|
-
return {
|
|
3624
|
-
...col,
|
|
3625
|
-
fieldInput: {
|
|
3626
|
-
...fi,
|
|
3627
|
-
relationType: derived
|
|
3628
|
-
}
|
|
3629
|
-
};
|
|
3630
|
-
});
|
|
3631
|
-
}, "enrichRelationTypes");
|
|
3632
4254
|
var buildLookup = /* @__PURE__ */ __name((columns) => {
|
|
3633
4255
|
if (!columns) return void 0;
|
|
3634
4256
|
const keyCol = columns.find((c) => c.idField);
|
|
@@ -3642,41 +4264,66 @@ var buildLookup = /* @__PURE__ */ __name((columns) => {
|
|
|
3642
4264
|
};
|
|
3643
4265
|
}, "buildLookup");
|
|
3644
4266
|
|
|
3645
|
-
// src/lib/crud/
|
|
3646
|
-
var
|
|
3647
|
-
var
|
|
4267
|
+
// src/lib/crud/hooks/hooks.types.ts
|
|
4268
|
+
var import_zod23 = require("zod");
|
|
4269
|
+
var WriteOpSchema = import_zod23.z.enum([
|
|
4270
|
+
"create",
|
|
4271
|
+
"update",
|
|
4272
|
+
"upsert",
|
|
4273
|
+
"delete"
|
|
4274
|
+
]);
|
|
4275
|
+
var ReadOpSchema = import_zod23.z.enum([
|
|
4276
|
+
"findAll",
|
|
4277
|
+
"findOne"
|
|
4278
|
+
]);
|
|
4279
|
+
var ResourceHooksSchema = import_zod23.z.object({
|
|
4280
|
+
beforeWrite: import_zod23.z.custom().optional(),
|
|
4281
|
+
afterWrite: import_zod23.z.custom().optional(),
|
|
4282
|
+
afterRead: import_zod23.z.custom().optional()
|
|
4283
|
+
});
|
|
4284
|
+
|
|
4285
|
+
// src/lib/crud/hooks/hooks.loader.ts
|
|
4286
|
+
var import_node_path8 = require("path");
|
|
4287
|
+
var loadResourceHooks = /* @__PURE__ */ __name(async (basePath) => {
|
|
4288
|
+
const file = findModule(basePath, "hooks");
|
|
4289
|
+
return file ? importDefault(file) : void 0;
|
|
4290
|
+
}, "loadResourceHooks");
|
|
3648
4291
|
var loadSubResourceHooks = /* @__PURE__ */ __name(async (subResources, basePath) => {
|
|
3649
4292
|
for (const sub of subResources) {
|
|
3650
|
-
const file = sub.name ?
|
|
4293
|
+
const file = sub.name ? findModule((0, import_node_path8.join)(basePath, "hooks"), sub.name) : void 0;
|
|
3651
4294
|
if (!file) continue;
|
|
3652
4295
|
const hooks = await importDefault(file);
|
|
3653
4296
|
if (hooks) sub.hooks = hooks;
|
|
3654
4297
|
}
|
|
3655
4298
|
}, "loadSubResourceHooks");
|
|
4299
|
+
|
|
4300
|
+
// src/lib/crud/loader/index.ts
|
|
4301
|
+
var import_node_fs6 = require("fs");
|
|
4302
|
+
var import_node_path9 = require("path");
|
|
3656
4303
|
var loadResourceConfigsFromDir = /* @__PURE__ */ __name(async (dirPath, baseUrl, enumsFile) => {
|
|
4304
|
+
if (!(0, import_node_fs6.existsSync)(dirPath)) return [];
|
|
3657
4305
|
const enums = loadEnumRegistry(dirPath, enumsFile);
|
|
3658
|
-
const entries = (0,
|
|
4306
|
+
const entries = (0, import_node_fs6.readdirSync)(dirPath, {
|
|
3659
4307
|
withFileTypes: true
|
|
3660
4308
|
});
|
|
3661
4309
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
3662
4310
|
const configs = [];
|
|
3663
4311
|
for (const dir of dirs) {
|
|
3664
|
-
const basePath = (0,
|
|
3665
|
-
const schemaFile =
|
|
4312
|
+
const basePath = (0, import_node_path9.join)(dirPath, dir);
|
|
4313
|
+
const schemaFile = findModule(basePath, "schema");
|
|
3666
4314
|
const schema = schemaFile ? await importDefault(schemaFile) : void 0;
|
|
3667
|
-
const
|
|
3668
|
-
const
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
const
|
|
3672
|
-
const
|
|
3673
|
-
const tableActions = await loadTableActions(json.tableActions ?? [], basePath);
|
|
4315
|
+
const hooks = await loadResourceHooks(basePath);
|
|
4316
|
+
const jsonFile = (0, import_node_path9.join)(basePath, "resource.json");
|
|
4317
|
+
if ((0, import_node_fs6.existsSync)(jsonFile)) {
|
|
4318
|
+
const json = readResourceJson(jsonFile).json;
|
|
4319
|
+
const actions = await loadActions(json.actions ?? [], basePath, "row");
|
|
4320
|
+
const tableActions = await loadActions(json.tableActions ?? [], basePath, "table");
|
|
3674
4321
|
const config = fromJson(json, schema, hooks, basePath, baseUrl, actions, tableActions, enums);
|
|
3675
4322
|
await loadSubResourceHooks(config.subResources ?? [], basePath);
|
|
3676
4323
|
configs.push(config);
|
|
3677
4324
|
continue;
|
|
3678
4325
|
}
|
|
3679
|
-
const tsFile =
|
|
4326
|
+
const tsFile = findModule(basePath, "resource");
|
|
3680
4327
|
if (tsFile) {
|
|
3681
4328
|
const config = await importDefault(tsFile);
|
|
3682
4329
|
if (config) configs.push(hooks ? {
|
|
@@ -3727,11 +4374,18 @@ var CroutonApiModule = class _CroutonApiModule {
|
|
|
3727
4374
|
static {
|
|
3728
4375
|
__name(this, "CroutonApiModule");
|
|
3729
4376
|
}
|
|
3730
|
-
|
|
4377
|
+
onModuleInit() {
|
|
4378
|
+
if (!BigInt.prototype.toJSON) {
|
|
4379
|
+
BigInt.prototype.toJSON = function() {
|
|
4380
|
+
return Number(this);
|
|
4381
|
+
};
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
static forResources(configs, dataSources, loader, { baseUrl }, config) {
|
|
3731
4385
|
const dataSourceRegistry = new DataSourceRegistry(dataSources);
|
|
3732
4386
|
const configRegistry = new ResourceConfigRegistry(loader, configs);
|
|
3733
4387
|
const controllers = [
|
|
3734
|
-
...configs.map((c) => createCrudController(c,
|
|
4388
|
+
...configs.map((c) => createCrudController(c, baseUrl)),
|
|
3735
4389
|
createAppLayoutController(configs, config.sidebarGroups, config.title, config.autoSave ?? true)
|
|
3736
4390
|
];
|
|
3737
4391
|
return {
|
|
@@ -3753,18 +4407,22 @@ var CroutonApiModule = class _CroutonApiModule {
|
|
|
3753
4407
|
]
|
|
3754
4408
|
};
|
|
3755
4409
|
}
|
|
3756
|
-
static async forResourceDir(dirPath, dataSourcesPath,
|
|
3757
|
-
const
|
|
3758
|
-
const
|
|
4410
|
+
static async forResourceDir(dirPath, dataSourcesPath, { baseUrl }) {
|
|
4411
|
+
const config = await loadConfig();
|
|
4412
|
+
const loader = new FileSystemResourceConfigLoader(dirPath, baseUrl, config.enumsFile);
|
|
4413
|
+
const configs = await loadResourceConfigsFromDir(dirPath, baseUrl, config.enumsFile);
|
|
3759
4414
|
const dataSources = await loadDataSourcesFromDir(dataSourcesPath);
|
|
3760
|
-
return _CroutonApiModule.forResources(configs, dataSources, loader,
|
|
4415
|
+
return _CroutonApiModule.forResources(configs, dataSources, loader, {
|
|
4416
|
+
baseUrl
|
|
4417
|
+
}, config);
|
|
3761
4418
|
}
|
|
3762
|
-
static forLoader(loader, configs, dataSources,
|
|
3763
|
-
|
|
4419
|
+
static async forLoader(loader, configs, dataSources, appConfig) {
|
|
4420
|
+
const config = await loadConfig();
|
|
4421
|
+
return _CroutonApiModule.forResources(configs, dataSources, loader, appConfig, config);
|
|
3764
4422
|
}
|
|
3765
4423
|
};
|
|
3766
4424
|
CroutonApiModule = _ts_decorate5([
|
|
3767
|
-
(0,
|
|
4425
|
+
(0, import_common12.Module)({
|
|
3768
4426
|
controllers: [],
|
|
3769
4427
|
providers: [],
|
|
3770
4428
|
exports: []
|
|
@@ -3778,6 +4436,8 @@ CroutonApiModule = _ts_decorate5([
|
|
|
3778
4436
|
ResourceConfigLoader,
|
|
3779
4437
|
ResourceConfigRegistry,
|
|
3780
4438
|
isOperationEnabled,
|
|
4439
|
+
isRowProcedureAction,
|
|
4440
|
+
isTableProcedureAction,
|
|
3781
4441
|
loadDataSourcesFromDir,
|
|
3782
4442
|
loadResourceConfigsFromDir,
|
|
3783
4443
|
resolveDefinition,
|