@scpxl/nodejs-framework 1.0.25 → 1.0.27
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/database/dynamic-entity.d.ts +17 -3
- package/dist/database/dynamic-entity.d.ts.map +1 -1
- package/dist/database/dynamic-entity.js +33 -14
- package/dist/database/dynamic-entity.js.map +2 -2
- package/dist/schemas/entity-builder.d.ts +35 -0
- package/dist/schemas/entity-builder.d.ts.map +1 -0
- package/dist/schemas/entity-builder.js +39 -0
- package/dist/schemas/entity-builder.js.map +7 -0
- package/dist/webserver/controller/entity.js +2 -2
- package/dist/webserver/controller/entity.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import { BaseEntity } from '@mikro-orm/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
export declare abstract class DynamicEntity extends BaseEntity {
|
|
4
|
-
|
|
5
|
-
static
|
|
4
|
+
/** Required fields for creating a new entity */
|
|
5
|
+
static createSchema: z.ZodSchema;
|
|
6
|
+
/** Allowed (partial) fields for updating an entity */
|
|
7
|
+
static updateSchema: z.ZodSchema;
|
|
8
|
+
/** Optional projection/read schema (includes persistence augment) */
|
|
9
|
+
static readSchema?: z.ZodSchema;
|
|
6
10
|
static get singularName(): string;
|
|
7
11
|
static get pluralName(): string;
|
|
8
12
|
static get singularNameLowerCase(): string;
|
|
9
13
|
static get pluralNameLowerCase(): string;
|
|
10
14
|
static get singularNameCapitalized(): string;
|
|
11
15
|
static get pluralNameCapitalized(): string;
|
|
12
|
-
static
|
|
16
|
+
static validateCreate<T>(item: unknown): {
|
|
17
|
+
error?: Error;
|
|
18
|
+
value?: T;
|
|
19
|
+
};
|
|
20
|
+
static validateUpdate<T>(item: unknown): {
|
|
13
21
|
error?: Error;
|
|
14
22
|
value?: T;
|
|
15
23
|
};
|
|
16
24
|
static getSearchFields(): string[];
|
|
25
|
+
static defineSchemas<Shape extends z.ZodRawShape, Updatable extends readonly (keyof Shape)[]>(options: {
|
|
26
|
+
shape: Shape;
|
|
27
|
+
updatableFields?: Updatable;
|
|
28
|
+
requireAtLeastOneOnUpdate?: boolean;
|
|
29
|
+
readAugment?: z.ZodRawShape;
|
|
30
|
+
}): void;
|
|
17
31
|
}
|
|
18
32
|
//# sourceMappingURL=dynamic-entity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-entity.d.ts","sourceRoot":"","sources":["../../src/database/dynamic-entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"dynamic-entity.d.ts","sourceRoot":"","sources":["../../src/database/dynamic-entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,8BAAsB,aAAc,SAAQ,UAAU;IACpD,gDAAgD;IAChD,OAAc,YAAY,EAAE,CAAC,CAAC,SAAS,CAAC;IACxC,sDAAsD;IACtD,OAAc,YAAY,EAAE,CAAC,CAAC,SAAS,CAAC;IACxC,qEAAqE;IACrE,OAAc,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IAEvC,WAAkB,YAAY,IAAI,MAAM,CAEvC;IAED,WAAkB,UAAU,IAAI,MAAM,CAErC;IAED,WAAkB,qBAAqB,IAAI,MAAM,CAEhD;IAED,WAAkB,mBAAmB,IAAI,MAAM,CAE9C;IAED,WAAkB,uBAAuB,IAAI,MAAM,CAElD;IAED,WAAkB,qBAAqB,IAAI,MAAM,CAEhD;WAEa,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,CAAA;KAAE;WAW9D,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAC,KAAK,CAAC,EAAE,CAAC,CAAA;KAAE;WAW9D,eAAe,IAAI,MAAM,EAAE;WAI3B,aAAa,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,EAAE,SAAS,SAAS,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE;QAC5G,KAAK,EAAE,KAAK,CAAC;QACb,eAAe,CAAC,EAAE,SAAS,CAAC;QAC5B,yBAAyB,CAAC,EAAE,OAAO,CAAC;QACpC,WAAW,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC;KAC7B,GAAG,IAAI;CAcT"}
|
|
@@ -2,12 +2,17 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
import { BaseEntity } from "@mikro-orm/core";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { buildEntitySchemas } from "../schemas/entity-builder.js";
|
|
5
6
|
class DynamicEntity extends BaseEntity {
|
|
6
7
|
static {
|
|
7
8
|
__name(this, "DynamicEntity");
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
static
|
|
10
|
+
/** Required fields for creating a new entity */
|
|
11
|
+
static createSchema;
|
|
12
|
+
/** Allowed (partial) fields for updating an entity */
|
|
13
|
+
static updateSchema;
|
|
14
|
+
/** Optional projection/read schema (includes persistence augment) */
|
|
15
|
+
static readSchema;
|
|
11
16
|
static get singularName() {
|
|
12
17
|
return "Item";
|
|
13
18
|
}
|
|
@@ -26,22 +31,22 @@ class DynamicEntity extends BaseEntity {
|
|
|
26
31
|
static get pluralNameCapitalized() {
|
|
27
32
|
return this.pluralName.charAt(0).toUpperCase() + this.pluralName.slice(1).toLowerCase();
|
|
28
33
|
}
|
|
29
|
-
static
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
static validateCreate(item) {
|
|
35
|
+
try {
|
|
36
|
+
return { value: this.createSchema.parse(item) };
|
|
37
|
+
} catch (err) {
|
|
38
|
+
if (err instanceof z.ZodError) {
|
|
39
|
+
return { error: new Error(err.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")) };
|
|
40
|
+
}
|
|
41
|
+
return { error: err };
|
|
37
42
|
}
|
|
43
|
+
}
|
|
44
|
+
static validateUpdate(item) {
|
|
38
45
|
try {
|
|
39
|
-
|
|
40
|
-
return { value };
|
|
46
|
+
return { value: this.updateSchema.parse(item) };
|
|
41
47
|
} catch (err) {
|
|
42
48
|
if (err instanceof z.ZodError) {
|
|
43
|
-
|
|
44
|
-
return { error };
|
|
49
|
+
return { error: new Error(err.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ")) };
|
|
45
50
|
}
|
|
46
51
|
return { error: err };
|
|
47
52
|
}
|
|
@@ -49,6 +54,20 @@ class DynamicEntity extends BaseEntity {
|
|
|
49
54
|
static getSearchFields() {
|
|
50
55
|
return [];
|
|
51
56
|
}
|
|
57
|
+
static defineSchemas(options) {
|
|
58
|
+
const { shape, updatableFields, requireAtLeastOneOnUpdate = true, readAugment } = options;
|
|
59
|
+
const schemas = buildEntitySchemas({
|
|
60
|
+
shape,
|
|
61
|
+
updatableFields,
|
|
62
|
+
requireAtLeastOneOnUpdate,
|
|
63
|
+
readAugment
|
|
64
|
+
});
|
|
65
|
+
this.createSchema = schemas.create;
|
|
66
|
+
this.updateSchema = schemas.update;
|
|
67
|
+
if (readAugment) {
|
|
68
|
+
this.readSchema = schemas.read;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
52
71
|
}
|
|
53
72
|
export {
|
|
54
73
|
DynamicEntity
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/database/dynamic-entity.ts"],
|
|
4
|
-
"sourcesContent": ["import { BaseEntity } from '@mikro-orm/core';\nimport { z } from 'zod';\n\nexport abstract class DynamicEntity extends BaseEntity {\n public static
|
|
5
|
-
"mappings": ";;AAAA,SAAS,kBAAkB;AAC3B,SAAS,SAAS;
|
|
4
|
+
"sourcesContent": ["import { BaseEntity } from '@mikro-orm/core';\nimport { z } from 'zod';\nimport { buildEntitySchemas } from '../schemas/entity-builder.js';\n\nexport abstract class DynamicEntity extends BaseEntity {\n /** Required fields for creating a new entity */\n public static createSchema: z.ZodSchema;\n /** Allowed (partial) fields for updating an entity */\n public static updateSchema: z.ZodSchema;\n /** Optional projection/read schema (includes persistence augment) */\n public static readSchema?: z.ZodSchema;\n\n public static get singularName(): string {\n return 'Item';\n }\n\n public static get pluralName(): string {\n return 'Items';\n }\n\n public static get singularNameLowerCase(): string {\n return this.singularName.toLowerCase();\n }\n\n public static get pluralNameLowerCase(): string {\n return this.pluralName.toLowerCase();\n }\n\n public static get singularNameCapitalized(): string {\n return this.singularName.charAt(0).toUpperCase() + this.singularName.slice(1).toLowerCase();\n }\n\n public static get pluralNameCapitalized(): string {\n return this.pluralName.charAt(0).toUpperCase() + this.pluralName.slice(1).toLowerCase();\n }\n\n public static validateCreate<T>(item: unknown): { error?: Error; value?: T } {\n try {\n return { value: this.createSchema.parse(item) as T };\n } catch (err) {\n if (err instanceof z.ZodError) {\n return { error: new Error(err.issues.map(i => `${i.path.join('.')}: ${i.message}`).join(', ')) };\n }\n return { error: err as Error };\n }\n }\n\n public static validateUpdate<T>(item: unknown): { error?: Error; value?: T } {\n try {\n return { value: this.updateSchema.parse(item) as T };\n } catch (err) {\n if (err instanceof z.ZodError) {\n return { error: new Error(err.issues.map(i => `${i.path.join('.')}: ${i.message}`).join(', ')) };\n }\n return { error: err as Error };\n }\n }\n\n public static getSearchFields(): string[] {\n return [];\n }\n\n public static defineSchemas<Shape extends z.ZodRawShape, Updatable extends readonly (keyof Shape)[]>(options: {\n shape: Shape;\n updatableFields?: Updatable;\n requireAtLeastOneOnUpdate?: boolean;\n readAugment?: z.ZodRawShape;\n }): void {\n const { shape, updatableFields, requireAtLeastOneOnUpdate = true, readAugment } = options;\n const schemas = buildEntitySchemas({\n shape,\n updatableFields: updatableFields as any,\n requireAtLeastOneOnUpdate,\n readAugment,\n });\n this.createSchema = schemas.create;\n this.updateSchema = schemas.update;\n if (readAugment) {\n this.readSchema = schemas.read;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,SAAS,kBAAkB;AAC3B,SAAS,SAAS;AAClB,SAAS,0BAA0B;AAE5B,MAAe,sBAAsB,WAAW;AAAA,EAJvD,OAIuD;AAAA;AAAA;AAAA;AAAA,EAErD,OAAc;AAAA;AAAA,EAEd,OAAc;AAAA;AAAA,EAEd,OAAc;AAAA,EAEd,WAAkB,eAAuB;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,WAAkB,aAAqB;AACrC,WAAO;AAAA,EACT;AAAA,EAEA,WAAkB,wBAAgC;AAChD,WAAO,KAAK,aAAa,YAAY;AAAA,EACvC;AAAA,EAEA,WAAkB,sBAA8B;AAC9C,WAAO,KAAK,WAAW,YAAY;AAAA,EACrC;AAAA,EAEA,WAAkB,0BAAkC;AAClD,WAAO,KAAK,aAAa,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,aAAa,MAAM,CAAC,EAAE,YAAY;AAAA,EAC5F;AAAA,EAEA,WAAkB,wBAAgC;AAChD,WAAO,KAAK,WAAW,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,WAAW,MAAM,CAAC,EAAE,YAAY;AAAA,EACxF;AAAA,EAEA,OAAc,eAAkB,MAA6C;AAC3E,QAAI;AACF,aAAO,EAAE,OAAO,KAAK,aAAa,MAAM,IAAI,EAAO;AAAA,IACrD,SAAS,KAAK;AACZ,UAAI,eAAe,EAAE,UAAU;AAC7B,eAAO,EAAE,OAAO,IAAI,MAAM,IAAI,OAAO,IAAI,OAAK,GAAG,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,MACjG;AACA,aAAO,EAAE,OAAO,IAAa;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,OAAc,eAAkB,MAA6C;AAC3E,QAAI;AACF,aAAO,EAAE,OAAO,KAAK,aAAa,MAAM,IAAI,EAAO;AAAA,IACrD,SAAS,KAAK;AACZ,UAAI,eAAe,EAAE,UAAU;AAC7B,eAAO,EAAE,OAAO,IAAI,MAAM,IAAI,OAAO,IAAI,OAAK,GAAG,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,MACjG;AACA,aAAO,EAAE,OAAO,IAAa;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,OAAc,kBAA4B;AACxC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,OAAc,cAAuF,SAK5F;AACP,UAAM,EAAE,OAAO,iBAAiB,4BAA4B,MAAM,YAAY,IAAI;AAClF,UAAM,UAAU,mBAAmB;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,SAAK,eAAe,QAAQ;AAC5B,SAAK,eAAe,QAAQ;AAC5B,QAAI,aAAa;AACf,WAAK,aAAa,QAAQ;AAAA,IAC5B;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Options for building standardized entity schemas.
|
|
4
|
+
* - shape: base create shape (required fields for creation)
|
|
5
|
+
* - updatableFields: subset of keys allowed in updates (defaults to all keys of shape)
|
|
6
|
+
* - readAugment: additional fields present on the persisted/read model (e.g. id, timestamps)
|
|
7
|
+
* - strict: apply .strict() to objects (defaults true)
|
|
8
|
+
*/
|
|
9
|
+
export interface BuildEntitySchemasOptions<Shape extends z.ZodRawShape, Updatable extends keyof Shape = keyof Shape, ReadAugment extends z.ZodRawShape = {}> {
|
|
10
|
+
shape: Shape;
|
|
11
|
+
updatableFields?: readonly Updatable[];
|
|
12
|
+
readAugment?: ReadAugment;
|
|
13
|
+
strict?: boolean;
|
|
14
|
+
requireAtLeastOneOnUpdate?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface BuiltEntitySchemas<Shape extends z.ZodRawShape, Updatable extends keyof Shape, ReadAugment extends z.ZodRawShape> {
|
|
17
|
+
create: z.ZodObject<Shape>;
|
|
18
|
+
/** Update schema is a partial over selected updatable keys. */
|
|
19
|
+
update: z.ZodTypeAny;
|
|
20
|
+
read: z.ZodObject<Shape & ReadAugment>;
|
|
21
|
+
keys: (keyof Shape)[];
|
|
22
|
+
updatableKeys: Updatable[];
|
|
23
|
+
}
|
|
24
|
+
export declare function buildEntitySchemas<Shape extends z.ZodRawShape, Updatable extends keyof Shape = keyof Shape, ReadAugment extends z.ZodRawShape = {}>(options: BuildEntitySchemasOptions<Shape, Updatable, ReadAugment>): BuiltEntitySchemas<Shape, Updatable, ReadAugment>;
|
|
25
|
+
export declare const HttpMethodSchema: z.ZodEnum<{
|
|
26
|
+
GET: "GET";
|
|
27
|
+
POST: "POST";
|
|
28
|
+
PUT: "PUT";
|
|
29
|
+
DELETE: "DELETE";
|
|
30
|
+
HEAD: "HEAD";
|
|
31
|
+
PATCH: "PATCH";
|
|
32
|
+
OPTIONS: "OPTIONS";
|
|
33
|
+
}>;
|
|
34
|
+
export declare const HttpStatusCodeSchema: z.core.$ZodBranded<z.ZodNumber, "HttpStatusCode">;
|
|
35
|
+
//# sourceMappingURL=entity-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-builder.d.ts","sourceRoot":"","sources":["../../src/schemas/entity-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB,CACxC,KAAK,SAAS,CAAC,CAAC,WAAW,EAC3B,SAAS,SAAS,MAAM,KAAK,GAAG,MAAM,KAAK,EAC3C,WAAW,SAAS,CAAC,CAAC,WAAW,GAAG,EAAE;IAEtC,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB,CACjC,KAAK,SAAS,CAAC,CAAC,WAAW,EAC3B,SAAS,SAAS,MAAM,KAAK,EAC7B,WAAW,SAAS,CAAC,CAAC,WAAW;IAEjC,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3B,+DAA+D;IAC/D,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;IACvC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACtB,aAAa,EAAE,SAAS,EAAE,CAAC;CAC5B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,SAAS,CAAC,CAAC,WAAW,EAC3B,SAAS,SAAS,MAAM,KAAK,GAAG,MAAM,KAAK,EAC3C,WAAW,SAAS,CAAC,CAAC,WAAW,GAAG,EAAE,EAEtC,OAAO,EAAE,yBAAyB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,GAChE,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,CAoCnD;AAGD,eAAO,MAAM,gBAAgB;;;;;;;;EAAuE,CAAC;AACrG,eAAO,MAAM,oBAAoB,mDAA+D,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
function buildEntitySchemas(options) {
|
|
5
|
+
const { shape, updatableFields, readAugment, strict = true, requireAtLeastOneOnUpdate = true } = options;
|
|
6
|
+
Object.freeze(shape);
|
|
7
|
+
const create = strict ? z.object(shape).strict() : z.object(shape);
|
|
8
|
+
const updatableKeys = updatableFields ? [...updatableFields] : Object.keys(shape);
|
|
9
|
+
const pickMask = updatableKeys.reduce((acc, key) => {
|
|
10
|
+
acc[String(key)] = true;
|
|
11
|
+
return acc;
|
|
12
|
+
}, {});
|
|
13
|
+
const updateBase = updatableFields ? create.pick(pickMask) : create;
|
|
14
|
+
let updateObject = updateBase.partial();
|
|
15
|
+
if (requireAtLeastOneOnUpdate) {
|
|
16
|
+
updateObject = updateObject.refine(
|
|
17
|
+
(value) => !!value && typeof value === "object" && Object.keys(value).length > 0,
|
|
18
|
+
"At least one field must be provided for update"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const readShape = { ...shape, ...readAugment ?? {} };
|
|
22
|
+
const read = strict ? z.object(readShape).strict() : z.object(readShape);
|
|
23
|
+
return {
|
|
24
|
+
create,
|
|
25
|
+
update: updateObject,
|
|
26
|
+
read,
|
|
27
|
+
keys: Object.keys(shape),
|
|
28
|
+
updatableKeys
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
__name(buildEntitySchemas, "buildEntitySchemas");
|
|
32
|
+
const HttpMethodSchema = z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]);
|
|
33
|
+
const HttpStatusCodeSchema = z.number().int().min(100).max(599).brand();
|
|
34
|
+
export {
|
|
35
|
+
HttpMethodSchema,
|
|
36
|
+
HttpStatusCodeSchema,
|
|
37
|
+
buildEntitySchemas
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=entity-builder.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/schemas/entity-builder.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod';\n\n/**\n * Options for building standardized entity schemas.\n * - shape: base create shape (required fields for creation)\n * - updatableFields: subset of keys allowed in updates (defaults to all keys of shape)\n * - readAugment: additional fields present on the persisted/read model (e.g. id, timestamps)\n * - strict: apply .strict() to objects (defaults true)\n */\nexport interface BuildEntitySchemasOptions<\n Shape extends z.ZodRawShape,\n Updatable extends keyof Shape = keyof Shape,\n ReadAugment extends z.ZodRawShape = {},\n> {\n shape: Shape;\n updatableFields?: readonly Updatable[];\n readAugment?: ReadAugment; // fields that exist after persistence (e.g. id, createdAt)\n strict?: boolean;\n requireAtLeastOneOnUpdate?: boolean; // default true\n}\n\nexport interface BuiltEntitySchemas<\n Shape extends z.ZodRawShape,\n Updatable extends keyof Shape,\n ReadAugment extends z.ZodRawShape,\n> {\n create: z.ZodObject<Shape>;\n /** Update schema is a partial over selected updatable keys. */\n update: z.ZodTypeAny;\n read: z.ZodObject<Shape & ReadAugment>;\n keys: (keyof Shape)[];\n updatableKeys: Updatable[];\n}\n\nexport function buildEntitySchemas<\n Shape extends z.ZodRawShape,\n Updatable extends keyof Shape = keyof Shape,\n ReadAugment extends z.ZodRawShape = {},\n>(\n options: BuildEntitySchemasOptions<Shape, Updatable, ReadAugment>,\n): BuiltEntitySchemas<Shape, Updatable, ReadAugment> {\n const { shape, updatableFields, readAugment, strict = true, requireAtLeastOneOnUpdate = true } = options;\n\n // Freeze to avoid accidental mutation\n Object.freeze(shape);\n\n const create = strict ? z.object(shape).strict() : z.object(shape);\n\n const updatableKeys = updatableFields ? [...updatableFields] : (Object.keys(shape) as Updatable[]);\n // Build pick mask for updatable keys\n const pickMask = updatableKeys.reduce<Record<string, true>>((acc, key) => {\n acc[String(key)] = true;\n return acc;\n }, {});\n\n const updateBase = updatableFields ? (create as z.ZodObject<any>).pick(pickMask) : (create as z.ZodObject<any>);\n let updateObject: z.ZodTypeAny = updateBase.partial();\n\n if (requireAtLeastOneOnUpdate) {\n updateObject = updateObject.refine(\n (value: unknown) =>\n !!value && typeof value === 'object' && Object.keys(value as Record<string, unknown>).length > 0,\n 'At least one field must be provided for update',\n );\n }\n\n const readShape = { ...shape, ...(readAugment ?? {}) } as Shape & ReadAugment;\n const read = strict ? z.object(readShape).strict() : z.object(readShape);\n\n return {\n create,\n update: updateObject,\n read: read as z.ZodObject<Shape & ReadAugment>,\n keys: Object.keys(shape) as (keyof Shape)[],\n updatableKeys,\n };\n}\n\n// Common atoms for reuse in entity schemas\nexport const HttpMethodSchema = z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']);\nexport const HttpStatusCodeSchema = z.number().int().min(100).max(599).brand<'HttpStatusCode'>();\n"],
|
|
5
|
+
"mappings": ";;AAAA,SAAS,SAAS;AAkCX,SAAS,mBAKd,SACmD;AACnD,QAAM,EAAE,OAAO,iBAAiB,aAAa,SAAS,MAAM,4BAA4B,KAAK,IAAI;AAGjG,SAAO,OAAO,KAAK;AAEnB,QAAM,SAAS,SAAS,EAAE,OAAO,KAAK,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAEjE,QAAM,gBAAgB,kBAAkB,CAAC,GAAG,eAAe,IAAK,OAAO,KAAK,KAAK;AAEjF,QAAM,WAAW,cAAc,OAA6B,CAAC,KAAK,QAAQ;AACxE,QAAI,OAAO,GAAG,CAAC,IAAI;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,aAAa,kBAAmB,OAA4B,KAAK,QAAQ,IAAK;AACpF,MAAI,eAA6B,WAAW,QAAQ;AAEpD,MAAI,2BAA2B;AAC7B,mBAAe,aAAa;AAAA,MAC1B,CAAC,UACC,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,OAAO,KAAK,KAAgC,EAAE,SAAS;AAAA,MACjG;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,EAAE,GAAG,OAAO,GAAI,eAAe,CAAC,EAAG;AACrD,QAAM,OAAO,SAAS,EAAE,OAAO,SAAS,EAAE,OAAO,IAAI,EAAE,OAAO,SAAS;AAEvE,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,MAAM,OAAO,KAAK,KAAK;AAAA,IACvB;AAAA,EACF;AACF;AA1CgB;AA6CT,MAAM,mBAAmB,EAAE,KAAK,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS,QAAQ,SAAS,CAAC;AAC5F,MAAM,uBAAuB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,IAAI,GAAG,EAAE,MAAwB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -288,7 +288,7 @@ class EntityController extends BaseController {
|
|
|
288
288
|
request = preCreateOneRequest;
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
|
-
const { error, value } = EntityClass.
|
|
291
|
+
const { error, value } = EntityClass.validateCreate(request.body);
|
|
292
292
|
if (error) {
|
|
293
293
|
return this.sendErrorResponse({ reply, error: error.message });
|
|
294
294
|
}
|
|
@@ -315,7 +315,7 @@ class EntityController extends BaseController {
|
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
317
|
const id = request.params.id;
|
|
318
|
-
const { error, value } = EntityClass.
|
|
318
|
+
const { error, value } = EntityClass.validateUpdate(request.body);
|
|
319
319
|
if (error) {
|
|
320
320
|
return this.sendErrorResponse({ reply, error: error.message });
|
|
321
321
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/webserver/controller/entity.ts"],
|
|
4
|
-
"sourcesContent": ["import 'reflect-metadata';\nimport path from 'path';\nimport type { EntityManager, FilterQuery, Populate } from '@mikro-orm/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { StatusCodes } from 'http-status-codes';\nimport BaseController from './base.js';\nimport type { DynamicEntity } from '../../database/dynamic-entity.js';\nimport { generateFormFields } from '../../database/dynamic-entity-form-decorators.js';\nimport { Helper } from '../../util/index.js';\nimport type { WebServerBaseControllerConstructorParams } from './base.interface.js';\n\nexport default abstract class EntityController extends BaseController {\n protected abstract entityName: string;\n\n protected entityManager: EntityManager;\n\n // Cache for entity modules to avoid repeated dynamic imports\n private static entityCache = new Map<string, typeof DynamicEntity>();\n\n constructor(props: WebServerBaseControllerConstructorParams) {\n super(props);\n\n const { databaseInstance } = props;\n\n this.entityManager = databaseInstance.getEntityManager();\n }\n\n protected getEntity = async (): Promise<typeof DynamicEntity | undefined> => {\n if (!this.applicationConfig.database || this.applicationConfig.database.enabled !== true) {\n throw new Error(`Database not enabled (Entity: ${this.entityName})`);\n }\n\n // Check cache first\n const cacheKey = `${this.applicationConfig.database.entitiesDirectory}:${this.entityName}`;\n if (EntityController.entityCache.has(cacheKey)) {\n return EntityController.entityCache.get(cacheKey);\n }\n\n // Define entity module path\n const entityModulePath = path.join(\n this.applicationConfig.database.entitiesDirectory,\n `${this.entityName}.${Helper.getScriptFileExtension()}`,\n );\n\n // Import entity module\n const entityModule = await import(entityModulePath);\n\n if (!entityModule?.[this.entityName]) {\n throw new Error(`Entity not found (Entity: ${this.entityName})`);\n }\n\n // Get entity class\n const EntityClass = entityModule[this.entityName];\n\n // Cache the entity for future use\n EntityController.entityCache.set(cacheKey, EntityClass);\n\n return EntityClass;\n };\n\n private getEntityProperties(entityClass: any): string[] {\n const properties: string[] = [];\n\n const reservedPropertyKeys = ['constructor', 'toJSON'];\n\n for (const propertyKey of Object.getOwnPropertyNames(entityClass.prototype)) {\n if (propertyKey.startsWith('__')) {\n continue;\n } else if (reservedPropertyKeys.includes(propertyKey)) {\n continue;\n }\n\n properties.push(propertyKey);\n }\n\n return properties;\n }\n\n public options = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const formFields = generateFormFields({ model: EntityClass });\n\n this.sendSuccessResponse({\n reply,\n data: {\n formFields,\n },\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n public metadata = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const formFields = generateFormFields({ model: EntityClass });\n\n this.sendSuccessResponse({\n reply,\n data: {\n formFields,\n },\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n // Pre-getMany hook (can be overridden in the child controller)\n protected async preGetMany(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n // Post-getMany hook (can be overridden in the child controller)\n // await this.postGetMany({ entityManager: this.entityManager, request, reply, data });\n protected async postGetMany(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n data: {\n items: any[];\n total: number;\n page: number;\n totalPages: number;\n limit: number;\n };\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n public getMany = async (\n request: FastifyRequest<{\n Querystring: {\n page: string;\n limit: string;\n filters: string;\n sort: string;\n 'sort-order': string;\n search: string;\n [key: string]: any;\n };\n }>,\n reply: FastifyReply,\n ) => {\n try {\n // Call preGetMany hook\n await this.preGetMany({\n entityManager: this.entityManager,\n request,\n reply,\n });\n\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n // Pagination parameters\n const page = parseInt(request.query.page) || 1;\n const limit = parseInt(request.query.limit);\n const offset = (page - 1) * (limit > 0 ? limit : 0);\n\n // Filtering and sorting\n const filters = request.query.filters ? JSON.parse(request.query.filters) : {};\n const sortOrder = request.query['sort-order'] || 'ASC';\n const orderBy = request.query.sort ? { [request.query.sort]: sortOrder } : { id: sortOrder };\n\n const normalizedQuery: { [key: string]: any } = {};\n\n for (const key in request.query) {\n // Skip prototype pollution attempts\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n continue;\n }\n\n // Only process own properties\n if (!Object.prototype.hasOwnProperty.call(request.query, key)) {\n continue;\n }\n\n if (key.endsWith('[]')) {\n const normalizedKey = key.slice(0, -2);\n\n // Safe property assignment\n if (normalizedKey !== '__proto__' && normalizedKey !== 'constructor' && normalizedKey !== 'prototype') {\n Reflect.set(normalizedQuery, normalizedKey, Reflect.get(request.query, key));\n }\n } else {\n Reflect.set(normalizedQuery, key, Reflect.get(request.query, key));\n }\n }\n\n // Build query options\n const options: {\n limit?: number;\n offset?: number;\n filters: FilterQuery<any>;\n orderBy: { [key: string]: string };\n } = {\n filters,\n offset,\n orderBy,\n };\n\n if (limit > 0) {\n options.limit = limit;\n }\n\n const entityProperties = this.getEntityProperties(EntityClass);\n const reservedQueryKeys = ['page', 'limit', 'filters', 'sort', 'populate', 'search'];\n const searchQuery = request.query.search || '';\n\n for (const key in normalizedQuery) {\n // Skip prototype pollution attempts\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n continue;\n }\n\n // Only process own properties\n if (!Object.prototype.hasOwnProperty.call(normalizedQuery, key)) {\n continue;\n }\n\n if (reservedQueryKeys.includes(key)) {\n continue;\n }\n\n if (!entityProperties.includes(key)) {\n const [relation, subProperty] = key.split('.');\n\n if (relation && subProperty) {\n // Validate relation and subProperty names\n if (\n relation === '__proto__' ||\n relation === 'constructor' ||\n relation === 'prototype' ||\n subProperty === '__proto__' ||\n subProperty === 'constructor' ||\n subProperty === 'prototype'\n ) {\n continue;\n }\n\n let queryValue = Reflect.get(normalizedQuery, key);\n\n if (!queryValue) continue;\n\n if (typeof queryValue === 'string' && queryValue.includes(',')) {\n queryValue = queryValue.split(',');\n }\n\n if (Array.isArray(queryValue)) {\n Reflect.set(options.filters, relation, {\n [subProperty]: { $in: queryValue },\n });\n } else {\n Reflect.set(options.filters, relation, {\n [subProperty]: queryValue,\n });\n }\n }\n\n continue;\n }\n\n let queryValue = Reflect.get(normalizedQuery, key);\n\n if (!queryValue) {\n continue;\n }\n\n if (typeof queryValue === 'string' && queryValue.includes(',')) {\n queryValue = queryValue.split(',');\n }\n\n if (Array.isArray(queryValue)) {\n Reflect.set(options.filters, key, { $in: queryValue });\n } else {\n Reflect.set(options.filters, key, queryValue);\n }\n }\n\n // Add search filter if a search query is provided\n if (searchQuery) {\n const searchFields = EntityClass.getSearchFields();\n\n options.filters.$or = searchFields\n .filter(field => {\n const isIntegerField = ['id', 'originId'].includes(field);\n\n return !isIntegerField;\n })\n .map(field => {\n return {\n [field]: { $like: `%${searchQuery}%` },\n };\n });\n }\n\n const populate = request.query.populate ? request.query.populate.split(',') : [];\n\n // Fetch items from the database\n const [items, total] = await this.entityManager.findAndCount(this.entityName, options.filters, {\n limit: options.limit,\n offset: options.offset,\n orderBy: options.orderBy,\n populate,\n });\n\n const totalPages = limit > 0 ? Math.ceil(total / limit) : 1;\n\n const data = {\n items,\n total,\n page,\n totalPages,\n limit: limit > 0 ? limit : total,\n };\n\n // Call postGetMany hook\n await this.postGetMany({\n entityManager: this.entityManager,\n request,\n reply,\n data,\n });\n\n reply.send({\n data: data.items,\n total_items: data.total,\n page: data.page,\n total_pages: data.totalPages,\n limit: data.limit,\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected async preGetOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n protected async postGetOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n public getOne = async (\n request: FastifyRequest<{\n Params: { id: number };\n Querystring: { populate: string };\n }>,\n reply: FastifyReply,\n ) => {\n try {\n await this.preGetOne({\n entityManager: this.entityManager,\n request,\n reply,\n });\n\n const queryPopulate = request.query.populate || null;\n const populateList: string[] = queryPopulate ? queryPopulate.split(',') : [];\n\n // Ensure populate is typed correctly for MikroORM\n const populate = populateList.map(field => `${field}.*`) as unknown as Populate<\n object,\n `${string}.*` | `${string}.$infer`\n >;\n\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n const id = request.params.id;\n\n const item = await this.entityManager.findOne(this.entityName, { id }, { populate });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n await this.postGetOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected preCreateOne = ({\n request,\n reply,\n }: {\n request: FastifyRequest;\n reply: FastifyReply;\n }): { request: FastifyRequest; reply: FastifyReply } => {\n return { request, reply };\n };\n\n protected async postCreateOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {}\n\n public createOne = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n // Listen for preCreateOne hook\n if (this.preCreateOne) {\n const { request: preCreateOneRequest } = await this.preCreateOne({\n request,\n reply,\n });\n if (preCreateOneRequest) {\n request = preCreateOneRequest;\n }\n }\n\n const { error, value } = EntityClass.validate(request.body, true);\n\n if (error) {\n return this.sendErrorResponse({ reply, error: error.message });\n }\n\n const item = this.entityManager.create(this.entityName, value as object);\n\n await this.entityManager.persistAndFlush(item);\n\n // Call postCreateOne hook\n await this.postCreateOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item, statusCode: StatusCodes.CREATED });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected async postUpdateOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {}\n\n public updateOne = async (request: FastifyRequest<{ Params: { id: number } }>, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n const id = request.params.id;\n\n const { error, value } = EntityClass.validate(request.body, false);\n\n if (error) {\n return this.sendErrorResponse({ reply, error: error.message });\n }\n\n const item = await this.entityManager.findOne(this.entityName, { id });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n this.entityManager.assign(item, value as object);\n\n await this.entityManager.persistAndFlush(item);\n\n // Call postUpdateOne hook\n await this.postUpdateOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n public deleteOne = async (request: FastifyRequest<{ Params: { id: number } }>, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const id = request.params.id;\n\n const item = await this.entityManager.findOne(this.entityName, { id });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n await this.entityManager.removeAndFlush(item);\n\n reply.status(StatusCodes.NO_CONTENT).send();\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import 'reflect-metadata';\nimport path from 'path';\nimport type { EntityManager, FilterQuery, Populate } from '@mikro-orm/core';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { StatusCodes } from 'http-status-codes';\nimport BaseController from './base.js';\nimport type { DynamicEntity } from '../../database/dynamic-entity.js';\nimport { generateFormFields } from '../../database/dynamic-entity-form-decorators.js';\nimport { Helper } from '../../util/index.js';\nimport type { WebServerBaseControllerConstructorParams } from './base.interface.js';\n\nexport default abstract class EntityController extends BaseController {\n protected abstract entityName: string;\n\n protected entityManager: EntityManager;\n\n // Cache for entity modules to avoid repeated dynamic imports\n private static entityCache = new Map<string, typeof DynamicEntity>();\n\n constructor(props: WebServerBaseControllerConstructorParams) {\n super(props);\n\n const { databaseInstance } = props;\n\n this.entityManager = databaseInstance.getEntityManager();\n }\n\n protected getEntity = async (): Promise<typeof DynamicEntity | undefined> => {\n if (!this.applicationConfig.database || this.applicationConfig.database.enabled !== true) {\n throw new Error(`Database not enabled (Entity: ${this.entityName})`);\n }\n\n // Check cache first\n const cacheKey = `${this.applicationConfig.database.entitiesDirectory}:${this.entityName}`;\n if (EntityController.entityCache.has(cacheKey)) {\n return EntityController.entityCache.get(cacheKey);\n }\n\n // Define entity module path\n const entityModulePath = path.join(\n this.applicationConfig.database.entitiesDirectory,\n `${this.entityName}.${Helper.getScriptFileExtension()}`,\n );\n\n // Import entity module\n const entityModule = await import(entityModulePath);\n\n if (!entityModule?.[this.entityName]) {\n throw new Error(`Entity not found (Entity: ${this.entityName})`);\n }\n\n // Get entity class\n const EntityClass = entityModule[this.entityName];\n\n // Cache the entity for future use\n EntityController.entityCache.set(cacheKey, EntityClass);\n\n return EntityClass;\n };\n\n private getEntityProperties(entityClass: any): string[] {\n const properties: string[] = [];\n\n const reservedPropertyKeys = ['constructor', 'toJSON'];\n\n for (const propertyKey of Object.getOwnPropertyNames(entityClass.prototype)) {\n if (propertyKey.startsWith('__')) {\n continue;\n } else if (reservedPropertyKeys.includes(propertyKey)) {\n continue;\n }\n\n properties.push(propertyKey);\n }\n\n return properties;\n }\n\n public options = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const formFields = generateFormFields({ model: EntityClass });\n\n this.sendSuccessResponse({\n reply,\n data: {\n formFields,\n },\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n public metadata = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const formFields = generateFormFields({ model: EntityClass });\n\n this.sendSuccessResponse({\n reply,\n data: {\n formFields,\n },\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n // Pre-getMany hook (can be overridden in the child controller)\n protected async preGetMany(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n // Post-getMany hook (can be overridden in the child controller)\n // await this.postGetMany({ entityManager: this.entityManager, request, reply, data });\n protected async postGetMany(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n data: {\n items: any[];\n total: number;\n page: number;\n totalPages: number;\n limit: number;\n };\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n public getMany = async (\n request: FastifyRequest<{\n Querystring: {\n page: string;\n limit: string;\n filters: string;\n sort: string;\n 'sort-order': string;\n search: string;\n [key: string]: any;\n };\n }>,\n reply: FastifyReply,\n ) => {\n try {\n // Call preGetMany hook\n await this.preGetMany({\n entityManager: this.entityManager,\n request,\n reply,\n });\n\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n // Pagination parameters\n const page = parseInt(request.query.page) || 1;\n const limit = parseInt(request.query.limit);\n const offset = (page - 1) * (limit > 0 ? limit : 0);\n\n // Filtering and sorting\n const filters = request.query.filters ? JSON.parse(request.query.filters) : {};\n const sortOrder = request.query['sort-order'] || 'ASC';\n const orderBy = request.query.sort ? { [request.query.sort]: sortOrder } : { id: sortOrder };\n\n const normalizedQuery: { [key: string]: any } = {};\n\n for (const key in request.query) {\n // Skip prototype pollution attempts\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n continue;\n }\n\n // Only process own properties\n if (!Object.prototype.hasOwnProperty.call(request.query, key)) {\n continue;\n }\n\n if (key.endsWith('[]')) {\n const normalizedKey = key.slice(0, -2);\n\n // Safe property assignment\n if (normalizedKey !== '__proto__' && normalizedKey !== 'constructor' && normalizedKey !== 'prototype') {\n Reflect.set(normalizedQuery, normalizedKey, Reflect.get(request.query, key));\n }\n } else {\n Reflect.set(normalizedQuery, key, Reflect.get(request.query, key));\n }\n }\n\n // Build query options\n const options: {\n limit?: number;\n offset?: number;\n filters: FilterQuery<any>;\n orderBy: { [key: string]: string };\n } = {\n filters,\n offset,\n orderBy,\n };\n\n if (limit > 0) {\n options.limit = limit;\n }\n\n const entityProperties = this.getEntityProperties(EntityClass);\n const reservedQueryKeys = ['page', 'limit', 'filters', 'sort', 'populate', 'search'];\n const searchQuery = request.query.search || '';\n\n for (const key in normalizedQuery) {\n // Skip prototype pollution attempts\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') {\n continue;\n }\n\n // Only process own properties\n if (!Object.prototype.hasOwnProperty.call(normalizedQuery, key)) {\n continue;\n }\n\n if (reservedQueryKeys.includes(key)) {\n continue;\n }\n\n if (!entityProperties.includes(key)) {\n const [relation, subProperty] = key.split('.');\n\n if (relation && subProperty) {\n // Validate relation and subProperty names\n if (\n relation === '__proto__' ||\n relation === 'constructor' ||\n relation === 'prototype' ||\n subProperty === '__proto__' ||\n subProperty === 'constructor' ||\n subProperty === 'prototype'\n ) {\n continue;\n }\n\n let queryValue = Reflect.get(normalizedQuery, key);\n\n if (!queryValue) continue;\n\n if (typeof queryValue === 'string' && queryValue.includes(',')) {\n queryValue = queryValue.split(',');\n }\n\n if (Array.isArray(queryValue)) {\n Reflect.set(options.filters, relation, {\n [subProperty]: { $in: queryValue },\n });\n } else {\n Reflect.set(options.filters, relation, {\n [subProperty]: queryValue,\n });\n }\n }\n\n continue;\n }\n\n let queryValue = Reflect.get(normalizedQuery, key);\n\n if (!queryValue) {\n continue;\n }\n\n if (typeof queryValue === 'string' && queryValue.includes(',')) {\n queryValue = queryValue.split(',');\n }\n\n if (Array.isArray(queryValue)) {\n Reflect.set(options.filters, key, { $in: queryValue });\n } else {\n Reflect.set(options.filters, key, queryValue);\n }\n }\n\n // Add search filter if a search query is provided\n if (searchQuery) {\n const searchFields = EntityClass.getSearchFields();\n\n options.filters.$or = searchFields\n .filter(field => {\n const isIntegerField = ['id', 'originId'].includes(field);\n\n return !isIntegerField;\n })\n .map(field => {\n return {\n [field]: { $like: `%${searchQuery}%` },\n };\n });\n }\n\n const populate = request.query.populate ? request.query.populate.split(',') : [];\n\n // Fetch items from the database\n const [items, total] = await this.entityManager.findAndCount(this.entityName, options.filters, {\n limit: options.limit,\n offset: options.offset,\n orderBy: options.orderBy,\n populate,\n });\n\n const totalPages = limit > 0 ? Math.ceil(total / limit) : 1;\n\n const data = {\n items,\n total,\n page,\n totalPages,\n limit: limit > 0 ? limit : total,\n };\n\n // Call postGetMany hook\n await this.postGetMany({\n entityManager: this.entityManager,\n request,\n reply,\n data,\n });\n\n reply.send({\n data: data.items,\n total_items: data.total,\n page: data.page,\n total_pages: data.totalPages,\n limit: data.limit,\n });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected async preGetOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n protected async postGetOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {\n // Default implementation: do nothing\n }\n\n public getOne = async (\n request: FastifyRequest<{\n Params: { id: number };\n Querystring: { populate: string };\n }>,\n reply: FastifyReply,\n ) => {\n try {\n await this.preGetOne({\n entityManager: this.entityManager,\n request,\n reply,\n });\n\n const queryPopulate = request.query.populate || null;\n const populateList: string[] = queryPopulate ? queryPopulate.split(',') : [];\n\n // Ensure populate is typed correctly for MikroORM\n const populate = populateList.map(field => `${field}.*`) as unknown as Populate<\n object,\n `${string}.*` | `${string}.$infer`\n >;\n\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n const id = request.params.id;\n\n const item = await this.entityManager.findOne(this.entityName, { id }, { populate });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n await this.postGetOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected preCreateOne = ({\n request,\n reply,\n }: {\n request: FastifyRequest;\n reply: FastifyReply;\n }): { request: FastifyRequest; reply: FastifyReply } => {\n return { request, reply };\n };\n\n protected async postCreateOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {}\n\n public createOne = async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n // Listen for preCreateOne hook\n if (this.preCreateOne) {\n const { request: preCreateOneRequest } = await this.preCreateOne({\n request,\n reply,\n });\n if (preCreateOneRequest) {\n request = preCreateOneRequest;\n }\n }\n\n const { error, value } = EntityClass.validateCreate(request.body);\n\n if (error) {\n return this.sendErrorResponse({ reply, error: error.message });\n }\n\n const item = this.entityManager.create(this.entityName, value as object);\n\n await this.entityManager.persistAndFlush(item);\n\n // Call postCreateOne hook\n await this.postCreateOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item, statusCode: StatusCodes.CREATED });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n protected async postUpdateOne(_: {\n entityManager: EntityManager;\n request: FastifyRequest;\n reply: FastifyReply;\n item: any;\n }): Promise<void> {}\n\n public updateOne = async (request: FastifyRequest<{ Params: { id: number } }>, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n return;\n }\n\n const id = request.params.id;\n\n const { error, value } = EntityClass.validateUpdate(request.body);\n\n if (error) {\n return this.sendErrorResponse({ reply, error: error.message });\n }\n\n const item = await this.entityManager.findOne(this.entityName, { id });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n this.entityManager.assign(item, value as object);\n\n await this.entityManager.persistAndFlush(item);\n\n // Call postUpdateOne hook\n await this.postUpdateOne({\n entityManager: this.entityManager,\n request,\n reply,\n item,\n });\n\n this.sendSuccessResponse({ reply, data: item });\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n\n public deleteOne = async (request: FastifyRequest<{ Params: { id: number } }>, reply: FastifyReply) => {\n try {\n const EntityClass = await this.getEntity();\n\n if (!EntityClass) {\n this.sendErrorResponse({ reply, error: 'Entity not found' });\n\n return;\n }\n\n const id = request.params.id;\n\n const item = await this.entityManager.findOne(this.entityName, { id });\n\n if (!item) {\n return this.sendNotFoundResponse(reply, `${EntityClass.singularNameCapitalized} not found`);\n }\n\n await this.entityManager.removeAndFlush(item);\n\n reply.status(StatusCodes.NO_CONTENT).send();\n } catch (error) {\n this.sendErrorResponse({ reply, error });\n }\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["queryValue"]
|
|
7
7
|
}
|