remult 0.25.8-exp.13 → 0.25.8-exp.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,18 @@
1
1
  import { repo } from '../index.js';
2
2
  import { dbNamesOf, shouldCreateEntity, shouldNotCreateField, } from '../src/filter/filter-consumer-bridge-to-sql-request.js';
3
- export async function compareMigrationSnapshot({ entities, snapshot, migrationBuilder: migrationBuilder, }) {
3
+ export async function compareMigrationSnapshot({ entities, snapshot, migrationBuilder, reporter, }) {
4
4
  snapshot = JSON.parse(JSON.stringify(snapshot));
5
+ let processedEntities = new Set();
5
6
  for (const entity of entities) {
6
7
  const meta = repo(entity).metadata;
7
8
  const e = await dbNamesOf(meta, (x) => x);
9
+ processedEntities.add(e.$entityName);
8
10
  if (shouldCreateEntity(meta, e)) {
9
11
  let entitySnapshot = snapshot.entities[e.$entityName];
10
12
  let createColumns = true;
11
13
  if (!entitySnapshot) {
12
14
  createColumns = false;
15
+ reporter('create table ' + e.$entityName);
13
16
  await migrationBuilder.createTable(meta);
14
17
  entitySnapshot = snapshot.entities[e.$entityName] = {
15
18
  key: meta.key,
@@ -17,16 +20,35 @@ export async function compareMigrationSnapshot({ entities, snapshot, migrationBu
17
20
  columns: {},
18
21
  };
19
22
  }
23
+ const processedColumns = new Set();
20
24
  for (const field of meta.fields) {
21
25
  if (!shouldNotCreateField(field, e)) {
22
- const column = entitySnapshot.columns[e.$dbNameOf(field)];
26
+ const columnDbName = e.$dbNameOf(field);
27
+ processedColumns.add(columnDbName);
28
+ const column = entitySnapshot.columns[columnDbName];
23
29
  if (!column) {
24
- if (createColumns)
30
+ if (createColumns) {
31
+ reporter('add column ' + e.$entityName + '.' + columnDbName);
25
32
  await migrationBuilder.addColumn(meta, field);
26
- entitySnapshot.columns[e.$dbNameOf(field)] = { key: field.key };
33
+ }
34
+ entitySnapshot.columns[columnDbName] = { key: field.key };
27
35
  }
28
36
  }
29
37
  }
38
+ for (const columnDbName of Object.keys(entitySnapshot.columns)) {
39
+ if (!processedColumns.has(columnDbName)) {
40
+ reporter('remove column ' + e.$entityName + '.' + columnDbName);
41
+ await migrationBuilder.removeColumn(e.$entityName, columnDbName);
42
+ delete entitySnapshot.columns[columnDbName];
43
+ }
44
+ }
45
+ }
46
+ }
47
+ for (const entityDbName of Object.keys(snapshot.entities)) {
48
+ if (!processedEntities.has(entityDbName)) {
49
+ reporter('remove table ' + entityDbName);
50
+ await migrationBuilder.removeTable(entityDbName);
51
+ delete snapshot.entities[entityDbName];
30
52
  }
31
53
  }
32
54
  return snapshot;
@@ -9,17 +9,16 @@ import { initDataProvider } from '../server/initDataProvider.js';
9
9
  * Generates migration scripts based on changes in entities.
10
10
  *
11
11
  * @param options - Configuration options for generating migrations.
12
- * @param options.entities - An array of entity classes to be included in the migration.
12
+ * @param options.entities - An array of entity classes whose changes will be included in the migration.
13
13
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
14
- * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/server/migrations'.
14
+ * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/migrations'.
15
15
  * @param options.snapshotFile - (Optional) The path to the file where the snapshot of the last known state will be stored. Default is 'migrations-snapshot.json' in the `migrationsFolder`.
16
16
  * @param options.migrationsTSFile - (Optional) The path to the TypeScript file where the generated migrations will be written. Default is 'migrations.ts' in the `migrationsFolder`.
17
- * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is true.
18
- * @see [Migrations](http://remult.dev/docs/migrations.html)
17
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is false.
18
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
19
19
  */
20
20
  export async function generateMigrations(options) {
21
- const migrationDir = options.migrationsFolder ||
22
- path.join(process.cwd(), 'src/server/migrations');
21
+ const migrationDir = options.migrationsFolder || path.join(process.cwd(), 'src/migrations');
23
22
  const snapshotFileName = options.snapshotFile || path.join(migrationDir, 'migrations-snapshot.json');
24
23
  const migrationsTSFilename = options.migrationsTSFile || path.join(migrationDir, 'migrations.ts');
25
24
  const dataProvider = await initDataProvider(options.dataProvider);
@@ -45,19 +44,25 @@ export async function generateMigrations(options) {
45
44
  };
46
45
  let migrationBuilder = new DefaultMigrationBuilder(code);
47
46
  if (isOfType(dataProvider, 'provideMigrationBuilder')) {
48
- migrationBuilder = dataProvider.provideMigrationBuilder(code);
47
+ migrationBuilder = new DefaultMigrationBuilder(code, dataProvider.provideMigrationBuilder(code));
49
48
  }
49
+ let reporter = (what) => console.log('[generateMigrations] ' + what);
50
50
  snapshot = await compareMigrationSnapshot({
51
51
  entities: options.entities,
52
52
  snapshot,
53
+ reporter,
53
54
  migrationBuilder,
54
55
  });
55
56
  if (steps.length)
56
57
  await updateMigrationsFile(migrationsTSFilename, [
57
58
  `async (${hasSql ? '{sql}' : ''})=>{\n${steps.join('\n')}\n}`,
58
59
  ]);
60
+ else {
61
+ reporter('No changes detected');
62
+ }
59
63
  fs.writeFileSync(snapshotFileName, JSON.stringify(snapshot, null, 2));
60
- if (options.endConnection !== false && isOfType(dataProvider, 'end')) {
64
+ if (options.endConnection && isOfType(dataProvider, 'end')) {
61
65
  await dataProvider.end();
62
66
  }
67
+ return steps.length > 0;
63
68
  }
@@ -10,9 +10,11 @@ import { cast, isOfType } from '../src/isOfType.js';
10
10
  * @param options.migrations - An object containing the migration scripts, each keyed by a unique identifier.
11
11
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
12
12
  * @param options.migrationsTable - (Optional) The name of the table that tracks applied migrations. Default is '__remult_migrations_version'.
13
- * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is true.
14
- * @see [Migrations](http://remult.dev/docs/migrations.html)
15
- */
13
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is false.
14
+ * @param options.beforeMigration - (Optional) A callback function that is called before each migration is applied. Receives an object with the migration index.
15
+ * @param options.afterMigration - (Optional) A callback function that is called after each migration is applied. Receives an object with the migration index and the duration of the migration.
16
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
17
+ */
16
18
  export async function migrate(options) {
17
19
  let migrationTableName = options.migrationsTable || '__remult_migrations_version';
18
20
  const dataProvider = await initDataProvider(options.dataProvider);
@@ -29,48 +31,60 @@ export async function migrate(options) {
29
31
  VersionInfo = __decorate([
30
32
  Entity(migrationTableName)
31
33
  ], VersionInfo);
32
- const repo = new Remult(dataProvider).repo(VersionInfo);
34
+ const steps = Object.entries(options.migrations).sort(([a], [b]) => parseInt(a) - parseInt(b));
33
35
  if (dataProvider.ensureSchema) {
34
- await dataProvider.ensureSchema([repo.metadata]);
35
- }
36
- let v = await repo.findFirst();
37
- if (!v) {
38
- v = repo.create();
39
- v.version = -1;
36
+ await dataProvider.ensureSchema([
37
+ new Remult(dataProvider).repo(VersionInfo).metadata,
38
+ ]);
40
39
  }
41
- const steps = Object.entries(options.migrations).sort(([a], [b]) => parseInt(a) - parseInt(b));
42
- for (const [stepText, action] of steps) {
43
- const step = parseInt(stepText);
44
- if (step < 0)
45
- throw new Error('Migration step number must be a non-negative integer');
46
- if (v.version >= step)
47
- continue;
48
- console.info('Executing migration step ' + step);
49
- console.time('Completed migration step ' + step);
50
- try {
51
- await doTransaction(remult, async (dp) => {
40
+ await doTransaction(remult, async (dp) => {
41
+ const repo = new Remult(dataProvider).repo(VersionInfo, dp);
42
+ let v = await repo.findFirst();
43
+ if (!v) {
44
+ v = repo.create();
45
+ v.version = -1;
46
+ }
47
+ for (const [stepText, action] of steps) {
48
+ const step = parseInt(stepText);
49
+ if (step < 0)
50
+ throw new Error('Migration step number must be a non-negative integer');
51
+ if (v.version >= step)
52
+ continue;
53
+ if (options.beforeMigration) {
54
+ await options.beforeMigration({ index: step });
55
+ }
56
+ const startTime = Date.now();
57
+ console.info('Executing migration step ' + step);
58
+ console.time('Completed migration step ' + step);
59
+ try {
52
60
  const utils = {
53
61
  sql: async (sql) => {
54
- await cast(dp, 'execute').execute(sql);
62
+ return await cast(dp, 'execute').execute(sql);
55
63
  },
56
64
  };
57
65
  await action(utils);
58
- });
59
- console.timeEnd('Completed migration step ' + step);
60
- }
61
- catch (err) {
62
- console.error('Failed to execute migration step ' + step);
63
- console.error(err);
64
- throw err;
66
+ if (options.afterMigration) {
67
+ await options.afterMigration({
68
+ index: step,
69
+ duration: Date.now() - startTime,
70
+ });
71
+ }
72
+ console.timeEnd('Completed migration step ' + step);
73
+ }
74
+ catch (err) {
75
+ console.error('Failed to execute migration step ' + step);
76
+ console.error(err);
77
+ throw err;
78
+ }
79
+ v.version = step;
80
+ await v.save();
65
81
  }
66
- v.version = step;
67
- await v.save();
68
- }
82
+ });
69
83
  }
70
84
  finally {
71
85
  remult.dataProvider = prev;
72
86
  }
73
- if (options.endConnection !== false && isOfType(dataProvider, 'end')) {
87
+ if (options.endConnection && isOfType(dataProvider, 'end')) {
74
88
  await dataProvider.end();
75
89
  }
76
90
  }
@@ -1,12 +1,36 @@
1
1
  export class DefaultMigrationBuilder {
2
2
  code;
3
- constructor(code) {
3
+ wrapped;
4
+ constructor(code, wrapped) {
4
5
  this.code = code;
6
+ this.wrapped = wrapped;
5
7
  }
6
8
  async createTable(meta) {
7
- this.code.addComment('create table ' + meta.entityType.name);
9
+ if (this.wrapped?.createTable) {
10
+ await this.wrapped.createTable(meta);
11
+ }
12
+ else
13
+ this.code.addComment('TODO: implement create table ' + meta.dbName);
8
14
  }
9
15
  async addColumn(meta, field) {
10
- this.code.addComment('add column ' + meta.entityType.name + '.' + field.key);
16
+ if (this.wrapped?.addColumn) {
17
+ await this.wrapped.addColumn(meta, field);
18
+ }
19
+ else
20
+ this.code.addComment('TODO: implement add column ' + meta.dbName + '.' + field.dbName);
21
+ }
22
+ async removeTable(entityDbName) {
23
+ if (this.wrapped?.removeTable) {
24
+ await this.wrapped.removeTable(entityDbName);
25
+ }
26
+ else
27
+ this.code.addComment('TODO: implement remove table ' + entityDbName);
28
+ }
29
+ async removeColumn(entityName, columnDbName) {
30
+ if (this.wrapped?.removeColumn) {
31
+ await this.wrapped.removeColumn(entityName, columnDbName);
32
+ }
33
+ else
34
+ this.code.addComment('TODO: implement remove column ' + entityName + '.' + columnDbName);
11
35
  }
12
36
  }
@@ -1,6 +1,9 @@
1
1
  // consider replacing with https://www.npmjs.com/package/ts-morph
2
2
  export async function updateMigrationsFile(filePath, steps) {
3
- const ts = await import('typescript');
3
+ let ts = await import('typescript');
4
+ //@ts-ignore
5
+ if (ts.default)
6
+ ts = ts.default;
4
7
  const fs = await import('fs');
5
8
  let maxStep = -1;
6
9
  function buildSteps() {
@@ -64,6 +64,27 @@ export class Filter {
64
64
  await Filter.fromEntityFilter(metadata, filter).__applyToConsumer(result);
65
65
  return result;
66
66
  }
67
+ /**
68
+ * Retrieves information about a filter, including precise values for each property.
69
+ * @template entityType The type of the entity being filtered.
70
+ * @param metadata The metadata of the entity being filtered.
71
+ * @param filter The filter to analyze.
72
+ * @returns A promise that resolves to a FilterInfo object containing the filter information.
73
+ * @example
74
+ * const info = await where.getInfo();
75
+ * console.log(info.preciseValues);
76
+ * // Output:
77
+ * // {
78
+ * // "customerId": ["1", "2", "3"], // Precise values inferred from the filter
79
+ * // "status": undefined, // Cannot infer precise values for 'status'
80
+ * // }
81
+
82
+ */
83
+ async getInfo() {
84
+ const result = new preciseValuesCollector();
85
+ await this.__applyToConsumer(result);
86
+ return result;
87
+ }
67
88
  static createCustom(rawFilterTranslator, key = '') {
68
89
  let rawFilterInfo = { key: key, rawFilterTranslator };
69
90
  return Object.assign((x) => {
@@ -248,7 +269,6 @@ export class Filter {
248
269
  return await filter();
249
270
  return filter;
250
271
  }
251
- //@internal
252
272
  toJson() {
253
273
  let r = new FilterSerializer();
254
274
  this.__applyToConsumer(r);
@@ -1,20 +1,24 @@
1
1
  export {};
2
- //p1 - It's good that there is no migrations added. (I also don't like automatic deletion), and it can be done manually if needed.
3
- //p1 - Maybe migrations-snapshots.json should have fields deleted to have a one to one representation of the db?
2
+ //p1 DOC DONE - It's good that there is no migrations added. (I also don't like automatic deletion), and it can be done manually if needed.
3
+ //p1 DOC DONE - Maybe migrations-snapshots.json should have fields deleted to have a one to one representation of the db?
4
+ //p1 DOC DONE - @JYT - should I write to console on generate migrations, what are the changes that I've found / the migration step id / not changes found
5
+ //p1 DOC DONE - re-discuss endConnection - default false - explain in docs
6
+ //p1 DONE - JYC log the migration request - beforeMigration. afterMigration
7
+ //p1 DONE - migration default folder should be source/migrations (also docs?)
8
+ //p1 DONE - @JYT run all migrations in a single transaction? or transaction per step?
9
+ //p1 Done - Filter.toJson should be public
4
10
  //p1 - I needed to modify the changes Field of changeLog entity to @Fields.json() to be nvarchar(max) too. Why is @Fields.object() only nvarchar(255) ? Or is it default size and can be adjusted?
5
- //p1 - Filter.toJson should be public
6
- //p1 - improve type augmentation doc with specific of the file and with the export trick
7
- //y1 - JY build issue
8
- //p1 - JY issue with the generate migration
9
- //y1 - migration default folder should be source/migrations (also docs?)
10
- //y1 - discuss the typing issue with relations in this case vs the ValueListFieldTypes
11
- //y1 - JYC log the migration request
12
- //y1 - JYC health check approach
11
+ //p1 - improve type augmentation doc with specific of the file and with the export trick - fix code samples to init reqiest error
12
+ //p1 - document custom filter without sql
13
+ //y1!! - JY build issue
14
+ //p1 - discuss the typing issue with relations in this case vs the ValueListFieldTypes - return partial with id values.
13
15
  //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
14
16
  //p1 - document new sqlite providers
15
17
  //p1 - article auth.js with express
16
18
  //p1 - document offline support
17
19
  //p1 - fix getting started article with using your own database connection
20
+ //p1 - create foreign key constraints in user code
21
+ //p1 - https://github.com/vuejs/vitepress/issues/3154#issuecomment-1784612839
18
22
  //y2 - should we validate relations
19
23
  //y1 - dependency of live query tables
20
24
  //y1 - consider replacing all errors with error classes that extend the base Error class
@@ -1,8 +1,9 @@
1
1
  import type { MigrationBuilder } from './migration-types.js';
2
- export declare function compareMigrationSnapshot({ entities, snapshot, migrationBuilder: migrationBuilder, }: {
2
+ export declare function compareMigrationSnapshot({ entities, snapshot, migrationBuilder, reporter, }: {
3
3
  entities: any[];
4
4
  snapshot: EntitiesSnapshot;
5
- migrationBuilder: MigrationBuilder;
5
+ migrationBuilder: Required<MigrationBuilder>;
6
+ reporter: (what: string) => void;
6
7
  }): Promise<{
7
8
  version: number;
8
9
  entities: Record<string, {
@@ -5,87 +5,152 @@ var tslib_1 = require("tslib");
5
5
  var index_js_1 = require("../index.js");
6
6
  var filter_consumer_bridge_to_sql_request_js_1 = require("../src/filter/filter-consumer-bridge-to-sql-request.js");
7
7
  function compareMigrationSnapshot(_a) {
8
- var entities = _a.entities, snapshot = _a.snapshot, migrationBuilder = _a.migrationBuilder;
8
+ var entities = _a.entities, snapshot = _a.snapshot, migrationBuilder = _a.migrationBuilder, reporter = _a.reporter;
9
9
  return tslib_1.__awaiter(this, void 0, void 0, function () {
10
- var entities_1, entities_1_1, entity, meta, e, entitySnapshot, createColumns, _b, _c, field, column, e_1_1, e_2_1;
11
- var e_2, _d, e_1, _e;
12
- return tslib_1.__generator(this, function (_f) {
13
- switch (_f.label) {
10
+ var processedEntities, entities_1, entities_1_1, entity, meta, e, entitySnapshot, createColumns, processedColumns, _b, _c, field, columnDbName, column, e_1_1, _d, _e, columnDbName, e_2_1, e_3_1, _f, _g, entityDbName, e_4_1;
11
+ var e_3, _h, e_1, _j, e_2, _k, e_4, _l;
12
+ return tslib_1.__generator(this, function (_m) {
13
+ switch (_m.label) {
14
14
  case 0:
15
15
  snapshot = JSON.parse(JSON.stringify(snapshot));
16
- _f.label = 1;
16
+ processedEntities = new Set();
17
+ _m.label = 1;
17
18
  case 1:
18
- _f.trys.push([1, 15, 16, 17]);
19
+ _m.trys.push([1, 23, 24, 25]);
19
20
  entities_1 = tslib_1.__values(entities), entities_1_1 = entities_1.next();
20
- _f.label = 2;
21
+ _m.label = 2;
21
22
  case 2:
22
- if (!!entities_1_1.done) return [3 /*break*/, 14];
23
+ if (!!entities_1_1.done) return [3 /*break*/, 22];
23
24
  entity = entities_1_1.value;
24
25
  meta = (0, index_js_1.repo)(entity).metadata;
25
26
  return [4 /*yield*/, (0, filter_consumer_bridge_to_sql_request_js_1.dbNamesOf)(meta, function (x) { return x; })];
26
27
  case 3:
27
- e = _f.sent();
28
- if (!(0, filter_consumer_bridge_to_sql_request_js_1.shouldCreateEntity)(meta, e)) return [3 /*break*/, 13];
28
+ e = _m.sent();
29
+ processedEntities.add(e.$entityName);
30
+ if (!(0, filter_consumer_bridge_to_sql_request_js_1.shouldCreateEntity)(meta, e)) return [3 /*break*/, 21];
29
31
  entitySnapshot = snapshot.entities[e.$entityName];
30
32
  createColumns = true;
31
33
  if (!!entitySnapshot) return [3 /*break*/, 5];
32
34
  createColumns = false;
35
+ reporter('create table ' + e.$entityName);
33
36
  return [4 /*yield*/, migrationBuilder.createTable(meta)];
34
37
  case 4:
35
- _f.sent();
38
+ _m.sent();
36
39
  entitySnapshot = snapshot.entities[e.$entityName] = {
37
40
  key: meta.key,
38
41
  className: meta.entityType.name,
39
42
  columns: {},
40
43
  };
41
- _f.label = 5;
44
+ _m.label = 5;
42
45
  case 5:
43
- _f.trys.push([5, 11, 12, 13]);
44
- _b = (e_1 = void 0, tslib_1.__values(meta.fields)), _c = _b.next();
45
- _f.label = 6;
46
+ processedColumns = new Set();
47
+ _m.label = 6;
46
48
  case 6:
47
- if (!!_c.done) return [3 /*break*/, 10];
49
+ _m.trys.push([6, 12, 13, 14]);
50
+ _b = (e_1 = void 0, tslib_1.__values(meta.fields)), _c = _b.next();
51
+ _m.label = 7;
52
+ case 7:
53
+ if (!!_c.done) return [3 /*break*/, 11];
48
54
  field = _c.value;
49
- if (!!(0, filter_consumer_bridge_to_sql_request_js_1.shouldNotCreateField)(field, e)) return [3 /*break*/, 9];
50
- column = entitySnapshot.columns[e.$dbNameOf(field)];
51
- if (!!column) return [3 /*break*/, 9];
52
- if (!createColumns) return [3 /*break*/, 8];
55
+ if (!!(0, filter_consumer_bridge_to_sql_request_js_1.shouldNotCreateField)(field, e)) return [3 /*break*/, 10];
56
+ columnDbName = e.$dbNameOf(field);
57
+ processedColumns.add(columnDbName);
58
+ column = entitySnapshot.columns[columnDbName];
59
+ if (!!column) return [3 /*break*/, 10];
60
+ if (!createColumns) return [3 /*break*/, 9];
61
+ reporter('add column ' + e.$entityName + '.' + columnDbName);
53
62
  return [4 /*yield*/, migrationBuilder.addColumn(meta, field)];
54
- case 7:
55
- _f.sent();
56
- _f.label = 8;
57
63
  case 8:
58
- entitySnapshot.columns[e.$dbNameOf(field)] = { key: field.key };
59
- _f.label = 9;
64
+ _m.sent();
65
+ _m.label = 9;
60
66
  case 9:
67
+ entitySnapshot.columns[columnDbName] = { key: field.key };
68
+ _m.label = 10;
69
+ case 10:
61
70
  _c = _b.next();
62
- return [3 /*break*/, 6];
63
- case 10: return [3 /*break*/, 13];
64
- case 11:
65
- e_1_1 = _f.sent();
66
- e_1 = { error: e_1_1 };
67
- return [3 /*break*/, 13];
71
+ return [3 /*break*/, 7];
72
+ case 11: return [3 /*break*/, 14];
68
73
  case 12:
74
+ e_1_1 = _m.sent();
75
+ e_1 = { error: e_1_1 };
76
+ return [3 /*break*/, 14];
77
+ case 13:
69
78
  try {
70
- if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
79
+ if (_c && !_c.done && (_j = _b.return)) _j.call(_b);
71
80
  }
72
81
  finally { if (e_1) throw e_1.error; }
73
82
  return [7 /*endfinally*/];
74
- case 13:
75
- entities_1_1 = entities_1.next();
76
- return [3 /*break*/, 2];
77
- case 14: return [3 /*break*/, 17];
83
+ case 14:
84
+ _m.trys.push([14, 19, 20, 21]);
85
+ _d = (e_2 = void 0, tslib_1.__values(Object.keys(entitySnapshot.columns))), _e = _d.next();
86
+ _m.label = 15;
78
87
  case 15:
79
- e_2_1 = _f.sent();
80
- e_2 = { error: e_2_1 };
81
- return [3 /*break*/, 17];
88
+ if (!!_e.done) return [3 /*break*/, 18];
89
+ columnDbName = _e.value;
90
+ if (!!processedColumns.has(columnDbName)) return [3 /*break*/, 17];
91
+ reporter('remove column ' + e.$entityName + '.' + columnDbName);
92
+ return [4 /*yield*/, migrationBuilder.removeColumn(e.$entityName, columnDbName)];
82
93
  case 16:
94
+ _m.sent();
95
+ delete entitySnapshot.columns[columnDbName];
96
+ _m.label = 17;
97
+ case 17:
98
+ _e = _d.next();
99
+ return [3 /*break*/, 15];
100
+ case 18: return [3 /*break*/, 21];
101
+ case 19:
102
+ e_2_1 = _m.sent();
103
+ e_2 = { error: e_2_1 };
104
+ return [3 /*break*/, 21];
105
+ case 20:
83
106
  try {
84
- if (entities_1_1 && !entities_1_1.done && (_d = entities_1.return)) _d.call(entities_1);
107
+ if (_e && !_e.done && (_k = _d.return)) _k.call(_d);
85
108
  }
86
109
  finally { if (e_2) throw e_2.error; }
87
110
  return [7 /*endfinally*/];
88
- case 17: return [2 /*return*/, snapshot];
111
+ case 21:
112
+ entities_1_1 = entities_1.next();
113
+ return [3 /*break*/, 2];
114
+ case 22: return [3 /*break*/, 25];
115
+ case 23:
116
+ e_3_1 = _m.sent();
117
+ e_3 = { error: e_3_1 };
118
+ return [3 /*break*/, 25];
119
+ case 24:
120
+ try {
121
+ if (entities_1_1 && !entities_1_1.done && (_h = entities_1.return)) _h.call(entities_1);
122
+ }
123
+ finally { if (e_3) throw e_3.error; }
124
+ return [7 /*endfinally*/];
125
+ case 25:
126
+ _m.trys.push([25, 30, 31, 32]);
127
+ _f = tslib_1.__values(Object.keys(snapshot.entities)), _g = _f.next();
128
+ _m.label = 26;
129
+ case 26:
130
+ if (!!_g.done) return [3 /*break*/, 29];
131
+ entityDbName = _g.value;
132
+ if (!!processedEntities.has(entityDbName)) return [3 /*break*/, 28];
133
+ reporter('remove table ' + entityDbName);
134
+ return [4 /*yield*/, migrationBuilder.removeTable(entityDbName)];
135
+ case 27:
136
+ _m.sent();
137
+ delete snapshot.entities[entityDbName];
138
+ _m.label = 28;
139
+ case 28:
140
+ _g = _f.next();
141
+ return [3 /*break*/, 26];
142
+ case 29: return [3 /*break*/, 32];
143
+ case 30:
144
+ e_4_1 = _m.sent();
145
+ e_4 = { error: e_4_1 };
146
+ return [3 /*break*/, 32];
147
+ case 31:
148
+ try {
149
+ if (_g && !_g.done && (_l = _f.return)) _l.call(_f);
150
+ }
151
+ finally { if (e_4) throw e_4.error; }
152
+ return [7 /*endfinally*/];
153
+ case 32: return [2 /*return*/, snapshot];
89
154
  }
90
155
  });
91
156
  });
@@ -3,13 +3,13 @@ import type { DataProvider } from '../index.js';
3
3
  * Generates migration scripts based on changes in entities.
4
4
  *
5
5
  * @param options - Configuration options for generating migrations.
6
- * @param options.entities - An array of entity classes to be included in the migration.
6
+ * @param options.entities - An array of entity classes whose changes will be included in the migration.
7
7
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
8
- * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/server/migrations'.
8
+ * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/migrations'.
9
9
  * @param options.snapshotFile - (Optional) The path to the file where the snapshot of the last known state will be stored. Default is 'migrations-snapshot.json' in the `migrationsFolder`.
10
10
  * @param options.migrationsTSFile - (Optional) The path to the TypeScript file where the generated migrations will be written. Default is 'migrations.ts' in the `migrationsFolder`.
11
- * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is true.
12
- * @see [Migrations](http://remult.dev/docs/migrations.html)
11
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is false.
12
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
13
13
  */
14
14
  export declare function generateMigrations(options: {
15
15
  entities: any[];
@@ -18,4 +18,4 @@ export declare function generateMigrations(options: {
18
18
  snapshotFile?: string;
19
19
  migrationsTSFile?: string;
20
20
  endConnection?: boolean;
21
- }): Promise<void>;
21
+ }): Promise<boolean>;
@@ -13,23 +13,22 @@ var initDataProvider_js_1 = require("../server/initDataProvider.js");
13
13
  * Generates migration scripts based on changes in entities.
14
14
  *
15
15
  * @param options - Configuration options for generating migrations.
16
- * @param options.entities - An array of entity classes to be included in the migration.
16
+ * @param options.entities - An array of entity classes whose changes will be included in the migration.
17
17
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
18
- * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/server/migrations'.
18
+ * @param options.migrationsFolder - (Optional) The path to the folder where migration scripts will be stored. Default is 'src/migrations'.
19
19
  * @param options.snapshotFile - (Optional) The path to the file where the snapshot of the last known state will be stored. Default is 'migrations-snapshot.json' in the `migrationsFolder`.
20
20
  * @param options.migrationsTSFile - (Optional) The path to the TypeScript file where the generated migrations will be written. Default is 'migrations.ts' in the `migrationsFolder`.
21
- * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is true.
22
- * @see [Migrations](http://remult.dev/docs/migrations.html)
21
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after generating migrations. Default is false.
22
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
23
23
  */
24
24
  function generateMigrations(options) {
25
25
  return tslib_1.__awaiter(this, void 0, void 0, function () {
26
- var migrationDir, snapshotFileName, migrationsTSFilename, dataProvider, _a, _b, p, snapshot, hasSql, steps, code, migrationBuilder;
26
+ var migrationDir, snapshotFileName, migrationsTSFilename, dataProvider, _a, _b, p, snapshot, hasSql, steps, code, migrationBuilder, reporter;
27
27
  var e_1, _c;
28
28
  return tslib_1.__generator(this, function (_d) {
29
29
  switch (_d.label) {
30
30
  case 0:
31
- migrationDir = options.migrationsFolder ||
32
- path_1.default.join(process.cwd(), 'src/server/migrations');
31
+ migrationDir = options.migrationsFolder || path_1.default.join(process.cwd(), 'src/migrations');
33
32
  snapshotFileName = options.snapshotFile || path_1.default.join(migrationDir, 'migrations-snapshot.json');
34
33
  migrationsTSFilename = options.migrationsTSFile || path_1.default.join(migrationDir, 'migrations.ts');
35
34
  return [4 /*yield*/, (0, initDataProvider_js_1.initDataProvider)(options.dataProvider)];
@@ -69,11 +68,13 @@ function generateMigrations(options) {
69
68
  };
70
69
  migrationBuilder = new migration_types_js_1.DefaultMigrationBuilder(code);
71
70
  if ((0, isOfType_js_1.isOfType)(dataProvider, 'provideMigrationBuilder')) {
72
- migrationBuilder = dataProvider.provideMigrationBuilder(code);
71
+ migrationBuilder = new migration_types_js_1.DefaultMigrationBuilder(code, dataProvider.provideMigrationBuilder(code));
73
72
  }
73
+ reporter = function (what) { return console.log('[generateMigrations] ' + what); };
74
74
  return [4 /*yield*/, (0, compare_migration_snapshots_js_1.compareMigrationSnapshot)({
75
75
  entities: options.entities,
76
76
  snapshot: snapshot,
77
+ reporter: reporter,
77
78
  migrationBuilder: migrationBuilder,
78
79
  })];
79
80
  case 2:
@@ -84,15 +85,18 @@ function generateMigrations(options) {
84
85
  ])];
85
86
  case 3:
86
87
  _d.sent();
87
- _d.label = 4;
88
+ return [3 /*break*/, 5];
88
89
  case 4:
90
+ reporter('No changes detected');
91
+ _d.label = 5;
92
+ case 5:
89
93
  fs_1.default.writeFileSync(snapshotFileName, JSON.stringify(snapshot, null, 2));
90
- if (!(options.endConnection !== false && (0, isOfType_js_1.isOfType)(dataProvider, 'end'))) return [3 /*break*/, 6];
94
+ if (!(options.endConnection && (0, isOfType_js_1.isOfType)(dataProvider, 'end'))) return [3 /*break*/, 7];
91
95
  return [4 /*yield*/, dataProvider.end()];
92
- case 5:
96
+ case 6:
93
97
  _d.sent();
94
- _d.label = 6;
95
- case 6: return [2 /*return*/];
98
+ _d.label = 7;
99
+ case 7: return [2 /*return*/, steps.length > 0];
96
100
  }
97
101
  });
98
102
  });
@@ -7,12 +7,21 @@ import type { Migrations } from './migration-types.js';
7
7
  * @param options.migrations - An object containing the migration scripts, each keyed by a unique identifier.
8
8
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
9
9
  * @param options.migrationsTable - (Optional) The name of the table that tracks applied migrations. Default is '__remult_migrations_version'.
10
- * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is true.
11
- * @see [Migrations](http://remult.dev/docs/migrations.html)
12
- */
10
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is false.
11
+ * @param options.beforeMigration - (Optional) A callback function that is called before each migration is applied. Receives an object with the migration index.
12
+ * @param options.afterMigration - (Optional) A callback function that is called after each migration is applied. Receives an object with the migration index and the duration of the migration.
13
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
14
+ */
13
15
  export declare function migrate(options: {
14
16
  migrations: Migrations;
15
17
  dataProvider: DataProvider | Promise<DataProvider> | (() => Promise<DataProvider | undefined>);
16
18
  migrationsTable?: string;
17
19
  endConnection?: boolean;
20
+ beforeMigration?: (info: {
21
+ index: number;
22
+ }) => void | Promise<void>;
23
+ afterMigration?: (info: {
24
+ index: number;
25
+ duration: number;
26
+ }) => void | Promise<void>;
18
27
  }): Promise<void>;
@@ -13,27 +13,28 @@ var isOfType_js_1 = require("../src/isOfType.js");
13
13
  * @param options.migrations - An object containing the migration scripts, each keyed by a unique identifier.
14
14
  * @param options.dataProvider - The data provider instance or a function returning a promise of the data provider.
15
15
  * @param options.migrationsTable - (Optional) The name of the table that tracks applied migrations. Default is '__remult_migrations_version'.
16
- * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is true.
17
- * @see [Migrations](http://remult.dev/docs/migrations.html)
18
- */
16
+ * @param options.endConnection - (Optional) Determines whether to close the database connection after applying migrations. Default is false.
17
+ * @param options.beforeMigration - (Optional) A callback function that is called before each migration is applied. Receives an object with the migration index.
18
+ * @param options.afterMigration - (Optional) A callback function that is called after each migration is applied. Receives an object with the migration index and the duration of the migration.
19
+ * @see [Migrations](https://remult.dev/docs/migrations.html)
20
+ */
19
21
  function migrate(options) {
20
22
  return tslib_1.__awaiter(this, void 0, void 0, function () {
21
- var migrationTableName, dataProvider, prev, VersionInfo, repo, v, steps, _loop_1, steps_1, steps_1_1, _a, stepText, action, e_1_1;
22
- var e_1, _b;
23
+ var migrationTableName, dataProvider, prev, VersionInfo_1, steps_1;
23
24
  var _this = this;
24
- return tslib_1.__generator(this, function (_c) {
25
- switch (_c.label) {
25
+ return tslib_1.__generator(this, function (_a) {
26
+ switch (_a.label) {
26
27
  case 0:
27
28
  migrationTableName = options.migrationsTable || '__remult_migrations_version';
28
29
  return [4 /*yield*/, (0, initDataProvider_js_1.initDataProvider)(options.dataProvider)];
29
30
  case 1:
30
- dataProvider = _c.sent();
31
+ dataProvider = _a.sent();
31
32
  prev = index_js_1.remult.dataProvider;
32
33
  index_js_1.remult.dataProvider = dataProvider;
33
- _c.label = 2;
34
+ _a.label = 2;
34
35
  case 2:
35
- _c.trys.push([2, , 14, 15]);
36
- VersionInfo = /** @class */ (function (_super) {
36
+ _a.trys.push([2, , 6, 7]);
37
+ VersionInfo_1 = /** @class */ (function (_super) {
37
38
  tslib_1.__extends(VersionInfo, _super);
38
39
  function VersionInfo() {
39
40
  var _this = _super !== null && _super.apply(this, arguments) || this;
@@ -43,125 +44,130 @@ function migrate(options) {
43
44
  tslib_1.__decorate([
44
45
  index_js_1.Fields.number(),
45
46
  tslib_1.__metadata("design:type", Object)
46
- ], VersionInfo.prototype, "version", void 0);
47
+ ], VersionInfo_1.prototype, "version", void 0);
47
48
  VersionInfo = tslib_1.__decorate([
48
49
  (0, index_js_1.Entity)(migrationTableName)
49
50
  ], VersionInfo);
50
51
  return VersionInfo;
51
52
  }(index_js_1.IdEntity));
52
- repo = new index_js_1.Remult(dataProvider).repo(VersionInfo);
53
- if (!dataProvider.ensureSchema) return [3 /*break*/, 4];
54
- return [4 /*yield*/, dataProvider.ensureSchema([repo.metadata])];
55
- case 3:
56
- _c.sent();
57
- _c.label = 4;
58
- case 4: return [4 /*yield*/, repo.findFirst()];
59
- case 5:
60
- v = _c.sent();
61
- if (!v) {
62
- v = repo.create();
63
- v.version = -1;
64
- }
65
- steps = Object.entries(options.migrations).sort(function (_a, _b) {
53
+ steps_1 = Object.entries(options.migrations).sort(function (_a, _b) {
66
54
  var _c = tslib_1.__read(_a, 1), a = _c[0];
67
55
  var _d = tslib_1.__read(_b, 1), b = _d[0];
68
56
  return parseInt(a) - parseInt(b);
69
57
  });
70
- _loop_1 = function (stepText, action) {
71
- var step, err_1;
72
- return tslib_1.__generator(this, function (_d) {
73
- switch (_d.label) {
58
+ if (!dataProvider.ensureSchema) return [3 /*break*/, 4];
59
+ return [4 /*yield*/, dataProvider.ensureSchema([
60
+ new index_js_1.Remult(dataProvider).repo(VersionInfo_1).metadata,
61
+ ])];
62
+ case 3:
63
+ _a.sent();
64
+ _a.label = 4;
65
+ case 4: return [4 /*yield*/, (0, context_js_1.doTransaction)(index_js_1.remult, function (dp) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
66
+ var repo, v, steps_2, steps_2_1, _a, stepText, action, step, startTime, utils, err_1, e_1_1;
67
+ var e_1, _b;
68
+ var _this = this;
69
+ return tslib_1.__generator(this, function (_c) {
70
+ switch (_c.label) {
74
71
  case 0:
72
+ repo = new index_js_1.Remult(dataProvider).repo(VersionInfo_1, dp);
73
+ return [4 /*yield*/, repo.findFirst()];
74
+ case 1:
75
+ v = _c.sent();
76
+ if (!v) {
77
+ v = repo.create();
78
+ v.version = -1;
79
+ }
80
+ _c.label = 2;
81
+ case 2:
82
+ _c.trys.push([2, 15, 16, 17]);
83
+ steps_2 = tslib_1.__values(steps_1), steps_2_1 = steps_2.next();
84
+ _c.label = 3;
85
+ case 3:
86
+ if (!!steps_2_1.done) return [3 /*break*/, 14];
87
+ _a = tslib_1.__read(steps_2_1.value, 2), stepText = _a[0], action = _a[1];
75
88
  step = parseInt(stepText);
76
89
  if (step < 0)
77
90
  throw new Error('Migration step number must be a non-negative integer');
78
91
  if (v.version >= step)
79
- return [2 /*return*/, "continue"];
92
+ return [3 /*break*/, 13];
93
+ if (!options.beforeMigration) return [3 /*break*/, 5];
94
+ return [4 /*yield*/, options.beforeMigration({ index: step })];
95
+ case 4:
96
+ _c.sent();
97
+ _c.label = 5;
98
+ case 5:
99
+ startTime = Date.now();
80
100
  console.info('Executing migration step ' + step);
81
101
  console.time('Completed migration step ' + step);
82
- _d.label = 1;
83
- case 1:
84
- _d.trys.push([1, 3, , 4]);
85
- return [4 /*yield*/, (0, context_js_1.doTransaction)(index_js_1.remult, function (dp) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
86
- var utils;
87
- var _this = this;
102
+ _c.label = 6;
103
+ case 6:
104
+ _c.trys.push([6, 10, , 11]);
105
+ utils = {
106
+ sql: function (sql) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
88
107
  return tslib_1.__generator(this, function (_a) {
89
108
  switch (_a.label) {
90
- case 0:
91
- utils = {
92
- sql: function (sql) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
93
- return tslib_1.__generator(this, function (_a) {
94
- switch (_a.label) {
95
- case 0: return [4 /*yield*/, (0, isOfType_js_1.cast)(dp, 'execute').execute(sql)];
96
- case 1:
97
- _a.sent();
98
- return [2 /*return*/];
99
- }
100
- });
101
- }); },
102
- };
103
- return [4 /*yield*/, action(utils)];
104
- case 1:
105
- _a.sent();
106
- return [2 /*return*/];
109
+ case 0: return [4 /*yield*/, (0, isOfType_js_1.cast)(dp, 'execute').execute(sql)];
110
+ case 1: return [2 /*return*/, _a.sent()];
107
111
  }
108
112
  });
109
- }); })];
110
- case 2:
111
- _d.sent();
113
+ }); },
114
+ };
115
+ return [4 /*yield*/, action(utils)];
116
+ case 7:
117
+ _c.sent();
118
+ if (!options.afterMigration) return [3 /*break*/, 9];
119
+ return [4 /*yield*/, options.afterMigration({
120
+ index: step,
121
+ duration: Date.now() - startTime,
122
+ })];
123
+ case 8:
124
+ _c.sent();
125
+ _c.label = 9;
126
+ case 9:
112
127
  console.timeEnd('Completed migration step ' + step);
113
- return [3 /*break*/, 4];
114
- case 3:
115
- err_1 = _d.sent();
128
+ return [3 /*break*/, 11];
129
+ case 10:
130
+ err_1 = _c.sent();
116
131
  console.error('Failed to execute migration step ' + step);
117
132
  console.error(err_1);
118
133
  throw err_1;
119
- case 4:
134
+ case 11:
120
135
  v.version = step;
121
136
  return [4 /*yield*/, v.save()];
122
- case 5:
123
- _d.sent();
124
- return [2 /*return*/];
137
+ case 12:
138
+ _c.sent();
139
+ _c.label = 13;
140
+ case 13:
141
+ steps_2_1 = steps_2.next();
142
+ return [3 /*break*/, 3];
143
+ case 14: return [3 /*break*/, 17];
144
+ case 15:
145
+ e_1_1 = _c.sent();
146
+ e_1 = { error: e_1_1 };
147
+ return [3 /*break*/, 17];
148
+ case 16:
149
+ try {
150
+ if (steps_2_1 && !steps_2_1.done && (_b = steps_2.return)) _b.call(steps_2);
151
+ }
152
+ finally { if (e_1) throw e_1.error; }
153
+ return [7 /*endfinally*/];
154
+ case 17: return [2 /*return*/];
125
155
  }
126
156
  });
127
- };
128
- _c.label = 6;
129
- case 6:
130
- _c.trys.push([6, 11, 12, 13]);
131
- steps_1 = tslib_1.__values(steps), steps_1_1 = steps_1.next();
132
- _c.label = 7;
133
- case 7:
134
- if (!!steps_1_1.done) return [3 /*break*/, 10];
135
- _a = tslib_1.__read(steps_1_1.value, 2), stepText = _a[0], action = _a[1];
136
- return [5 /*yield**/, _loop_1(stepText, action)];
137
- case 8:
138
- _c.sent();
139
- _c.label = 9;
140
- case 9:
141
- steps_1_1 = steps_1.next();
157
+ }); })];
158
+ case 5:
159
+ _a.sent();
142
160
  return [3 /*break*/, 7];
143
- case 10: return [3 /*break*/, 13];
144
- case 11:
145
- e_1_1 = _c.sent();
146
- e_1 = { error: e_1_1 };
147
- return [3 /*break*/, 13];
148
- case 12:
149
- try {
150
- if (steps_1_1 && !steps_1_1.done && (_b = steps_1.return)) _b.call(steps_1);
151
- }
152
- finally { if (e_1) throw e_1.error; }
153
- return [7 /*endfinally*/];
154
- case 13: return [3 /*break*/, 15];
155
- case 14:
161
+ case 6:
156
162
  index_js_1.remult.dataProvider = prev;
157
163
  return [7 /*endfinally*/];
158
- case 15:
159
- if (!(options.endConnection !== false && (0, isOfType_js_1.isOfType)(dataProvider, 'end'))) return [3 /*break*/, 17];
164
+ case 7:
165
+ if (!(options.endConnection && (0, isOfType_js_1.isOfType)(dataProvider, 'end'))) return [3 /*break*/, 9];
160
166
  return [4 /*yield*/, dataProvider.end()];
161
- case 16:
162
- _c.sent();
163
- _c.label = 17;
164
- case 17: return [2 /*return*/];
167
+ case 8:
168
+ _a.sent();
169
+ _a.label = 9;
170
+ case 9: return [2 /*return*/];
165
171
  }
166
172
  });
167
173
  });
@@ -5,6 +5,8 @@ export interface CanBuildMigrations {
5
5
  export interface MigrationBuilder {
6
6
  createTable(meta: EntityMetadata): Promise<void>;
7
7
  addColumn(meta: EntityMetadata, field: FieldMetadata): Promise<void>;
8
+ removeTable?(entityDbName: string): unknown;
9
+ removeColumn?($entityName: string, columnDbName: string): unknown;
8
10
  }
9
11
  export interface MigrationCode {
10
12
  addSql(sql: string): void;
@@ -35,9 +37,12 @@ export type Migrations = Record<number, (utils: MigrationUtils) => Promise<unkno
35
37
  export type MigrationUtils = {
36
38
  sql(sql: string): Promise<unknown>;
37
39
  };
38
- export declare class DefaultMigrationBuilder implements MigrationBuilder {
40
+ export declare class DefaultMigrationBuilder implements Required<MigrationBuilder> {
39
41
  code: MigrationCode;
40
- constructor(code: MigrationCode);
42
+ private wrapped?;
43
+ constructor(code: MigrationCode, wrapped?: MigrationBuilder);
41
44
  createTable(meta: EntityMetadata): Promise<void>;
42
45
  addColumn(meta: EntityMetadata, field: FieldMetadata): Promise<void>;
46
+ removeTable(entityDbName: string): Promise<void>;
47
+ removeColumn(entityName: string, columnDbName: string): Promise<void>;
43
48
  }
@@ -3,22 +3,83 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultMigrationBuilder = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var DefaultMigrationBuilder = /** @class */ (function () {
6
- function DefaultMigrationBuilder(code) {
6
+ function DefaultMigrationBuilder(code, wrapped) {
7
7
  this.code = code;
8
+ this.wrapped = wrapped;
8
9
  }
9
10
  DefaultMigrationBuilder.prototype.createTable = function (meta) {
11
+ var _a;
10
12
  return tslib_1.__awaiter(this, void 0, void 0, function () {
11
- return tslib_1.__generator(this, function (_a) {
12
- this.code.addComment('create table ' + meta.entityType.name);
13
- return [2 /*return*/];
13
+ return tslib_1.__generator(this, function (_b) {
14
+ switch (_b.label) {
15
+ case 0:
16
+ if (!((_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.createTable)) return [3 /*break*/, 2];
17
+ return [4 /*yield*/, this.wrapped.createTable(meta)];
18
+ case 1:
19
+ _b.sent();
20
+ return [3 /*break*/, 3];
21
+ case 2:
22
+ this.code.addComment('TODO: implement create table ' + meta.dbName);
23
+ _b.label = 3;
24
+ case 3: return [2 /*return*/];
25
+ }
14
26
  });
15
27
  });
16
28
  };
17
29
  DefaultMigrationBuilder.prototype.addColumn = function (meta, field) {
30
+ var _a;
18
31
  return tslib_1.__awaiter(this, void 0, void 0, function () {
19
- return tslib_1.__generator(this, function (_a) {
20
- this.code.addComment('add column ' + meta.entityType.name + '.' + field.key);
21
- return [2 /*return*/];
32
+ return tslib_1.__generator(this, function (_b) {
33
+ switch (_b.label) {
34
+ case 0:
35
+ if (!((_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.addColumn)) return [3 /*break*/, 2];
36
+ return [4 /*yield*/, this.wrapped.addColumn(meta, field)];
37
+ case 1:
38
+ _b.sent();
39
+ return [3 /*break*/, 3];
40
+ case 2:
41
+ this.code.addComment('TODO: implement add column ' + meta.dbName + '.' + field.dbName);
42
+ _b.label = 3;
43
+ case 3: return [2 /*return*/];
44
+ }
45
+ });
46
+ });
47
+ };
48
+ DefaultMigrationBuilder.prototype.removeTable = function (entityDbName) {
49
+ var _a;
50
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
51
+ return tslib_1.__generator(this, function (_b) {
52
+ switch (_b.label) {
53
+ case 0:
54
+ if (!((_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.removeTable)) return [3 /*break*/, 2];
55
+ return [4 /*yield*/, this.wrapped.removeTable(entityDbName)];
56
+ case 1:
57
+ _b.sent();
58
+ return [3 /*break*/, 3];
59
+ case 2:
60
+ this.code.addComment('TODO: implement remove table ' + entityDbName);
61
+ _b.label = 3;
62
+ case 3: return [2 /*return*/];
63
+ }
64
+ });
65
+ });
66
+ };
67
+ DefaultMigrationBuilder.prototype.removeColumn = function (entityName, columnDbName) {
68
+ var _a;
69
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
70
+ return tslib_1.__generator(this, function (_b) {
71
+ switch (_b.label) {
72
+ case 0:
73
+ if (!((_a = this.wrapped) === null || _a === void 0 ? void 0 : _a.removeColumn)) return [3 /*break*/, 2];
74
+ return [4 /*yield*/, this.wrapped.removeColumn(entityName, columnDbName)];
75
+ case 1:
76
+ _b.sent();
77
+ return [3 /*break*/, 3];
78
+ case 2:
79
+ this.code.addComment('TODO: implement remove column ' + entityName + '.' + columnDbName);
80
+ _b.label = 3;
81
+ case 3: return [2 /*return*/];
82
+ }
22
83
  });
23
84
  });
24
85
  };
@@ -16,6 +16,9 @@ function updateMigrationsFile(filePath, steps) {
16
16
  case 0: return [4 /*yield*/, Promise.resolve().then(function () { return tslib_1.__importStar(require('typescript')); })];
17
17
  case 1:
18
18
  ts = _a.sent();
19
+ //@ts-ignore
20
+ if (ts.default)
21
+ ts = ts.default;
19
22
  return [4 /*yield*/, Promise.resolve().then(function () { return tslib_1.__importStar(require('fs')); })];
20
23
  case 2:
21
24
  fs = _a.sent();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.25.8-exp.13",
3
+ "version": "0.25.8-exp.15",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "homepage": "https://remult.dev",
6
6
  "repository": {
@@ -30,6 +30,23 @@ export declare class Filter {
30
30
 
31
31
  */
32
32
  static getInfo<entityType>(metadata: EntityMetadata<entityType>, filter: EntityFilter<entityType>): Promise<FilterInfo<entityType>>;
33
+ /**
34
+ * Retrieves information about a filter, including precise values for each property.
35
+ * @template entityType The type of the entity being filtered.
36
+ * @param metadata The metadata of the entity being filtered.
37
+ * @param filter The filter to analyze.
38
+ * @returns A promise that resolves to a FilterInfo object containing the filter information.
39
+ * @example
40
+ * const info = await where.getInfo();
41
+ * console.log(info.preciseValues);
42
+ * // Output:
43
+ * // {
44
+ * // "customerId": ["1", "2", "3"], // Precise values inferred from the filter
45
+ * // "status": undefined, // Cannot infer precise values for 'status'
46
+ * // }
47
+
48
+ */
49
+ getInfo<entityType>(): Promise<FilterInfo<entityType>>;
33
50
  /**
34
51
  * Creates a custom filter. Custom filters are evaluated on the backend, ensuring security and efficiency.
35
52
  * When the filter is used in the frontend, only its name is sent to the backend via the API,
@@ -158,6 +175,7 @@ export declare class Filter {
158
175
  * @returns {Promise<EntityFilter<entityType>>} The resolved entity filter.
159
176
  */
160
177
  static resolve<entityType>(filter: EntityFilter<entityType> | (() => EntityFilter<entityType> | Promise<EntityFilter<entityType>>)): Promise<EntityFilter<entityType>>;
178
+ toJson(): any;
161
179
  }
162
180
  export interface FilterConsumer {
163
181
  or(orElements: Filter[]): any;
@@ -101,6 +101,37 @@ var Filter = /** @class */ (function () {
101
101
  });
102
102
  });
103
103
  };
104
+ /**
105
+ * Retrieves information about a filter, including precise values for each property.
106
+ * @template entityType The type of the entity being filtered.
107
+ * @param metadata The metadata of the entity being filtered.
108
+ * @param filter The filter to analyze.
109
+ * @returns A promise that resolves to a FilterInfo object containing the filter information.
110
+ * @example
111
+ * const info = await where.getInfo();
112
+ * console.log(info.preciseValues);
113
+ * // Output:
114
+ * // {
115
+ * // "customerId": ["1", "2", "3"], // Precise values inferred from the filter
116
+ * // "status": undefined, // Cannot infer precise values for 'status'
117
+ * // }
118
+
119
+ */
120
+ Filter.prototype.getInfo = function () {
121
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
122
+ var result;
123
+ return tslib_1.__generator(this, function (_a) {
124
+ switch (_a.label) {
125
+ case 0:
126
+ result = new preciseValuesCollector();
127
+ return [4 /*yield*/, this.__applyToConsumer(result)];
128
+ case 1:
129
+ _a.sent();
130
+ return [2 /*return*/, result];
131
+ }
132
+ });
133
+ });
134
+ };
104
135
  Filter.createCustom = function (rawFilterTranslator, key) {
105
136
  if (key === void 0) { key = ''; }
106
137
  var rawFilterInfo = { key: key, rawFilterTranslator: rawFilterTranslator };
@@ -295,7 +326,6 @@ var Filter = /** @class */ (function () {
295
326
  });
296
327
  });
297
328
  };
298
- //@internal
299
329
  Filter.prototype.toJson = function () {
300
330
  var r = new FilterSerializer();
301
331
  this.__applyToConsumer(r);
@@ -240,7 +240,7 @@ export declare type MembersOnly<T> = {
240
240
  };
241
241
  export declare type idType<entityType> = entityType extends {
242
242
  id?: infer U;
243
- } ? U : string | number;
243
+ } ? U extends string ? string : U extends number ? number : string | number : string | number;
244
244
  /**used to perform CRUD operations on an `entityType` */
245
245
  export interface Repository<entityType> {
246
246
  /** returns a result array based on the provided options */
@@ -1,21 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //p1 - It's good that there is no migrations added. (I also don't like automatic deletion), and it can be done manually if needed.
4
- //p1 - Maybe migrations-snapshots.json should have fields deleted to have a one to one representation of the db?
3
+ //p1 DOC DONE - It's good that there is no migrations added. (I also don't like automatic deletion), and it can be done manually if needed.
4
+ //p1 DOC DONE - Maybe migrations-snapshots.json should have fields deleted to have a one to one representation of the db?
5
+ //p1 DOC DONE - @JYT - should I write to console on generate migrations, what are the changes that I've found / the migration step id / not changes found
6
+ //p1 DOC DONE - re-discuss endConnection - default false - explain in docs
7
+ //p1 DONE - JYC log the migration request - beforeMigration. afterMigration
8
+ //p1 DONE - migration default folder should be source/migrations (also docs?)
9
+ //p1 DONE - @JYT run all migrations in a single transaction? or transaction per step?
10
+ //p1 Done - Filter.toJson should be public
5
11
  //p1 - I needed to modify the changes Field of changeLog entity to @Fields.json() to be nvarchar(max) too. Why is @Fields.object() only nvarchar(255) ? Or is it default size and can be adjusted?
6
- //p1 - Filter.toJson should be public
7
- //p1 - improve type augmentation doc with specific of the file and with the export trick
8
- //y1 - JY build issue
9
- //p1 - JY issue with the generate migration
10
- //y1 - migration default folder should be source/migrations (also docs?)
11
- //y1 - discuss the typing issue with relations in this case vs the ValueListFieldTypes
12
- //y1 - JYC log the migration request
13
- //y1 - JYC health check approach
12
+ //p1 - improve type augmentation doc with specific of the file and with the export trick - fix code samples to init reqiest error
13
+ //p1 - document custom filter without sql
14
+ //y1!! - JY build issue
15
+ //p1 - discuss the typing issue with relations in this case vs the ValueListFieldTypes - return partial with id values.
14
16
  //p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
15
17
  //p1 - document new sqlite providers
16
18
  //p1 - article auth.js with express
17
19
  //p1 - document offline support
18
20
  //p1 - fix getting started article with using your own database connection
21
+ //p1 - create foreign key constraints in user code
22
+ //p1 - https://github.com/vuejs/vitepress/issues/3154#issuecomment-1784612839
19
23
  //y2 - should we validate relations
20
24
  //y1 - dependency of live query tables
21
25
  //y1 - consider replacing all errors with error classes that extend the base Error class