@arcaelas/dynamite 1.0.17 → 1.0.19
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/package.json +40 -2
- package/src/@types/index.d.ts +116 -75
- package/src/core/client.d.ts +36 -0
- package/src/core/client.js +80 -27
- package/src/core/decorator.d.ts +44 -0
- package/src/core/decorator.js +133 -0
- package/src/core/method.d.ts +73 -0
- package/src/core/method.js +140 -0
- package/src/core/table.d.ts +44 -86
- package/src/core/table.js +510 -310
- package/src/decorators/indexes.d.ts +38 -0
- package/src/decorators/indexes.js +67 -0
- package/src/decorators/relations.d.ts +55 -0
- package/src/decorators/relations.js +84 -0
- package/src/decorators/timestamps.d.ts +54 -0
- package/src/decorators/timestamps.js +67 -0
- package/src/decorators/transforms.d.ts +86 -0
- package/src/decorators/transforms.js +154 -0
- package/src/index.d.ts +10 -16
- package/src/index.js +50 -32
- package/src/index.test.d.ts +13 -0
- package/src/index.test.js +1992 -0
- package/src/utils/relations.d.ts +34 -12
- package/src/utils/relations.js +109 -133
- package/src/@types/index.js +0 -9
- package/src/core/wrapper.d.ts +0 -17
- package/src/core/wrapper.js +0 -46
- package/src/decorators/belongs_to.d.ts +0 -1
- package/src/decorators/belongs_to.js +0 -24
- package/src/decorators/created_at.d.ts +0 -1
- package/src/decorators/created_at.js +0 -11
- package/src/decorators/default.d.ts +0 -1
- package/src/decorators/default.js +0 -47
- package/src/decorators/has_many.d.ts +0 -1
- package/src/decorators/has_many.js +0 -24
- package/src/decorators/index.d.ts +0 -11
- package/src/decorators/index.js +0 -36
- package/src/decorators/index_sort.d.ts +0 -12
- package/src/decorators/index_sort.js +0 -43
- package/src/decorators/mutate.d.ts +0 -2
- package/src/decorators/mutate.js +0 -51
- package/src/decorators/name.d.ts +0 -1
- package/src/decorators/name.js +0 -28
- package/src/decorators/not_null.d.ts +0 -1
- package/src/decorators/not_null.js +0 -13
- package/src/decorators/primary_key.d.ts +0 -6
- package/src/decorators/primary_key.js +0 -30
- package/src/decorators/updated_at.d.ts +0 -12
- package/src/decorators/updated_at.js +0 -26
- package/src/decorators/validate.d.ts +0 -1
- package/src/decorators/validate.js +0 -53
- package/src/utils/batch-relations.d.ts +0 -14
- package/src/utils/batch-relations.js +0 -131
- package/src/utils/circular-detector.d.ts +0 -82
- package/src/utils/circular-detector.js +0 -212
- package/src/utils/memory-manager.d.ts +0 -42
- package/src/utils/memory-manager.js +0 -107
- package/src/utils/naming.d.ts +0 -8
- package/src/utils/naming.js +0 -18
- package/src/utils/projection.d.ts +0 -12
- package/src/utils/projection.js +0 -51
- package/src/utils/security-validator.d.ts +0 -49
- package/src/utils/security-validator.js +0 -163
- package/src/utils/throttle-manager.d.ts +0 -78
- package/src/utils/throttle-manager.js +0 -201
- package/src/utils/transaction-manager.d.ts +0 -88
- package/src/utils/transaction-manager.js +0 -300
package/package.json
CHANGED
|
@@ -47,7 +47,45 @@
|
|
|
47
47
|
"docs": "mkdocs gh-deploy --force",
|
|
48
48
|
"deploy": "npm version patch && npm publish --access=public",
|
|
49
49
|
"prepublishOnly": "yarn build",
|
|
50
|
-
"postpublish": "find src -type f \\( -name '*.js' -o -name '*.d.ts' -o -name '*.map' \\) -delete"
|
|
50
|
+
"postpublish": "find src -type f \\( -name '*.js' -o -name '*.d.ts' -o -name '*.map' \\) -delete",
|
|
51
|
+
"test": "yarn test:db:start && yarn test:run; yarn test:db:stop",
|
|
52
|
+
"test:run": "node --expose-gc ./node_modules/.bin/jest src/index.test.ts --runInBand --verbose",
|
|
53
|
+
"test:watch": "yarn test:db:start && node --expose-gc ./node_modules/.bin/jest src/index.test.ts --watch --runInBand",
|
|
54
|
+
"test:db:start": "docker run -d -p 8000:8000 --name dynamite-test-db amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb || echo 'DynamoDB ya está corriendo'",
|
|
55
|
+
"test:db:stop": "docker stop dynamite-test-db && docker rm dynamite-test-db || true",
|
|
56
|
+
"test:db:clean": "docker stop dynamite-test-db && docker rm dynamite-test-db; docker run -d -p 8000:8000 --name dynamite-test-db amazon/dynamodb-local -jar DynamoDBLocal.jar -sharedDb"
|
|
51
57
|
},
|
|
52
|
-
"version": "1.0.
|
|
58
|
+
"version": "1.0.19",
|
|
59
|
+
"jest": {
|
|
60
|
+
"preset": "ts-jest",
|
|
61
|
+
"testEnvironment": "node",
|
|
62
|
+
"roots": [
|
|
63
|
+
"<rootDir>/src"
|
|
64
|
+
],
|
|
65
|
+
"testMatch": [
|
|
66
|
+
"**/*.test.ts"
|
|
67
|
+
],
|
|
68
|
+
"moduleNameMapper": {
|
|
69
|
+
"^~/(.*)$": "<rootDir>/src/$1",
|
|
70
|
+
"^@type/(.*)$": "<rootDir>/src/@types/$1"
|
|
71
|
+
},
|
|
72
|
+
"transform": {
|
|
73
|
+
"^.+\\.ts$": [
|
|
74
|
+
"ts-jest",
|
|
75
|
+
{
|
|
76
|
+
"tsconfig": {
|
|
77
|
+
"experimentalDecorators": true,
|
|
78
|
+
"emitDecoratorMetadata": true,
|
|
79
|
+
"esModuleInterop": true,
|
|
80
|
+
"target": "ES2022",
|
|
81
|
+
"module": "commonjs"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"testTimeout": 60000,
|
|
87
|
+
"maxWorkers": 1,
|
|
88
|
+
"detectOpenHandles": false,
|
|
89
|
+
"forceExit": true
|
|
90
|
+
}
|
|
53
91
|
}
|
package/src/@types/index.d.ts
CHANGED
|
@@ -1,96 +1,137 @@
|
|
|
1
|
+
/*
|
|
2
|
+
@file index.ts
|
|
3
|
+
@descripcion Tipos públicos de Dynamite ORM
|
|
4
|
+
@autor Miguel Alejandro
|
|
5
|
+
@fecha 2025-08-07
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Brands para tipos especiales
|
|
1
9
|
export declare const HasManyBrand: unique symbol;
|
|
2
10
|
export declare const BelongsToBrand: unique symbol;
|
|
11
|
+
export declare const HasOneBrand: unique symbol;
|
|
3
12
|
export declare const NonAttributeBrand: unique symbol;
|
|
4
13
|
export declare const CreationOptionalBrand: unique symbol;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
export type BelongsTo<T> = (T | null) & {
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
export type
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export type CreationOptional<T> = T & {
|
|
15
|
-
[CreationOptionalBrand]?: true;
|
|
16
|
-
};
|
|
14
|
+
|
|
15
|
+
// Relaciones y atributos especiales
|
|
16
|
+
export type HasMany<T> = T[] & { [HasManyBrand]?: true };
|
|
17
|
+
export type BelongsTo<T> = (T | null) & { [BelongsToBrand]?: true };
|
|
18
|
+
export type HasOne<T> = (T | null) & { [HasOneBrand]?: true };
|
|
19
|
+
export type NonAttribute<T> = T & { [NonAttributeBrand]?: true };
|
|
20
|
+
export type CreationOptional<T> = T & { [CreationOptionalBrand]?: true };
|
|
21
|
+
|
|
22
|
+
// Atributos inferidos (excluye relaciones, non-attributes y funciones)
|
|
17
23
|
export type InferAttributes<T> = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
[K in keyof T as T[K] extends (...args: any[]) => any
|
|
25
|
+
? never
|
|
26
|
+
: T[K] extends { [HasManyBrand]?: true }
|
|
27
|
+
? never
|
|
28
|
+
: T[K] extends { [BelongsToBrand]?: true }
|
|
29
|
+
? never
|
|
30
|
+
: T[K] extends { [HasOneBrand]?: true }
|
|
31
|
+
? never
|
|
32
|
+
: T[K] extends { [NonAttributeBrand]?: true }
|
|
33
|
+
? never
|
|
34
|
+
: K]: T[K];
|
|
25
35
|
};
|
|
36
|
+
|
|
26
37
|
export type FilterableAttributes<T> = {
|
|
27
|
-
|
|
28
|
-
};
|
|
29
|
-
type SelectResult<T, A extends keyof T> = Pick<T, A>;
|
|
30
|
-
type ResolveIncludeType<T, K extends keyof T> = T[K] extends HasMany<infer U> ? U[] : T[K] extends BelongsTo<infer U> ? U | null : never;
|
|
31
|
-
export type IncludeOptions = {
|
|
32
|
-
where?: Record<string, any>;
|
|
33
|
-
attributes?: string[];
|
|
34
|
-
limit?: number;
|
|
35
|
-
skip?: number;
|
|
36
|
-
order?: "ASC" | "DESC";
|
|
38
|
+
[K in keyof InferAttributes<T>]: InferAttributes<T>[K];
|
|
37
39
|
};
|
|
38
|
-
|
|
39
|
-
[K in keyof I]: K extends keyof T ? ResolveIncludeType<T, K> : never;
|
|
40
|
-
}, A>;
|
|
40
|
+
|
|
41
41
|
export type WhereOptions<T> = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
where?: Partial<FilterableAttributes<T>>;
|
|
43
|
+
skip?: number;
|
|
44
|
+
limit?: number;
|
|
45
|
+
order?: "ASC" | "DESC";
|
|
46
|
+
attributes?: (keyof FilterableAttributes<T>)[];
|
|
47
|
+
include?: {
|
|
48
|
+
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> | HasOne<any>
|
|
49
|
+
? IncludeRelationOptions | true
|
|
50
|
+
: never;
|
|
51
|
+
};
|
|
50
52
|
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
|
|
54
|
+
/** Filtros de query sin cláusula where */
|
|
55
|
+
export type QueryFilters<T> = Omit<WhereOptions<T>, "where">;
|
|
56
|
+
|
|
57
|
+
/** @deprecated Usa QueryFilters */
|
|
58
|
+
export type WhereOptionsWithoutWhere<T> = QueryFilters<T>;
|
|
59
|
+
|
|
60
|
+
export type QueryOperator =
|
|
61
|
+
| "="
|
|
62
|
+
| "!="
|
|
63
|
+
| "<"
|
|
64
|
+
| "<="
|
|
65
|
+
| ">"
|
|
66
|
+
| ">="
|
|
67
|
+
| "in"
|
|
68
|
+
| "not-in"
|
|
69
|
+
| "contains"
|
|
70
|
+
| "begins-with";
|
|
71
|
+
|
|
72
|
+
/** Entrada de validador con soporte lazy */
|
|
73
|
+
export interface ValidatorEntry {
|
|
74
|
+
fn: (value: any) => boolean | string;
|
|
75
|
+
lazy?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Tipos para core/wrapper
|
|
53
79
|
export interface Column {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
name: string;
|
|
81
|
+
index?: true;
|
|
82
|
+
indexSort?: true;
|
|
83
|
+
primaryKey?: boolean;
|
|
84
|
+
nullable?: boolean;
|
|
85
|
+
unique?: true;
|
|
86
|
+
createdAt?: boolean;
|
|
87
|
+
updatedAt?: boolean;
|
|
88
|
+
softDelete?: boolean;
|
|
89
|
+
lazy_validators?: ((value: any) => boolean | string)[];
|
|
90
|
+
relation?: RelationMetadata;
|
|
65
91
|
}
|
|
92
|
+
|
|
66
93
|
export interface RelationMetadata {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
94
|
+
type: "hasMany" | "belongsTo" | "hasOne";
|
|
95
|
+
targetModel: () => any;
|
|
96
|
+
foreignKey: string;
|
|
97
|
+
localKey?: string;
|
|
71
98
|
}
|
|
99
|
+
|
|
72
100
|
export interface WrapperEntry {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
101
|
+
name: string;
|
|
102
|
+
columns: Map<string | symbol, Column>;
|
|
103
|
+
relations: Map<string | symbol, RelationMetadata>;
|
|
76
104
|
}
|
|
105
|
+
|
|
106
|
+
/** Alias para WrapperEntry (usado internamente con SCHEMA symbol) */
|
|
107
|
+
export type SchemaEntry = WrapperEntry;
|
|
108
|
+
|
|
109
|
+
// Tipos internos del where de Table
|
|
77
110
|
export type IncludeRelationOptions = {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
where?: Record<string, any>;
|
|
112
|
+
attributes?: string[];
|
|
113
|
+
order?: "ASC" | "DESC";
|
|
114
|
+
skip?: number;
|
|
115
|
+
limit?: number;
|
|
116
|
+
include?: Record<string, IncludeRelationOptions | true>;
|
|
84
117
|
};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
118
|
+
|
|
119
|
+
/** Opciones de query para métodos where(), first(), last(), etc. */
|
|
120
|
+
export type QueryOptions<T> = {
|
|
121
|
+
order?: "ASC" | "DESC";
|
|
122
|
+
skip?: number;
|
|
123
|
+
limit?: number;
|
|
124
|
+
attributes?: (keyof InferAttributes<T>)[];
|
|
125
|
+
include?: {
|
|
126
|
+
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> | HasOne<any>
|
|
127
|
+
? IncludeRelationOptions | true
|
|
128
|
+
: never;
|
|
129
|
+
};
|
|
93
130
|
};
|
|
131
|
+
|
|
132
|
+
/** @deprecated Usa QueryOptions */
|
|
133
|
+
export type WhereQueryOptions<T> = QueryOptions<T>;
|
|
134
|
+
|
|
135
|
+
// Tipos utilitarios para decoradores
|
|
94
136
|
export type Mutate = (value: any) => any;
|
|
95
137
|
export type Validate = (value: any) => boolean | string;
|
|
96
|
-
export {};
|
package/src/core/client.d.ts
CHANGED
|
@@ -44,6 +44,20 @@ export declare class Dynamite {
|
|
|
44
44
|
* Disconnect and cleanup DynamoDB client
|
|
45
45
|
*/
|
|
46
46
|
disconnect(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Ejecutar operaciones en una transacción atómica.
|
|
49
|
+
* Si cualquier operación falla, todas se revierten automáticamente.
|
|
50
|
+
*
|
|
51
|
+
* @param callback Función que recibe el contexto de transacción
|
|
52
|
+
* @returns Resultado del callback
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* await dynamite.tx(async (tx) => {
|
|
56
|
+
* const user = await User.create({ name: "Juan" }, tx);
|
|
57
|
+
* await Order.create({ user_id: user.id, total: 100 }, tx);
|
|
58
|
+
* });
|
|
59
|
+
*/
|
|
60
|
+
tx<R>(callback: (tx: TransactionContext) => Promise<R>): Promise<R>;
|
|
47
61
|
/**
|
|
48
62
|
* Create table with automatic GSI detection and creation
|
|
49
63
|
* @param ctor Table class constructor
|
|
@@ -67,3 +81,25 @@ export declare const hasGlobalClient: () => boolean;
|
|
|
67
81
|
* Require global client for Table operations (throws if not available)
|
|
68
82
|
*/
|
|
69
83
|
export declare const requireClient: () => DynamoDBClient;
|
|
84
|
+
/**
|
|
85
|
+
* Contexto de transacción para agrupar operaciones atómicas.
|
|
86
|
+
* Máximo 25 operaciones por transacción (límite DynamoDB).
|
|
87
|
+
*/
|
|
88
|
+
export declare class TransactionContext {
|
|
89
|
+
private operations;
|
|
90
|
+
private client;
|
|
91
|
+
constructor(client: DynamoDBClient);
|
|
92
|
+
/**
|
|
93
|
+
* Agregar operación Put a la transacción
|
|
94
|
+
*/
|
|
95
|
+
addPut(table_name: string, item: Record<string, any>): void;
|
|
96
|
+
/**
|
|
97
|
+
* Agregar operación Delete a la transacción
|
|
98
|
+
*/
|
|
99
|
+
addDelete(table_name: string, key: Record<string, any>): void;
|
|
100
|
+
/**
|
|
101
|
+
* Confirmar todas las operaciones de la transacción.
|
|
102
|
+
* Si alguna falla, todas se revierten.
|
|
103
|
+
*/
|
|
104
|
+
commit(): Promise<void>;
|
|
105
|
+
}
|
package/src/core/client.js
CHANGED
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
* @autor Miguel Alejandro
|
|
6
6
|
* @fecha 2025-01-27
|
|
7
7
|
*/
|
|
8
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
-
};
|
|
11
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.requireClient = exports.hasGlobalClient = exports.getGlobalClient = exports.setGlobalClient = exports.Dynamite = void 0;
|
|
9
|
+
exports.TransactionContext = exports.requireClient = exports.hasGlobalClient = exports.getGlobalClient = exports.setGlobalClient = exports.Dynamite = void 0;
|
|
13
10
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
14
|
-
const
|
|
11
|
+
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
12
|
+
const decorator_1 = require("./decorator");
|
|
15
13
|
/**
|
|
16
14
|
* Centralized Dynamite client for managing DynamoDB connections and table synchronization
|
|
17
15
|
*/
|
|
@@ -72,39 +70,47 @@ class Dynamite {
|
|
|
72
70
|
console.warn("Error during Dynamite disconnect:", error);
|
|
73
71
|
}
|
|
74
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Ejecutar operaciones en una transacción atómica.
|
|
75
|
+
* Si cualquier operación falla, todas se revierten automáticamente.
|
|
76
|
+
*
|
|
77
|
+
* @param callback Función que recibe el contexto de transacción
|
|
78
|
+
* @returns Resultado del callback
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* await dynamite.tx(async (tx) => {
|
|
82
|
+
* const user = await User.create({ name: "Juan" }, tx);
|
|
83
|
+
* await Order.create({ user_id: user.id, total: 100 }, tx);
|
|
84
|
+
* });
|
|
85
|
+
*/
|
|
86
|
+
async tx(callback) {
|
|
87
|
+
const ctx = new TransactionContext(this.client);
|
|
88
|
+
const result = await callback(ctx);
|
|
89
|
+
await ctx.commit();
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
75
92
|
/**
|
|
76
93
|
* Create table with automatic GSI detection and creation
|
|
77
94
|
* @param ctor Table class constructor
|
|
78
95
|
*/
|
|
79
96
|
async createTableWithGSI(ctor) {
|
|
80
|
-
const meta =
|
|
97
|
+
const meta = ctor[decorator_1.SCHEMA];
|
|
81
98
|
if (!meta)
|
|
82
|
-
throw new Error(`Class ${ctor.name} not registered
|
|
83
|
-
const cols =
|
|
84
|
-
const pk = cols.find((c) => c.index);
|
|
99
|
+
throw new Error(`Class ${ctor.name} not registered. Use decorators.`);
|
|
100
|
+
const cols = Object.values(meta.columns);
|
|
101
|
+
const pk = cols.find((c) => c.store?.index);
|
|
85
102
|
if (!pk)
|
|
86
103
|
throw new Error(`PartitionKey missing in ${ctor.name}`);
|
|
87
|
-
const sk = cols.find((c) => c.indexSort);
|
|
104
|
+
const sk = cols.find((c) => c.store?.indexSort);
|
|
88
105
|
const attr = new Map();
|
|
89
|
-
attr.set(pk.name, "S");
|
|
106
|
+
attr.set(pk.name || 'id', "S");
|
|
90
107
|
if (sk)
|
|
91
|
-
attr.set(sk.name, "S");
|
|
92
|
-
|
|
93
|
-
const gsiDefinitions = [
|
|
94
|
-
|
|
95
|
-
.map((relation) => {
|
|
96
|
-
const { foreignKey } = relation;
|
|
97
|
-
if (!attr.has(foreignKey))
|
|
98
|
-
attr.set(foreignKey, "S");
|
|
99
|
-
return {
|
|
100
|
-
IndexName: `GSI${gsiIndex++}_${foreignKey}`,
|
|
101
|
-
KeySchema: [{ AttributeName: foreignKey, KeyType: "HASH" }],
|
|
102
|
-
Projection: { ProjectionType: "ALL" },
|
|
103
|
-
};
|
|
104
|
-
});
|
|
105
|
-
const schema = [{ AttributeName: pk.name, KeyType: "HASH" }];
|
|
108
|
+
attr.set(sk.name || 'id', "S");
|
|
109
|
+
// Temporalmente deshabilitamos la creación automática de GSI hasta implementar relaciones
|
|
110
|
+
const gsiDefinitions = [];
|
|
111
|
+
const schema = [{ AttributeName: pk.name || 'id', KeyType: "HASH" }];
|
|
106
112
|
if (sk && sk.name !== pk.name)
|
|
107
|
-
schema.push({ AttributeName: sk.name, KeyType: "RANGE" });
|
|
113
|
+
schema.push({ AttributeName: sk.name || 'id', KeyType: "RANGE" });
|
|
108
114
|
try {
|
|
109
115
|
await this.client.send(new client_dynamodb_1.CreateTableCommand({
|
|
110
116
|
TableName: meta.name,
|
|
@@ -161,4 +167,51 @@ const requireClient = () => {
|
|
|
161
167
|
return globalClient;
|
|
162
168
|
};
|
|
163
169
|
exports.requireClient = requireClient;
|
|
170
|
+
/**
|
|
171
|
+
* Contexto de transacción para agrupar operaciones atómicas.
|
|
172
|
+
* Máximo 25 operaciones por transacción (límite DynamoDB).
|
|
173
|
+
*/
|
|
174
|
+
class TransactionContext {
|
|
175
|
+
constructor(client) {
|
|
176
|
+
this.operations = [];
|
|
177
|
+
this.client = client;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Agregar operación Put a la transacción
|
|
181
|
+
*/
|
|
182
|
+
addPut(table_name, item) {
|
|
183
|
+
this.operations.push({
|
|
184
|
+
Put: {
|
|
185
|
+
TableName: table_name,
|
|
186
|
+
Item: (0, util_dynamodb_1.marshall)(item, { removeUndefinedValues: true }),
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Agregar operación Delete a la transacción
|
|
192
|
+
*/
|
|
193
|
+
addDelete(table_name, key) {
|
|
194
|
+
this.operations.push({
|
|
195
|
+
Delete: {
|
|
196
|
+
TableName: table_name,
|
|
197
|
+
Key: (0, util_dynamodb_1.marshall)(key),
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Confirmar todas las operaciones de la transacción.
|
|
203
|
+
* Si alguna falla, todas se revierten.
|
|
204
|
+
*/
|
|
205
|
+
async commit() {
|
|
206
|
+
if (this.operations.length === 0)
|
|
207
|
+
return;
|
|
208
|
+
if (this.operations.length > 25) {
|
|
209
|
+
throw new Error(`Transacción excede el límite de 25 operaciones (tiene ${this.operations.length})`);
|
|
210
|
+
}
|
|
211
|
+
await this.client.send(new client_dynamodb_1.TransactWriteItemsCommand({
|
|
212
|
+
TransactItems: this.operations,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
exports.TransactionContext = TransactionContext;
|
|
164
217
|
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file decorator.ts
|
|
3
|
+
* @description Sistema de decoradores minimalista con Symbol storage
|
|
4
|
+
* @autor Miguel Alejandro
|
|
5
|
+
* @fecha 2025-01-28
|
|
6
|
+
*/
|
|
7
|
+
export declare const SCHEMA: unique symbol;
|
|
8
|
+
export declare const VALUES: unique symbol;
|
|
9
|
+
declare function toSnakePlural(str: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* @description Factory para crear decoradores con soporte de argumentos y composición
|
|
12
|
+
* @param callback Función que recibe (schema, col, params) para configurar la columna
|
|
13
|
+
* @returns Función que acepta argumentos y retorna PropertyDecorator
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* const Default = decorator((schema, col, params) => {
|
|
17
|
+
* const fallback = params[0];
|
|
18
|
+
* col.get.push((value) => value ?? fallback());
|
|
19
|
+
* });
|
|
20
|
+
* // Uso: @Default(uuid)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function decorator(callback: (schema: any, col: any, params: any[]) => void): (...params: any[]) => (target: any, propertyKey: string | symbol) => void;
|
|
24
|
+
/**
|
|
25
|
+
* @description Factory para decoradores de relación
|
|
26
|
+
* @param relation_type Tipo de relación ("hasMany", "hasOne", "belongsTo")
|
|
27
|
+
* @param RelatedTable Clase de la tabla relacionada
|
|
28
|
+
* @param options Opciones de relación (foreignKey, localKey)
|
|
29
|
+
* @returns PropertyDecorator
|
|
30
|
+
*/
|
|
31
|
+
export declare function relationDecorator(relation_type: string, RelatedTable: any, options?: any): (target: any, propertyKey: string | symbol) => void;
|
|
32
|
+
/**
|
|
33
|
+
* @description Función helper para obtener metadatos del esquema
|
|
34
|
+
* @param ctor Constructor de la clase
|
|
35
|
+
* @returns SchemaEntry
|
|
36
|
+
*/
|
|
37
|
+
export declare function getSchema(ctor: Function): any;
|
|
38
|
+
/**
|
|
39
|
+
* @description Función helper para verificar que existe esquema
|
|
40
|
+
* @param ctor Constructor de la clase
|
|
41
|
+
* @returns SchemaEntry
|
|
42
|
+
*/
|
|
43
|
+
export declare function ensureSchema(ctor: Function): any;
|
|
44
|
+
export { toSnakePlural };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file decorator.ts
|
|
4
|
+
* @description Sistema de decoradores minimalista con Symbol storage
|
|
5
|
+
* @autor Miguel Alejandro
|
|
6
|
+
* @fecha 2025-01-28
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.VALUES = exports.SCHEMA = void 0;
|
|
10
|
+
exports.decorator = decorator;
|
|
11
|
+
exports.relationDecorator = relationDecorator;
|
|
12
|
+
exports.getSchema = getSchema;
|
|
13
|
+
exports.ensureSchema = ensureSchema;
|
|
14
|
+
exports.toSnakePlural = toSnakePlural;
|
|
15
|
+
// Symbols para autocontención (exportados desde table.ts pero también aquí compatibilidad)
|
|
16
|
+
exports.SCHEMA = Symbol('dynamite:schema');
|
|
17
|
+
exports.VALUES = Symbol('dynamite:values');
|
|
18
|
+
// Helper simple para snake_case plural
|
|
19
|
+
function toSnakePlural(str) {
|
|
20
|
+
const snake = str
|
|
21
|
+
.replace(/([A-Z])/g, "_$1")
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/^_/, "");
|
|
24
|
+
return snake.endsWith("s") ? snake : snake + "s";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @description Factory para crear decoradores con soporte de argumentos y composición
|
|
28
|
+
* @param callback Función que recibe (schema, col, params) para configurar la columna
|
|
29
|
+
* @returns Función que acepta argumentos y retorna PropertyDecorator
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const Default = decorator((schema, col, params) => {
|
|
33
|
+
* const fallback = params[0];
|
|
34
|
+
* col.get.push((value) => value ?? fallback());
|
|
35
|
+
* });
|
|
36
|
+
* // Uso: @Default(uuid)
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function decorator(callback) {
|
|
40
|
+
return (...params) => {
|
|
41
|
+
return function (target, propertyKey) {
|
|
42
|
+
const table_class = target.constructor;
|
|
43
|
+
const column_name = String(propertyKey);
|
|
44
|
+
// Inicializar SCHEMA si no existe
|
|
45
|
+
if (!table_class[exports.SCHEMA]) {
|
|
46
|
+
table_class[exports.SCHEMA] = {
|
|
47
|
+
name: toSnakePlural(table_class.name),
|
|
48
|
+
primary_key: 'id',
|
|
49
|
+
columns: {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Crear columna si no existe
|
|
53
|
+
if (!table_class[exports.SCHEMA].columns[column_name]) {
|
|
54
|
+
table_class[exports.SCHEMA].columns[column_name] = {
|
|
55
|
+
name: column_name,
|
|
56
|
+
get: [],
|
|
57
|
+
set: [],
|
|
58
|
+
store: {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const col = table_class[exports.SCHEMA].columns[column_name];
|
|
62
|
+
// Ejecutar callback con (schema, col, params)
|
|
63
|
+
callback(table_class, col, params);
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* @description Factory para decoradores de relación
|
|
69
|
+
* @param relation_type Tipo de relación ("hasMany", "hasOne", "belongsTo")
|
|
70
|
+
* @param RelatedTable Clase de la tabla relacionada
|
|
71
|
+
* @param options Opciones de relación (foreignKey, localKey)
|
|
72
|
+
* @returns PropertyDecorator
|
|
73
|
+
*/
|
|
74
|
+
function relationDecorator(relation_type, RelatedTable, options = {}) {
|
|
75
|
+
return function (target, propertyKey) {
|
|
76
|
+
const table_class = target.constructor;
|
|
77
|
+
const column_name = String(propertyKey);
|
|
78
|
+
if (!table_class[exports.SCHEMA]) {
|
|
79
|
+
table_class[exports.SCHEMA] = {
|
|
80
|
+
name: toSnakePlural(table_class.name),
|
|
81
|
+
primary_key: 'id',
|
|
82
|
+
columns: {}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// **Validación: No sobreescribir columnas existentes**
|
|
86
|
+
if (table_class[exports.SCHEMA].columns[column_name]) {
|
|
87
|
+
throw new Error(`Column '${column_name}' already exists. Cannot apply relation decorator.`);
|
|
88
|
+
}
|
|
89
|
+
// Crear columna virtual para relación
|
|
90
|
+
table_class[exports.SCHEMA].columns[column_name] = {
|
|
91
|
+
name: column_name,
|
|
92
|
+
get: [],
|
|
93
|
+
set: [],
|
|
94
|
+
store: {
|
|
95
|
+
relation: {
|
|
96
|
+
type: relation_type,
|
|
97
|
+
target: RelatedTable,
|
|
98
|
+
foreignKey: options.foreignKey,
|
|
99
|
+
localKey: options.localKey,
|
|
100
|
+
nullable: options.nullable
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* @description Función helper para obtener metadatos del esquema
|
|
108
|
+
* @param ctor Constructor de la clase
|
|
109
|
+
* @returns SchemaEntry
|
|
110
|
+
*/
|
|
111
|
+
function getSchema(ctor) {
|
|
112
|
+
const schema = ctor[exports.SCHEMA];
|
|
113
|
+
if (!schema) {
|
|
114
|
+
throw new Error(`Schema not found for ${ctor.name}. Use decorators first.`);
|
|
115
|
+
}
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @description Función helper para verificar que existe esquema
|
|
120
|
+
* @param ctor Constructor de la clase
|
|
121
|
+
* @returns SchemaEntry
|
|
122
|
+
*/
|
|
123
|
+
function ensureSchema(ctor) {
|
|
124
|
+
if (!ctor[exports.SCHEMA]) {
|
|
125
|
+
ctor[exports.SCHEMA] = {
|
|
126
|
+
name: toSnakePlural(ctor.name),
|
|
127
|
+
primary_key: 'id',
|
|
128
|
+
columns: {}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return ctor[exports.SCHEMA];
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=decorator.js.map
|