@uql/core 3.4.0 → 3.4.2

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.
@@ -1,2150 +1,1014 @@
1
- import { isSqlQuerier } from '../type/index.js';
2
- export { isSqlQuerier } from '../type/index.js';
3
- import { AbstractDialect } from '../dialect/index.js';
4
- import { getMeta, getEntities } from '../entity/index.js';
5
- import { escapeSqlId, getKeys } from '../util/index.js';
6
- import { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';
7
- import { join, basename, extname, dirname } from 'node:path';
8
- import { pathToFileURL } from 'node:url';
1
+ import { _ as _apply_decs_2203_r } from './cc-BEf4wTUm.js';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { Id, Field, ManyToOne, Entity, OneToOne, OneToMany, ManyToMany } from '../entity/index.js';
4
+ import '../type/index.js';
5
+ import { raw, lowerFirst, upperFirst, getKeys } from '../util/index.js';
6
+ import 'reflect-metadata';
7
+ import { describe, afterAll, afterEach, beforeEach, beforeAll, it } from 'bun:test';
9
8
 
9
+ var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, /**
10
+ * auto-generated primary-key (when the `onInsert` property is omitted).
11
+ */ _init_id, /**
12
+ * foreign-keys are really simple to specify with the `reference` property.
13
+ */ _init_companyId, /**
14
+ * The `Relation` wrapper type can be used in ESM projects for the relations to
15
+ * avoid circular dependency issues.
16
+ */ _init_company, _init_creatorId, _init_creator, /**
17
+ * 'onInsert' property can be used to specify a custom mechanism for
18
+ * obtaining the value of a field when inserting:
19
+ */ _init_createdAt, /**
20
+ * 'onUpdate' property can be used to specify a custom mechanism for
21
+ * obtaining the value of a field when updating:
22
+ */ _init_updatedAt, _dec7, _initClass, _BaseEntity, _dec8, _dec9, _dec10, _init_name, _init_description, _init_kind, _dec11, _initClass1, _BaseEntity1, _dec12, _dec13, /**
23
+ * an entity can specify its own ID Field and still inherit the others
24
+ * columns/relations from its parent entity.
25
+ */ _init_pk, _init_picture, _dec14, _initClass2, _BaseEntity2, _dec15, _dec16, _dec17, _dec18, _dec19, _init_name1, _init_email, _init_password, /**
26
+ * `mappedBy` property can be a callback or a string (callback is useful for auto-refactoring).
27
+ */ _init_profile, _init_users, _dec20, _initClass3, _dec21, _dec22, _init_id1, _init_name2, _dec23, _initClass4, _BaseEntity3, _dec24, _dec25, _dec26, _dec27, _init_name3, _init_description1, _init_parentLedgerId, _init_parentLedger, _dec28, _initClass5, _BaseEntity4, _dec29, _dec30, _dec31, /**
28
+ * an entity can override the ID Field and still inherit the others
29
+ * columns/relations from its parent entity.
30
+ * 'onInsert' property can be used to specify a custom mechanism for
31
+ * auto-generating the primary-key's value when inserting.
32
+ */ _init_pk1, _init_name4, _init_description2, _dec32, _initClass6, _BaseEntity5, _dec33, _dec34, _dec35, _dec36, _dec37, _init_name5, _init_percentage, _init_categoryId, _init_category, _init_description3, _dec38, _initClass7, _BaseEntity6, _dec39, _dec40, _dec41, _init_name6, _init_measureUnits, /**
33
+ * `onDelete` callback allows to specify which field will be used when deleting/querying this entity.
34
+ */ _init_deletedAt, _dec42, _initClass8, _BaseEntity7, _dec43, _dec44, _dec45, _dec46, _init_name7, _init_categoryId1, _init_category1, _init_deletedAt1, _dec47, _initClass9, _BaseEntity8, _dec48, _dec49, _dec50, _init_name8, _init_address, _init_description4, _dec51, _initClass10, _BaseEntity9, _dec52, _dec53, _dec54, _dec55, _dec56, _dec57, _dec58, _dec59, _dec60, _dec61, _dec62, _dec63, _dec64, _dec65, _dec66, _init_name9, _init_description5, _init_code, _init_buyLedgerAccountId, _init_buyLedgerAccount, _init_saleLedgerAccountId, _init_saleLedgerAccount, _init_taxId, _init_tax, _init_measureUnitId, _init_measureUnit, _init_salePrice, _init_inventoryable, _init_tags, _init_tagsCount, _dec67, _initClass11, _BaseEntity10, _dec68, _dec69, _dec70, _init_name10, _init_items, _init_itemsCount, _dec71, _initClass12, _dec72, _dec73, _dec74, _init_id2, _init_itemId, _init_tagId, _dec75, _initClass13, _BaseEntity11, _dec76, _dec77, _dec78, _init_itemAdjustments, _init_date, _init_description6, _dec79, _initClass14, _BaseEntity12, _dec80, _dec81, _dec82, _dec83, _dec84, _dec85, _dec86, _dec87, _init_itemId1, _init_item, _init_number, _init_buyPrice, _init_storehouseId, _init_storehouse, _init_inventoryAdjustmentId, _init_inventoryAdjustment;
35
+ _dec = Id(), _dec1 = Field({
36
+ reference: ()=>_Company
37
+ }), _dec2 = ManyToOne({
38
+ entity: ()=>_Company
39
+ }), _dec3 = Field({
40
+ reference: ()=>_User
41
+ }), _dec4 = ManyToOne({
42
+ entity: ()=>_User
43
+ }), _dec5 = Field({
44
+ onInsert: Date.now
45
+ }), _dec6 = Field({
46
+ onUpdate: Date.now
47
+ });
10
48
  /**
11
- * Abstract base class for SQL schema generation
12
- */ class AbstractSchemaGenerator extends AbstractDialect {
13
- constructor(namingStrategy, escapeIdChar = '`'){
14
- super(namingStrategy), this.escapeIdChar = escapeIdChar;
15
- }
16
- /**
17
- * Escape an identifier (table name, column name, etc.)
18
- */ escapeId(identifier) {
19
- return escapeSqlId(identifier, this.escapeIdChar);
20
- }
21
- generateCreateTable(entity, options = {}) {
22
- const meta = getMeta(entity);
23
- const tableName = this.resolveTableName(entity, meta);
24
- const columns = this.generateColumnDefinitions(meta);
25
- const constraints = this.generateTableConstraints(meta);
26
- const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
27
- let sql = `CREATE TABLE ${ifNotExists}${this.escapeId(tableName)} (\n`;
28
- sql += columns.map((col)=>` ${col}`).join(',\n');
29
- if (constraints.length > 0) {
30
- sql += ',\n';
31
- sql += constraints.map((c)=>` ${c}`).join(',\n');
32
- }
33
- sql += '\n)';
34
- sql += this.getTableOptions(meta);
35
- sql += ';';
36
- return sql;
37
- }
38
- generateDropTable(entity) {
39
- const meta = getMeta(entity);
40
- const tableName = this.resolveTableName(entity, meta);
41
- return `DROP TABLE IF EXISTS ${this.escapeId(tableName)};`;
42
- }
43
- generateAlterTable(diff) {
44
- const statements = [];
45
- const tableName = this.escapeId(diff.tableName);
46
- // Add new columns
47
- if (diff.columnsToAdd?.length) {
48
- for (const column of diff.columnsToAdd){
49
- const colDef = this.generateColumnDefinitionFromSchema(column);
50
- statements.push(`ALTER TABLE ${tableName} ADD COLUMN ${colDef};`);
51
- }
52
- }
53
- // Alter existing columns
54
- if (diff.columnsToAlter?.length) {
55
- for (const { to } of diff.columnsToAlter){
56
- const colDef = this.generateColumnDefinitionFromSchema(to);
57
- const colStatements = this.generateAlterColumnStatements(diff.tableName, to, colDef);
58
- statements.push(...colStatements);
59
- }
60
- }
61
- // Drop columns
62
- if (diff.columnsToDrop?.length) {
63
- for (const columnName of diff.columnsToDrop){
64
- statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(columnName)};`);
65
- }
66
- }
67
- // Add indexes
68
- if (diff.indexesToAdd?.length) {
69
- for (const index of diff.indexesToAdd){
70
- statements.push(this.generateCreateIndex(diff.tableName, index));
71
- }
72
- }
73
- // Drop indexes
74
- if (diff.indexesToDrop?.length) {
75
- for (const indexName of diff.indexesToDrop){
76
- statements.push(this.generateDropIndex(diff.tableName, indexName));
77
- }
78
- }
79
- return statements;
80
- }
81
- generateAlterTableDown(diff) {
82
- const statements = [];
83
- const tableName = this.escapeId(diff.tableName);
84
- // Rollback additions by dropping columns
85
- if (diff.columnsToAdd?.length) {
86
- for (const column of diff.columnsToAdd){
87
- statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(column.name)};`);
88
- }
89
- }
90
- return statements;
91
- }
92
- generateCreateIndex(tableName, index) {
93
- const unique = index.unique ? 'UNIQUE ' : '';
94
- const columns = index.columns.map((c)=>this.escapeId(c)).join(', ');
95
- return `CREATE ${unique}INDEX ${this.escapeId(index.name)} ON ${this.escapeId(tableName)} (${columns});`;
96
- }
97
- generateDropIndex(tableName, indexName) {
98
- return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
99
- }
100
- /**
101
- * Generate column definitions from entity metadata
102
- */ generateColumnDefinitions(meta) {
103
- const columns = [];
104
- const fieldKeys = getKeys(meta.fields);
105
- for (const key of fieldKeys){
106
- const field = meta.fields[key];
107
- if (field?.virtual) continue; // Skip virtual fields
108
- const colDef = this.generateColumnDefinition(key, field, meta);
109
- columns.push(colDef);
110
- }
111
- return columns;
112
- }
113
- /**
114
- * Generate a single column definition
115
- */ generateColumnDefinition(fieldKey, field, meta) {
116
- const columnName = this.escapeId(this.resolveColumnName(fieldKey, field));
117
- const isId = field.isId === true;
118
- const isPrimaryKey = isId && meta.id === fieldKey;
119
- // Determine SQL type
120
- let sqlType;
121
- if (isPrimaryKey && field.autoIncrement !== false && !field.onInsert) {
122
- // Auto-increment primary key
123
- sqlType = this.serialPrimaryKeyType;
124
- } else {
125
- sqlType = this.getSqlType(field, field.type);
126
- }
127
- let definition = `${columnName} ${sqlType}`;
128
- // PRIMARY KEY constraint (for non-serial types)
129
- if (isPrimaryKey && !sqlType.includes('PRIMARY KEY')) {
130
- definition += ' PRIMARY KEY';
131
- }
132
- // NULL/NOT NULL
133
- if (!isPrimaryKey) {
134
- const nullable = field.nullable ?? true;
135
- if (!nullable) {
136
- definition += ' NOT NULL';
137
- }
138
- }
139
- // UNIQUE constraint
140
- if (field.unique && !isPrimaryKey) {
141
- definition += ' UNIQUE';
142
- }
143
- // DEFAULT value
144
- if (field.defaultValue !== undefined) {
145
- definition += ` DEFAULT ${this.formatDefaultValue(field.defaultValue)}`;
146
- }
147
- // COMMENT (if supported)
148
- if (field.comment) {
149
- definition += this.generateColumnComment(this.resolveTableName(meta.entity, meta), this.resolveColumnName(fieldKey, field), field.comment);
150
- }
151
- return definition;
152
- }
153
- /**
154
- * Generate column definition from a ColumnSchema object
155
- */ generateColumnDefinitionFromSchema(column) {
156
- const columnName = this.escapeId(column.name);
157
- let type = column.type;
158
- if (column.length && !type.includes('(')) {
159
- type = `${type}(${column.length})`;
160
- } else if (column.precision !== undefined && !type.includes('(')) {
161
- if (column.scale !== undefined) {
162
- type = `${type}(${column.precision}, ${column.scale})`;
163
- } else {
164
- type = `${type}(${column.precision})`;
165
- }
166
- }
167
- let definition = `${columnName} ${type}`;
168
- if (column.isPrimaryKey) {
169
- definition += ' PRIMARY KEY';
170
- }
171
- if (!column.nullable && !column.isPrimaryKey) {
172
- definition += ' NOT NULL';
173
- }
174
- if (column.isUnique && !column.isPrimaryKey) {
175
- definition += ' UNIQUE';
176
- }
177
- if (column.defaultValue !== undefined) {
178
- definition += ` DEFAULT ${this.formatDefaultValue(column.defaultValue)}`;
179
- }
180
- return definition;
181
- }
182
- /**
183
- * Generate table constraints (indexes, foreign keys, etc.)
184
- */ generateTableConstraints(meta) {
185
- const constraints = [];
186
- const fieldKeys = getKeys(meta.fields);
187
- const tableName = this.resolveTableName(meta.entity, meta);
188
- // Generate indexes from field options
189
- for (const key of fieldKeys){
190
- const field = meta.fields[key];
191
- if (field?.index) {
192
- const columnName = this.resolveColumnName(key, field);
193
- const indexName = typeof field.index === 'string' ? field.index : `idx_${tableName}_${columnName}`;
194
- constraints.push(`INDEX ${this.escapeId(indexName)} (${this.escapeId(columnName)})`);
195
- }
196
- }
197
- // Generate foreign key constraints from references
198
- for (const key of fieldKeys){
199
- const field = meta.fields[key];
200
- if (field?.reference) {
201
- const refEntity = field.reference();
202
- const refMeta = getMeta(refEntity);
203
- const refIdField = refMeta.fields[refMeta.id];
204
- const columnName = this.resolveColumnName(key, field);
205
- const refTableName = this.resolveTableName(refEntity, refMeta);
206
- const refColumnName = this.resolveColumnName(refMeta.id, refIdField);
207
- const fkName = `fk_${tableName}_${columnName}`;
208
- constraints.push(`CONSTRAINT ${this.escapeId(fkName)} FOREIGN KEY (${this.escapeId(columnName)}) ` + `REFERENCES ${this.escapeId(refTableName)} (${this.escapeId(refColumnName)})`);
209
- }
210
- }
211
- return constraints;
212
- }
213
- getSqlType(field, fieldType) {
214
- // Use explicit column type if specified
215
- if (field.columnType) {
216
- return this.mapColumnType(field.columnType, field);
217
- }
218
- // Handle special types
219
- if (field.type === 'json' || field.type === 'jsonb') {
220
- return this.mapColumnType(field.type, field);
221
- }
222
- if (field.type === 'vector') {
223
- return this.mapColumnType('vector', field);
224
- }
225
- // Infer from TypeScript type
226
- const type = fieldType ?? field.type;
227
- if (type === Number || type === 'number') {
228
- return field.precision ? this.mapColumnType('decimal', field) : 'BIGINT';
229
- }
230
- if (type === String || type === 'string') {
231
- const length = field.length ?? 255;
232
- return `VARCHAR(${length})`;
233
- }
234
- if (type === Boolean || type === 'boolean') {
235
- return this.getBooleanType();
236
- }
237
- if (type === Date || type === 'date') {
238
- return 'TIMESTAMP';
239
- }
240
- if (type === BigInt || type === 'bigint') {
241
- return 'BIGINT';
242
- }
243
- // Default to VARCHAR
244
- return `VARCHAR(${field.length ?? 255})`;
245
- }
246
- /**
247
- * Get table options (e.g., ENGINE for MySQL)
248
- */ getTableOptions(meta) {
249
- return '';
250
- }
251
- /**
252
- * Format a default value for SQL
253
- */ formatDefaultValue(value) {
254
- if (value === null) {
255
- return 'NULL';
256
- }
257
- if (typeof value === 'string') {
258
- return `'${value.replace(/'/g, "''")}'`;
259
- }
260
- if (typeof value === 'boolean') {
261
- return value ? 'TRUE' : 'FALSE';
262
- }
263
- if (typeof value === 'number' || typeof value === 'bigint') {
264
- return String(value);
265
- }
266
- if (value instanceof Date) {
267
- return `'${value.toISOString()}'`;
268
- }
269
- return String(value);
270
- }
271
- /**
272
- * Compare two schemas and return the differences
273
- */ diffSchema(entity, currentSchema) {
274
- const meta = getMeta(entity);
275
- if (!currentSchema) {
276
- // Table doesn't exist, need to create
277
- return {
278
- tableName: this.resolveTableName(entity, meta),
279
- type: 'create'
280
- };
281
- }
282
- const columnsToAdd = [];
283
- const columnsToAlter = [];
284
- const columnsToDrop = [];
285
- const currentColumns = new Map(currentSchema.columns.map((c)=>[
286
- c.name,
287
- c
288
- ]));
289
- const fieldKeys = getKeys(meta.fields);
290
- // Check for new or altered columns
291
- for (const key of fieldKeys){
292
- const field = meta.fields[key];
293
- if (field?.virtual) continue;
294
- const columnName = this.resolveColumnName(key, field);
295
- const currentColumn = currentColumns.get(columnName);
296
- if (!currentColumn) {
297
- // Column needs to be added
298
- columnsToAdd.push(this.fieldToColumnSchema(key, field, meta));
299
- } else {
300
- // Check if column needs alteration
301
- const desiredColumn = this.fieldToColumnSchema(key, field, meta);
302
- if (this.columnsNeedAlteration(currentColumn, desiredColumn)) {
303
- columnsToAlter.push({
304
- from: currentColumn,
305
- to: desiredColumn
306
- });
307
- }
308
- }
309
- currentColumns.delete(columnName);
310
- }
311
- // Remaining columns in currentColumns should be dropped
312
- for (const [name] of currentColumns){
313
- columnsToDrop.push(name);
314
- }
315
- if (columnsToAdd.length === 0 && columnsToAlter.length === 0 && columnsToDrop.length === 0) {
316
- return undefined; // No changes needed
317
- }
318
- return {
319
- tableName: this.resolveTableName(entity, meta),
320
- type: 'alter',
321
- columnsToAdd: columnsToAdd.length > 0 ? columnsToAdd : undefined,
322
- columnsToAlter: columnsToAlter.length > 0 ? columnsToAlter : undefined,
323
- columnsToDrop: columnsToDrop.length > 0 ? columnsToDrop : undefined
324
- };
325
- }
326
- /**
327
- * Convert field options to ColumnSchema
328
- */ fieldToColumnSchema(fieldKey, field, meta) {
329
- const isId = field.isId === true;
330
- const isPrimaryKey = isId && meta.id === fieldKey;
331
- return {
332
- name: this.resolveColumnName(fieldKey, field),
333
- type: this.getSqlType(field, field.type),
334
- nullable: field.nullable ?? !isPrimaryKey,
335
- defaultValue: field.defaultValue,
336
- isPrimaryKey,
337
- isAutoIncrement: isPrimaryKey && field.autoIncrement !== false && !field.onInsert,
338
- isUnique: field.unique ?? false,
339
- length: field.length,
340
- precision: field.precision,
341
- scale: field.scale,
342
- comment: field.comment
343
- };
344
- }
345
- /**
346
- * Check if two columns differ enough to require alteration
347
- */ columnsNeedAlteration(current, desired) {
348
- // Compare relevant properties
349
- return current.type.toLowerCase() !== desired.type.toLowerCase() || current.nullable !== desired.nullable || current.isUnique !== desired.isUnique || JSON.stringify(current.defaultValue) !== JSON.stringify(desired.defaultValue);
49
+ * an `abstract` class can (optionally) be used as the base "template" for the entities
50
+ * (so common fields' declaration is easily reused).
51
+ */ class BaseEntity {
52
+ static{
53
+ ({ e: [_init_id, _init_companyId, _init_company, _init_creatorId, _init_creator, _init_createdAt, _init_updatedAt] } = _apply_decs_2203_r(this, [
54
+ [
55
+ _dec,
56
+ 0,
57
+ "id"
58
+ ],
59
+ [
60
+ _dec1,
61
+ 0,
62
+ "companyId"
63
+ ],
64
+ [
65
+ _dec2,
66
+ 0,
67
+ "company"
68
+ ],
69
+ [
70
+ _dec3,
71
+ 0,
72
+ "creatorId"
73
+ ],
74
+ [
75
+ _dec4,
76
+ 0,
77
+ "creator"
78
+ ],
79
+ [
80
+ _dec5,
81
+ 0,
82
+ "createdAt"
83
+ ],
84
+ [
85
+ _dec6,
86
+ 0,
87
+ "updatedAt"
88
+ ]
89
+ ], []));
90
+ }
91
+ constructor(){
92
+ this.id = _init_id(this);
93
+ this.companyId = _init_companyId(this);
94
+ this.company = _init_company(this);
95
+ this.creatorId = _init_creatorId(this);
96
+ this.creator = _init_creator(this);
97
+ this.createdAt = _init_createdAt(this);
98
+ this.updatedAt = _init_updatedAt(this);
350
99
  }
351
100
  }
352
-
353
- /**
354
- * MySQL/MariaDB-specific schema generator
355
- */ class MysqlSchemaGenerator extends AbstractSchemaGenerator {
356
- mapColumnType(columnType, field) {
357
- switch(columnType){
358
- case 'int':
359
- return 'INT';
360
- case 'smallint':
361
- return 'SMALLINT';
362
- case 'bigint':
363
- return 'BIGINT';
364
- case 'float':
365
- return 'FLOAT';
366
- case 'double':
367
- case 'real':
368
- return 'DOUBLE';
369
- case 'decimal':
370
- case 'numeric':
371
- if (field.precision !== undefined) {
372
- if (field.scale !== undefined) {
373
- return `DECIMAL(${field.precision}, ${field.scale})`;
374
- }
375
- return `DECIMAL(${field.precision})`;
376
- }
377
- return 'DECIMAL(10, 2)';
378
- case 'boolean':
379
- return 'TINYINT(1)';
380
- case 'char':
381
- return `CHAR(${field.length ?? 1})`;
382
- case 'varchar':
383
- return `VARCHAR(${field.length ?? 255})`;
384
- case 'text':
385
- return 'TEXT';
386
- case 'uuid':
387
- return 'CHAR(36)';
388
- case 'date':
389
- return 'DATE';
390
- case 'time':
391
- return 'TIME';
392
- case 'timestamp':
393
- case 'timestamptz':
394
- return 'TIMESTAMP';
395
- case 'json':
396
- case 'jsonb':
397
- return 'JSON';
398
- case 'blob':
399
- case 'bytea':
400
- return 'BLOB';
401
- case 'vector':
402
- // MySQL doesn't have native vector support, use JSON
403
- return 'JSON';
404
- case 'serial':
405
- return 'INT AUTO_INCREMENT';
406
- case 'bigserial':
407
- return 'BIGINT AUTO_INCREMENT';
408
- default:
409
- return 'TEXT';
410
- }
411
- }
412
- getBooleanType() {
413
- return 'TINYINT(1)';
414
- }
415
- generateAlterColumnStatements(tableName, column, newDefinition) {
416
- return [
417
- `ALTER TABLE ${this.escapeId(tableName)} MODIFY COLUMN ${newDefinition};`
418
- ];
419
- }
420
- generateColumnComment(tableName, columnName, comment) {
421
- const escapedComment = comment.replace(/'/g, "''");
422
- return ` COMMENT '${escapedComment}'`;
423
- }
424
- getTableOptions() {
425
- return ' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci';
426
- }
427
- generateDropIndex(tableName, indexName) {
428
- // MySQL requires table name in DROP INDEX
429
- return `DROP INDEX ${this.escapeId(indexName)} ON ${this.escapeId(tableName)};`;
101
+ let _Company;
102
+ _dec7 = Entity(), _dec8 = Field(), _dec9 = Field(), _dec10 = Field({
103
+ type: 'jsonb'
104
+ });
105
+ class Company extends (_BaseEntity = BaseEntity) {
106
+ static{
107
+ ({ e: [_init_name, _init_description, _init_kind], c: [_Company, _initClass] } = _apply_decs_2203_r(this, [
108
+ [
109
+ _dec8,
110
+ 0,
111
+ "name"
112
+ ],
113
+ [
114
+ _dec9,
115
+ 0,
116
+ "description"
117
+ ],
118
+ [
119
+ _dec10,
120
+ 0,
121
+ "kind"
122
+ ]
123
+ ], [
124
+ _dec7
125
+ ], _BaseEntity));
126
+ }
127
+ static{
128
+ _initClass();
430
129
  }
431
130
  constructor(...args){
432
- super(...args), this.serialPrimaryKeyType = 'INT AUTO_INCREMENT PRIMARY KEY';
131
+ super(...args), this.name = _init_name(this), this.description = _init_description(this), this.kind = _init_kind(this);
433
132
  }
434
133
  }
435
-
436
- /**
437
- * PostgreSQL-specific schema generator
438
- */ class PostgresSchemaGenerator extends AbstractSchemaGenerator {
439
- constructor(namingStrategy){
440
- super(namingStrategy, '"'), this.serialPrimaryKeyType = 'SERIAL PRIMARY KEY';
134
+ let _Profile;
135
+ _dec11 = Entity({
136
+ name: 'user_profile'
137
+ }), _dec12 = Id(), _dec13 = Field({
138
+ name: 'image'
139
+ });
140
+ class Profile extends (_BaseEntity1 = BaseEntity) {
141
+ static{
142
+ ({ e: [_init_pk, _init_picture], c: [_Profile, _initClass1] } = _apply_decs_2203_r(this, [
143
+ [
144
+ _dec12,
145
+ 0,
146
+ "pk"
147
+ ],
148
+ [
149
+ _dec13,
150
+ 0,
151
+ "picture"
152
+ ]
153
+ ], [
154
+ _dec11
155
+ ], _BaseEntity1));
156
+ }
157
+ static{
158
+ _initClass1();
441
159
  }
442
- mapColumnType(columnType, field) {
443
- switch(columnType){
444
- case 'int':
445
- return 'INTEGER';
446
- case 'smallint':
447
- return 'SMALLINT';
448
- case 'bigint':
449
- return 'BIGINT';
450
- case 'float':
451
- case 'double':
452
- case 'real':
453
- return 'DOUBLE PRECISION';
454
- case 'decimal':
455
- case 'numeric':
456
- if (field.precision !== undefined) {
457
- if (field.scale !== undefined) {
458
- return `NUMERIC(${field.precision}, ${field.scale})`;
459
- }
460
- return `NUMERIC(${field.precision})`;
461
- }
462
- return 'NUMERIC';
463
- case 'boolean':
464
- return 'BOOLEAN';
465
- case 'char':
466
- return `CHAR(${field.length ?? 1})`;
467
- case 'varchar':
468
- return `VARCHAR(${field.length ?? 255})`;
469
- case 'text':
470
- return 'TEXT';
471
- case 'uuid':
472
- return 'UUID';
473
- case 'date':
474
- return 'DATE';
475
- case 'time':
476
- return 'TIME';
477
- case 'timestamp':
478
- return 'TIMESTAMP';
479
- case 'timestamptz':
480
- return 'TIMESTAMPTZ';
481
- case 'json':
482
- return 'JSON';
483
- case 'jsonb':
484
- return 'JSONB';
485
- case 'blob':
486
- case 'bytea':
487
- return 'BYTEA';
488
- case 'vector':
489
- if (field.length) {
490
- return `VECTOR(${field.length})`;
491
- }
492
- return 'VECTOR';
493
- case 'serial':
494
- return 'SERIAL';
495
- case 'bigserial':
496
- return 'BIGSERIAL';
497
- default:
498
- return 'TEXT';
499
- }
500
- }
501
- getBooleanType() {
502
- return 'BOOLEAN';
503
- }
504
- generateAlterColumnStatements(tableName, column, newDefinition) {
505
- const statements = [];
506
- const escapedTableName = this.escapeId(tableName);
507
- const escapedColumnName = this.escapeId(column.name);
508
- // PostgreSQL uses separate ALTER COLUMN clauses for different changes
509
- // 1. Change type
510
- statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} TYPE ${column.type};`);
511
- // 2. Change nullability
512
- if (column.nullable) {
513
- statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} DROP NOT NULL;`);
514
- } else {
515
- statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} SET NOT NULL;`);
516
- }
517
- // 3. Change default value
518
- if (column.defaultValue !== undefined) {
519
- statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} SET DEFAULT ${this.formatDefaultValue(column.defaultValue)};`);
520
- } else {
521
- statements.push(`ALTER TABLE ${escapedTableName} ALTER COLUMN ${escapedColumnName} DROP DEFAULT;`);
522
- }
523
- return statements;
524
- }
525
- generateColumnComment(tableName, columnName, comment) {
526
- // PostgreSQL handles comments separately via COMMENT ON COLUMN
527
- return '';
528
- }
529
- /**
530
- * Generate COMMENT ON COLUMN statement for PostgreSQL
531
- */ generateColumnCommentStatement(tableName, columnName, comment) {
532
- const escapedComment = comment.replace(/'/g, "''");
533
- return `COMMENT ON COLUMN ${this.escapeId(tableName)}.${this.escapeId(columnName)} IS '${escapedComment}';`;
534
- }
535
- generateDropIndex(tableName, indexName) {
536
- // PostgreSQL doesn't require table name in DROP INDEX
537
- return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
160
+ constructor(...args){
161
+ super(...args), this.pk = _init_pk(this), this.picture = _init_picture(this);
538
162
  }
539
163
  }
540
-
541
- /**
542
- * SQLite-specific schema generator
543
- */ class SqliteSchemaGenerator extends AbstractSchemaGenerator {
544
- mapColumnType(columnType, field) {
545
- // SQLite has dynamic typing, but we use type affinity
546
- switch(columnType){
547
- case 'int':
548
- case 'smallint':
549
- case 'bigint':
550
- case 'serial':
551
- case 'bigserial':
552
- return 'INTEGER';
553
- case 'float':
554
- case 'double':
555
- case 'real':
556
- case 'decimal':
557
- case 'numeric':
558
- return 'REAL';
559
- case 'boolean':
560
- return 'INTEGER'; // SQLite uses 0/1 for booleans
561
- case 'char':
562
- case 'varchar':
563
- case 'text':
564
- case 'uuid':
565
- return 'TEXT';
566
- case 'date':
567
- case 'time':
568
- case 'timestamp':
569
- case 'timestamptz':
570
- return 'TEXT'; // SQLite stores dates as TEXT or INTEGER
571
- case 'json':
572
- case 'jsonb':
573
- return 'TEXT'; // SQLite stores JSON as TEXT
574
- case 'blob':
575
- case 'bytea':
576
- return 'BLOB';
577
- case 'vector':
578
- return 'TEXT'; // Store as JSON array
579
- default:
580
- return 'TEXT';
581
- }
582
- }
583
- getBooleanType() {
584
- return 'INTEGER';
585
- }
586
- generateAlterColumnStatements(tableName, column, newDefinition) {
587
- // SQLite has very limited ALTER TABLE support
588
- // Column type changes require recreating the table
589
- throw new Error(`SQLite does not support altering column '${column.name}' in table '${tableName}'. ` + 'You need to recreate the table to change column types.');
590
- }
591
- generateColumnComment(tableName, columnName, comment) {
592
- return '';
593
- }
594
- generateDropIndex(tableName, indexName) {
595
- return `DROP INDEX IF EXISTS ${this.escapeId(indexName)};`;
596
- }
597
- formatDefaultValue(value) {
598
- if (typeof value === 'boolean') {
599
- return value ? '1' : '0';
600
- }
601
- return super.formatDefaultValue(value);
164
+ let _User;
165
+ _dec14 = Entity(), _dec15 = Field(), _dec16 = Field({
166
+ updatable: false
167
+ }), _dec17 = Field({
168
+ eager: false
169
+ }), _dec18 = OneToOne({
170
+ entity: ()=>_Profile,
171
+ mappedBy: (profile)=>profile.creator,
172
+ cascade: true
173
+ }), _dec19 = OneToMany({
174
+ entity: ()=>_User,
175
+ mappedBy: 'creator'
176
+ });
177
+ class User extends (_BaseEntity2 = BaseEntity) {
178
+ static{
179
+ ({ e: [_init_name1, _init_email, _init_password, _init_profile, _init_users], c: [_User, _initClass2] } = _apply_decs_2203_r(this, [
180
+ [
181
+ _dec15,
182
+ 0,
183
+ "name"
184
+ ],
185
+ [
186
+ _dec16,
187
+ 0,
188
+ "email"
189
+ ],
190
+ [
191
+ _dec17,
192
+ 0,
193
+ "password"
194
+ ],
195
+ [
196
+ _dec18,
197
+ 0,
198
+ "profile"
199
+ ],
200
+ [
201
+ _dec19,
202
+ 0,
203
+ "users"
204
+ ]
205
+ ], [
206
+ _dec14
207
+ ], _BaseEntity2));
208
+ }
209
+ static{
210
+ _initClass2();
602
211
  }
603
212
  constructor(...args){
604
- super(...args), this.serialPrimaryKeyType = 'INTEGER PRIMARY KEY AUTOINCREMENT';
213
+ super(...args), this.name = _init_name1(this), this.email = _init_email(this), this.password = _init_password(this), this.profile = _init_profile(this), this.users = _init_users(this);
605
214
  }
606
215
  }
607
-
608
- /**
609
- * MySQL/MariaDB schema introspector.
610
- * Works with both MySQL and MariaDB as they share the same information_schema structure.
611
- */ class MysqlSchemaIntrospector {
612
- constructor(pool){
613
- this.pool = pool;
614
- }
615
- async getTableSchema(tableName) {
616
- const querier = await this.getQuerier();
617
- try {
618
- const exists = await this.tableExistsInternal(querier, tableName);
619
- if (!exists) {
620
- return undefined;
621
- }
622
- const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
623
- this.getColumns(querier, tableName),
624
- this.getIndexes(querier, tableName),
625
- this.getForeignKeys(querier, tableName),
626
- this.getPrimaryKey(querier, tableName)
627
- ]);
628
- return {
629
- name: tableName,
630
- columns,
631
- primaryKey,
632
- indexes,
633
- foreignKeys
634
- };
635
- } finally{
636
- await querier.release();
637
- }
638
- }
639
- async getTableNames() {
640
- const querier = await this.getQuerier();
641
- try {
642
- const sql = `
643
- SELECT TABLE_NAME as table_name
644
- FROM information_schema.TABLES
645
- WHERE TABLE_SCHEMA = DATABASE()
646
- AND TABLE_TYPE = 'BASE TABLE'
647
- ORDER BY TABLE_NAME
648
- `;
649
- const results = await querier.all(sql);
650
- return results.map((r)=>r.table_name);
651
- } finally{
652
- await querier.release();
653
- }
654
- }
655
- async tableExists(tableName) {
656
- const querier = await this.getQuerier();
657
- try {
658
- return this.tableExistsInternal(querier, tableName);
659
- } finally{
660
- await querier.release();
661
- }
662
- }
663
- async tableExistsInternal(querier, tableName) {
664
- const sql = `
665
- SELECT COUNT(*) as count
666
- FROM information_schema.TABLES
667
- WHERE TABLE_SCHEMA = DATABASE()
668
- AND TABLE_NAME = ?
669
- `;
670
- const results = await querier.all(sql, [
671
- tableName
672
- ]);
673
- return (results[0]?.count ?? 0) > 0;
674
- }
675
- async getQuerier() {
676
- const querier = await this.pool.getQuerier();
677
- if (!isSqlQuerier(querier)) {
678
- await querier.release();
679
- throw new Error('MysqlSchemaIntrospector requires a SQL-based querier');
680
- }
681
- return querier;
682
- }
683
- async getColumns(querier, tableName) {
684
- const sql = `
685
- SELECT
686
- COLUMN_NAME as column_name,
687
- DATA_TYPE as data_type,
688
- COLUMN_TYPE as column_type,
689
- IS_NULLABLE as is_nullable,
690
- COLUMN_DEFAULT as column_default,
691
- CHARACTER_MAXIMUM_LENGTH as character_maximum_length,
692
- NUMERIC_PRECISION as numeric_precision,
693
- NUMERIC_SCALE as numeric_scale,
694
- COLUMN_KEY as column_key,
695
- EXTRA as extra,
696
- COLUMN_COMMENT as column_comment
697
- FROM information_schema.COLUMNS
698
- WHERE TABLE_SCHEMA = DATABASE()
699
- AND TABLE_NAME = ?
700
- ORDER BY ORDINAL_POSITION
701
- `;
702
- const results = await querier.all(sql, [
703
- tableName
704
- ]);
705
- return results.map((row)=>({
706
- name: row.column_name,
707
- type: row.data_type.toUpperCase(),
708
- nullable: row.is_nullable === 'YES',
709
- defaultValue: this.parseDefaultValue(row.column_default),
710
- isPrimaryKey: row.column_key === 'PRI',
711
- isAutoIncrement: row.extra.toLowerCase().includes('auto_increment'),
712
- isUnique: row.column_key === 'UNI',
713
- length: row.character_maximum_length ?? undefined,
714
- precision: row.numeric_precision ?? undefined,
715
- scale: row.numeric_scale ?? undefined,
716
- comment: row.column_comment || undefined
717
- }));
718
- }
719
- async getIndexes(querier, tableName) {
720
- const sql = `
721
- SELECT
722
- INDEX_NAME as index_name,
723
- GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
724
- NOT NON_UNIQUE as is_unique
725
- FROM information_schema.STATISTICS
726
- WHERE TABLE_SCHEMA = DATABASE()
727
- AND TABLE_NAME = ?
728
- AND INDEX_NAME != 'PRIMARY'
729
- GROUP BY INDEX_NAME, NON_UNIQUE
730
- ORDER BY INDEX_NAME
731
- `;
732
- const results = await querier.all(sql, [
733
- tableName
734
- ]);
735
- return results.map((row)=>({
736
- name: row.index_name,
737
- columns: row.columns.split(','),
738
- unique: Boolean(row.is_unique)
739
- }));
740
- }
741
- async getForeignKeys(querier, tableName) {
742
- const sql = `
743
- SELECT
744
- kcu.CONSTRAINT_NAME as constraint_name,
745
- GROUP_CONCAT(kcu.COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) as columns,
746
- kcu.REFERENCED_TABLE_NAME as referenced_table,
747
- GROUP_CONCAT(kcu.REFERENCED_COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) as referenced_columns,
748
- rc.DELETE_RULE as delete_rule,
749
- rc.UPDATE_RULE as update_rule
750
- FROM information_schema.KEY_COLUMN_USAGE kcu
751
- JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
752
- ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
753
- AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
754
- WHERE kcu.TABLE_SCHEMA = DATABASE()
755
- AND kcu.TABLE_NAME = ?
756
- AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
757
- GROUP BY kcu.CONSTRAINT_NAME, kcu.REFERENCED_TABLE_NAME, rc.DELETE_RULE, rc.UPDATE_RULE
758
- ORDER BY kcu.CONSTRAINT_NAME
759
- `;
760
- const results = await querier.all(sql, [
761
- tableName
762
- ]);
763
- return results.map((row)=>({
764
- name: row.constraint_name,
765
- columns: row.columns.split(','),
766
- referencedTable: row.referenced_table,
767
- referencedColumns: row.referenced_columns.split(','),
768
- onDelete: this.normalizeReferentialAction(row.delete_rule),
769
- onUpdate: this.normalizeReferentialAction(row.update_rule)
770
- }));
771
- }
772
- async getPrimaryKey(querier, tableName) {
773
- const sql = `
774
- SELECT COLUMN_NAME as column_name
775
- FROM information_schema.KEY_COLUMN_USAGE
776
- WHERE TABLE_SCHEMA = DATABASE()
777
- AND TABLE_NAME = ?
778
- AND CONSTRAINT_NAME = 'PRIMARY'
779
- ORDER BY ORDINAL_POSITION
780
- `;
781
- const results = await querier.all(sql, [
782
- tableName
783
- ]);
784
- if (results.length === 0) {
785
- return undefined;
786
- }
787
- return results.map((r)=>r.column_name);
788
- }
789
- parseDefaultValue(defaultValue) {
790
- if (defaultValue === null) {
791
- return undefined;
792
- }
793
- // Check for common patterns
794
- if (defaultValue === 'NULL') {
795
- return null;
796
- }
797
- if (defaultValue === 'CURRENT_TIMESTAMP') {
798
- return defaultValue;
799
- }
800
- if (/^'.*'$/.test(defaultValue)) {
801
- return defaultValue.slice(1, -1);
802
- }
803
- if (/^-?\d+$/.test(defaultValue)) {
804
- return Number.parseInt(defaultValue, 10);
805
- }
806
- if (/^-?\d+\.\d+$/.test(defaultValue)) {
807
- return Number.parseFloat(defaultValue);
808
- }
809
- return defaultValue;
810
- }
811
- normalizeReferentialAction(action) {
812
- switch(action.toUpperCase()){
813
- case 'CASCADE':
814
- return 'CASCADE';
815
- case 'SET NULL':
816
- return 'SET NULL';
817
- case 'RESTRICT':
818
- return 'RESTRICT';
819
- case 'NO ACTION':
820
- return 'NO ACTION';
821
- default:
822
- return undefined;
823
- }
216
+ let _UserWithNonUpdatableId;
217
+ _dec20 = Entity(), _dec21 = Id({
218
+ updatable: false
219
+ }), _dec22 = Field();
220
+ class UserWithNonUpdatableId {
221
+ static{
222
+ ({ e: [_init_id1, _init_name2], c: [_UserWithNonUpdatableId, _initClass3] } = _apply_decs_2203_r(this, [
223
+ [
224
+ _dec21,
225
+ 0,
226
+ "id"
227
+ ],
228
+ [
229
+ _dec22,
230
+ 0,
231
+ "name"
232
+ ]
233
+ ], [
234
+ _dec20
235
+ ]));
236
+ }
237
+ static{
238
+ _initClass3();
239
+ }
240
+ constructor(){
241
+ this.id = _init_id1(this);
242
+ this.name = _init_name2(this);
824
243
  }
825
244
  }
826
- /**
827
- * Alias for MysqlSchemaIntrospector.
828
- * MariaDB uses the same information_schema structure as MySQL.
829
- */ const MariadbSchemaIntrospector = MysqlSchemaIntrospector;
830
-
831
- /**
832
- * PostgreSQL schema introspector
833
- */ class PostgresSchemaIntrospector {
834
- constructor(pool){
835
- this.pool = pool;
836
- }
837
- async getTableSchema(tableName) {
838
- const querier = await this.getQuerier();
839
- try {
840
- const exists = await this.tableExistsInternal(querier, tableName);
841
- if (!exists) {
842
- return undefined;
843
- }
844
- const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
845
- this.getColumns(querier, tableName),
846
- this.getIndexes(querier, tableName),
847
- this.getForeignKeys(querier, tableName),
848
- this.getPrimaryKey(querier, tableName)
849
- ]);
850
- return {
851
- name: tableName,
852
- columns,
853
- primaryKey,
854
- indexes,
855
- foreignKeys
856
- };
857
- } finally{
858
- await querier.release();
859
- }
860
- }
861
- async getTableNames() {
862
- const querier = await this.getQuerier();
863
- try {
864
- const sql = `
865
- SELECT table_name
866
- FROM information_schema.tables
867
- WHERE table_schema = 'public'
868
- AND table_type = 'BASE TABLE'
869
- ORDER BY table_name
870
- `;
871
- const results = await querier.all(sql);
872
- return results.map((r)=>r.table_name);
873
- } finally{
874
- await querier.release();
875
- }
245
+ let _LedgerAccount;
246
+ _dec23 = Entity(), _dec24 = Field(), _dec25 = Field(), _dec26 = Field({
247
+ reference: ()=>_LedgerAccount
248
+ }), _dec27 = ManyToOne();
249
+ class LedgerAccount extends (_BaseEntity3 = BaseEntity) {
250
+ static{
251
+ ({ e: [_init_name3, _init_description1, _init_parentLedgerId, _init_parentLedger], c: [_LedgerAccount, _initClass4] } = _apply_decs_2203_r(this, [
252
+ [
253
+ _dec24,
254
+ 0,
255
+ "name"
256
+ ],
257
+ [
258
+ _dec25,
259
+ 0,
260
+ "description"
261
+ ],
262
+ [
263
+ _dec26,
264
+ 0,
265
+ "parentLedgerId"
266
+ ],
267
+ [
268
+ _dec27,
269
+ 0,
270
+ "parentLedger"
271
+ ]
272
+ ], [
273
+ _dec23
274
+ ], _BaseEntity3));
275
+ }
276
+ static{
277
+ _initClass4();
876
278
  }
877
- async tableExists(tableName) {
878
- const querier = await this.getQuerier();
879
- try {
880
- return this.tableExistsInternal(querier, tableName);
881
- } finally{
882
- await querier.release();
883
- }
884
- }
885
- async tableExistsInternal(querier, tableName) {
886
- const sql = `
887
- SELECT EXISTS (
888
- SELECT FROM information_schema.tables
889
- WHERE table_schema = 'public'
890
- AND table_name = $1
891
- ) AS exists
892
- `;
893
- const results = await querier.all(sql, [
894
- tableName
895
- ]);
896
- return results[0]?.exists ?? false;
897
- }
898
- async getQuerier() {
899
- const querier = await this.pool.getQuerier();
900
- if (!isSqlQuerier(querier)) {
901
- await querier.release();
902
- throw new Error('PostgresSchemaIntrospector requires a SQL-based querier');
903
- }
904
- return querier;
905
- }
906
- async getColumns(querier, tableName) {
907
- const sql = /*sql*/ `
908
- SELECT
909
- c.column_name,
910
- c.data_type,
911
- c.udt_name,
912
- c.is_nullable,
913
- c.column_default,
914
- c.character_maximum_length,
915
- c.numeric_precision,
916
- c.numeric_scale,
917
- COALESCE(
918
- (SELECT TRUE FROM information_schema.table_constraints tc
919
- JOIN information_schema.key_column_usage kcu
920
- ON tc.constraint_name = kcu.constraint_name
921
- WHERE tc.table_name = c.table_name
922
- AND tc.constraint_type = 'PRIMARY KEY'
923
- AND kcu.column_name = c.column_name
924
- LIMIT 1),
925
- FALSE
926
- ) AS is_primary_key,
927
- COALESCE(
928
- (SELECT TRUE FROM information_schema.table_constraints tc
929
- JOIN information_schema.key_column_usage kcu
930
- ON tc.constraint_name = kcu.constraint_name
931
- WHERE tc.table_name = c.table_name
932
- AND tc.constraint_type = 'UNIQUE'
933
- AND kcu.column_name = c.column_name
934
- LIMIT 1),
935
- FALSE
936
- ) AS is_unique,
937
- pg_catalog.col_description(
938
- (SELECT oid FROM pg_catalog.pg_class WHERE relname = c.table_name),
939
- c.ordinal_position
940
- ) AS column_comment
941
- FROM information_schema.columns c
942
- WHERE c.table_schema = 'public'
943
- AND c.table_name = $1
944
- ORDER BY c.ordinal_position
945
- `;
946
- const results = await querier.all(sql, [
947
- tableName
948
- ]);
949
- return results.map((row)=>({
950
- name: row.column_name,
951
- type: this.normalizeType(row.data_type, row.udt_name),
952
- nullable: row.is_nullable === 'YES',
953
- defaultValue: this.parseDefaultValue(row.column_default),
954
- isPrimaryKey: row.is_primary_key,
955
- isAutoIncrement: this.isAutoIncrement(row.column_default),
956
- isUnique: row.is_unique,
957
- length: row.character_maximum_length ?? undefined,
958
- precision: row.numeric_precision ?? undefined,
959
- scale: row.numeric_scale ?? undefined,
960
- comment: row.column_comment ?? undefined
961
- }));
962
- }
963
- async getIndexes(querier, tableName) {
964
- const sql = /*sql*/ `
965
- SELECT
966
- i.relname AS index_name,
967
- array_agg(a.attname ORDER BY k.n) AS columns,
968
- ix.indisunique AS is_unique
969
- FROM pg_class t
970
- JOIN pg_index ix ON t.oid = ix.indrelid
971
- JOIN pg_class i ON i.oid = ix.indexrelid
972
- JOIN pg_namespace n ON n.oid = t.relnamespace
973
- CROSS JOIN LATERAL unnest(ix.indkey) WITH ORDINALITY AS k(attnum, n)
974
- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = k.attnum
975
- WHERE t.relname = $1
976
- AND n.nspname = 'public'
977
- AND NOT ix.indisprimary
978
- GROUP BY i.relname, ix.indisunique
979
- ORDER BY i.relname
980
- `;
981
- const results = await querier.all(sql, [
982
- tableName
983
- ]);
984
- return results.map((row)=>({
985
- name: row.index_name,
986
- columns: row.columns,
987
- unique: row.is_unique
988
- }));
989
- }
990
- async getForeignKeys(querier, tableName) {
991
- const sql = /*sql*/ `
992
- SELECT
993
- tc.constraint_name,
994
- array_agg(kcu.column_name ORDER BY kcu.ordinal_position) AS columns,
995
- ccu.table_name AS referenced_table,
996
- array_agg(ccu.column_name ORDER BY kcu.ordinal_position) AS referenced_columns,
997
- rc.delete_rule,
998
- rc.update_rule
999
- FROM information_schema.table_constraints tc
1000
- JOIN information_schema.key_column_usage kcu
1001
- ON tc.constraint_name = kcu.constraint_name
1002
- AND tc.table_schema = kcu.table_schema
1003
- JOIN information_schema.constraint_column_usage ccu
1004
- ON ccu.constraint_name = tc.constraint_name
1005
- AND ccu.table_schema = tc.table_schema
1006
- JOIN information_schema.referential_constraints rc
1007
- ON rc.constraint_name = tc.constraint_name
1008
- AND rc.constraint_schema = tc.table_schema
1009
- WHERE tc.constraint_type = 'FOREIGN KEY'
1010
- AND tc.table_name = $1
1011
- AND tc.table_schema = 'public'
1012
- GROUP BY tc.constraint_name, ccu.table_name, rc.delete_rule, rc.update_rule
1013
- ORDER BY tc.constraint_name
1014
- `;
1015
- const results = await querier.all(sql, [
1016
- tableName
1017
- ]);
1018
- return results.map((row)=>({
1019
- name: row.constraint_name,
1020
- columns: row.columns,
1021
- referencedTable: row.referenced_table,
1022
- referencedColumns: row.referenced_columns,
1023
- onDelete: this.normalizeReferentialAction(row.delete_rule),
1024
- onUpdate: this.normalizeReferentialAction(row.update_rule)
1025
- }));
1026
- }
1027
- async getPrimaryKey(querier, tableName) {
1028
- const sql = /*sql*/ `
1029
- SELECT kcu.column_name
1030
- FROM information_schema.table_constraints tc
1031
- JOIN information_schema.key_column_usage kcu
1032
- ON tc.constraint_name = kcu.constraint_name
1033
- AND tc.table_schema = kcu.table_schema
1034
- WHERE tc.constraint_type = 'PRIMARY KEY'
1035
- AND tc.table_name = $1
1036
- AND tc.table_schema = 'public'
1037
- ORDER BY kcu.ordinal_position
1038
- `;
1039
- const results = await querier.all(sql, [
1040
- tableName
1041
- ]);
1042
- if (results.length === 0) {
1043
- return undefined;
1044
- }
1045
- return results.map((r)=>r.column_name);
1046
- }
1047
- normalizeType(dataType, udtName) {
1048
- // Handle user-defined types and arrays
1049
- if (dataType === 'USER-DEFINED') {
1050
- return udtName.toUpperCase();
1051
- }
1052
- if (dataType === 'ARRAY') {
1053
- return `${udtName.replace(/^_/, '')}[]`;
1054
- }
1055
- return dataType.toUpperCase();
1056
- }
1057
- parseDefaultValue(defaultValue) {
1058
- if (!defaultValue) {
1059
- return undefined;
1060
- }
1061
- // Remove type casting
1062
- const cleaned = defaultValue.replace(/::[a-z_]+(\[\])?/gi, '').trim();
1063
- // Check for common patterns
1064
- if (cleaned.startsWith("'") && cleaned.endsWith("'")) {
1065
- return cleaned.slice(1, -1);
1066
- }
1067
- if (cleaned === 'true' || cleaned === 'false') {
1068
- return cleaned === 'true';
1069
- }
1070
- if (cleaned === 'NULL') {
1071
- return null;
1072
- }
1073
- if (/^-?\d+$/.test(cleaned)) {
1074
- return Number.parseInt(cleaned, 10);
1075
- }
1076
- if (/^-?\d+\.\d+$/.test(cleaned)) {
1077
- return Number.parseFloat(cleaned);
1078
- }
1079
- // Return as-is for functions like CURRENT_TIMESTAMP, nextval(), etc.
1080
- return defaultValue;
1081
- }
1082
- isAutoIncrement(defaultValue) {
1083
- if (!defaultValue) {
1084
- return false;
1085
- }
1086
- return defaultValue.includes('nextval(');
1087
- }
1088
- normalizeReferentialAction(action) {
1089
- switch(action.toUpperCase()){
1090
- case 'CASCADE':
1091
- return 'CASCADE';
1092
- case 'SET NULL':
1093
- return 'SET NULL';
1094
- case 'RESTRICT':
1095
- return 'RESTRICT';
1096
- case 'NO ACTION':
1097
- return 'NO ACTION';
1098
- default:
1099
- return undefined;
1100
- }
279
+ constructor(...args){
280
+ super(...args), this.name = _init_name3(this), this.description = _init_description1(this), this.parentLedgerId = _init_parentLedgerId(this), this.parentLedger = _init_parentLedger(this);
1101
281
  }
1102
282
  }
1103
-
1104
- /**
1105
- * SQLite schema introspector
1106
- */ class SqliteSchemaIntrospector {
1107
- constructor(pool){
1108
- this.pool = pool;
283
+ let _TaxCategory;
284
+ _dec28 = Entity(), _dec29 = Id({
285
+ onInsert: randomUUID
286
+ }), _dec30 = Field(), _dec31 = Field();
287
+ class TaxCategory extends (_BaseEntity4 = BaseEntity) {
288
+ static{
289
+ ({ e: [_init_pk1, _init_name4, _init_description2], c: [_TaxCategory, _initClass5] } = _apply_decs_2203_r(this, [
290
+ [
291
+ _dec29,
292
+ 0,
293
+ "pk"
294
+ ],
295
+ [
296
+ _dec30,
297
+ 0,
298
+ "name"
299
+ ],
300
+ [
301
+ _dec31,
302
+ 0,
303
+ "description"
304
+ ]
305
+ ], [
306
+ _dec28
307
+ ], _BaseEntity4));
308
+ }
309
+ static{
310
+ _initClass5();
1109
311
  }
1110
- async getTableSchema(tableName) {
1111
- const querier = await this.getQuerier();
1112
- try {
1113
- const exists = await this.tableExistsInternal(querier, tableName);
1114
- if (!exists) {
1115
- return undefined;
1116
- }
1117
- const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
1118
- this.getColumns(querier, tableName),
1119
- this.getIndexes(querier, tableName),
1120
- this.getForeignKeys(querier, tableName),
1121
- this.getPrimaryKey(querier, tableName)
1122
- ]);
1123
- return {
1124
- name: tableName,
1125
- columns,
1126
- primaryKey,
1127
- indexes,
1128
- foreignKeys
1129
- };
1130
- } finally{
1131
- await querier.release();
1132
- }
1133
- }
1134
- async getTableNames() {
1135
- const querier = await this.getQuerier();
1136
- try {
1137
- const sql = /*sql*/ `
1138
- SELECT name
1139
- FROM sqlite_master
1140
- WHERE type = 'table'
1141
- AND name NOT LIKE 'sqlite_%'
1142
- ORDER BY name
1143
- `;
1144
- const results = await querier.all(sql);
1145
- return results.map((r)=>r.name);
1146
- } finally{
1147
- await querier.release();
1148
- }
1149
- }
1150
- async tableExists(tableName) {
1151
- const querier = await this.getQuerier();
1152
- try {
1153
- return this.tableExistsInternal(querier, tableName);
1154
- } finally{
1155
- await querier.release();
1156
- }
1157
- }
1158
- async tableExistsInternal(querier, tableName) {
1159
- const sql = /*sql*/ `
1160
- SELECT COUNT(*) as count
1161
- FROM sqlite_master
1162
- WHERE type = 'table'
1163
- AND name = ?
1164
- `;
1165
- const results = await querier.all(sql, [
1166
- tableName
1167
- ]);
1168
- return (results[0]?.count ?? 0) > 0;
1169
- }
1170
- async getQuerier() {
1171
- const querier = await this.pool.getQuerier();
1172
- if (!isSqlQuerier(querier)) {
1173
- await querier.release();
1174
- throw new Error('SqliteSchemaIntrospector requires a SQL-based querier');
1175
- }
1176
- return querier;
1177
- }
1178
- async getColumns(querier, tableName) {
1179
- // SQLite uses PRAGMA for table info
1180
- const sql = `PRAGMA table_info(${this.escapeId(tableName)})`;
1181
- const results = await querier.all(sql);
1182
- // Get unique columns from indexes
1183
- const uniqueColumns = await this.getUniqueColumns(querier, tableName);
1184
- return results.map((row)=>({
1185
- name: row.name,
1186
- type: this.normalizeType(row.type),
1187
- nullable: row.notnull === 0,
1188
- defaultValue: this.parseDefaultValue(row.dflt_value),
1189
- isPrimaryKey: row.pk > 0,
1190
- isAutoIncrement: row.pk > 0 && row.type.toUpperCase() === 'INTEGER',
1191
- isUnique: uniqueColumns.has(row.name),
1192
- length: this.extractLength(row.type),
1193
- precision: undefined,
1194
- scale: undefined,
1195
- comment: undefined
1196
- }));
1197
- }
1198
- async getUniqueColumns(querier, tableName) {
1199
- const sql = `PRAGMA index_list(${this.escapeId(tableName)})`;
1200
- const indexes = await querier.all(sql);
1201
- const uniqueColumns = new Set();
1202
- for (const index of indexes){
1203
- if (index.unique && index.origin === 'u') {
1204
- const indexInfo = await querier.all(`PRAGMA index_info(${this.escapeId(index.name)})`);
1205
- // Only single-column unique constraints
1206
- if (indexInfo.length === 1) {
1207
- uniqueColumns.add(indexInfo[0].name);
1208
- }
1209
- }
1210
- }
1211
- return uniqueColumns;
312
+ constructor(...args){
313
+ super(...args), this.pk = _init_pk1(this), this.name = _init_name4(this), this.description = _init_description2(this);
1212
314
  }
1213
- async getIndexes(querier, tableName) {
1214
- const sql = `PRAGMA index_list(${this.escapeId(tableName)})`;
1215
- const indexes = await querier.all(sql);
1216
- const result = [];
1217
- for (const index of indexes){
1218
- // Skip auto-generated indexes (primary key, unique constraints)
1219
- if (index.origin !== 'c') {
1220
- continue;
1221
- }
1222
- const columns = await querier.all(`PRAGMA index_info(${this.escapeId(index.name)})`);
1223
- result.push({
1224
- name: index.name,
1225
- columns: columns.map((c)=>c.name),
1226
- unique: Boolean(index.unique)
1227
- });
1228
- }
1229
- return result;
315
+ }
316
+ let _Tax;
317
+ _dec32 = Entity(), _dec33 = Field(), _dec34 = Field(), _dec35 = Field({
318
+ reference: ()=>_TaxCategory
319
+ }), _dec36 = ManyToOne(), _dec37 = Field();
320
+ class Tax extends (_BaseEntity5 = BaseEntity) {
321
+ static{
322
+ ({ e: [_init_name5, _init_percentage, _init_categoryId, _init_category, _init_description3], c: [_Tax, _initClass6] } = _apply_decs_2203_r(this, [
323
+ [
324
+ _dec33,
325
+ 0,
326
+ "name"
327
+ ],
328
+ [
329
+ _dec34,
330
+ 0,
331
+ "percentage"
332
+ ],
333
+ [
334
+ _dec35,
335
+ 0,
336
+ "categoryId"
337
+ ],
338
+ [
339
+ _dec36,
340
+ 0,
341
+ "category"
342
+ ],
343
+ [
344
+ _dec37,
345
+ 0,
346
+ "description"
347
+ ]
348
+ ], [
349
+ _dec32
350
+ ], _BaseEntity5));
351
+ }
352
+ static{
353
+ _initClass6();
1230
354
  }
1231
- async getForeignKeys(querier, tableName) {
1232
- const sql = `PRAGMA foreign_key_list(${this.escapeId(tableName)})`;
1233
- const results = await querier.all(sql);
1234
- // Group by id to handle composite foreign keys
1235
- const grouped = new Map();
1236
- for (const row of results){
1237
- const existing = grouped.get(row.id) ?? [];
1238
- existing.push(row);
1239
- grouped.set(row.id, existing);
1240
- }
1241
- return Array.from(grouped.entries()).map(([id, rows])=>{
1242
- const first = rows[0];
1243
- return {
1244
- name: `fk_${tableName}_${id}`,
1245
- columns: rows.map((r)=>r.from),
1246
- referencedTable: first.table,
1247
- referencedColumns: rows.map((r)=>r.to),
1248
- onDelete: this.normalizeReferentialAction(first.on_delete),
1249
- onUpdate: this.normalizeReferentialAction(first.on_update)
1250
- };
1251
- });
355
+ constructor(...args){
356
+ super(...args), this.name = _init_name5(this), this.percentage = _init_percentage(this), this.categoryId = _init_categoryId(this), this.category = _init_category(this), this.description = _init_description3(this);
1252
357
  }
1253
- async getPrimaryKey(querier, tableName) {
1254
- const sql = `PRAGMA table_info(${this.escapeId(tableName)})`;
1255
- const results = await querier.all(sql);
1256
- const pkColumns = results.filter((r)=>r.pk > 0).sort((a, b)=>a.pk - b.pk);
1257
- if (pkColumns.length === 0) {
1258
- return undefined;
1259
- }
1260
- return pkColumns.map((r)=>r.name);
358
+ }
359
+ let _MeasureUnitCategory;
360
+ _dec38 = Entity({
361
+ softDelete: true
362
+ }), _dec39 = Field(), _dec40 = OneToMany({
363
+ entity: ()=>_MeasureUnit,
364
+ mappedBy: (measureUnit)=>measureUnit.categoryId
365
+ }), _dec41 = Field({
366
+ onDelete: Date.now
367
+ });
368
+ class MeasureUnitCategory extends (_BaseEntity6 = BaseEntity) {
369
+ static{
370
+ ({ e: [_init_name6, _init_measureUnits, _init_deletedAt], c: [_MeasureUnitCategory, _initClass7] } = _apply_decs_2203_r(this, [
371
+ [
372
+ _dec39,
373
+ 0,
374
+ "name"
375
+ ],
376
+ [
377
+ _dec40,
378
+ 0,
379
+ "measureUnits"
380
+ ],
381
+ [
382
+ _dec41,
383
+ 0,
384
+ "deletedAt"
385
+ ]
386
+ ], [
387
+ _dec38
388
+ ], _BaseEntity6));
389
+ }
390
+ static{
391
+ _initClass7();
1261
392
  }
1262
- escapeId(identifier) {
1263
- return `\`${identifier.replace(/`/g, '``')}\``;
393
+ constructor(...args){
394
+ super(...args), this.name = _init_name6(this), this.measureUnits = _init_measureUnits(this), this.deletedAt = _init_deletedAt(this);
1264
395
  }
1265
- normalizeType(type) {
1266
- // Extract base type without length/precision
1267
- const match = type.match(/^([A-Za-z]+)/);
1268
- return match ? match[1].toUpperCase() : type.toUpperCase();
396
+ }
397
+ let _MeasureUnit;
398
+ _dec42 = Entity({
399
+ softDelete: true
400
+ }), _dec43 = Field(), _dec44 = Field({
401
+ reference: ()=>_MeasureUnitCategory
402
+ }), _dec45 = ManyToOne({
403
+ cascade: 'persist'
404
+ }), _dec46 = Field({
405
+ onDelete: Date.now
406
+ });
407
+ class MeasureUnit extends (_BaseEntity7 = BaseEntity) {
408
+ static{
409
+ ({ e: [_init_name7, _init_categoryId1, _init_category1, _init_deletedAt1], c: [_MeasureUnit, _initClass8] } = _apply_decs_2203_r(this, [
410
+ [
411
+ _dec43,
412
+ 0,
413
+ "name"
414
+ ],
415
+ [
416
+ _dec44,
417
+ 0,
418
+ "categoryId"
419
+ ],
420
+ [
421
+ _dec45,
422
+ 0,
423
+ "category"
424
+ ],
425
+ [
426
+ _dec46,
427
+ 0,
428
+ "deletedAt"
429
+ ]
430
+ ], [
431
+ _dec42
432
+ ], _BaseEntity7));
433
+ }
434
+ static{
435
+ _initClass8();
1269
436
  }
1270
- extractLength(type) {
1271
- const match = type.match(/\((\d+)\)/);
1272
- return match ? Number.parseInt(match[1], 10) : undefined;
437
+ constructor(...args){
438
+ super(...args), this.name = _init_name7(this), this.categoryId = _init_categoryId1(this), this.category = _init_category1(this), this.deletedAt = _init_deletedAt1(this);
1273
439
  }
1274
- parseDefaultValue(defaultValue) {
1275
- if (defaultValue === null) {
1276
- return undefined;
1277
- }
1278
- // Check for common patterns
1279
- if (defaultValue === 'NULL') {
1280
- return null;
1281
- }
1282
- if (defaultValue === 'CURRENT_TIMESTAMP' || defaultValue === 'CURRENT_DATE' || defaultValue === 'CURRENT_TIME') {
1283
- return defaultValue;
1284
- }
1285
- if (/^'.*'$/.test(defaultValue)) {
1286
- return defaultValue.slice(1, -1);
1287
- }
1288
- if (/^-?\d+$/.test(defaultValue)) {
1289
- return Number.parseInt(defaultValue, 10);
1290
- }
1291
- if (/^-?\d+\.\d+$/.test(defaultValue)) {
1292
- return Number.parseFloat(defaultValue);
1293
- }
1294
- return defaultValue;
440
+ }
441
+ let _Storehouse;
442
+ _dec47 = Entity(), _dec48 = Field(), _dec49 = Field(), _dec50 = Field();
443
+ class Storehouse extends (_BaseEntity8 = BaseEntity) {
444
+ static{
445
+ ({ e: [_init_name8, _init_address, _init_description4], c: [_Storehouse, _initClass9] } = _apply_decs_2203_r(this, [
446
+ [
447
+ _dec48,
448
+ 0,
449
+ "name"
450
+ ],
451
+ [
452
+ _dec49,
453
+ 0,
454
+ "address"
455
+ ],
456
+ [
457
+ _dec50,
458
+ 0,
459
+ "description"
460
+ ]
461
+ ], [
462
+ _dec47
463
+ ], _BaseEntity8));
464
+ }
465
+ static{
466
+ _initClass9();
1295
467
  }
1296
- normalizeReferentialAction(action) {
1297
- switch(action.toUpperCase()){
1298
- case 'CASCADE':
1299
- return 'CASCADE';
1300
- case 'SET NULL':
1301
- return 'SET NULL';
1302
- case 'RESTRICT':
1303
- return 'RESTRICT';
1304
- case 'NO ACTION':
1305
- return 'NO ACTION';
1306
- default:
1307
- return undefined;
1308
- }
468
+ constructor(...args){
469
+ super(...args), this.name = _init_name8(this), this.address = _init_address(this), this.description = _init_description4(this);
1309
470
  }
1310
471
  }
1311
-
1312
- class MongoSchemaGenerator extends AbstractDialect {
1313
- generateCreateTable(entity) {
1314
- const meta = getMeta(entity);
1315
- const collectionName = this.resolveTableName(entity, meta);
1316
- const indexes = [];
1317
- for(const key in meta.fields){
1318
- const field = meta.fields[key];
1319
- if (field.index) {
1320
- const columnName = this.resolveColumnName(key, field);
1321
- const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
1322
- indexes.push({
1323
- name: indexName,
1324
- columns: [
1325
- columnName
1326
- ],
1327
- unique: !!field.unique
1328
- });
1329
- }
1330
- }
1331
- return JSON.stringify({
1332
- action: 'createCollection',
1333
- name: collectionName,
1334
- indexes
472
+ let _Item;
473
+ _dec51 = Entity(), _dec52 = Field(), _dec53 = Field(), _dec54 = Field(), _dec55 = Field({
474
+ reference: ()=>_LedgerAccount
475
+ }), _dec56 = ManyToOne(), _dec57 = Field({
476
+ reference: ()=>_LedgerAccount
477
+ }), _dec58 = ManyToOne(), _dec59 = Field({
478
+ reference: ()=>_Tax
479
+ }), _dec60 = ManyToOne(), _dec61 = Field({
480
+ reference: ()=>_MeasureUnit
481
+ }), _dec62 = ManyToOne(), _dec63 = Field(), _dec64 = Field(), _dec65 = ManyToMany({
482
+ entity: ()=>_Tag,
483
+ through: ()=>_ItemTag,
484
+ cascade: true
485
+ }), _dec66 = Field({
486
+ /**
487
+ * `virtual` property allows defining the value for a non-persistent field,
488
+ * such value might be a scalar or a (`raw`) function. Virtual-fields can
489
+ * be used in `$select` and `$where` as a common field whose value is
490
+ * replaced is replaced at runtime.
491
+ */ virtual: raw(({ ctx, escapedPrefix, dialect })=>{
492
+ ctx.append('(');
493
+ dialect.count(ctx, _ItemTag, {
494
+ $where: {
495
+ itemId: raw(({ ctx })=>{
496
+ ctx.append(escapedPrefix + dialect.escapeId('id'));
497
+ })
498
+ }
499
+ }, {
500
+ autoPrefix: true
1335
501
  });
502
+ ctx.append(')');
503
+ })
504
+ });
505
+ class Item extends (_BaseEntity9 = BaseEntity) {
506
+ static{
507
+ ({ e: [_init_name9, _init_description5, _init_code, _init_buyLedgerAccountId, _init_buyLedgerAccount, _init_saleLedgerAccountId, _init_saleLedgerAccount, _init_taxId, _init_tax, _init_measureUnitId, _init_measureUnit, _init_salePrice, _init_inventoryable, _init_tags, _init_tagsCount], c: [_Item, _initClass10] } = _apply_decs_2203_r(this, [
508
+ [
509
+ _dec52,
510
+ 0,
511
+ "name"
512
+ ],
513
+ [
514
+ _dec53,
515
+ 0,
516
+ "description"
517
+ ],
518
+ [
519
+ _dec54,
520
+ 0,
521
+ "code"
522
+ ],
523
+ [
524
+ _dec55,
525
+ 0,
526
+ "buyLedgerAccountId"
527
+ ],
528
+ [
529
+ _dec56,
530
+ 0,
531
+ "buyLedgerAccount"
532
+ ],
533
+ [
534
+ _dec57,
535
+ 0,
536
+ "saleLedgerAccountId"
537
+ ],
538
+ [
539
+ _dec58,
540
+ 0,
541
+ "saleLedgerAccount"
542
+ ],
543
+ [
544
+ _dec59,
545
+ 0,
546
+ "taxId"
547
+ ],
548
+ [
549
+ _dec60,
550
+ 0,
551
+ "tax"
552
+ ],
553
+ [
554
+ _dec61,
555
+ 0,
556
+ "measureUnitId"
557
+ ],
558
+ [
559
+ _dec62,
560
+ 0,
561
+ "measureUnit"
562
+ ],
563
+ [
564
+ _dec63,
565
+ 0,
566
+ "salePrice"
567
+ ],
568
+ [
569
+ _dec64,
570
+ 0,
571
+ "inventoryable"
572
+ ],
573
+ [
574
+ _dec65,
575
+ 0,
576
+ "tags"
577
+ ],
578
+ [
579
+ _dec66,
580
+ 0,
581
+ "tagsCount"
582
+ ]
583
+ ], [
584
+ _dec51
585
+ ], _BaseEntity9));
586
+ }
587
+ static{
588
+ _initClass10();
1336
589
  }
1337
- generateDropTable(entity) {
1338
- const meta = getMeta(entity);
1339
- const collectionName = this.resolveTableName(entity, meta);
1340
- return JSON.stringify({
1341
- action: 'dropCollection',
1342
- name: collectionName
1343
- });
1344
- }
1345
- generateAlterTable(diff) {
1346
- const statements = [];
1347
- if (diff.indexesToAdd?.length) {
1348
- for (const index of diff.indexesToAdd){
1349
- statements.push(this.generateCreateIndex(diff.tableName, index));
1350
- }
1351
- }
1352
- return statements;
1353
- }
1354
- generateAlterTableDown(diff) {
1355
- const statements = [];
1356
- if (diff.indexesToAdd?.length) {
1357
- for (const index of diff.indexesToAdd){
1358
- statements.push(this.generateDropIndex(diff.tableName, index.name));
1359
- }
1360
- }
1361
- return statements;
1362
- }
1363
- generateCreateIndex(tableName, index) {
1364
- const key = {};
1365
- for (const col of index.columns){
1366
- key[col] = 1;
1367
- }
1368
- return JSON.stringify({
1369
- action: 'createIndex',
1370
- collection: tableName,
1371
- name: index.name,
1372
- key,
1373
- options: {
1374
- unique: index.unique,
1375
- name: index.name
1376
- }
1377
- });
590
+ constructor(...args){
591
+ super(...args), this.name = _init_name9(this), this.description = _init_description5(this), this.code = _init_code(this), this.buyLedgerAccountId = _init_buyLedgerAccountId(this), this.buyLedgerAccount = _init_buyLedgerAccount(this), this.saleLedgerAccountId = _init_saleLedgerAccountId(this), this.saleLedgerAccount = _init_saleLedgerAccount(this), this.taxId = _init_taxId(this), this.tax = _init_tax(this), this.measureUnitId = _init_measureUnitId(this), this.measureUnit = _init_measureUnit(this), this.salePrice = _init_salePrice(this), this.inventoryable = _init_inventoryable(this), this.tags = _init_tags(this), this.tagsCount = _init_tagsCount(this);
1378
592
  }
1379
- generateDropIndex(tableName, indexName) {
1380
- return JSON.stringify({
1381
- action: 'dropIndex',
1382
- collection: tableName,
1383
- name: indexName
593
+ }
594
+ let _Tag;
595
+ _dec67 = Entity(), _dec68 = Field(), _dec69 = ManyToMany({
596
+ entity: ()=>_Item,
597
+ mappedBy: (item)=>item.tags
598
+ }), _dec70 = Field({
599
+ virtual: raw(({ ctx, escapedPrefix, dialect })=>{
600
+ ctx.append('(');
601
+ dialect.count(ctx, _ItemTag, {
602
+ $where: {
603
+ tagId: raw(({ ctx })=>{
604
+ ctx.append(escapedPrefix + dialect.escapeId('id'));
605
+ })
606
+ }
607
+ }, {
608
+ autoPrefix: true
1384
609
  });
610
+ ctx.append(')');
611
+ })
612
+ });
613
+ class Tag extends (_BaseEntity10 = BaseEntity) {
614
+ static{
615
+ ({ e: [_init_name10, _init_items, _init_itemsCount], c: [_Tag, _initClass11] } = _apply_decs_2203_r(this, [
616
+ [
617
+ _dec68,
618
+ 0,
619
+ "name"
620
+ ],
621
+ [
622
+ _dec69,
623
+ 0,
624
+ "items"
625
+ ],
626
+ [
627
+ _dec70,
628
+ 0,
629
+ "itemsCount"
630
+ ]
631
+ ], [
632
+ _dec67
633
+ ], _BaseEntity10));
634
+ }
635
+ static{
636
+ _initClass11();
1385
637
  }
1386
- getSqlType() {
1387
- return '';
1388
- }
1389
- diffSchema(entity, currentSchema) {
1390
- const meta = getMeta(entity);
1391
- const collectionName = this.resolveTableName(entity, meta);
1392
- if (!currentSchema) {
1393
- return {
1394
- tableName: collectionName,
1395
- type: 'create'
1396
- };
1397
- }
1398
- const indexesToAdd = [];
1399
- const existingIndexes = new Set(currentSchema.indexes?.map((i)=>i.name) ?? []);
1400
- for(const key in meta.fields){
1401
- const field = meta.fields[key];
1402
- if (field.index) {
1403
- const columnName = this.resolveColumnName(key, field);
1404
- const indexName = typeof field.index === 'string' ? field.index : `idx_${collectionName}_${columnName}`;
1405
- if (!existingIndexes.has(indexName)) {
1406
- indexesToAdd.push({
1407
- name: indexName,
1408
- columns: [
1409
- columnName
1410
- ],
1411
- unique: !!field.unique
1412
- });
1413
- }
1414
- }
1415
- }
1416
- if (indexesToAdd.length === 0) {
1417
- return undefined;
1418
- }
1419
- return {
1420
- tableName: collectionName,
1421
- type: 'alter',
1422
- indexesToAdd
1423
- };
638
+ constructor(...args){
639
+ super(...args), this.name = _init_name10(this), this.items = _init_items(this), this.itemsCount = _init_itemsCount(this);
1424
640
  }
1425
641
  }
1426
-
1427
- class MongoSchemaIntrospector {
1428
- constructor(pool){
1429
- this.pool = pool;
1430
- }
1431
- async getTableSchema(tableName) {
1432
- const querier = await this.pool.getQuerier();
1433
- try {
1434
- const { db } = querier;
1435
- const collections = await db.listCollections({
1436
- name: tableName
1437
- }).toArray();
1438
- if (collections.length === 0) {
1439
- return undefined;
1440
- }
1441
- // MongoDB doesn't have a fixed schema, but we can look at the indexes
1442
- const indexes = await db.collection(tableName).indexes();
1443
- return {
1444
- name: tableName,
1445
- columns: [],
1446
- indexes: indexes.map((idx)=>({
1447
- name: idx.name,
1448
- columns: Object.keys(idx.key),
1449
- unique: !!idx.unique
1450
- }))
1451
- };
1452
- } finally{
1453
- await querier.release();
1454
- }
1455
- }
1456
- async getTableNames() {
1457
- const querier = await this.pool.getQuerier();
1458
- try {
1459
- const { db } = querier;
1460
- const collections = await db.listCollections().toArray();
1461
- return collections.map((c)=>c.name);
1462
- } finally{
1463
- await querier.release();
1464
- }
1465
- }
1466
- async tableExists(tableName) {
1467
- const names = await this.getTableNames();
1468
- return names.includes(tableName);
642
+ let _ItemTag;
643
+ _dec71 = Entity(), _dec72 = Id(), _dec73 = Field({
644
+ reference: ()=>_Item
645
+ }), _dec74 = Field({
646
+ reference: ()=>_Tag
647
+ });
648
+ class ItemTag {
649
+ static{
650
+ ({ e: [_init_id2, _init_itemId, _init_tagId], c: [_ItemTag, _initClass12] } = _apply_decs_2203_r(this, [
651
+ [
652
+ _dec72,
653
+ 0,
654
+ "id"
655
+ ],
656
+ [
657
+ _dec73,
658
+ 0,
659
+ "itemId"
660
+ ],
661
+ [
662
+ _dec74,
663
+ 0,
664
+ "tagId"
665
+ ]
666
+ ], [
667
+ _dec71
668
+ ]));
669
+ }
670
+ static{
671
+ _initClass12();
672
+ }
673
+ constructor(){
674
+ this.id = _init_id2(this);
675
+ this.itemId = _init_itemId(this);
676
+ this.tagId = _init_tagId(this);
1469
677
  }
1470
678
  }
1471
-
1472
- /**
1473
- * Stores migration state in a database table.
1474
- * Uses the querier's dialect for escaping and placeholders.
1475
- */ class DatabaseMigrationStorage {
1476
- constructor(pool, options = {}){
1477
- this.pool = pool;
1478
- this.storageInitialized = false;
1479
- this.tableName = options.tableName ?? 'uql_migrations';
1480
- }
1481
- async ensureStorage() {
1482
- if (this.storageInitialized) {
1483
- return;
1484
- }
1485
- const querier = await this.pool.getQuerier();
1486
- if (!isSqlQuerier(querier)) {
1487
- await querier.release();
1488
- throw new Error('DatabaseMigrationStorage requires a SQL-based querier');
1489
- }
1490
- try {
1491
- await this.createTableIfNotExists(querier);
1492
- this.storageInitialized = true;
1493
- } finally{
1494
- await querier.release();
1495
- }
679
+ let _InventoryAdjustment;
680
+ _dec75 = Entity(), _dec76 = OneToMany({
681
+ entity: ()=>_ItemAdjustment,
682
+ mappedBy: (rel)=>rel.inventoryAdjustment,
683
+ cascade: true
684
+ }), _dec77 = Field(), _dec78 = Field();
685
+ class InventoryAdjustment extends (_BaseEntity11 = BaseEntity) {
686
+ static{
687
+ ({ e: [_init_itemAdjustments, _init_date, _init_description6], c: [_InventoryAdjustment, _initClass13] } = _apply_decs_2203_r(this, [
688
+ [
689
+ _dec76,
690
+ 0,
691
+ "itemAdjustments"
692
+ ],
693
+ [
694
+ _dec77,
695
+ 0,
696
+ "date"
697
+ ],
698
+ [
699
+ _dec78,
700
+ 0,
701
+ "description"
702
+ ]
703
+ ], [
704
+ _dec75
705
+ ], _BaseEntity11));
706
+ }
707
+ static{
708
+ _initClass13();
1496
709
  }
1497
- async createTableIfNotExists(querier) {
1498
- const { escapeId } = querier.dialect;
1499
- const sql = `
1500
- CREATE TABLE IF NOT EXISTS ${escapeId(this.tableName)} (
1501
- ${escapeId('name')} VARCHAR(255) PRIMARY KEY,
1502
- ${escapeId('executed_at')} TIMESTAMP DEFAULT CURRENT_TIMESTAMP
1503
- )
1504
- `;
1505
- await querier.run(sql);
1506
- }
1507
- async executed() {
1508
- await this.ensureStorage();
1509
- const querier = await this.pool.getQuerier();
1510
- if (!isSqlQuerier(querier)) {
1511
- await querier.release();
1512
- throw new Error('DatabaseMigrationStorage requires a SQL-based querier');
1513
- }
1514
- try {
1515
- const { escapeId } = querier.dialect;
1516
- const sql = `SELECT ${escapeId('name')} FROM ${escapeId(this.tableName)} ORDER BY ${escapeId('name')} ASC`;
1517
- const results = await querier.all(sql);
1518
- return results.map((r)=>r.name);
1519
- } finally{
1520
- await querier.release();
1521
- }
710
+ constructor(...args){
711
+ super(...args), this.itemAdjustments = _init_itemAdjustments(this), this.date = _init_date(this), this.description = _init_description6(this);
1522
712
  }
1523
- /**
1524
- * Log a migration as executed - uses provided querier (within transaction)
1525
- */ async logWithQuerier(querier, migrationName) {
1526
- await this.ensureStorage();
1527
- const { escapeId, placeholder } = querier.dialect;
1528
- const sql = `INSERT INTO ${escapeId(this.tableName)} (${escapeId('name')}) VALUES (${placeholder(1)})`;
1529
- await querier.run(sql, [
1530
- migrationName
1531
- ]);
713
+ }
714
+ let _ItemAdjustment;
715
+ _dec79 = Entity(), _dec80 = Field({
716
+ reference: ()=>_Item
717
+ }), _dec81 = ManyToOne(), _dec82 = Field(), _dec83 = Field(), _dec84 = Field({
718
+ reference: ()=>_Storehouse
719
+ }), _dec85 = ManyToOne(), _dec86 = Field({
720
+ reference: ()=>_InventoryAdjustment
721
+ }), _dec87 = ManyToOne();
722
+ class ItemAdjustment extends (_BaseEntity12 = BaseEntity) {
723
+ static{
724
+ ({ e: [_init_itemId1, _init_item, _init_number, _init_buyPrice, _init_storehouseId, _init_storehouse, _init_inventoryAdjustmentId, _init_inventoryAdjustment], c: [_ItemAdjustment, _initClass14] } = _apply_decs_2203_r(this, [
725
+ [
726
+ _dec80,
727
+ 0,
728
+ "itemId"
729
+ ],
730
+ [
731
+ _dec81,
732
+ 0,
733
+ "item"
734
+ ],
735
+ [
736
+ _dec82,
737
+ 0,
738
+ "number"
739
+ ],
740
+ [
741
+ _dec83,
742
+ 0,
743
+ "buyPrice"
744
+ ],
745
+ [
746
+ _dec84,
747
+ 0,
748
+ "storehouseId"
749
+ ],
750
+ [
751
+ _dec85,
752
+ 0,
753
+ "storehouse"
754
+ ],
755
+ [
756
+ _dec86,
757
+ 0,
758
+ "inventoryAdjustmentId"
759
+ ],
760
+ [
761
+ _dec87,
762
+ 0,
763
+ "inventoryAdjustment"
764
+ ]
765
+ ], [
766
+ _dec79
767
+ ], _BaseEntity12));
768
+ }
769
+ static{
770
+ _initClass14();
1532
771
  }
1533
- /**
1534
- * Unlog a migration - uses provided querier (within transaction)
1535
- */ async unlogWithQuerier(querier, migrationName) {
1536
- await this.ensureStorage();
1537
- const { escapeId, placeholder } = querier.dialect;
1538
- const sql = `DELETE FROM ${escapeId(this.tableName)} WHERE ${escapeId('name')} = ${placeholder(1)}`;
1539
- await querier.run(sql, [
1540
- migrationName
1541
- ]);
772
+ constructor(...args){
773
+ super(...args), this.itemId = _init_itemId1(this), this.item = _init_item(this), this.number = _init_number(this), this.buyPrice = _init_buyPrice(this), this.storehouseId = _init_storehouseId(this), this.storehouse = _init_storehouse(this), this.inventoryAdjustmentId = _init_inventoryAdjustmentId(this), this.inventoryAdjustment = _init_inventoryAdjustment(this);
1542
774
  }
1543
775
  }
1544
776
 
1545
- /**
1546
- * Main class for managing database migrations
1547
- */ class Migrator {
1548
- constructor(pool, options = {}){
1549
- this.pool = pool;
1550
- this.dialect = options.dialect ?? pool.dialect ?? 'postgres';
1551
- this.storage = options.storage ?? new DatabaseMigrationStorage(pool, {
1552
- tableName: options.tableName
1553
- });
1554
- this.migrationsPath = options.migrationsPath ?? './migrations';
1555
- this.logger = options.logger ?? (()=>{});
1556
- this.entities = options.entities ?? [];
1557
- this.schemaIntrospector = this.createIntrospector();
1558
- this.schemaGenerator = options.schemaGenerator ?? this.createGenerator(options.namingStrategy);
1559
- }
1560
- /**
1561
- * Set the schema generator for DDL operations
1562
- */ setSchemaGenerator(generator) {
1563
- this.schemaGenerator = generator;
1564
- }
1565
- createIntrospector() {
1566
- switch(this.dialect){
1567
- case 'postgres':
1568
- return new PostgresSchemaIntrospector(this.pool);
1569
- case 'mysql':
1570
- return new MysqlSchemaIntrospector(this.pool);
1571
- case 'mariadb':
1572
- return new MariadbSchemaIntrospector(this.pool);
1573
- case 'sqlite':
1574
- return new SqliteSchemaIntrospector(this.pool);
1575
- case 'mongodb':
1576
- return new MongoSchemaIntrospector(this.pool);
1577
- default:
1578
- return undefined;
1579
- }
1580
- }
1581
- createGenerator(namingStrategy) {
1582
- switch(this.dialect){
1583
- case 'postgres':
1584
- return new PostgresSchemaGenerator(namingStrategy);
1585
- case 'mysql':
1586
- return new MysqlSchemaGenerator(namingStrategy);
1587
- case 'mariadb':
1588
- return new MysqlSchemaGenerator(namingStrategy);
1589
- case 'sqlite':
1590
- return new SqliteSchemaGenerator(namingStrategy);
1591
- case 'mongodb':
1592
- return new MongoSchemaGenerator(namingStrategy);
1593
- default:
1594
- return undefined;
1595
- }
1596
- }
1597
- /**
1598
- * Get the SQL dialect
1599
- */ getDialect() {
1600
- return this.dialect;
1601
- }
1602
- /**
1603
- * Get all discovered migrations from the migrations directory
1604
- */ async getMigrations() {
1605
- const files = await this.getMigrationFiles();
1606
- const migrations = [];
1607
- for (const file of files){
1608
- const migration = await this.loadMigration(file);
1609
- if (migration) {
1610
- migrations.push(migration);
1611
- }
1612
- }
1613
- // Sort by name (which typically includes timestamp)
1614
- return migrations.sort((a, b)=>a.name.localeCompare(b.name));
1615
- }
1616
- /**
1617
- * Get list of pending migrations (not yet executed)
1618
- */ async pending() {
1619
- const [migrations, executed] = await Promise.all([
1620
- this.getMigrations(),
1621
- this.storage.executed()
1622
- ]);
1623
- const executedSet = new Set(executed);
1624
- return migrations.filter((m)=>!executedSet.has(m.name));
1625
- }
1626
- /**
1627
- * Get list of executed migrations
1628
- */ async executed() {
1629
- return this.storage.executed();
1630
- }
1631
- /**
1632
- * Run all pending migrations
1633
- */ async up(options = {}) {
1634
- const pendingMigrations = await this.pending();
1635
- const results = [];
1636
- let migrationsToRun = pendingMigrations;
1637
- if (options.to) {
1638
- const toIndex = migrationsToRun.findIndex((m)=>m.name === options.to);
1639
- if (toIndex === -1) {
1640
- throw new Error(`Migration '${options.to}' not found`);
1641
- }
1642
- migrationsToRun = migrationsToRun.slice(0, toIndex + 1);
1643
- }
1644
- if (options.step !== undefined) {
1645
- migrationsToRun = migrationsToRun.slice(0, options.step);
1646
- }
1647
- for (const migration of migrationsToRun){
1648
- const result = await this.runMigration(migration, 'up');
1649
- results.push(result);
1650
- if (!result.success) {
1651
- break; // Stop on first failure
1652
- }
1653
- }
1654
- return results;
1655
- }
1656
- /**
1657
- * Rollback migrations
1658
- */ async down(options = {}) {
1659
- const [migrations, executed] = await Promise.all([
1660
- this.getMigrations(),
1661
- this.storage.executed()
1662
- ]);
1663
- const executedSet = new Set(executed);
1664
- const executedMigrations = migrations.filter((m)=>executedSet.has(m.name)).reverse(); // Rollback in reverse order
1665
- const results = [];
1666
- let migrationsToRun = executedMigrations;
1667
- if (options.to) {
1668
- const toIndex = migrationsToRun.findIndex((m)=>m.name === options.to);
1669
- if (toIndex === -1) {
1670
- throw new Error(`Migration '${options.to}' not found`);
1671
- }
1672
- migrationsToRun = migrationsToRun.slice(0, toIndex + 1);
1673
- }
1674
- if (options.step !== undefined) {
1675
- migrationsToRun = migrationsToRun.slice(0, options.step);
1676
- }
1677
- for (const migration of migrationsToRun){
1678
- const result = await this.runMigration(migration, 'down');
1679
- results.push(result);
1680
- if (!result.success) {
1681
- break; // Stop on first failure
1682
- }
1683
- }
1684
- return results;
1685
- }
1686
- /**
1687
- * Run a single migration within a transaction
1688
- */ async runMigration(migration, direction) {
1689
- const startTime = Date.now();
1690
- const querier = await this.pool.getQuerier();
1691
- if (!isSqlQuerier(querier)) {
1692
- await querier.release();
1693
- throw new Error('Migrator requires a SQL-based querier');
1694
- }
1695
- try {
1696
- this.logger(`${direction === 'up' ? 'Running' : 'Reverting'} migration: ${migration.name}`);
1697
- await querier.beginTransaction();
1698
- if (direction === 'up') {
1699
- await migration.up(querier);
1700
- // Log within the same transaction
1701
- await this.storage.logWithQuerier(querier, migration.name);
1702
- } else {
1703
- await migration.down(querier);
1704
- // Unlog within the same transaction
1705
- await this.storage.unlogWithQuerier(querier, migration.name);
1706
- }
1707
- await querier.commitTransaction();
1708
- const duration = Date.now() - startTime;
1709
- this.logger(`Migration ${migration.name} ${direction === 'up' ? 'applied' : 'reverted'} in ${duration}ms`);
1710
- return {
1711
- name: migration.name,
1712
- direction,
1713
- duration,
1714
- success: true
1715
- };
1716
- } catch (error) {
1717
- await querier.rollbackTransaction();
1718
- const duration = Date.now() - startTime;
1719
- this.logger(`Migration ${migration.name} failed: ${error.message}`);
1720
- return {
1721
- name: migration.name,
1722
- direction,
1723
- duration,
1724
- success: false,
1725
- error: error
1726
- };
1727
- } finally{
1728
- await querier.release();
1729
- }
777
+ const holder = globalThis;
778
+ const metaKey = '@uql/core/entity/decorator';
779
+ const metas = holder[metaKey] ?? new Map();
780
+ holder[metaKey] = metas;
781
+ function getEntities() {
782
+ return [
783
+ ...metas.entries()
784
+ ].reduce((acc, [key, val])=>{
785
+ if (val.id) {
786
+ acc.push(key);
787
+ }
788
+ return acc;
789
+ }, []);
790
+ }
791
+ function ensureMeta(entity) {
792
+ let meta = metas.get(entity);
793
+ if (meta) {
794
+ return meta;
795
+ }
796
+ meta = {
797
+ entity,
798
+ fields: {},
799
+ relations: {}
800
+ };
801
+ metas.set(entity, meta);
802
+ return meta;
803
+ }
804
+ function getMeta(entity) {
805
+ const meta = metas.get(entity);
806
+ if (!meta) {
807
+ throw TypeError(`'${entity.name}' is not an entity`);
1730
808
  }
1731
- /**
1732
- * Generate a new migration file
1733
- */ async generate(name) {
1734
- const timestamp = this.getTimestamp();
1735
- const fileName = `${timestamp}_${this.slugify(name)}.ts`;
1736
- const filePath = join(this.migrationsPath, fileName);
1737
- const content = this.generateMigrationContent(name);
1738
- const { writeFile, mkdir } = await import('node:fs/promises');
1739
- await mkdir(this.migrationsPath, {
1740
- recursive: true
1741
- });
1742
- await writeFile(filePath, content, 'utf-8');
1743
- this.logger(`Created migration: ${filePath}`);
1744
- return filePath;
809
+ if (meta.processed) {
810
+ return meta;
1745
811
  }
1746
- /**
1747
- * Generate a migration based on entity schema differences
1748
- */ async generateFromEntities(name) {
1749
- if (!this.schemaGenerator) {
1750
- throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
1751
- }
1752
- const diffs = await this.getDiffs();
1753
- const upStatements = [];
1754
- const downStatements = [];
1755
- for (const diff of diffs){
1756
- if (diff.type === 'create') {
1757
- const entity = this.findEntityForTable(diff.tableName);
1758
- if (entity) {
1759
- upStatements.push(this.schemaGenerator.generateCreateTable(entity));
1760
- downStatements.push(this.schemaGenerator.generateDropTable(entity));
812
+ meta.processed = true;
813
+ return fillRelations(meta);
814
+ }
815
+ function fillRelations(meta) {
816
+ for(const relKey in meta.relations){
817
+ const relOpts = meta.relations[relKey];
818
+ if (relOpts.references) {
819
+ continue;
820
+ }
821
+ if (relOpts.mappedBy) {
822
+ fillInverseSideRelations(relOpts);
823
+ continue;
824
+ }
825
+ const relEntity = relOpts.entity();
826
+ const relMeta = ensureMeta(relEntity);
827
+ if (relOpts.cardinality === 'mm') {
828
+ const idName = meta.fields[meta.id].name;
829
+ const relIdName = relMeta.fields[relMeta.id].name;
830
+ const source = lowerFirst(meta.name) + upperFirst(idName);
831
+ const target = lowerFirst(relMeta.name) + upperFirst(relIdName);
832
+ relOpts.references = [
833
+ {
834
+ local: source,
835
+ foreign: meta.id
836
+ },
837
+ {
838
+ local: target,
839
+ foreign: relMeta.id
1761
840
  }
1762
- } else if (diff.type === 'alter') {
1763
- const alterStatements = this.schemaGenerator.generateAlterTable(diff);
1764
- upStatements.push(...alterStatements);
1765
- const alterDownStatements = this.schemaGenerator.generateAlterTableDown(diff);
1766
- downStatements.push(...alterDownStatements);
1767
- }
1768
- }
1769
- if (upStatements.length === 0) {
1770
- this.logger('No schema changes detected.');
1771
- return '';
1772
- }
1773
- const timestamp = this.getTimestamp();
1774
- const fileName = `${timestamp}_${this.slugify(name)}.ts`;
1775
- const filePath = join(this.migrationsPath, fileName);
1776
- const content = this.generateMigrationContentWithStatements(name, upStatements, downStatements.reverse());
1777
- const { writeFile, mkdir } = await import('node:fs/promises');
1778
- await mkdir(this.migrationsPath, {
1779
- recursive: true
1780
- });
1781
- await writeFile(filePath, content, 'utf-8');
1782
- this.logger(`Created migration from entities: ${filePath}`);
1783
- return filePath;
1784
- }
1785
- /**
1786
- * Get all schema differences between entities and database
1787
- */ async getDiffs() {
1788
- if (!this.schemaGenerator || !this.schemaIntrospector) {
1789
- throw new Error('Schema generator and introspector must be set');
1790
- }
1791
- const entities = this.entities.length > 0 ? this.entities : getEntities();
1792
- const diffs = [];
1793
- for (const entity of entities){
1794
- const meta = getMeta(entity);
1795
- const tableName = this.schemaGenerator.resolveTableName(entity, meta);
1796
- const currentSchema = await this.schemaIntrospector.getTableSchema(tableName);
1797
- const diff = this.schemaGenerator.diffSchema(entity, currentSchema);
1798
- if (diff) {
1799
- diffs.push(diff);
1800
- }
1801
- }
1802
- return diffs;
1803
- }
1804
- findEntityForTable(tableName) {
1805
- const entities = this.entities.length > 0 ? this.entities : getEntities();
1806
- for (const entity of entities){
1807
- const meta = getMeta(entity);
1808
- const name = this.schemaGenerator.resolveTableName(entity, meta);
1809
- if (name === tableName) {
1810
- return entity;
1811
- }
1812
- }
1813
- return undefined;
1814
- }
1815
- /**
1816
- * Sync schema directly (for development only - not for production!)
1817
- */ async sync(options = {}) {
1818
- if (options.force) {
1819
- return this.syncForce();
1820
- }
1821
- return this.autoSync({
1822
- safe: true
1823
- });
1824
- }
1825
- /**
1826
- * Drops and recreates all tables (Development only!)
1827
- */ async syncForce() {
1828
- if (!this.schemaGenerator) {
1829
- throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
1830
- }
1831
- const entities = this.entities.length > 0 ? this.entities : getEntities();
1832
- const querier = await this.pool.getQuerier();
1833
- if (!isSqlQuerier(querier)) {
1834
- await querier.release();
1835
- throw new Error('Migrator requires a SQL-based querier');
1836
- }
1837
- try {
1838
- await querier.beginTransaction();
1839
- // Drop all tables first (in reverse order for foreign keys)
1840
- for (const entity of [
1841
- ...entities
1842
- ].reverse()){
1843
- const dropSql = this.schemaGenerator.generateDropTable(entity);
1844
- this.logger(`Executing: ${dropSql}`);
1845
- await querier.run(dropSql);
1846
- }
1847
- // Create all tables
1848
- for (const entity of entities){
1849
- const createSql = this.schemaGenerator.generateCreateTable(entity);
1850
- this.logger(`Executing: ${createSql}`);
1851
- await querier.run(createSql);
1852
- }
1853
- await querier.commitTransaction();
1854
- this.logger('Schema sync (force) completed');
1855
- } catch (error) {
1856
- await querier.rollbackTransaction();
1857
- throw error;
1858
- } finally{
1859
- await querier.release();
1860
- }
1861
- }
1862
- /**
1863
- * Safely synchronizes the schema by only adding missing tables and columns.
1864
- */ async autoSync(options = {}) {
1865
- if (!this.schemaGenerator || !this.schemaIntrospector) {
1866
- throw new Error('Schema generator and introspector must be set');
1867
- }
1868
- const diffs = await this.getDiffs();
1869
- const statements = [];
1870
- for (const diff of diffs){
1871
- if (diff.type === 'create') {
1872
- const entity = this.findEntityForTable(diff.tableName);
1873
- if (entity) {
1874
- statements.push(this.schemaGenerator.generateCreateTable(entity));
841
+ ];
842
+ } else {
843
+ relOpts.references = [
844
+ {
845
+ local: `${relKey}Id`,
846
+ foreign: relMeta.id
1875
847
  }
1876
- } else if (diff.type === 'alter') {
1877
- const filteredDiff = this.filterDiff(diff, options);
1878
- const alterStatements = this.schemaGenerator.generateAlterTable(filteredDiff);
1879
- statements.push(...alterStatements);
1880
- }
1881
- }
1882
- if (statements.length === 0) {
1883
- if (options.logging) this.logger('Schema is already in sync.');
1884
- return;
848
+ ];
1885
849
  }
1886
- await this.executeSyncStatements(statements, options);
1887
- }
1888
- filterDiff(diff, options) {
1889
- const filteredDiff = {
1890
- ...diff
1891
- };
1892
- if (options.safe !== false) {
1893
- // In safe mode, we only allow additions
1894
- delete filteredDiff.columnsToDrop;
1895
- delete filteredDiff.indexesToDrop;
1896
- delete filteredDiff.foreignKeysToDrop;
1897
- }
1898
- if (!options.drop) {
1899
- delete filteredDiff.columnsToDrop;
1900
- }
1901
- return filteredDiff;
1902
- }
1903
- async executeSyncStatements(statements, options) {
1904
- const querier = await this.pool.getQuerier();
1905
- try {
1906
- if (this.dialect === 'mongodb') {
1907
- await this.executeMongoSyncStatements(statements, options, querier);
1908
- } else {
1909
- await this.executeSqlSyncStatements(statements, options, querier);
1910
- }
1911
- if (options.logging) this.logger('Schema synchronization completed');
1912
- } catch (error) {
1913
- if (this.dialect !== 'mongodb' && isSqlQuerier(querier)) {
1914
- await querier.rollbackTransaction();
1915
- }
1916
- throw error;
1917
- } finally{
1918
- await querier.release();
850
+ if (relOpts.through) {
851
+ fillThroughRelations(relOpts.through());
1919
852
  }
1920
853
  }
1921
- async executeMongoSyncStatements(statements, options, querier) {
1922
- const db = querier.db;
1923
- for (const stmt of statements){
1924
- const cmd = JSON.parse(stmt);
1925
- if (options.logging) this.logger(`Executing MongoDB: ${stmt}`);
1926
- const collectionName = cmd.name || cmd.collection;
1927
- if (!collectionName) {
1928
- throw new Error(`MongoDB command missing collection name: ${stmt}`);
854
+ return meta;
855
+ }
856
+ function fillInverseSideRelations(relOpts) {
857
+ const relEntity = relOpts.entity();
858
+ const relMeta = getMeta(relEntity);
859
+ relOpts.mappedBy = getMappedByRelationKey(relOpts);
860
+ if (relMeta.fields[relOpts.mappedBy]) {
861
+ relOpts.references = [
862
+ {
863
+ local: relMeta.id,
864
+ foreign: relOpts.mappedBy
1929
865
  }
1930
- const collection = db.collection(collectionName);
1931
- if (cmd.action === 'createCollection') {
1932
- await db.createCollection(cmd.name);
1933
- if (cmd.indexes?.length) {
1934
- for (const idx of cmd.indexes){
1935
- const key = Object.fromEntries(idx.columns.map((c)=>[
1936
- c,
1937
- 1
1938
- ]));
1939
- await collection.createIndex(key, {
1940
- unique: idx.unique,
1941
- name: idx.name
1942
- });
866
+ ];
867
+ return;
868
+ }
869
+ const mappedByRelation = relMeta.relations[relOpts.mappedBy];
870
+ if (relOpts.cardinality === 'm1' || relOpts.cardinality === 'mm') {
871
+ relOpts.references = mappedByRelation.references.slice().reverse();
872
+ relOpts.through = mappedByRelation.through;
873
+ return;
874
+ }
875
+ relOpts.references = mappedByRelation.references.map(({ local, foreign })=>({
876
+ local: foreign,
877
+ foreign: local
878
+ }));
879
+ }
880
+ function fillThroughRelations(entity) {
881
+ const meta = ensureMeta(entity);
882
+ meta.relations = getKeys(meta.fields).reduce((relations, key)=>{
883
+ const { reference } = meta.fields[key];
884
+ if (reference) {
885
+ const relEntity = reference();
886
+ const relMeta = ensureMeta(relEntity);
887
+ const relKey = key.slice(0, -relMeta.id.length);
888
+ const relOpts = {
889
+ entity: reference,
890
+ cardinality: 'm1',
891
+ references: [
892
+ {
893
+ local: key,
894
+ foreign: relMeta.id
1943
895
  }
1944
- }
1945
- } else if (cmd.action === 'dropCollection') {
1946
- await collection.drop();
1947
- } else if (cmd.action === 'createIndex') {
1948
- await collection.createIndex(cmd.key, cmd.options);
1949
- } else if (cmd.action === 'dropIndex') {
1950
- await collection.dropIndex(cmd.name);
1951
- }
1952
- }
1953
- }
1954
- async executeSqlSyncStatements(statements, options, querier) {
1955
- if (!isSqlQuerier(querier)) {
1956
- throw new Error('Migrator requires a SQL-based querier for this dialect');
1957
- }
1958
- await querier.beginTransaction();
1959
- for (const sql of statements){
1960
- if (options.logging) this.logger(`Executing: ${sql}`);
1961
- await querier.run(sql);
1962
- }
1963
- await querier.commitTransaction();
1964
- }
1965
- /**
1966
- * Get migration status
1967
- */ async status() {
1968
- const [pending, executed] = await Promise.all([
1969
- this.pending().then((m)=>m.map((x)=>x.name)),
1970
- this.executed()
1971
- ]);
1972
- return {
1973
- pending,
1974
- executed
1975
- };
1976
- }
1977
- /**
1978
- * Get migration files from the migrations directory
1979
- */ async getMigrationFiles() {
1980
- try {
1981
- const files = await readdir(this.migrationsPath);
1982
- return files.filter((f)=>/\.(ts|js|mjs)$/.test(f)).filter((f)=>!f.endsWith('.d.ts')).sort();
1983
- } catch (error) {
1984
- if (error.code === 'ENOENT') {
1985
- return [];
1986
- }
1987
- throw error;
1988
- }
1989
- }
1990
- /**
1991
- * Load a migration from a file
1992
- */ async loadMigration(fileName) {
1993
- const filePath = join(this.migrationsPath, fileName);
1994
- const fileUrl = pathToFileURL(filePath).href;
1995
- try {
1996
- const module = await import(fileUrl);
1997
- const migration = module.default ?? module;
1998
- if (this.isMigration(migration)) {
1999
- return {
2000
- name: this.getMigrationName(fileName),
2001
- up: migration.up.bind(migration),
2002
- down: migration.down.bind(migration)
2003
- };
2004
- }
2005
- this.logger(`Warning: ${fileName} is not a valid migration`);
2006
- return undefined;
2007
- } catch (error) {
2008
- this.logger(`Error loading migration ${fileName}: ${error.message}`);
2009
- return undefined;
896
+ ]
897
+ };
898
+ relations[relKey] = relOpts;
2010
899
  }
2011
- }
2012
- /**
2013
- * Check if an object is a valid migration
2014
- */ isMigration(obj) {
2015
- return typeof obj === 'object' && obj !== undefined && obj !== null && typeof obj.up === 'function' && typeof obj.down === 'function';
2016
- }
2017
- /**
2018
- * Extract migration name from filename
2019
- */ getMigrationName(fileName) {
2020
- return basename(fileName, extname(fileName));
2021
- }
2022
- /**
2023
- * Generate timestamp string for migration names
2024
- */ getTimestamp() {
2025
- const now = new Date();
2026
- return [
2027
- now.getFullYear(),
2028
- String(now.getMonth() + 1).padStart(2, '0'),
2029
- String(now.getDate()).padStart(2, '0'),
2030
- String(now.getHours()).padStart(2, '0'),
2031
- String(now.getMinutes()).padStart(2, '0'),
2032
- String(now.getSeconds()).padStart(2, '0')
2033
- ].join('');
2034
- }
2035
- /**
2036
- * Convert a string to a slug for filenames
2037
- */ slugify(text) {
2038
- return text.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
2039
- }
2040
- /**
2041
- * Generate migration file content
2042
- */ generateMigrationContent(name) {
2043
- return /*ts*/ `import type { SqlQuerier } from '@uql/migrate';
2044
-
2045
- /**
2046
- * Migration: ${name}
2047
- * Created: ${new Date().toISOString()}
2048
- */
2049
- export default {
2050
- async up(querier: SqlQuerier): Promise<void> {
2051
- // Add your migration logic here
2052
- // Example:
2053
- // await querier.run(\`
2054
- // CREATE TABLE "users" (
2055
- // "id" SERIAL PRIMARY KEY,
2056
- // "name" VARCHAR(255) NOT NULL,
2057
- // "email" VARCHAR(255) UNIQUE NOT NULL,
2058
- // "createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2059
- // )
2060
- // \`);
2061
- },
2062
-
2063
- async down(querier: SqlQuerier): Promise<void> {
2064
- // Add your rollback logic here
2065
- // Example:
2066
- // await querier.run(\`DROP TABLE IF EXISTS "users"\`);
2067
- },
2068
- };
2069
- `;
2070
- }
2071
- /**
2072
- * Generate migration file content with SQL statements
2073
- */ generateMigrationContentWithStatements(name, upStatements, downStatements) {
2074
- const upSql = upStatements.map((s)=>/*ts*/ ` await querier.run(\`${s}\`);`).join('\n');
2075
- const downSql = downStatements.map((s)=>/*ts*/ ` await querier.run(\`${s}\`);`).join('\n');
2076
- return /*ts*/ `import type { SqlQuerier } from '@uql/migrate';
2077
-
2078
- /**
2079
- * Migration: ${name}
2080
- * Created: ${new Date().toISOString()}
2081
- * Generated from entity definitions
2082
- */
2083
- export default {
2084
- async up(querier: SqlQuerier): Promise<void> {
2085
- ${upSql}
2086
- },
2087
-
2088
- async down(querier: SqlQuerier): Promise<void> {
2089
- ${downSql}
2090
- },
2091
- };
2092
- `;
2093
- }
900
+ return relations;
901
+ }, {});
2094
902
  }
2095
- /**
2096
- * Helper function to define a migration with proper typing
2097
- */ function defineMigration(migration) {
2098
- return migration;
903
+ function getMappedByRelationKey(relOpts) {
904
+ if (typeof relOpts.mappedBy === 'function') {
905
+ const relEntity = relOpts.entity();
906
+ const relMeta = ensureMeta(relEntity);
907
+ const keyMap = getRelationKeyMap(relMeta);
908
+ return relOpts.mappedBy(keyMap);
909
+ }
910
+ return relOpts.mappedBy;
911
+ }
912
+ function getRelationKeyMap(meta) {
913
+ return getKeys(meta.fields).concat(getKeys(meta.relations)).reduce((acc, key)=>{
914
+ acc[key] = key;
915
+ return acc;
916
+ }, {});
2099
917
  }
2100
918
 
2101
- /**
2102
- * Stores migration state in a JSON file (useful for development/testing)
2103
- */ class JsonMigrationStorage {
2104
- constructor(filePath = './migrations/.uql-migrations.json'){
2105
- this.cache = null;
2106
- this.filePath = filePath;
2107
- }
2108
- async ensureStorage() {
2109
- try {
2110
- await this.load();
2111
- } catch {
2112
- // File doesn't exist, create it
2113
- await this.save([]);
2114
- }
2115
- }
2116
- async executed() {
2117
- await this.ensureStorage();
2118
- return this.cache ?? [];
2119
- }
2120
- async logWithQuerier(_querier, migrationName) {
2121
- const executed = await this.executed();
2122
- if (!executed.includes(migrationName)) {
2123
- executed.push(migrationName);
2124
- executed.sort();
2125
- await this.save(executed);
919
+ async function createTables(querier, primaryKeyType) {
920
+ const entities = getEntities();
921
+ await Promise.all(entities.map((entity)=>{
922
+ const sql = getDdlForTable(entity, querier, primaryKeyType);
923
+ return querier.run(sql);
924
+ }));
925
+ }
926
+ async function dropTables(querier) {
927
+ const entities = getEntities();
928
+ await Promise.all(entities.map((entity)=>{
929
+ const meta = getMeta(entity);
930
+ const sql = `DROP TABLE IF EXISTS ${querier.dialect.escapeId(meta.name)}`;
931
+ return querier.run(sql);
932
+ }));
933
+ }
934
+ async function clearTables(querier) {
935
+ const entities = getEntities();
936
+ await Promise.all(entities.map((entity)=>{
937
+ const ctx = querier.dialect.createContext();
938
+ querier.dialect.delete(ctx, entity, {});
939
+ return querier.run(ctx.sql, ctx.values);
940
+ }));
941
+ }
942
+ function getDdlForTable(entity, querier, primaryKeyType) {
943
+ const meta = getMeta(entity);
944
+ let sql = `CREATE TABLE ${querier.dialect.escapeId(meta.name)} (\n\t`;
945
+ const insertableIdType = 'VARCHAR(36)';
946
+ const defaultType = 'VARCHAR(255)';
947
+ const columns = getKeys(meta.fields).map((key)=>{
948
+ const field = meta.fields[key];
949
+ let propSql = querier.dialect.escapeId(field.name) + ' ';
950
+ if (field.isId) {
951
+ propSql += field.onInsert ? `${insertableIdType} PRIMARY KEY` : primaryKeyType;
952
+ } else {
953
+ if (field.type === Number) {
954
+ propSql += 'BIGINT';
955
+ } else if (field.type === Date) {
956
+ propSql += 'TIMESTAMP';
957
+ } else {
958
+ propSql += defaultType;
959
+ }
2126
960
  }
2127
- }
2128
- async unlogWithQuerier(_querier, migrationName) {
2129
- const executed = await this.executed();
2130
- const index = executed.indexOf(migrationName);
2131
- if (index !== -1) {
2132
- executed.splice(index, 1);
2133
- await this.save(executed);
961
+ return propSql;
962
+ });
963
+ sql += columns.join(',\n\t');
964
+ sql += `\n);`;
965
+ // log('sql', sql);
966
+ return sql;
967
+ }
968
+
969
+ function createSpec(spec) {
970
+ const proto = Object.getPrototypeOf(spec);
971
+ let describeFn;
972
+ const specName = proto.constructor.name;
973
+ if (specName.startsWith('fff')) {
974
+ describeFn = describe.only;
975
+ } else if (specName.startsWith('xxx')) {
976
+ describeFn = describe.skip;
977
+ } else {
978
+ describeFn = describe;
979
+ }
980
+ describeFn(specName, ()=>createTestCases(spec));
981
+ }
982
+ function createTestCases(spec) {
983
+ let proto = Object.getPrototypeOf(spec);
984
+ const processedMethodsMap = {};
985
+ while(proto.constructor !== Object){
986
+ for (const key of Object.getOwnPropertyNames(proto)){
987
+ const isProcessed = processedMethodsMap[key];
988
+ processedMethodsMap[key] = true;
989
+ if (isProcessed || key === 'constructor' || typeof spec[key] !== 'function') {
990
+ continue;
991
+ }
992
+ const callback = spec[key].bind(spec);
993
+ if (hooks[key]) {
994
+ hooks[key](callback);
995
+ } else if (key.startsWith('should')) {
996
+ it(key, callback);
997
+ } else if (key.startsWith('fffShould')) {
998
+ it.only(key, callback);
999
+ } else if (key.startsWith('xxxShould')) {
1000
+ it.skip(key, callback);
1001
+ }
2134
1002
  }
2135
- }
2136
- async load() {
2137
- const content = await readFile(this.filePath, 'utf-8');
2138
- this.cache = JSON.parse(content);
2139
- }
2140
- async save(migrations) {
2141
- await mkdir(dirname(this.filePath), {
2142
- recursive: true
2143
- });
2144
- await writeFile(this.filePath, JSON.stringify(migrations, null, 2), 'utf-8');
2145
- this.cache = migrations;
1003
+ proto = Object.getPrototypeOf(proto);
2146
1004
  }
2147
1005
  }
1006
+ const hooks = {
1007
+ beforeAll,
1008
+ beforeEach,
1009
+ afterEach,
1010
+ afterAll
1011
+ };
2148
1012
 
2149
- export { AbstractSchemaGenerator, DatabaseMigrationStorage, JsonMigrationStorage, MysqlSchemaGenerator as MariadbSchemaGenerator, MariadbSchemaIntrospector, Migrator, MysqlSchemaGenerator, MysqlSchemaIntrospector, PostgresSchemaGenerator, PostgresSchemaIntrospector, SqliteSchemaGenerator, SqliteSchemaIntrospector, defineMigration };
1013
+ export { BaseEntity, _Company as Company, _InventoryAdjustment as InventoryAdjustment, _Item as Item, _ItemAdjustment as ItemAdjustment, _ItemTag as ItemTag, _LedgerAccount as LedgerAccount, _MeasureUnit as MeasureUnit, _MeasureUnitCategory as MeasureUnitCategory, _Profile as Profile, _Storehouse as Storehouse, _Tag as Tag, _Tax as Tax, _TaxCategory as TaxCategory, _User as User, _UserWithNonUpdatableId as UserWithNonUpdatableId, clearTables, createSpec, createTables, dropTables };
2150
1014
  //# sourceMappingURL=uql-browser.min.js.map