@arcaelas/dynamite 1.0.17 → 1.0.18
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 +27 -21
- 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 +9 -16
- package/src/index.js +35 -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/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.18",
|
|
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,5 +1,6 @@
|
|
|
1
1
|
export declare const HasManyBrand: unique symbol;
|
|
2
2
|
export declare const BelongsToBrand: unique symbol;
|
|
3
|
+
export declare const HasOneBrand: unique symbol;
|
|
3
4
|
export declare const NonAttributeBrand: unique symbol;
|
|
4
5
|
export declare const CreationOptionalBrand: unique symbol;
|
|
5
6
|
export type HasMany<T> = T[] & {
|
|
@@ -8,6 +9,9 @@ export type HasMany<T> = T[] & {
|
|
|
8
9
|
export type BelongsTo<T> = (T | null) & {
|
|
9
10
|
[BelongsToBrand]?: true;
|
|
10
11
|
};
|
|
12
|
+
export type HasOne<T> = (T | null) & {
|
|
13
|
+
[HasOneBrand]?: true;
|
|
14
|
+
};
|
|
11
15
|
export type NonAttribute<T> = T & {
|
|
12
16
|
[NonAttributeBrand]?: true;
|
|
13
17
|
};
|
|
@@ -19,6 +23,8 @@ export type InferAttributes<T> = {
|
|
|
19
23
|
[HasManyBrand]?: true;
|
|
20
24
|
} ? never : T[K] extends {
|
|
21
25
|
[BelongsToBrand]?: true;
|
|
26
|
+
} ? never : T[K] extends {
|
|
27
|
+
[HasOneBrand]?: true;
|
|
22
28
|
} ? never : T[K] extends {
|
|
23
29
|
[NonAttributeBrand]?: true;
|
|
24
30
|
} ? never : K]: T[K];
|
|
@@ -26,18 +32,6 @@ export type InferAttributes<T> = {
|
|
|
26
32
|
export type FilterableAttributes<T> = {
|
|
27
33
|
[K in keyof InferAttributes<T>]: InferAttributes<T>[K];
|
|
28
34
|
};
|
|
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";
|
|
37
|
-
};
|
|
38
|
-
export type QueryResult<T, A extends keyof T = keyof T, I extends Record<string, any> = {}> = SelectResult<T & {
|
|
39
|
-
[K in keyof I]: K extends keyof T ? ResolveIncludeType<T, K> : never;
|
|
40
|
-
}, A>;
|
|
41
35
|
export type WhereOptions<T> = {
|
|
42
36
|
where?: Partial<FilterableAttributes<T>>;
|
|
43
37
|
skip?: number;
|
|
@@ -45,16 +39,21 @@ export type WhereOptions<T> = {
|
|
|
45
39
|
order?: "ASC" | "DESC";
|
|
46
40
|
attributes?: (keyof FilterableAttributes<T>)[];
|
|
47
41
|
include?: {
|
|
48
|
-
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> ?
|
|
42
|
+
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> | HasOne<any> ? IncludeRelationOptions | true : never;
|
|
49
43
|
};
|
|
50
44
|
};
|
|
51
|
-
|
|
45
|
+
/** Filtros de query sin cláusula where */
|
|
46
|
+
export type QueryFilters<T> = Omit<WhereOptions<T>, "where">;
|
|
47
|
+
/** @deprecated Usa QueryFilters */
|
|
48
|
+
export type WhereOptionsWithoutWhere<T> = QueryFilters<T>;
|
|
52
49
|
export type QueryOperator = "=" | "!=" | "<" | "<=" | ">" | ">=" | "in" | "not-in" | "contains" | "begins-with";
|
|
50
|
+
/** Entrada de validador con soporte lazy */
|
|
51
|
+
export interface ValidatorEntry {
|
|
52
|
+
fn: (value: any) => boolean | string;
|
|
53
|
+
lazy?: boolean;
|
|
54
|
+
}
|
|
53
55
|
export interface Column {
|
|
54
56
|
name: string;
|
|
55
|
-
default?: any | (() => any);
|
|
56
|
-
mutate?: ((value: any) => any)[];
|
|
57
|
-
validate?: ((value: any) => boolean | string)[];
|
|
58
57
|
index?: true;
|
|
59
58
|
indexSort?: true;
|
|
60
59
|
primaryKey?: boolean;
|
|
@@ -62,9 +61,12 @@ export interface Column {
|
|
|
62
61
|
unique?: true;
|
|
63
62
|
createdAt?: boolean;
|
|
64
63
|
updatedAt?: boolean;
|
|
64
|
+
softDelete?: boolean;
|
|
65
|
+
lazy_validators?: ((value: any) => boolean | string)[];
|
|
66
|
+
relation?: RelationMetadata;
|
|
65
67
|
}
|
|
66
68
|
export interface RelationMetadata {
|
|
67
|
-
type: "hasMany" | "belongsTo";
|
|
69
|
+
type: "hasMany" | "belongsTo" | "hasOne";
|
|
68
70
|
targetModel: () => any;
|
|
69
71
|
foreignKey: string;
|
|
70
72
|
localKey?: string;
|
|
@@ -74,6 +76,8 @@ export interface WrapperEntry {
|
|
|
74
76
|
columns: Map<string | symbol, Column>;
|
|
75
77
|
relations: Map<string | symbol, RelationMetadata>;
|
|
76
78
|
}
|
|
79
|
+
/** Alias para WrapperEntry (usado internamente con SCHEMA symbol) */
|
|
80
|
+
export type SchemaEntry = WrapperEntry;
|
|
77
81
|
export type IncludeRelationOptions = {
|
|
78
82
|
where?: Record<string, any>;
|
|
79
83
|
attributes?: string[];
|
|
@@ -82,15 +86,17 @@ export type IncludeRelationOptions = {
|
|
|
82
86
|
limit?: number;
|
|
83
87
|
include?: Record<string, IncludeRelationOptions | true>;
|
|
84
88
|
};
|
|
85
|
-
|
|
89
|
+
/** Opciones de query para métodos where(), first(), last(), etc. */
|
|
90
|
+
export type QueryOptions<T> = {
|
|
86
91
|
order?: "ASC" | "DESC";
|
|
87
92
|
skip?: number;
|
|
88
93
|
limit?: number;
|
|
89
94
|
attributes?: (keyof InferAttributes<T>)[];
|
|
90
95
|
include?: {
|
|
91
|
-
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> ? IncludeRelationOptions | true : never;
|
|
96
|
+
[K in keyof T]?: T[K] extends HasMany<any> | BelongsTo<any> | HasOne<any> ? IncludeRelationOptions | true : never;
|
|
92
97
|
};
|
|
93
98
|
};
|
|
99
|
+
/** @deprecated Usa QueryOptions */
|
|
100
|
+
export type WhereQueryOptions<T> = QueryOptions<T>;
|
|
94
101
|
export type Mutate = (value: any) => any;
|
|
95
102
|
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
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file method.ts
|
|
3
|
+
* @descripcion Sistema de métodos extensibles para Table
|
|
4
|
+
* @autor Miguel Alejandro
|
|
5
|
+
*/
|
|
6
|
+
/** Handler para métodos estáticos: (TableClass, args) => result */
|
|
7
|
+
export type StaticMethodHandler<R = any> = (table: any, args: any[]) => R | Promise<R>;
|
|
8
|
+
/** Handler para métodos de instancia: (TableClass, instance, args) => result */
|
|
9
|
+
export type InstanceMethodHandler<R = any> = (table: any, instance: any, args: any[]) => R | Promise<R>;
|
|
10
|
+
/**
|
|
11
|
+
* @description Configura la clase Table para el sistema de métodos
|
|
12
|
+
* @param TableClass Clase Table
|
|
13
|
+
*/
|
|
14
|
+
export declare function setTableClass(TableClass: any): void;
|
|
15
|
+
/**
|
|
16
|
+
* @description Registra un método estático en Table y lo define en la clase
|
|
17
|
+
* @param name Nombre del método
|
|
18
|
+
* @param handler Función que implementa el método
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* registerStaticMethod('customFind', function(t, args) {
|
|
22
|
+
* const meta = mustMeta(t);
|
|
23
|
+
* return instances;
|
|
24
|
+
* });
|
|
25
|
+
* // Uso: User.customFind(...)
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerStaticMethod<R = any>(name: string, handler: StaticMethodHandler<R>): void;
|
|
29
|
+
/**
|
|
30
|
+
* @description Registra un método de instancia en Table y lo define en el prototype
|
|
31
|
+
* @param name Nombre del método
|
|
32
|
+
* @param handler Función que implementa el método
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* registerInstanceMethod('duplicate', function(t, m, args) {
|
|
36
|
+
* return t.create({ ...m.toJSON(), id: undefined });
|
|
37
|
+
* });
|
|
38
|
+
* // Uso: user.duplicate()
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function registerInstanceMethod<R = any>(name: string, handler: InstanceMethodHandler<R>): void;
|
|
42
|
+
/**
|
|
43
|
+
* @description Obtiene un método estático registrado
|
|
44
|
+
*/
|
|
45
|
+
export declare function getStaticMethod(name: string): StaticMethodHandler | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* @description Obtiene un método de instancia registrado
|
|
48
|
+
*/
|
|
49
|
+
export declare function getInstanceMethod(name: string): InstanceMethodHandler | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* @description Ejecuta un método estático registrado
|
|
52
|
+
*/
|
|
53
|
+
export declare function callStaticMethod<R = any>(name: string, table: any, args: any[]): R | Promise<R>;
|
|
54
|
+
/**
|
|
55
|
+
* @description Ejecuta un método de instancia registrado
|
|
56
|
+
*/
|
|
57
|
+
export declare function callInstanceMethod<R = any>(name: string, table: any, instance: any, args: any[]): R | Promise<R>;
|
|
58
|
+
/**
|
|
59
|
+
* @description Verifica si un método estático está registrado
|
|
60
|
+
*/
|
|
61
|
+
export declare function hasStaticMethod(name: string): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* @description Verifica si un método de instancia está registrado
|
|
64
|
+
*/
|
|
65
|
+
export declare function hasInstanceMethod(name: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* @description Lista todos los métodos estáticos registrados
|
|
68
|
+
*/
|
|
69
|
+
export declare function listStaticMethods(): string[];
|
|
70
|
+
/**
|
|
71
|
+
* @description Lista todos los métodos de instancia registrados
|
|
72
|
+
*/
|
|
73
|
+
export declare function listInstanceMethods(): string[];
|