@arcaelas/dynamite 1.0.19 → 1.0.23
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 +11 -42
- package/src/scripts/load_seed.d.ts +5 -0
- package/src/scripts/load_seed.js +54 -0
- package/src/src/@types/index.d.ts +188 -0
- package/src/src/@types/index.js +9 -0
- package/src/{core → src/core}/client.d.ts +4 -16
- package/src/{core → src/core}/client.js +115 -38
- package/src/{core → src/core}/decorator.d.ts +0 -15
- package/src/{core → src/core}/decorator.js +5 -35
- package/src/src/core/table.d.ts +81 -0
- package/src/src/core/table.js +892 -0
- package/src/{decorators → src/decorators}/indexes.d.ts +1 -1
- package/src/{decorators → src/decorators}/indexes.js +12 -20
- package/src/{decorators → src/decorators}/relations.d.ts +20 -1
- package/src/{decorators → src/decorators}/relations.js +32 -2
- package/src/{decorators → src/decorators}/timestamps.d.ts +4 -4
- package/src/{decorators → src/decorators}/timestamps.js +11 -6
- package/src/{decorators → src/decorators}/transforms.d.ts +17 -4
- package/src/{decorators → src/decorators}/transforms.js +40 -28
- package/src/src/index.d.ts +15 -0
- package/src/{index.js → src/index.js} +8 -22
- package/src/src/index.test.d.ts +6 -0
- package/src/src/index.test.js +789 -0
- package/src/{utils → src/utils}/relations.d.ts +5 -3
- package/src/src/utils/relations.js +216 -0
- package/src/@types/index.d.ts +0 -137
- package/src/core/method.d.ts +0 -73
- package/src/core/method.js +0 -140
- package/src/core/table.d.ts +0 -56
- package/src/core/table.js +0 -659
- package/src/index.d.ts +0 -16
- package/src/index.test.d.ts +0 -13
- package/src/index.test.js +0 -1992
- package/src/utils/relations.js +0 -141
package/package.json
CHANGED
|
@@ -9,22 +9,20 @@
|
|
|
9
9
|
"url": "https://github.com/arcaelas/dynamite/issues"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
+
"@arcaelas/utils": "^2.0.8",
|
|
12
13
|
"@aws-sdk/client-dynamodb": "3.329.0",
|
|
13
14
|
"@aws-sdk/lib-dynamodb": "3.329.0",
|
|
14
15
|
"pluralize": "^8.0.0",
|
|
15
16
|
"uuid": "^11.1.0"
|
|
16
17
|
},
|
|
17
18
|
"devDependencies": {
|
|
18
|
-
"@
|
|
19
|
+
"@eslint/js": "^9.39.1",
|
|
19
20
|
"@types/node": "^24.0.6",
|
|
20
|
-
"
|
|
21
|
-
"reflect-metadata": "^0.2.2",
|
|
22
|
-
"ts-jest": "^29.4.0",
|
|
23
|
-
"ts-node": "^10.9.2",
|
|
21
|
+
"globals": "^16.5.0",
|
|
24
22
|
"tsc-alias": "^1.8.16",
|
|
25
|
-
"tsconfig-paths": "^4.2.0",
|
|
26
23
|
"tsx": "^4.20.3",
|
|
27
|
-
"typescript": "^5.8.3"
|
|
24
|
+
"typescript": "^5.8.3",
|
|
25
|
+
"typescript-eslint": "^8.49.0"
|
|
28
26
|
},
|
|
29
27
|
"files": [
|
|
30
28
|
"src/**/*.js",
|
|
@@ -48,44 +46,15 @@
|
|
|
48
46
|
"deploy": "npm version patch && npm publish --access=public",
|
|
49
47
|
"prepublishOnly": "yarn build",
|
|
50
48
|
"postpublish": "find src -type f \\( -name '*.js' -o -name '*.d.ts' -o -name '*.map' \\) -delete",
|
|
49
|
+
"seed:generate": "tsx scripts/generate_seed.ts",
|
|
50
|
+
"seed:load": "tsx scripts/load_seed.ts",
|
|
51
|
+
"seed:full": "yarn seed:generate && yarn seed:load",
|
|
51
52
|
"test": "yarn test:db:start && yarn test:run; yarn test:db:stop",
|
|
52
|
-
"test:run": "
|
|
53
|
-
"test:watch": "yarn test:db:start &&
|
|
53
|
+
"test:run": "NODE_OPTIONS='--expose-gc' tsx --tsconfig tsx.config.json src/index.test.ts",
|
|
54
|
+
"test:watch": "yarn test:db:start && NODE_OPTIONS='--expose-gc' tsx watch src/index.test.ts",
|
|
54
55
|
"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
56
|
"test:db:stop": "docker stop dynamite-test-db && docker rm dynamite-test-db || true",
|
|
56
57
|
"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"
|
|
57
58
|
},
|
|
58
|
-
"version": "1.0.
|
|
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
|
-
}
|
|
59
|
+
"version": "1.0.23"
|
|
91
60
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file load_seed.ts
|
|
4
|
+
* @description Carga archivos JSON de seed en DynamoDB con inserción por lotes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.load_all = load_all;
|
|
8
|
+
const promises_1 = require("fs/promises");
|
|
9
|
+
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
10
|
+
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
11
|
+
const client_1 = require("../src/core/client");
|
|
12
|
+
async function load_file(filepath, table_name) {
|
|
13
|
+
const file_content = await (0, promises_1.readFile)(filepath, "utf-8");
|
|
14
|
+
const json = JSON.parse(file_content);
|
|
15
|
+
const client = (0, client_1.requireClient)();
|
|
16
|
+
console.log(` Total records: ${json.count}`);
|
|
17
|
+
for (let i = 0; i < json.data.length; i += 25) {
|
|
18
|
+
const chunk = json.data.slice(i, i + 25);
|
|
19
|
+
await Promise.all(chunk.map((item) => client.send(new client_dynamodb_1.PutItemCommand({
|
|
20
|
+
TableName: table_name,
|
|
21
|
+
Item: (0, util_dynamodb_1.marshall)(item, { removeUndefinedValues: true }),
|
|
22
|
+
}))));
|
|
23
|
+
if ((i + 25) % 1000 === 0 || i + 25 >= json.data.length) {
|
|
24
|
+
const loaded = Math.min(i + 25, json.data.length);
|
|
25
|
+
console.log(` Loaded ${loaded}/${json.count} records...`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return json.count;
|
|
29
|
+
}
|
|
30
|
+
async function load_all() {
|
|
31
|
+
console.log("📦 Loading seed data into DynamoDB...\n");
|
|
32
|
+
const start_time = Date.now();
|
|
33
|
+
const loads = [
|
|
34
|
+
{ file: "/tmp/dynamite_seed_users.json", table: "test_users" },
|
|
35
|
+
{ file: "/tmp/dynamite_seed_categories.json", table: "test_categories" },
|
|
36
|
+
{ file: "/tmp/dynamite_seed_roles.json", table: "test_roles" },
|
|
37
|
+
{ file: "/tmp/dynamite_seed_products.json", table: "test_products" },
|
|
38
|
+
{ file: "/tmp/dynamite_seed_orders.json", table: "test_orders" },
|
|
39
|
+
];
|
|
40
|
+
for (const { file, table } of loads) {
|
|
41
|
+
console.log(`\n📄 Loading ${file}...`);
|
|
42
|
+
const count = await load_file(file, table);
|
|
43
|
+
console.log(`✅ Loaded ${count} records into ${table}`);
|
|
44
|
+
}
|
|
45
|
+
const elapsed = ((Date.now() - start_time) / 1000).toFixed(2);
|
|
46
|
+
console.log(`\n✅ Seed loading complete in ${elapsed}s`);
|
|
47
|
+
}
|
|
48
|
+
if (require.main === module) {
|
|
49
|
+
load_all().catch((error) => {
|
|
50
|
+
console.error("❌ Error loading seed data:", error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=load_seed.js.map
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.ts
|
|
3
|
+
* @description Sistema de tipos para Dynamite ORM - Arquitectura con type-safety completo
|
|
4
|
+
* @autor Miguel Alejandro
|
|
5
|
+
* @fecha 2025-01-28
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Marca para identificar campos que no son atributos de BD (relaciones, métodos).
|
|
9
|
+
*/
|
|
10
|
+
export declare const NonAttributeBrand: unique symbol;
|
|
11
|
+
/**
|
|
12
|
+
* Marca para identificar campos opcionales en create() pero presentes en el modelo.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CreationOptionalBrand: unique symbol;
|
|
15
|
+
/**
|
|
16
|
+
* Wrapper para marcar relaciones y campos virtuales que no se persisten en BD.
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* class User extends Table<User> {
|
|
20
|
+
* @HasMany(() => Post, 'user_id')
|
|
21
|
+
* declare posts: NonAttribute<Post[]>;
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export type NonAttribute<T> = T & {
|
|
26
|
+
[NonAttributeBrand]?: true;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Wrapper para marcar campos opcionales en create() (auto-generados: id, timestamps).
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* class User extends Table<User> {
|
|
33
|
+
* @PrimaryKey() id: CreationOptional<string>;
|
|
34
|
+
* @CreatedAt() created_at: CreationOptional<number>;
|
|
35
|
+
* name: string;
|
|
36
|
+
* }
|
|
37
|
+
* await User.create({ name: 'Juan' }); // id y created_at opcionales
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export type CreationOptional<T> = T & {
|
|
41
|
+
[CreationOptionalBrand]?: true;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Extrae atributos de BD usando intersección:
|
|
45
|
+
* - Primera parte: atributos REQUERIDOS con modificador `-?`
|
|
46
|
+
* - Segunda parte: atributos OPCIONALES (solo CreationOptional)
|
|
47
|
+
* - Ambos excluyen: never, undefined, null, NonAttribute, métodos
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* class User {
|
|
51
|
+
* id: CreationOptional<string>;
|
|
52
|
+
* created_at: CreationOptional<number>;
|
|
53
|
+
* name: string;
|
|
54
|
+
* email: string;
|
|
55
|
+
* posts: NonAttribute<Post[]>;
|
|
56
|
+
* save(): void;
|
|
57
|
+
* }
|
|
58
|
+
* // InferAttributes<User> = { name: string, email: string } & { id?: string, created_at?: number }
|
|
59
|
+
* // = { name: string, email: string, id?: string, created_at?: number }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export type InferAttributes<T> = {
|
|
63
|
+
[K in keyof T as T[K] extends ((...args: any[]) => any) | {
|
|
64
|
+
[NonAttributeBrand]?: true;
|
|
65
|
+
} | {
|
|
66
|
+
[CreationOptionalBrand]?: true;
|
|
67
|
+
} ? never : K]-?: Exclude<T[K], null | undefined | never>;
|
|
68
|
+
} & {
|
|
69
|
+
[K in keyof T as T[K] extends {
|
|
70
|
+
[CreationOptionalBrand]?: true;
|
|
71
|
+
} ? K : never]?: Exclude<T[K], null | undefined | never>;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Extrae solo relaciones (NonAttribute) preservando tipo Model | Model[]
|
|
75
|
+
* Excluye propiedades de Object.prototype para evitar conflictos
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* class User extends Table<User> {
|
|
79
|
+
* id: string;
|
|
80
|
+
* posts: NonAttribute<Post[]>; // Array
|
|
81
|
+
* profile: NonAttribute<Profile>; // Individual
|
|
82
|
+
* settings: NonAttribute<Settings | null>; // Nullable
|
|
83
|
+
* }
|
|
84
|
+
* // InferRelations<User> = { posts: Post[], profile: Profile, settings: Settings | null }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
type ObjectBuiltinKeys = keyof Record<string, never> | 'toString' | 'valueOf' | 'hasOwnProperty' | 'isPrototypeOf' | 'propertyIsEnumerable' | 'toLocaleString' | 'constructor';
|
|
88
|
+
export type InferRelations<T> = {
|
|
89
|
+
[K in keyof T as T[K] extends NonAttribute<any> ? K extends ObjectBuiltinKeys ? never : K : never]: T[K] extends NonAttribute<infer P> ? P : never;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Extrae solo relaciones (campos NonAttribute).
|
|
93
|
+
* Usado internamente para validación.
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* class User {
|
|
97
|
+
* id: string;
|
|
98
|
+
* name: string;
|
|
99
|
+
* posts: NonAttribute<Post[]>;
|
|
100
|
+
* profile: NonAttribute<Profile>;
|
|
101
|
+
* }
|
|
102
|
+
* // PickRelations<User> = { posts: Post[], profile: Profile }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export type PickRelations<T> = {
|
|
106
|
+
[K in keyof T as T[K] extends NonAttribute<any> ? K : never]: T[K] extends NonAttribute<infer U> ? U : never;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Operadores de comparación soportados.
|
|
110
|
+
* Solo 8 operadores permitidos según especificación del usuario.
|
|
111
|
+
*/
|
|
112
|
+
export type QueryOperator = "=" | "$eq" | "<>" | "!=" | "$ne" | "<" | "$lt" | "<=" | "$lte" | ">" | "$gt" | ">=" | "$gte" | "in" | "$in" | "include" | "$include";
|
|
113
|
+
/**
|
|
114
|
+
* Opciones de query con pre-cache de atributos (A) y relaciones (R)
|
|
115
|
+
* @template T - Modelo de tabla
|
|
116
|
+
* @template A - Cache de InferAttributes<T>
|
|
117
|
+
* @template R - Cache de InferRelations<T>
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* await User.where({}, {
|
|
121
|
+
* where: {
|
|
122
|
+
* name: 'Juan',
|
|
123
|
+
* age: { $gte: 18, $lte: 65 }
|
|
124
|
+
* },
|
|
125
|
+
* order: 'DESC',
|
|
126
|
+
* limit: 10,
|
|
127
|
+
* attributes: ['name', 'email'],
|
|
128
|
+
* include: {
|
|
129
|
+
* posts: {
|
|
130
|
+
* where: { published: true },
|
|
131
|
+
* limit: 5,
|
|
132
|
+
* include: { comments: true }
|
|
133
|
+
* },
|
|
134
|
+
* profile: {
|
|
135
|
+
* attributes: ['bio', 'avatar']
|
|
136
|
+
* }
|
|
137
|
+
* }
|
|
138
|
+
* });
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export interface WhereOptions<T, A = InferAttributes<T>, R = InferRelations<T>> {
|
|
142
|
+
/** Filtros de búsqueda */
|
|
143
|
+
where?: {
|
|
144
|
+
[K in keyof A]?: A[K] | {
|
|
145
|
+
[N in QueryOperator]?: A[K];
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
/** Orden de resultados */
|
|
149
|
+
order?: "ASC" | "DESC";
|
|
150
|
+
/** Número de items a saltar */
|
|
151
|
+
offset?: number;
|
|
152
|
+
/** Alias de offset */
|
|
153
|
+
skip?: number;
|
|
154
|
+
/** Número máximo de items a retornar */
|
|
155
|
+
limit?: number;
|
|
156
|
+
/** Campos a seleccionar */
|
|
157
|
+
attributes?: Array<keyof A>;
|
|
158
|
+
/** Relaciones a cargar (recursivo) */
|
|
159
|
+
include?: {
|
|
160
|
+
[K in keyof R]?: NonNullable<R[K]> extends Array<infer U> ? true | WhereOptions<U> : true | Pick<WhereOptions<NonNullable<R[K]>>, "attributes" | "include">;
|
|
161
|
+
};
|
|
162
|
+
/** Incluir registros soft-deleted */
|
|
163
|
+
_includeTrashed?: boolean;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Input para create() - InferAttributes ya maneja opcional/requerido con intersección
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* class User extends Table<User> {
|
|
170
|
+
* id: CreationOptional<string>;
|
|
171
|
+
* created_at: CreationOptional<number>;
|
|
172
|
+
* name: string;
|
|
173
|
+
* email: string;
|
|
174
|
+
* }
|
|
175
|
+
* // CreateInput<User> = { name: string, email: string, id?: string, created_at?: number }
|
|
176
|
+
* await User.create({ name: 'Juan', email: 'juan@example.com' }); // id y created_at opcionales
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export type CreateInput<T> = InferAttributes<T>;
|
|
180
|
+
/**
|
|
181
|
+
* Tipo para update() - todos los campos parciales.
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* await user.update({ name: 'Ana' }); // Todos los campos opcionales
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export type UpdateInput<T> = Partial<InferAttributes<T>>;
|
|
188
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file index.ts
|
|
4
|
+
* @description Sistema de tipos para Dynamite ORM - Arquitectura con type-safety completo
|
|
5
|
+
* @autor Miguel Alejandro
|
|
6
|
+
* @fecha 2025-01-28
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -25,25 +25,13 @@ export declare class Dynamite {
|
|
|
25
25
|
*/
|
|
26
26
|
constructor(config: DynamiteConfig);
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* Connect client and synchronize all declared tables and their GSIs (upsert pattern)
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
connect(): Promise<void>;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Auto-crear pivot tables para relaciones ManyToMany
|
|
33
33
|
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Get the underlying DynamoDB client
|
|
37
|
-
*/
|
|
38
|
-
getClient(): DynamoDBClient;
|
|
39
|
-
/**
|
|
40
|
-
* Check if client is connected and tables are synced
|
|
41
|
-
*/
|
|
42
|
-
isReady(): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* Disconnect and cleanup DynamoDB client
|
|
45
|
-
*/
|
|
46
|
-
disconnect(): void;
|
|
34
|
+
private createPivotTables;
|
|
47
35
|
/**
|
|
48
36
|
* Ejecutar operaciones en una transacción atómica.
|
|
49
37
|
* Si cualquier operación falla, todas se revierten automáticamente.
|
|
@@ -5,6 +5,39 @@
|
|
|
5
5
|
* @autor Miguel Alejandro
|
|
6
6
|
* @fecha 2025-01-27
|
|
7
7
|
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
8
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
42
|
exports.TransactionContext = exports.requireClient = exports.hasGlobalClient = exports.getGlobalClient = exports.setGlobalClient = exports.Dynamite = void 0;
|
|
10
43
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
@@ -26,49 +59,90 @@ class Dynamite {
|
|
|
26
59
|
this.tables = tables;
|
|
27
60
|
}
|
|
28
61
|
/**
|
|
29
|
-
*
|
|
62
|
+
* Connect client and synchronize all declared tables and their GSIs (upsert pattern)
|
|
30
63
|
*/
|
|
31
|
-
async
|
|
64
|
+
async connect() {
|
|
32
65
|
if (this.synced && this.connected)
|
|
33
66
|
return;
|
|
34
|
-
|
|
35
|
-
this.synced = true;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Connect the client for Table operations
|
|
39
|
-
*/
|
|
40
|
-
connect() {
|
|
41
|
-
if (this.connected)
|
|
42
|
-
return;
|
|
67
|
+
// Set global client for Table operations
|
|
43
68
|
(0, exports.setGlobalClient)(this.client);
|
|
44
69
|
this.connected = true;
|
|
70
|
+
// Crear tablas principales con GSIs
|
|
71
|
+
await Promise.all(this.tables.map((table) => this.createTableWithGSI(table)));
|
|
72
|
+
// Crear pivot tables para relaciones ManyToMany
|
|
73
|
+
await this.createPivotTables();
|
|
74
|
+
this.synced = true;
|
|
45
75
|
}
|
|
46
76
|
/**
|
|
47
|
-
*
|
|
48
|
-
*/
|
|
49
|
-
getClient() {
|
|
50
|
-
return this.client;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Check if client is connected and tables are synced
|
|
54
|
-
*/
|
|
55
|
-
isReady() {
|
|
56
|
-
return this.connected && this.synced;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Disconnect and cleanup DynamoDB client
|
|
77
|
+
* Auto-crear pivot tables para relaciones ManyToMany
|
|
60
78
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
async createPivotTables() {
|
|
80
|
+
const pivot_tables = new Map();
|
|
81
|
+
// Recolectar nombres de pivot tables con metadata
|
|
82
|
+
for (const table_class of this.tables) {
|
|
83
|
+
const schema = table_class[decorator_1.SCHEMA];
|
|
84
|
+
if (!schema)
|
|
85
|
+
continue;
|
|
86
|
+
for (const col_name in schema.columns) {
|
|
87
|
+
const col = schema.columns[col_name];
|
|
88
|
+
const relation = col.store?.relation;
|
|
89
|
+
if (relation?.type === "ManyToMany" && relation.pivotTable) {
|
|
90
|
+
pivot_tables.set(relation.pivotTable, {
|
|
91
|
+
foreignKey: relation.foreignKey || "source_id",
|
|
92
|
+
relatedKey: relation.relatedKey || "target_id"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
71
96
|
}
|
|
97
|
+
// Crear cada pivot table con GSIs usando los nombres de columna correctos
|
|
98
|
+
const { CreateTableCommand } = await Promise.resolve().then(() => __importStar(require("@aws-sdk/client-dynamodb")));
|
|
99
|
+
await Promise.all(Array.from(pivot_tables.entries()).map(async ([pivot_name, metadata]) => {
|
|
100
|
+
const { foreignKey, relatedKey } = metadata;
|
|
101
|
+
try {
|
|
102
|
+
await this.client.send(new CreateTableCommand({
|
|
103
|
+
TableName: pivot_name,
|
|
104
|
+
KeySchema: [{ AttributeName: "id", KeyType: "HASH" }],
|
|
105
|
+
AttributeDefinitions: [
|
|
106
|
+
{ AttributeName: "id", AttributeType: "S" },
|
|
107
|
+
{ AttributeName: foreignKey, AttributeType: "S" },
|
|
108
|
+
{ AttributeName: relatedKey, AttributeType: "S" },
|
|
109
|
+
],
|
|
110
|
+
GlobalSecondaryIndexes: [
|
|
111
|
+
{
|
|
112
|
+
IndexName: `${foreignKey}_index`,
|
|
113
|
+
KeySchema: [{ AttributeName: foreignKey, KeyType: "HASH" }],
|
|
114
|
+
Projection: { ProjectionType: "ALL" },
|
|
115
|
+
ProvisionedThroughput: {
|
|
116
|
+
ReadCapacityUnits: 5,
|
|
117
|
+
WriteCapacityUnits: 5,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
IndexName: `${relatedKey}_index`,
|
|
122
|
+
KeySchema: [{ AttributeName: relatedKey, KeyType: "HASH" }],
|
|
123
|
+
Projection: { ProjectionType: "ALL" },
|
|
124
|
+
ProvisionedThroughput: {
|
|
125
|
+
ReadCapacityUnits: 5,
|
|
126
|
+
WriteCapacityUnits: 5,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
ProvisionedThroughput: {
|
|
131
|
+
ReadCapacityUnits: 5,
|
|
132
|
+
WriteCapacityUnits: 5,
|
|
133
|
+
},
|
|
134
|
+
}));
|
|
135
|
+
console.log(`✓ Pivot table '${pivot_name}' created`);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
if (error.name === "ResourceInUseException") {
|
|
139
|
+
console.log(`✓ Pivot table '${pivot_name}' already exists`);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}));
|
|
72
146
|
}
|
|
73
147
|
/**
|
|
74
148
|
* Ejecutar operaciones en una transacción atómica.
|
|
@@ -103,14 +177,17 @@ class Dynamite {
|
|
|
103
177
|
throw new Error(`PartitionKey missing in ${ctor.name}`);
|
|
104
178
|
const sk = cols.find((c) => c.store?.indexSort);
|
|
105
179
|
const attr = new Map();
|
|
106
|
-
attr.set(pk.name ||
|
|
180
|
+
attr.set(pk.name || "id", "S");
|
|
107
181
|
if (sk)
|
|
108
|
-
attr.set(sk.name ||
|
|
182
|
+
attr.set(sk.name || "id", "S");
|
|
109
183
|
// Temporalmente deshabilitamos la creación automática de GSI hasta implementar relaciones
|
|
110
184
|
const gsiDefinitions = [];
|
|
111
|
-
const schema = [{ AttributeName: pk.name ||
|
|
185
|
+
const schema = [{ AttributeName: pk.name || "id", KeyType: "HASH" }];
|
|
112
186
|
if (sk && sk.name !== pk.name)
|
|
113
|
-
schema.push({
|
|
187
|
+
schema.push({
|
|
188
|
+
AttributeName: sk.name || "id",
|
|
189
|
+
KeyType: "RANGE",
|
|
190
|
+
});
|
|
114
191
|
try {
|
|
115
192
|
await this.client.send(new client_dynamodb_1.CreateTableCommand({
|
|
116
193
|
TableName: meta.name,
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* @fecha 2025-01-28
|
|
6
6
|
*/
|
|
7
7
|
export declare const SCHEMA: unique symbol;
|
|
8
|
-
export declare const VALUES: unique symbol;
|
|
9
|
-
declare function toSnakePlural(str: string): string;
|
|
10
8
|
/**
|
|
11
9
|
* @description Factory para crear decoradores con soporte de argumentos y composición
|
|
12
10
|
* @param callback Función que recibe (schema, col, params) para configurar la columna
|
|
@@ -29,16 +27,3 @@ export declare function decorator(callback: (schema: any, col: any, params: any[
|
|
|
29
27
|
* @returns PropertyDecorator
|
|
30
28
|
*/
|
|
31
29
|
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 };
|
|
@@ -6,15 +6,11 @@
|
|
|
6
6
|
* @fecha 2025-01-28
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.SCHEMA = void 0;
|
|
10
10
|
exports.decorator = decorator;
|
|
11
11
|
exports.relationDecorator = relationDecorator;
|
|
12
|
-
exports.getSchema = getSchema;
|
|
13
|
-
exports.ensureSchema = ensureSchema;
|
|
14
|
-
exports.toSnakePlural = toSnakePlural;
|
|
15
12
|
// Symbols para autocontención (exportados desde table.ts pero también aquí compatibilidad)
|
|
16
13
|
exports.SCHEMA = Symbol('dynamite:schema');
|
|
17
|
-
exports.VALUES = Symbol('dynamite:values');
|
|
18
14
|
// Helper simple para snake_case plural
|
|
19
15
|
function toSnakePlural(str) {
|
|
20
16
|
const snake = str
|
|
@@ -41,8 +37,8 @@ function decorator(callback) {
|
|
|
41
37
|
return function (target, propertyKey) {
|
|
42
38
|
const table_class = target.constructor;
|
|
43
39
|
const column_name = String(propertyKey);
|
|
44
|
-
// Inicializar SCHEMA
|
|
45
|
-
if (!table_class
|
|
40
|
+
// Inicializar SCHEMA SI NO TIENE UNO PROPIO (no heredado)
|
|
41
|
+
if (!Object.prototype.hasOwnProperty.call(table_class, exports.SCHEMA)) {
|
|
46
42
|
table_class[exports.SCHEMA] = {
|
|
47
43
|
name: toSnakePlural(table_class.name),
|
|
48
44
|
primary_key: 'id',
|
|
@@ -75,7 +71,8 @@ function relationDecorator(relation_type, RelatedTable, options = {}) {
|
|
|
75
71
|
return function (target, propertyKey) {
|
|
76
72
|
const table_class = target.constructor;
|
|
77
73
|
const column_name = String(propertyKey);
|
|
78
|
-
|
|
74
|
+
// Inicializar SCHEMA SI NO TIENE UNO PROPIO (no heredado)
|
|
75
|
+
if (!Object.prototype.hasOwnProperty.call(table_class, exports.SCHEMA)) {
|
|
79
76
|
table_class[exports.SCHEMA] = {
|
|
80
77
|
name: toSnakePlural(table_class.name),
|
|
81
78
|
primary_key: 'id',
|
|
@@ -103,31 +100,4 @@ function relationDecorator(relation_type, RelatedTable, options = {}) {
|
|
|
103
100
|
};
|
|
104
101
|
};
|
|
105
102
|
}
|
|
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
103
|
//# sourceMappingURL=decorator.js.map
|