remult 0.25.8-exp.14 → 0.25.8-exp.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/migrations/compare-migration-snapshots.js +26 -4
- package/esm/migrations/generate-migrations.js +13 -8
- package/esm/migrations/migrate.js +47 -33
- package/esm/migrations/migration-types.js +27 -3
- package/esm/migrations/update-migrations-ts-file.js +4 -1
- package/esm/remult-knex/index.js +12 -4
- package/esm/src/filter/filter-interfaces.js +88 -35
- package/esm/src/remult3/RepositoryImplementation.js +20 -7
- package/esm/src/remult3/remult3.js +10 -11
- package/migrations/compare-migration-snapshots.d.ts +3 -2
- package/migrations/compare-migration-snapshots.js +107 -42
- package/migrations/generate-migrations.d.ts +5 -5
- package/migrations/generate-migrations.js +17 -13
- package/migrations/migrate.d.ts +12 -3
- package/migrations/migrate.js +104 -98
- package/migrations/migration-types.d.ts +7 -2
- package/migrations/migration-types.js +68 -7
- package/migrations/update-migrations-ts-file.js +3 -0
- package/package.json +1 -1
- package/remult-knex/index.js +12 -4
- package/src/filter/filter-interfaces.d.ts +20 -2
- package/src/filter/filter-interfaces.js +104 -50
- package/src/remult3/RepositoryImplementation.d.ts +1 -1
- package/src/remult3/RepositoryImplementation.js +20 -6
- package/src/remult3/remult3.js +10 -11
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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/
|
|
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
|
|
18
|
-
* @see [Migrations](
|
|
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
|
|
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
|
|
14
|
-
* @
|
|
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
|
|
34
|
+
const steps = Object.entries(options.migrations).sort(([a], [b]) => parseInt(a) - parseInt(b));
|
|
33
35
|
if (dataProvider.ensureSchema) {
|
|
34
|
-
await dataProvider.ensureSchema([
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
await v.save();
|
|
68
|
-
}
|
|
82
|
+
});
|
|
69
83
|
}
|
|
70
84
|
finally {
|
|
71
85
|
remult.dataProvider = prev;
|
|
72
86
|
}
|
|
73
|
-
if (options.endConnection
|
|
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
|
-
|
|
3
|
+
wrapped;
|
|
4
|
+
constructor(code, wrapped) {
|
|
4
5
|
this.code = code;
|
|
6
|
+
this.wrapped = wrapped;
|
|
5
7
|
}
|
|
6
8
|
async createTable(meta) {
|
|
7
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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() {
|
package/esm/remult-knex/index.js
CHANGED
|
@@ -507,16 +507,24 @@ export function buildColumn(x, dbName, b, supportsJson = true) {
|
|
|
507
507
|
else if (x.valueConverter.fieldTypeInDb == 'json')
|
|
508
508
|
if (supportsJson)
|
|
509
509
|
b.json(dbName);
|
|
510
|
-
else
|
|
511
|
-
b.text(dbName);
|
|
510
|
+
else {
|
|
511
|
+
let c = b.text(dbName);
|
|
512
|
+
if (!x.allowNull)
|
|
513
|
+
c.defaultTo('').notNullable();
|
|
514
|
+
}
|
|
512
515
|
else
|
|
513
516
|
b.specificType(dbName, x.valueConverter.fieldTypeInDb);
|
|
514
517
|
}
|
|
515
|
-
else {
|
|
518
|
+
else if (x.valueType === String) {
|
|
516
519
|
let c = b.string(dbName, x.options.maxLength);
|
|
517
520
|
if (!x.allowNull)
|
|
518
521
|
c.defaultTo('').notNullable();
|
|
519
522
|
}
|
|
523
|
+
else {
|
|
524
|
+
let c = b.text(dbName);
|
|
525
|
+
if (!x.allowNull)
|
|
526
|
+
c.defaultTo('').notNullable();
|
|
527
|
+
}
|
|
520
528
|
}
|
|
521
529
|
function logSql(who) {
|
|
522
530
|
if (KnexSchemaBuilder.logToConsole)
|
|
@@ -547,7 +555,7 @@ class KnexBridgeToSQLCommand {
|
|
|
547
555
|
param(val) {
|
|
548
556
|
if (Array.isArray(val))
|
|
549
557
|
val = JSON.stringify(val);
|
|
550
|
-
const key = ':' +
|
|
558
|
+
const key = ':' + this.i++;
|
|
551
559
|
this.values[key.substring(1)] = val;
|
|
552
560
|
return key;
|
|
553
561
|
}
|
|
@@ -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);
|
|
@@ -769,24 +789,55 @@ export function __updateEntityBasedOnWhere(entityDefs, where, r) {
|
|
|
769
789
|
}
|
|
770
790
|
}
|
|
771
791
|
class preciseValuesCollector {
|
|
772
|
-
|
|
773
|
-
preciseValues = {
|
|
792
|
+
rawValues = {};
|
|
793
|
+
preciseValues = new Proxy(this.rawValues, {
|
|
794
|
+
get: (target, prop) => {
|
|
795
|
+
if (prop in target) {
|
|
796
|
+
let result = target[prop];
|
|
797
|
+
if (result.bad)
|
|
798
|
+
return undefined;
|
|
799
|
+
if (result.values.length > 0) {
|
|
800
|
+
const relInfo = getRelationFieldInfo(result.field);
|
|
801
|
+
if (relInfo) {
|
|
802
|
+
if (relInfo.type === 'reference') {
|
|
803
|
+
return result.values.map((x) => {
|
|
804
|
+
return relInfo.toRepo.metadata.idMetadata.getIdFilter(x);
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
else
|
|
808
|
+
throw new Error('Only relations toOne without field are supported.');
|
|
809
|
+
}
|
|
810
|
+
return result.values;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return undefined;
|
|
814
|
+
},
|
|
815
|
+
});
|
|
774
816
|
ok(col, ...val) {
|
|
775
|
-
|
|
776
|
-
return;
|
|
777
|
-
let x = this.preciseValues[col];
|
|
817
|
+
let x = this.rawValues[col.key];
|
|
778
818
|
if (!x) {
|
|
779
|
-
this.
|
|
819
|
+
this.rawValues[col.key] = {
|
|
820
|
+
field: col,
|
|
821
|
+
bad: false,
|
|
822
|
+
values: [...val],
|
|
823
|
+
};
|
|
780
824
|
}
|
|
781
825
|
else {
|
|
782
|
-
x.push(...val.filter((y) => !x.includes(y)));
|
|
826
|
+
x.values.push(...val.filter((y) => !x.values.includes(y)));
|
|
783
827
|
}
|
|
784
828
|
}
|
|
785
829
|
notOk(col) {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
830
|
+
let x = this.rawValues[col.key];
|
|
831
|
+
if (!x) {
|
|
832
|
+
this.rawValues[col.key] = {
|
|
833
|
+
field: col,
|
|
834
|
+
bad: true,
|
|
835
|
+
values: [],
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
x.bad = true;
|
|
840
|
+
}
|
|
790
841
|
}
|
|
791
842
|
or(orElements) {
|
|
792
843
|
const result = orElements.map((or) => {
|
|
@@ -795,59 +846,61 @@ class preciseValuesCollector {
|
|
|
795
846
|
return x;
|
|
796
847
|
});
|
|
797
848
|
for (const or of result) {
|
|
798
|
-
for (const key in or.
|
|
799
|
-
if (Object.prototype.hasOwnProperty.call(or.
|
|
800
|
-
const element = or.
|
|
801
|
-
if (element)
|
|
802
|
-
|
|
849
|
+
for (const key in or.rawValues) {
|
|
850
|
+
if (Object.prototype.hasOwnProperty.call(or.rawValues, key)) {
|
|
851
|
+
const element = or.rawValues[key];
|
|
852
|
+
if (element) {
|
|
853
|
+
if (element.bad)
|
|
854
|
+
this.notOk(element.field);
|
|
855
|
+
else {
|
|
856
|
+
this.ok(element.field, ...element.values);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
803
859
|
}
|
|
804
860
|
}
|
|
805
|
-
for (const key of or.bad) {
|
|
806
|
-
this.notOk(key);
|
|
807
|
-
}
|
|
808
861
|
}
|
|
809
|
-
for (const key in this.
|
|
810
|
-
if (Object.prototype.hasOwnProperty.call(this.
|
|
862
|
+
for (const key in this.rawValues) {
|
|
863
|
+
if (Object.prototype.hasOwnProperty.call(this.rawValues, key)) {
|
|
811
864
|
for (const r of result) {
|
|
812
|
-
const element = r.
|
|
865
|
+
const element = r.rawValues[key];
|
|
813
866
|
if (!element)
|
|
814
|
-
this.notOk(key);
|
|
867
|
+
this.notOk(this.rawValues[key].field);
|
|
815
868
|
}
|
|
816
869
|
}
|
|
817
870
|
}
|
|
818
871
|
}
|
|
819
872
|
isEqualTo(col, val) {
|
|
820
|
-
this.ok(col
|
|
873
|
+
this.ok(col, val);
|
|
821
874
|
}
|
|
822
875
|
isDifferentFrom(col, val) {
|
|
823
|
-
this.notOk(col
|
|
876
|
+
this.notOk(col);
|
|
824
877
|
}
|
|
825
878
|
isNull(col) {
|
|
826
|
-
this.ok(col
|
|
879
|
+
this.ok(col, null);
|
|
827
880
|
}
|
|
828
881
|
isNotNull(col) {
|
|
829
|
-
this.notOk(col
|
|
882
|
+
this.notOk(col);
|
|
830
883
|
}
|
|
831
884
|
isGreaterOrEqualTo(col, val) {
|
|
832
|
-
this.notOk(col
|
|
885
|
+
this.notOk(col);
|
|
833
886
|
}
|
|
834
887
|
isGreaterThan(col, val) {
|
|
835
|
-
this.notOk(col
|
|
888
|
+
this.notOk(col);
|
|
836
889
|
}
|
|
837
890
|
isLessOrEqualTo(col, val) {
|
|
838
|
-
this.notOk(col
|
|
891
|
+
this.notOk(col);
|
|
839
892
|
}
|
|
840
893
|
isLessThan(col, val) {
|
|
841
|
-
this.notOk(col
|
|
894
|
+
this.notOk(col);
|
|
842
895
|
}
|
|
843
896
|
containsCaseInsensitive(col, val) {
|
|
844
|
-
this.notOk(col
|
|
897
|
+
this.notOk(col);
|
|
845
898
|
}
|
|
846
899
|
notContainsCaseInsensitive(col, val) {
|
|
847
|
-
this.notOk(col
|
|
900
|
+
this.notOk(col);
|
|
848
901
|
}
|
|
849
902
|
isIn(col, val) {
|
|
850
|
-
this.ok(col
|
|
903
|
+
this.ok(col, ...val);
|
|
851
904
|
}
|
|
852
905
|
custom(key, customItem) { }
|
|
853
906
|
databaseCustom(databaseCustom) { }
|
|
@@ -638,7 +638,7 @@ export class RepositoryImplementation {
|
|
|
638
638
|
async count(where) {
|
|
639
639
|
return this._edp.count(await this._translateWhereToFilter(where));
|
|
640
640
|
}
|
|
641
|
-
async deleteMany({ where }) {
|
|
641
|
+
async deleteMany({ where, }) {
|
|
642
642
|
Filter.throwErrorIfFilterIsEmpty(where, 'deleteMany');
|
|
643
643
|
if (this._dataProvider.isProxy) {
|
|
644
644
|
return this._edp.deleteMany(await this._translateWhereToFilter(where));
|
|
@@ -775,13 +775,16 @@ export class RepositoryImplementation {
|
|
|
775
775
|
if (this.metadata.options.backendPreprocessFilter) {
|
|
776
776
|
where = await this.metadata.options.backendPreprocessFilter(where, {
|
|
777
777
|
metadata: this.metadata,
|
|
778
|
-
getFilterInfo: (filter) => Filter.getInfo(this.metadata, filter || where)
|
|
778
|
+
getFilterInfo: (filter) => Filter.getInfo(this.metadata, filter || where),
|
|
779
779
|
});
|
|
780
780
|
}
|
|
781
781
|
if (this.metadata.options.backendPrefilter) {
|
|
782
782
|
let z = where;
|
|
783
783
|
where = {
|
|
784
|
-
$and: [
|
|
784
|
+
$and: [
|
|
785
|
+
z,
|
|
786
|
+
await Filter.resolve(this.metadata.options.backendPrefilter),
|
|
787
|
+
],
|
|
785
788
|
};
|
|
786
789
|
}
|
|
787
790
|
}
|
|
@@ -1934,9 +1937,13 @@ class EntityFullInfo {
|
|
|
1934
1937
|
getIdFilter: (...ids) => {
|
|
1935
1938
|
if (this.idMetadata.field instanceof CompoundIdField) {
|
|
1936
1939
|
let field = this.idMetadata.field;
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
}
|
|
1940
|
+
if (ids.length == 1) {
|
|
1941
|
+
return field.isEqualTo(ids[0]);
|
|
1942
|
+
}
|
|
1943
|
+
else
|
|
1944
|
+
return {
|
|
1945
|
+
$or: ids.map((x) => field.isEqualTo(x)),
|
|
1946
|
+
};
|
|
1940
1947
|
}
|
|
1941
1948
|
if (ids.length == 1)
|
|
1942
1949
|
return {
|
|
@@ -1971,7 +1978,13 @@ export function ValueListFieldType(...options) {
|
|
|
1971
1978
|
;
|
|
1972
1979
|
(o.valueConverter = ValueListInfo.get(type)),
|
|
1973
1980
|
(o.displayValue = (item, val) => val?.caption);
|
|
1974
|
-
o.validate = (entity, ref) =>
|
|
1981
|
+
o.validate = (entity, ref) => {
|
|
1982
|
+
const values = ValueListInfo.get(type).getValues();
|
|
1983
|
+
if (ref.value && !values.find((v) => v === ref.value)) {
|
|
1984
|
+
ref.value = values.find((v) => v.id === ref.value.id) || ref.value;
|
|
1985
|
+
}
|
|
1986
|
+
return Validators.in(values)(entity, ref);
|
|
1987
|
+
};
|
|
1975
1988
|
}, ...options)(type, context);
|
|
1976
1989
|
};
|
|
1977
1990
|
}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//p1 -
|
|
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
|
|
2
|
+
//y1 - JY suggestion of having generate as part of the ensure schema - not sure
|
|
3
|
+
//y1 - currently for string fields we default for 255 in knex (which is their default) why not just use text (varchar max) - and only use exact length when one is provided?
|
|
4
|
+
//p1 - document custom filter without sql
|
|
13
5
|
//p1 - admin url1 - pocketbase use /_ for the admin //{allowed?:Allowed,url?:string}
|
|
14
6
|
//p1 - document new sqlite providers
|
|
15
7
|
//p1 - article auth.js with express
|
|
16
8
|
//p1 - document offline support
|
|
17
9
|
//p1 - fix getting started article with using your own database connection
|
|
10
|
+
//p1 - create foreign key constraints in user code
|
|
11
|
+
//p1 - https://github.com/vuejs/vitepress/issues/3154#issuecomment-1784612839
|
|
18
12
|
//y2 - should we validate relations
|
|
19
13
|
//y1 - dependency of live query tables
|
|
14
|
+
//y1 - consider the bot that updates dependencies
|
|
20
15
|
//y1 - consider replacing all errors with error classes that extend the base Error class
|
|
21
16
|
//y2 - should enforce integer - currently we probably round / truncate it
|
|
17
|
+
//y1 - talk about filter on objects that are not loaded - {
|
|
18
|
+
//category: repo(CompoundId).create({ company: 7, index: 3, name: '' }),
|
|
19
|
+
// }
|
|
20
|
+
//y1 - consider id to also support keyof (id:['company','index'])
|
|
22
21
|
/*y1 - talk about modules in init express with entities/controllers,initRequest,initApi
|
|
23
22
|
- support get with backend method, with url search params as the first parameter, & url as second parameter
|
|
24
23
|
- support returning redirect, and plain html (For sign in scenarios)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { MigrationBuilder } from './migration-types.js';
|
|
2
|
-
export declare function compareMigrationSnapshot({ entities, snapshot, 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, {
|