@arcaelas/dynamite 1.0.10 → 1.0.13
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/LICENSE.txt +32 -0
- package/README.txt +206 -0
- package/package.json +5 -4
- package/src/@types/index.d.ts +96 -0
- package/src/@types/index.js +9 -0
- package/{build/src → src}/core/client.d.ts +4 -0
- package/{build/src → src}/core/client.js +14 -2
- package/src/core/table.d.ts +98 -0
- package/src/core/table.js +459 -0
- package/src/core/wrapper.d.ts +17 -0
- package/src/core/wrapper.js +46 -0
- package/src/decorators/belongs_to.d.ts +1 -0
- package/src/decorators/belongs_to.js +24 -0
- package/src/decorators/created_at.d.ts +1 -0
- package/{build/src → src}/decorators/created_at.js +0 -7
- package/src/decorators/default.d.ts +1 -0
- package/{build/src → src}/decorators/default.js +2 -12
- package/src/decorators/has_many.d.ts +1 -0
- package/src/decorators/has_many.js +24 -0
- package/{build/src → src}/decorators/index.d.ts +4 -1
- package/src/decorators/index.js +36 -0
- package/src/decorators/index_sort.d.ts +12 -0
- package/src/decorators/index_sort.js +43 -0
- package/src/decorators/mutate.d.ts +2 -0
- package/{build/src → src}/decorators/mutate.js +2 -11
- package/src/decorators/name.d.ts +1 -0
- package/src/decorators/name.js +28 -0
- package/src/decorators/not_null.d.ts +1 -0
- package/{build/src → src}/decorators/not_null.js +0 -7
- package/src/decorators/primary_key.d.ts +6 -0
- package/src/decorators/primary_key.js +30 -0
- package/src/decorators/updated_at.d.ts +12 -0
- package/src/decorators/updated_at.js +26 -0
- package/src/decorators/validate.d.ts +1 -0
- package/{build/src → src}/decorators/validate.js +0 -7
- package/{build/src → src}/index.d.ts +9 -0
- package/{build/src → src}/index.js +14 -5
- package/{build/src → src}/utils/batch-relations.js +2 -1
- package/src/utils/circular-detector.d.ts +82 -0
- package/src/utils/circular-detector.js +209 -0
- package/src/utils/memory-manager.d.ts +42 -0
- package/src/utils/memory-manager.js +108 -0
- package/{build/src → src}/utils/projection.js +3 -2
- package/src/utils/relations.d.ts +17 -0
- package/src/utils/relations.js +166 -0
- package/src/utils/security-validator.d.ts +49 -0
- package/src/utils/security-validator.js +152 -0
- package/src/utils/throttle-manager.d.ts +78 -0
- package/src/utils/throttle-manager.js +196 -0
- package/src/utils/transaction-manager.d.ts +88 -0
- package/src/utils/transaction-manager.js +298 -0
- package/build/__tests__/crud.spec.d.ts +0 -7
- package/build/__tests__/crud.spec.js +0 -287
- package/build/__tests__/crud.spec.js.map +0 -1
- package/build/__tests__/debug-decorators.spec.d.ts +0 -7
- package/build/__tests__/debug-decorators.spec.js +0 -143
- package/build/__tests__/debug-decorators.spec.js.map +0 -1
- package/build/__tests__/decorators.spec.d.ts +0 -7
- package/build/__tests__/decorators.spec.js +0 -203
- package/build/__tests__/decorators.spec.js.map +0 -1
- package/build/__tests__/instance-crud.spec.d.ts +0 -7
- package/build/__tests__/instance-crud.spec.js +0 -184
- package/build/__tests__/instance-crud.spec.js.map +0 -1
- package/build/src/core/client.js.map +0 -1
- package/build/src/core/table.d.ts +0 -164
- package/build/src/core/table.js +0 -406
- package/build/src/core/table.js.map +0 -1
- package/build/src/core/wrapper.d.ts +0 -54
- package/build/src/core/wrapper.js +0 -27
- package/build/src/core/wrapper.js.map +0 -1
- package/build/src/decorators/created_at.d.ts +0 -8
- package/build/src/decorators/created_at.js.map +0 -1
- package/build/src/decorators/default.d.ts +0 -8
- package/build/src/decorators/default.js.map +0 -1
- package/build/src/decorators/index.js +0 -26
- package/build/src/decorators/index.js.map +0 -1
- package/build/src/decorators/index_sort.d.ts +0 -8
- package/build/src/decorators/index_sort.js +0 -30
- package/build/src/decorators/index_sort.js.map +0 -1
- package/build/src/decorators/mutate.d.ts +0 -9
- package/build/src/decorators/mutate.js.map +0 -1
- package/build/src/decorators/name.d.ts +0 -8
- package/build/src/decorators/name.js +0 -42
- package/build/src/decorators/name.js.map +0 -1
- package/build/src/decorators/not_null.d.ts +0 -8
- package/build/src/decorators/not_null.js.map +0 -1
- package/build/src/decorators/primary_key.d.ts +0 -8
- package/build/src/decorators/primary_key.js +0 -26
- package/build/src/decorators/primary_key.js.map +0 -1
- package/build/src/decorators/updated_at.d.ts +0 -8
- package/build/src/decorators/updated_at.js +0 -18
- package/build/src/decorators/updated_at.js.map +0 -1
- package/build/src/decorators/validate.d.ts +0 -8
- package/build/src/decorators/validate.js.map +0 -1
- package/build/src/index.js.map +0 -1
- package/build/src/utils/batch-relations.js.map +0 -1
- package/build/src/utils/naming.js.map +0 -1
- package/build/src/utils/projection.js.map +0 -1
- package/build/src/utils/relations.d.ts +0 -23
- package/build/src/utils/relations.js +0 -205
- package/build/src/utils/relations.js.map +0 -1
- /package/{build/src → src}/utils/batch-relations.d.ts +0 -0
- /package/{build/src → src}/utils/naming.d.ts +0 -0
- /package/{build/src → src}/utils/naming.js +0 -0
- /package/{build/src → src}/utils/projection.d.ts +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file index.ts
|
|
4
|
+
* @descripcion Decorador @Index para Partition Key
|
|
5
|
+
* @autor Miguel Alejandro
|
|
6
|
+
* @fecha 2025-01-27
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.default = Index;
|
|
10
|
+
const wrapper_1 = require("../core/wrapper");
|
|
11
|
+
const naming_1 = require("../utils/naming");
|
|
12
|
+
/**
|
|
13
|
+
* Decorador para marcar propiedad como Partition Key.
|
|
14
|
+
* Configura la propiedad como índice principal para consultas.
|
|
15
|
+
*/
|
|
16
|
+
function Index() {
|
|
17
|
+
return (target, prop) => {
|
|
18
|
+
const ctor = target.constructor;
|
|
19
|
+
const tableName = (0, naming_1.toSnakePlural)(ctor.name);
|
|
20
|
+
const entry = (0, wrapper_1.ensureConfig)(ctor, tableName);
|
|
21
|
+
// Verificar si ya existe un índice primario
|
|
22
|
+
const existingIndex = [...entry.columns.values()].find((col) => col.index === true);
|
|
23
|
+
if (existingIndex && existingIndex.name !== String(prop)) {
|
|
24
|
+
throw new Error(`La tabla ${tableName} ya tiene definida una PartitionKey (${existingIndex.name}). ` +
|
|
25
|
+
`No se puede definir otra PartitionKey en '${String(prop)}'`);
|
|
26
|
+
}
|
|
27
|
+
// Obtener o crear la columna y marcar como índice
|
|
28
|
+
const column = (0, wrapper_1.ensureColumn)(entry, prop, String(prop));
|
|
29
|
+
column.index = true;
|
|
30
|
+
// Asegurar que la columna no sea nula por defecto
|
|
31
|
+
if (column.nullable === undefined) {
|
|
32
|
+
column.nullable = false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index_sort.ts
|
|
3
|
+
* @descripcion Decorador @IndexSort para Sort Key
|
|
4
|
+
* @autor Miguel Alejandro
|
|
5
|
+
* @fecha 2025-01-27
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Decorador para marcar propiedad como Sort Key.
|
|
9
|
+
* Configura la propiedad como clave de ordenación para consultas.
|
|
10
|
+
* Requiere que exista una PartitionKey (@Index) definida previamente.
|
|
11
|
+
*/
|
|
12
|
+
export default function IndexSort(): PropertyDecorator;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file index_sort.ts
|
|
4
|
+
* @descripcion Decorador @IndexSort para Sort Key
|
|
5
|
+
* @autor Miguel Alejandro
|
|
6
|
+
* @fecha 2025-01-27
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.default = IndexSort;
|
|
10
|
+
const wrapper_1 = require("../core/wrapper");
|
|
11
|
+
const naming_1 = require("../utils/naming");
|
|
12
|
+
/**
|
|
13
|
+
* Decorador para marcar propiedad como Sort Key.
|
|
14
|
+
* Configura la propiedad como clave de ordenación para consultas.
|
|
15
|
+
* Requiere que exista una PartitionKey (@Index) definida previamente.
|
|
16
|
+
*/
|
|
17
|
+
function IndexSort() {
|
|
18
|
+
return (target, prop) => {
|
|
19
|
+
const ctor = target.constructor;
|
|
20
|
+
const tableName = (0, naming_1.toSnakePlural)(ctor.name);
|
|
21
|
+
const entry = (0, wrapper_1.ensureConfig)(ctor, tableName);
|
|
22
|
+
// Verificar que exista una PartitionKey definida
|
|
23
|
+
const hasPartitionKey = [...entry.columns.values()].some((col) => col.index === true);
|
|
24
|
+
if (!hasPartitionKey) {
|
|
25
|
+
throw new Error(`No se puede definir una SortKey en '${String(prop)}' sin una PartitionKey. ` +
|
|
26
|
+
`Asegúrate de marcar una propiedad con @Index primero.`);
|
|
27
|
+
}
|
|
28
|
+
// Verificar si ya existe una SortKey
|
|
29
|
+
const existingSortKey = [...entry.columns.values()].find((col) => col.indexSort === true);
|
|
30
|
+
if (existingSortKey && existingSortKey.name !== String(prop)) {
|
|
31
|
+
throw new Error(`La tabla ${tableName} ya tiene una SortKey definida (${existingSortKey.name}). ` +
|
|
32
|
+
`No se puede definir otra SortKey en '${String(prop)}'`);
|
|
33
|
+
}
|
|
34
|
+
// Obtener o crear la columna y marcar como índice de ordenación
|
|
35
|
+
const column = (0, wrapper_1.ensureColumn)(entry, prop, String(prop));
|
|
36
|
+
column.indexSort = true;
|
|
37
|
+
// Asegurar que la columna no sea nula por defecto
|
|
38
|
+
if (column.nullable === undefined) {
|
|
39
|
+
column.nullable = false;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index_sort.js.map
|
|
@@ -1,20 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file mutate.ts
|
|
4
|
-
* @descripcion Decorador @Mutate para transformaciones
|
|
5
|
-
* @autor Miguel Alejandro
|
|
6
|
-
* @fecha 2025-01-27
|
|
7
|
-
*/
|
|
8
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
3
|
exports.default = Mutate;
|
|
10
4
|
const wrapper_1 = require("../core/wrapper");
|
|
11
5
|
const naming_1 = require("../utils/naming");
|
|
12
|
-
/** Decorador para aplicar transformaciones automáticas a propiedades */
|
|
13
6
|
function Mutate(fn) {
|
|
14
|
-
typeof fn !== "function"
|
|
15
|
-
(
|
|
16
|
-
throw new TypeError("@Mutate requiere función");
|
|
17
|
-
})();
|
|
7
|
+
if (typeof fn !== "function")
|
|
8
|
+
throw new TypeError("@Mutate requiere función");
|
|
18
9
|
return (target, prop) => {
|
|
19
10
|
const ctor = target.constructor;
|
|
20
11
|
const entry = (0, wrapper_1.ensureConfig)(ctor, (0, naming_1.toSnakePlural)(ctor.name));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function Name(label: string): ClassDecorator & PropertyDecorator;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = Name;
|
|
4
|
+
const wrapper_1 = require("../core/wrapper");
|
|
5
|
+
const naming_1 = require("../utils/naming");
|
|
6
|
+
function Name(label) {
|
|
7
|
+
if (!label || typeof label !== "string")
|
|
8
|
+
throw new TypeError("@Name requiere una cadena no vacía");
|
|
9
|
+
return (target, prop) => {
|
|
10
|
+
const ctor = prop === undefined ? target : target.constructor;
|
|
11
|
+
const entry = (0, wrapper_1.ensureConfig)(ctor, (0, naming_1.toSnakePlural)(ctor.name));
|
|
12
|
+
if (prop === undefined) {
|
|
13
|
+
const auto = (0, naming_1.toSnakePlural)(ctor.name);
|
|
14
|
+
if (entry.name !== auto && entry.name !== label && entry.name) {
|
|
15
|
+
throw new Error(`La clase ${ctor.name} ya tiene un @Name distinto (${entry.name})`);
|
|
16
|
+
}
|
|
17
|
+
entry.name = label;
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const col = (0, wrapper_1.ensureColumn)(entry, prop, label);
|
|
21
|
+
if (col.name && col.name !== label) {
|
|
22
|
+
throw new Error(`La columna '${String(prop)}' ya tiene @Name distinto (${col.name})`);
|
|
23
|
+
}
|
|
24
|
+
col.name = label;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function NotNull(): PropertyDecorator;
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file not_null.ts
|
|
4
|
-
* @descripcion Decorador @NotNull para validación no-null
|
|
5
|
-
* @autor Miguel Alejandro
|
|
6
|
-
* @fecha 2025-01-27
|
|
7
|
-
*/
|
|
8
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
4
|
};
|
|
11
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
6
|
exports.default = NotNull;
|
|
13
7
|
const validate_1 = __importDefault(require("./validate"));
|
|
14
|
-
/** Decorador para validar que el valor no sea nulo, indefinido o string vacío */
|
|
15
8
|
function NotNull() {
|
|
16
9
|
return validate_1.default((value) => value !== null &&
|
|
17
10
|
value !== undefined &&
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decorador para marcar una propiedad como clave primaria.
|
|
3
|
+
* Aplica automáticamente @Index y @IndexSort y marca el campo como primaryKey en los metadatos.
|
|
4
|
+
* @param name - Nombre opcional para el índice (no utilizado actualmente, mantenido por compatibilidad)
|
|
5
|
+
*/
|
|
6
|
+
export default function PrimaryKey(name?: string): PropertyDecorator;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = PrimaryKey;
|
|
7
|
+
const wrapper_1 = require("../core/wrapper");
|
|
8
|
+
const naming_1 = require("../utils/naming");
|
|
9
|
+
const index_1 = __importDefault(require("./index"));
|
|
10
|
+
const index_sort_1 = __importDefault(require("./index_sort"));
|
|
11
|
+
/**
|
|
12
|
+
* Decorador para marcar una propiedad como clave primaria.
|
|
13
|
+
* Aplica automáticamente @Index y @IndexSort y marca el campo como primaryKey en los metadatos.
|
|
14
|
+
* @param name - Nombre opcional para el índice (no utilizado actualmente, mantenido por compatibilidad)
|
|
15
|
+
*/
|
|
16
|
+
function PrimaryKey(name = "primary") {
|
|
17
|
+
return (target, prop) => {
|
|
18
|
+
// Aplicar decoradores de índice
|
|
19
|
+
(0, index_1.default)()(target, prop);
|
|
20
|
+
(0, index_sort_1.default)()(target, prop);
|
|
21
|
+
// Marcar explícitamente como clave primaria en los metadatos
|
|
22
|
+
const ctor = target.constructor;
|
|
23
|
+
const entry = (0, wrapper_1.ensureConfig)(ctor, (0, naming_1.toSnakePlural)(ctor.name));
|
|
24
|
+
const column = (0, wrapper_1.ensureColumn)(entry, prop, String(prop));
|
|
25
|
+
// Establecer metadatos adicionales para clave primaria
|
|
26
|
+
column.primaryKey = true;
|
|
27
|
+
column.nullable = false; // Las claves primarias no pueden ser nulas
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=primary_key.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decorador que marca una propiedad para que se actualice automáticamente con la fecha/hora actual
|
|
3
|
+
* cada vez que se guarde el modelo.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* class User extends Table<User> {
|
|
7
|
+
* @PrimaryKey() id: string;
|
|
8
|
+
* name: string;
|
|
9
|
+
* @UpdatedAt() updated_at: string;
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
export default function UpdatedAt(): (target: any, propertyKey: string | symbol) => void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = UpdatedAt;
|
|
4
|
+
const wrapper_1 = require("../core/wrapper");
|
|
5
|
+
/**
|
|
6
|
+
* Decorador que marca una propiedad para que se actualice automáticamente con la fecha/hora actual
|
|
7
|
+
* cada vez que se guarde el modelo.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* class User extends Table<User> {
|
|
11
|
+
* @PrimaryKey() id: string;
|
|
12
|
+
* name: string;
|
|
13
|
+
* @UpdatedAt() updated_at: string;
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
function UpdatedAt() {
|
|
17
|
+
return function (target, propertyKey) {
|
|
18
|
+
const ctor = target.constructor;
|
|
19
|
+
const entry = (0, wrapper_1.ensureConfig)(ctor, ctor.name);
|
|
20
|
+
const column = (0, wrapper_1.ensureColumn)(entry, propertyKey, propertyKey);
|
|
21
|
+
// Marcamos la columna como updatedAt para que se actualice automáticamente al guardar
|
|
22
|
+
column.updatedAt = true;
|
|
23
|
+
// No establecemos un valor por defecto aquí, se establecerá en el método save()
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=updated_at.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function Validate(validators: ((v: unknown) => true | string) | ((v: unknown) => true | string)[]): PropertyDecorator;
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @file validate.ts
|
|
4
|
-
* @descripcion Decorador @Validate para validaciones
|
|
5
|
-
* @autor Miguel Alejandro
|
|
6
|
-
* @fecha 2025-01-27
|
|
7
|
-
*/
|
|
8
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
3
|
exports.default = Validate;
|
|
10
4
|
const wrapper_1 = require("../core/wrapper");
|
|
11
5
|
const naming_1 = require("../utils/naming");
|
|
12
|
-
/** Decorador para aplicar validaciones a propiedades */
|
|
13
6
|
function Validate(validators) {
|
|
14
7
|
const list = Array.isArray(validators) ? validators : [validators];
|
|
15
8
|
if (!list.length || list.some((v) => typeof v !== "function")) {
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* @descripcion Punto de entrada público de la librería
|
|
4
|
+
* @autor Miguel Alejandro
|
|
5
|
+
* @fecha 2025-08-07
|
|
6
|
+
*/
|
|
1
7
|
export { Dynamite } from "./core/client";
|
|
2
8
|
export { default as Table } from "./core/table";
|
|
9
|
+
export { default as BelongsTo } from "./decorators/belongs_to";
|
|
3
10
|
export { default as CreatedAt } from "./decorators/created_at";
|
|
4
11
|
export { default as Default } from "./decorators/default";
|
|
12
|
+
export { default as HasMany } from "./decorators/has_many";
|
|
5
13
|
export { default as Index } from "./decorators/index";
|
|
6
14
|
export { default as IndexSort } from "./decorators/index_sort";
|
|
7
15
|
export { default as Mutate } from "./decorators/mutate";
|
|
@@ -11,3 +19,4 @@ export { default as PrimaryKey } from "./decorators/primary_key";
|
|
|
11
19
|
export { default as UpdatedAt } from "./decorators/updated_at";
|
|
12
20
|
export { default as Validate } from "./decorators/validate";
|
|
13
21
|
export { belongsTo, hasMany } from "./utils/relations";
|
|
22
|
+
export type { BelongsTo as BelongsToType, HasMany as HasManyType, NonAttribute, CreationOptional, InferAttributes, FilterableAttributes, QueryOperator, QueryResult, WhereOptions, WhereOptionsWithoutWhere, } from "./@types/index";
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file index.ts
|
|
4
|
+
* @descripcion Punto de entrada público de la librería
|
|
5
|
+
* @autor Miguel Alejandro
|
|
6
|
+
* @fecha 2025-08-07
|
|
7
|
+
*/
|
|
2
8
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
10
|
};
|
|
5
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.hasMany = exports.belongsTo = exports.Validate = exports.UpdatedAt = exports.PrimaryKey = exports.NotNull = exports.Name = exports.Mutate = exports.IndexSort = exports.Index = exports.Default = exports.CreatedAt = exports.Table = exports.Dynamite = void 0;
|
|
7
|
-
//
|
|
12
|
+
exports.hasMany = exports.belongsTo = exports.Validate = exports.UpdatedAt = exports.PrimaryKey = exports.NotNull = exports.Name = exports.Mutate = exports.IndexSort = exports.Index = exports.HasMany = exports.Default = exports.CreatedAt = exports.BelongsTo = exports.Table = exports.Dynamite = void 0;
|
|
13
|
+
// Clases núcleo
|
|
8
14
|
var client_1 = require("./core/client");
|
|
9
15
|
Object.defineProperty(exports, "Dynamite", { enumerable: true, get: function () { return client_1.Dynamite; } });
|
|
10
16
|
var table_1 = require("./core/table");
|
|
11
17
|
Object.defineProperty(exports, "Table", { enumerable: true, get: function () { return __importDefault(table_1).default; } });
|
|
12
|
-
//
|
|
13
|
-
|
|
18
|
+
// Decoradores
|
|
19
|
+
var belongs_to_1 = require("./decorators/belongs_to");
|
|
20
|
+
Object.defineProperty(exports, "BelongsTo", { enumerable: true, get: function () { return __importDefault(belongs_to_1).default; } });
|
|
14
21
|
var created_at_1 = require("./decorators/created_at");
|
|
15
22
|
Object.defineProperty(exports, "CreatedAt", { enumerable: true, get: function () { return __importDefault(created_at_1).default; } });
|
|
16
23
|
var default_1 = require("./decorators/default");
|
|
17
24
|
Object.defineProperty(exports, "Default", { enumerable: true, get: function () { return __importDefault(default_1).default; } });
|
|
25
|
+
var has_many_1 = require("./decorators/has_many");
|
|
26
|
+
Object.defineProperty(exports, "HasMany", { enumerable: true, get: function () { return __importDefault(has_many_1).default; } });
|
|
18
27
|
var index_1 = require("./decorators/index");
|
|
19
28
|
Object.defineProperty(exports, "Index", { enumerable: true, get: function () { return __importDefault(index_1).default; } });
|
|
20
29
|
var index_sort_1 = require("./decorators/index_sort");
|
|
@@ -31,7 +40,7 @@ var updated_at_1 = require("./decorators/updated_at");
|
|
|
31
40
|
Object.defineProperty(exports, "UpdatedAt", { enumerable: true, get: function () { return __importDefault(updated_at_1).default; } });
|
|
32
41
|
var validate_1 = require("./decorators/validate");
|
|
33
42
|
Object.defineProperty(exports, "Validate", { enumerable: true, get: function () { return __importDefault(validate_1).default; } });
|
|
34
|
-
//
|
|
43
|
+
// Relaciones
|
|
35
44
|
var relations_1 = require("./utils/relations");
|
|
36
45
|
Object.defineProperty(exports, "belongsTo", { enumerable: true, get: function () { return relations_1.belongsTo; } });
|
|
37
46
|
Object.defineProperty(exports, "hasMany", { enumerable: true, get: function () { return relations_1.hasMany; } });
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.cleanupRelationCache = exports.processIncludesBatch = exports.batchLoadBelongsTo = exports.batchLoadHasMany = void 0;
|
|
10
|
+
const wrapper_1 = require("../core/wrapper");
|
|
10
11
|
/** Cache multi-nivel con TTL */
|
|
11
12
|
const relCache = new Map();
|
|
12
13
|
/** Batch loader para hasMany relations */
|
|
@@ -76,7 +77,7 @@ exports.batchLoadBelongsTo = batchLoadBelongsTo;
|
|
|
76
77
|
const processIncludesBatch = async (Model, items, include, depth = 0) => {
|
|
77
78
|
if (!include || depth > 10 || !items.length)
|
|
78
79
|
return items;
|
|
79
|
-
const meta =
|
|
80
|
+
const meta = (0, wrapper_1.mustMeta)(Model);
|
|
80
81
|
// Process all relations in parallel
|
|
81
82
|
const relationPromises = Object.entries(include).map(async ([relationKey, relationOptions]) => {
|
|
82
83
|
const relation = meta.relations.get(relationKey);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file circular-detector.ts
|
|
3
|
+
* @description Detection and prevention of circular references in relations
|
|
4
|
+
* @author Miguel Alejandro
|
|
5
|
+
* @fecha 2025-08-31
|
|
6
|
+
*/
|
|
7
|
+
interface TraversalPath {
|
|
8
|
+
modelName: string;
|
|
9
|
+
relationKey: string;
|
|
10
|
+
depth: number;
|
|
11
|
+
}
|
|
12
|
+
interface CircularDetectionConfig {
|
|
13
|
+
maxDepth: number;
|
|
14
|
+
maxIncludeDepth: number;
|
|
15
|
+
trackingEnabled: boolean;
|
|
16
|
+
}
|
|
17
|
+
declare class CircularReferenceDetector {
|
|
18
|
+
private static readonly DEFAULT_CONFIG;
|
|
19
|
+
private config;
|
|
20
|
+
private activePaths;
|
|
21
|
+
private pathHistory;
|
|
22
|
+
constructor(config?: Partial<CircularDetectionConfig>);
|
|
23
|
+
/**
|
|
24
|
+
* Verificar si una ruta de inclusión es segura
|
|
25
|
+
*/
|
|
26
|
+
validateIncludePath(modelName: string, includeOptions: any, currentDepth?: number, visitedModels?: Set<string>): void;
|
|
27
|
+
/**
|
|
28
|
+
* Validar estructura de relaciones en tiempo de definición
|
|
29
|
+
*/
|
|
30
|
+
validateRelationStructure(models: Map<string, any>, maxAllowedCycles?: number): ValidationResult;
|
|
31
|
+
/**
|
|
32
|
+
* Construir grafo de relaciones
|
|
33
|
+
*/
|
|
34
|
+
private buildRelationGraph;
|
|
35
|
+
/**
|
|
36
|
+
* Detectar ciclos usando DFS
|
|
37
|
+
*/
|
|
38
|
+
private detectCycles;
|
|
39
|
+
/**
|
|
40
|
+
* Generar sugerencias para resolver ciclos
|
|
41
|
+
*/
|
|
42
|
+
private generateSuggestions;
|
|
43
|
+
/**
|
|
44
|
+
* Inferir nombre del modelo target (simplificado)
|
|
45
|
+
*/
|
|
46
|
+
private inferTargetModelName;
|
|
47
|
+
/**
|
|
48
|
+
* Crear contexto de rastreo para includes anidados
|
|
49
|
+
*/
|
|
50
|
+
createTrackingContext(): CircularTracker;
|
|
51
|
+
/**
|
|
52
|
+
* Obtener historial de paths para debugging
|
|
53
|
+
*/
|
|
54
|
+
getPathHistory(): TraversalPath[];
|
|
55
|
+
/**
|
|
56
|
+
* Limpiar historial
|
|
57
|
+
*/
|
|
58
|
+
clearHistory(): void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Tracker específico para una operación de include
|
|
62
|
+
*/
|
|
63
|
+
declare class CircularTracker {
|
|
64
|
+
private visitedModels;
|
|
65
|
+
private currentPath;
|
|
66
|
+
private maxDepth;
|
|
67
|
+
constructor(maxDepth: number);
|
|
68
|
+
enter(modelName: string): void;
|
|
69
|
+
exit(modelName: string): void;
|
|
70
|
+
getCurrentPath(): string[];
|
|
71
|
+
}
|
|
72
|
+
interface ValidationResult {
|
|
73
|
+
isValid: boolean;
|
|
74
|
+
cycles: string[][];
|
|
75
|
+
suggestions: string[];
|
|
76
|
+
}
|
|
77
|
+
declare class CircularReferenceError extends Error {
|
|
78
|
+
constructor(message: string);
|
|
79
|
+
}
|
|
80
|
+
declare const circularDetector: CircularReferenceDetector;
|
|
81
|
+
export { CircularReferenceDetector, CircularTracker, CircularReferenceError, ValidationResult, circularDetector };
|
|
82
|
+
export default circularDetector;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file circular-detector.ts
|
|
4
|
+
* @description Detection and prevention of circular references in relations
|
|
5
|
+
* @author Miguel Alejandro
|
|
6
|
+
* @fecha 2025-08-31
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.circularDetector = exports.CircularReferenceError = exports.CircularTracker = exports.CircularReferenceDetector = void 0;
|
|
10
|
+
class CircularReferenceDetector {
|
|
11
|
+
static { this.DEFAULT_CONFIG = {
|
|
12
|
+
maxDepth: 10,
|
|
13
|
+
maxIncludeDepth: 5,
|
|
14
|
+
trackingEnabled: true,
|
|
15
|
+
}; }
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.activePaths = new Set();
|
|
18
|
+
this.pathHistory = [];
|
|
19
|
+
this.config = { ...CircularReferenceDetector.DEFAULT_CONFIG, ...config };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verificar si una ruta de inclusión es segura
|
|
23
|
+
*/
|
|
24
|
+
validateIncludePath(modelName, includeOptions, currentDepth = 0, visitedModels = new Set()) {
|
|
25
|
+
// Verificar profundidad máxima
|
|
26
|
+
if (currentDepth > this.config.maxIncludeDepth) {
|
|
27
|
+
throw new CircularReferenceError(`Include depth exceeded limit of ${this.config.maxIncludeDepth} at model: ${modelName}`);
|
|
28
|
+
}
|
|
29
|
+
// Detectar referencia circular directa
|
|
30
|
+
if (visitedModels.has(modelName)) {
|
|
31
|
+
const path = Array.from(visitedModels).join(' -> ') + ` -> ${modelName}`;
|
|
32
|
+
throw new CircularReferenceError(`Circular reference detected in include path: ${path}`);
|
|
33
|
+
}
|
|
34
|
+
if (!includeOptions || typeof includeOptions !== 'object') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const newVisited = new Set(visitedModels);
|
|
38
|
+
newVisited.add(modelName);
|
|
39
|
+
// Validar cada relación incluida
|
|
40
|
+
for (const [relationKey, relationOptions] of Object.entries(includeOptions)) {
|
|
41
|
+
if (this.config.trackingEnabled) {
|
|
42
|
+
this.pathHistory.push({
|
|
43
|
+
modelName,
|
|
44
|
+
relationKey,
|
|
45
|
+
depth: currentDepth,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Si la relación tiene sus propios includes, validar recursivamente
|
|
49
|
+
if (relationOptions && typeof relationOptions === 'object' && 'include' in relationOptions) {
|
|
50
|
+
// Aquí necesitaríamos obtener el modelo target de la relación
|
|
51
|
+
// Por simplicidad, usamos el relationKey como modelo (esto debería mejorar)
|
|
52
|
+
const targetModelName = this.inferTargetModelName(relationKey);
|
|
53
|
+
this.validateIncludePath(targetModelName, relationOptions.include, currentDepth + 1, newVisited);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validar estructura de relaciones en tiempo de definición
|
|
59
|
+
*/
|
|
60
|
+
validateRelationStructure(models, maxAllowedCycles = 0) {
|
|
61
|
+
const graph = this.buildRelationGraph(models);
|
|
62
|
+
const cycles = this.detectCycles(graph);
|
|
63
|
+
return {
|
|
64
|
+
isValid: cycles.length <= maxAllowedCycles,
|
|
65
|
+
cycles,
|
|
66
|
+
suggestions: this.generateSuggestions(cycles),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Construir grafo de relaciones
|
|
71
|
+
*/
|
|
72
|
+
buildRelationGraph(models) {
|
|
73
|
+
const graph = new Map();
|
|
74
|
+
for (const [modelName, model] of models.entries()) {
|
|
75
|
+
const relations = [];
|
|
76
|
+
// Esto necesitaría acceso a los metadatos del modelo
|
|
77
|
+
// Por ahora simulamos la extracción de relaciones
|
|
78
|
+
const meta = model.getMeta?.();
|
|
79
|
+
if (meta?.relations) {
|
|
80
|
+
for (const [relationKey, relation] of meta.relations.entries()) {
|
|
81
|
+
const targetModel = relation.targetModel?.()?.name || relationKey;
|
|
82
|
+
relations.push(targetModel);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
graph.set(modelName, relations);
|
|
86
|
+
}
|
|
87
|
+
return graph;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Detectar ciclos usando DFS
|
|
91
|
+
*/
|
|
92
|
+
detectCycles(graph) {
|
|
93
|
+
const cycles = [];
|
|
94
|
+
const visited = new Set();
|
|
95
|
+
const recursionStack = new Set();
|
|
96
|
+
const dfs = (node, path) => {
|
|
97
|
+
if (recursionStack.has(node)) {
|
|
98
|
+
// Ciclo detectado
|
|
99
|
+
const cycleStart = path.indexOf(node);
|
|
100
|
+
const cycle = path.slice(cycleStart).concat(node);
|
|
101
|
+
cycles.push(cycle);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (visited.has(node)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
visited.add(node);
|
|
108
|
+
recursionStack.add(node);
|
|
109
|
+
path.push(node);
|
|
110
|
+
const neighbors = graph.get(node) || [];
|
|
111
|
+
for (const neighbor of neighbors) {
|
|
112
|
+
dfs(neighbor, [...path]);
|
|
113
|
+
}
|
|
114
|
+
recursionStack.delete(node);
|
|
115
|
+
};
|
|
116
|
+
for (const node of graph.keys()) {
|
|
117
|
+
if (!visited.has(node)) {
|
|
118
|
+
dfs(node, []);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return cycles;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Generar sugerencias para resolver ciclos
|
|
125
|
+
*/
|
|
126
|
+
generateSuggestions(cycles) {
|
|
127
|
+
const suggestions = [];
|
|
128
|
+
for (const cycle of cycles) {
|
|
129
|
+
if (cycle.length === 2) {
|
|
130
|
+
suggestions.push(`Circular reference between ${cycle[0]} and ${cycle[1]}. Consider using lazy loading or removing one direction.`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
suggestions.push(`Complex circular reference detected: ${cycle.join(' -> ')}. Consider breaking the cycle at the weakest relationship.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (cycles.length > 0) {
|
|
137
|
+
suggestions.push('General: Use @NonAttribute for computed relationships or implement lazy loading to break cycles.');
|
|
138
|
+
}
|
|
139
|
+
return suggestions;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Inferir nombre del modelo target (simplificado)
|
|
143
|
+
*/
|
|
144
|
+
inferTargetModelName(relationKey) {
|
|
145
|
+
// Conversión simple: posts -> Post, user -> User
|
|
146
|
+
return relationKey.charAt(0).toUpperCase() + relationKey.slice(1).replace(/s$/, '');
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Crear contexto de rastreo para includes anidados
|
|
150
|
+
*/
|
|
151
|
+
createTrackingContext() {
|
|
152
|
+
return new CircularTracker(this.config.maxIncludeDepth);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Obtener historial de paths para debugging
|
|
156
|
+
*/
|
|
157
|
+
getPathHistory() {
|
|
158
|
+
return [...this.pathHistory];
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Limpiar historial
|
|
162
|
+
*/
|
|
163
|
+
clearHistory() {
|
|
164
|
+
this.pathHistory = [];
|
|
165
|
+
this.activePaths.clear();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.CircularReferenceDetector = CircularReferenceDetector;
|
|
169
|
+
/**
|
|
170
|
+
* Tracker específico para una operación de include
|
|
171
|
+
*/
|
|
172
|
+
class CircularTracker {
|
|
173
|
+
constructor(maxDepth) {
|
|
174
|
+
this.visitedModels = new Set();
|
|
175
|
+
this.currentPath = [];
|
|
176
|
+
this.maxDepth = maxDepth;
|
|
177
|
+
}
|
|
178
|
+
enter(modelName) {
|
|
179
|
+
if (this.currentPath.length >= this.maxDepth) {
|
|
180
|
+
throw new CircularReferenceError(`Maximum include depth ${this.maxDepth} exceeded at: ${modelName}`);
|
|
181
|
+
}
|
|
182
|
+
if (this.visitedModels.has(modelName)) {
|
|
183
|
+
const cyclePath = this.currentPath.join(' -> ') + ` -> ${modelName}`;
|
|
184
|
+
throw new CircularReferenceError(`Circular reference detected: ${cyclePath}`);
|
|
185
|
+
}
|
|
186
|
+
this.visitedModels.add(modelName);
|
|
187
|
+
this.currentPath.push(modelName);
|
|
188
|
+
}
|
|
189
|
+
exit(modelName) {
|
|
190
|
+
this.currentPath.pop();
|
|
191
|
+
this.visitedModels.delete(modelName);
|
|
192
|
+
}
|
|
193
|
+
getCurrentPath() {
|
|
194
|
+
return [...this.currentPath];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.CircularTracker = CircularTracker;
|
|
198
|
+
class CircularReferenceError extends Error {
|
|
199
|
+
constructor(message) {
|
|
200
|
+
super(message);
|
|
201
|
+
this.name = 'CircularReferenceError';
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.CircularReferenceError = CircularReferenceError;
|
|
205
|
+
// Instancia singleton
|
|
206
|
+
const circularDetector = new CircularReferenceDetector();
|
|
207
|
+
exports.circularDetector = circularDetector;
|
|
208
|
+
exports.default = circularDetector;
|
|
209
|
+
//# sourceMappingURL=circular-detector.js.map
|