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