@mikro-orm/core 7.2.0-dev.2 → 7.2.0-dev.21
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/EntityManager.d.ts +42 -8
- package/EntityManager.js +256 -70
- package/MikroORM.d.ts +4 -0
- package/MikroORM.js +9 -0
- package/cache/CacheAdapter.d.ts +6 -4
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +7 -2
- package/connections/Connection.d.ts +10 -1
- package/connections/Connection.js +9 -0
- package/drivers/DatabaseDriver.d.ts +17 -1
- package/drivers/DatabaseDriver.js +203 -41
- package/drivers/IDatabaseDriver.d.ts +1 -0
- package/entity/Collection.js +4 -2
- package/entity/EntityFactory.js +6 -0
- package/entity/EntityLoader.d.ts +7 -1
- package/entity/EntityLoader.js +46 -11
- package/entity/EntityRepository.d.ts +4 -5
- package/entity/EntityRepository.js +7 -2
- package/entity/defineEntity.d.ts +48 -14
- package/entity/defineEntity.js +32 -1
- package/enums.d.ts +5 -1
- package/enums.js +2 -0
- package/errors.d.ts +36 -0
- package/errors.js +90 -0
- package/events/EventManager.js +6 -3
- package/exceptions.d.ts +5 -0
- package/exceptions.js +5 -0
- package/hydration/ObjectHydrator.d.ts +2 -0
- package/hydration/ObjectHydrator.js +15 -8
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +5 -2
- package/metadata/MetadataDiscovery.d.ts +3 -0
- package/metadata/MetadataDiscovery.js +161 -28
- package/metadata/MetadataProvider.js +1 -1
- package/metadata/MetadataStorage.d.ts +4 -3
- package/metadata/MetadataStorage.js +32 -2
- package/metadata/Routine.js +4 -1
- package/metadata/types.d.ts +19 -3
- package/naming-strategy/AbstractNamingStrategy.js +2 -1
- package/naming-strategy/NamingStrategy.d.ts +2 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +24 -3
- package/platforms/Platform.js +63 -1
- package/types/BigIntType.d.ts +1 -0
- package/types/BigIntType.js +23 -0
- package/types/DateTimeType.d.ts +1 -0
- package/types/DateTimeType.js +8 -0
- package/types/StringType.d.ts +14 -3
- package/types/StringType.js +34 -4
- package/types/TextType.d.ts +2 -4
- package/types/TextType.js +2 -8
- package/types/Type.d.ts +11 -0
- package/types/Type.js +4 -4
- package/types/index.d.ts +2 -2
- package/typings.d.ts +58 -2
- package/typings.js +24 -1
- package/unit-of-work/ChangeSet.js +10 -7
- package/unit-of-work/ChangeSetPersister.js +32 -20
- package/unit-of-work/UnitOfWork.js +11 -4
- package/utils/AbstractMigrator.d.ts +1 -1
- package/utils/AbstractMigrator.js +3 -2
- package/utils/Configuration.d.ts +15 -1
- package/utils/Configuration.js +13 -2
- package/utils/Cursor.d.ts +2 -0
- package/utils/Cursor.js +44 -39
- package/utils/DataloaderUtils.js +2 -1
- package/utils/EntityComparator.d.ts +2 -0
- package/utils/EntityComparator.js +11 -4
- package/utils/QueryHelper.d.ts +17 -0
- package/utils/QueryHelper.js +100 -4
- package/utils/RawQueryFragment.d.ts +6 -0
- package/utils/RawQueryFragment.js +15 -6
- package/utils/RequestContext.d.ts +2 -2
- package/utils/RequestContext.js +11 -2
- package/utils/TransactionManager.d.ts +6 -0
- package/utils/TransactionManager.js +36 -3
- package/utils/Utils.d.ts +14 -2
- package/utils/Utils.js +36 -6
- package/utils/clone.js +6 -0
- package/utils/env-vars.js +1 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/rls-utils.d.ts +35 -0
- package/utils/rls-utils.js +97 -0
- package/utils/upsert-utils.d.ts +9 -1
- package/utils/upsert-utils.js +26 -3
package/platforms/Platform.js
CHANGED
|
@@ -413,6 +413,7 @@ export class Platform {
|
|
|
413
413
|
getSearchJsonPropertySQL(path, type, aliased) {
|
|
414
414
|
return path;
|
|
415
415
|
}
|
|
416
|
+
/** When `aliased` is a string, it holds an explicit alias the key was prefixed with. */
|
|
416
417
|
getSearchJsonPropertyKey(path, type, aliased, value) {
|
|
417
418
|
return path.join('.');
|
|
418
419
|
}
|
|
@@ -424,7 +425,8 @@ export class Platform {
|
|
|
424
425
|
return o;
|
|
425
426
|
}
|
|
426
427
|
if (path.length === 1) {
|
|
427
|
-
|
|
428
|
+
const key = typeof alias === 'string' ? `${alias}.${path[0]}` : path[0];
|
|
429
|
+
o[key] = value;
|
|
428
430
|
return o;
|
|
429
431
|
}
|
|
430
432
|
const type = this.getJsonValueType(value);
|
|
@@ -465,6 +467,18 @@ export class Platform {
|
|
|
465
467
|
convertsJsonAutomatically() {
|
|
466
468
|
return true;
|
|
467
469
|
}
|
|
470
|
+
/** Whether date values inside JSON documents keep their native type (e.g. BSON dates), instead of being serialized to ISO strings. */
|
|
471
|
+
preservesDatesInsideJson() {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
/** Whether `nulls first`/`nulls last` can be requested in an `orderBy`. */
|
|
475
|
+
supportsNullsOrdering() {
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
/** Where nulls land when an `orderBy` requests no explicit placement: lowest (`asc` puts them first) or highest. */
|
|
479
|
+
sortsNullsLowest() {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
468
482
|
/** Converts a JS value to its JSON database representation (typically JSON.stringify). */
|
|
469
483
|
convertJsonToDatabaseValue(value, context) {
|
|
470
484
|
return JSON.stringify(value);
|
|
@@ -610,6 +624,14 @@ export class Platform {
|
|
|
610
624
|
Object.defineProperty(copy, JsonProperty, { enumerable: false, value: true });
|
|
611
625
|
return copy;
|
|
612
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Builds the correlated subquery used as the formula of a virtual to-one relation defined via `through`.
|
|
629
|
+
* @internal
|
|
630
|
+
*/
|
|
631
|
+
/* v8 ignore next 3 */
|
|
632
|
+
getThroughRelationFormula(prop, columns) {
|
|
633
|
+
throw new Error(`${this.constructor.name} does not support the 'through' option of ${prop.name}`);
|
|
634
|
+
}
|
|
613
635
|
/** Initializes the platform with the ORM configuration. */
|
|
614
636
|
setConfig(config) {
|
|
615
637
|
this.config = config;
|
|
@@ -716,11 +738,51 @@ export class Platform {
|
|
|
716
738
|
supportsDeferredUniqueConstraints() {
|
|
717
739
|
return true;
|
|
718
740
|
}
|
|
741
|
+
/** Whether the platform supports row level security (PostgreSQL). */
|
|
742
|
+
supportsRowLevelSecurity() {
|
|
743
|
+
return false;
|
|
744
|
+
}
|
|
745
|
+
/** Whether the driver can apply the session context on every pooled connection acquire (`sessionContext: 'connection'`). */
|
|
746
|
+
supportsConnectionSessionContext() {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* SQL cast suffix (e.g. `'::uuid'`, or `''` when none is needed) applied when an RLS filter reads a session
|
|
751
|
+
* variable via `current_setting()` as the given column type, or `null` if the type has no automatic cast.
|
|
752
|
+
*/
|
|
753
|
+
getCurrentSettingCast(mappedType) {
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
719
756
|
/** Platform-specific validation of entity metadata. */
|
|
720
757
|
validateMetadata(meta) {
|
|
721
758
|
if (meta.partitionBy && !this.supportsPartitionedTables()) {
|
|
722
759
|
throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
|
|
723
760
|
}
|
|
761
|
+
const declaresRls = meta.policies.length > 0 || !!meta.rowLevelSecurity;
|
|
762
|
+
if (declaresRls && !this.supportsRowLevelSecurity()) {
|
|
763
|
+
throw MetadataError.rowLevelSecurityNotSupportedByDriver(meta);
|
|
764
|
+
}
|
|
765
|
+
// STI hierarchies share a single table, so only the root may declare policies; `root` is optional-chained
|
|
766
|
+
// as `validateMetadata` is public API and tolerates partially populated metadata
|
|
767
|
+
if (declaresRls && meta.root?.inheritanceType === 'sti' && meta.root !== meta) {
|
|
768
|
+
throw MetadataError.rowLevelSecurityOnNonRootStiEntity(meta);
|
|
769
|
+
}
|
|
770
|
+
if (meta.root?.inheritanceType === 'sti' && meta.root !== meta) {
|
|
771
|
+
for (const filter of Object.values(meta.filters)) {
|
|
772
|
+
// inherited root filters share the def object; only defs declared on the child itself are a problem,
|
|
773
|
+
// as non-root STI metas never reach the schema generator and the policy would silently not exist
|
|
774
|
+
if (filter.rls && meta.root.filters[filter.name] !== filter) {
|
|
775
|
+
throw MetadataError.rlsFilterOnNonRootStiEntity(meta, filter.name);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
if (!this.supportsRowLevelSecurity()) {
|
|
780
|
+
for (const filter of Object.values(meta.filters)) {
|
|
781
|
+
if (filter.rls) {
|
|
782
|
+
throw MetadataError.rlsFilterNotSupportedByDriver(meta, filter.name);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
724
786
|
}
|
|
725
787
|
/**
|
|
726
788
|
* Generates a custom order by statement given a set of in order values, eg.
|
package/types/BigIntType.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class BigIntType<Mode extends 'bigint' | 'number' | 'string' = 'b
|
|
|
11
11
|
convertToDatabaseValue(value: JSTypeByMode<Mode> | null | undefined): string | null | undefined;
|
|
12
12
|
convertToJSValue(value: string | bigint | null | undefined): JSTypeByMode<Mode> | null | undefined;
|
|
13
13
|
toJSON(value: JSTypeByMode<Mode> | null | undefined): JSTypeByMode<Mode> | null | undefined;
|
|
14
|
+
fromJSON(value: unknown): JSTypeByMode<Mode> | null | undefined;
|
|
14
15
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
15
16
|
compareAsType(): string;
|
|
16
17
|
compareValues(a: string, b: string): boolean;
|
package/types/BigIntType.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Type } from './Type.js';
|
|
2
|
+
import { ValidationError } from '../errors.js';
|
|
2
3
|
/**
|
|
3
4
|
* This type will automatically convert string values returned from the database to native JS bigints (default)
|
|
4
5
|
* or numbers (safe only for values up to `Number.MAX_SAFE_INTEGER`), or strings, depending on the `mode`.
|
|
@@ -36,6 +37,28 @@ export class BigIntType extends Type {
|
|
|
36
37
|
}
|
|
37
38
|
return this.convertToDatabaseValue(value);
|
|
38
39
|
}
|
|
40
|
+
fromJSON(value) {
|
|
41
|
+
// the serialized form is a decimal string, or a plain number in `number` mode
|
|
42
|
+
const valid = (typeof value === 'string' && /^-?\d+$/.test(value)) || (typeof value === 'number' && Number.isInteger(value));
|
|
43
|
+
if (!valid) {
|
|
44
|
+
throw ValidationError.invalidType(BigIntType, value, 'JSON');
|
|
45
|
+
}
|
|
46
|
+
switch (this.mode) {
|
|
47
|
+
case 'number': {
|
|
48
|
+
// `Number` silently rounds past `MAX_SAFE_INTEGER`, tampered cursors must fail loudly
|
|
49
|
+
const num = Number(value);
|
|
50
|
+
if (!Number.isSafeInteger(num)) {
|
|
51
|
+
throw ValidationError.invalidType(BigIntType, value, 'JSON');
|
|
52
|
+
}
|
|
53
|
+
return num;
|
|
54
|
+
}
|
|
55
|
+
case 'string':
|
|
56
|
+
return String(value);
|
|
57
|
+
case 'bigint':
|
|
58
|
+
default:
|
|
59
|
+
return BigInt(value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
39
62
|
getColumnType(prop, platform) {
|
|
40
63
|
return platform.getBigIntTypeDeclarationSQL(prop);
|
|
41
64
|
}
|
package/types/DateTimeType.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { EntityProperty } from '../typings.js';
|
|
|
5
5
|
export declare class DateTimeType extends Type<Date, string> {
|
|
6
6
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
7
7
|
compareAsType(): string;
|
|
8
|
+
fromJSON(value: unknown): Date;
|
|
8
9
|
get runtimeType(): string;
|
|
9
10
|
ensureComparable(): boolean;
|
|
10
11
|
getDefaultLength(platform: Platform): number;
|
package/types/DateTimeType.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Type } from './Type.js';
|
|
2
|
+
import { ValidationError } from '../errors.js';
|
|
2
3
|
/** Maps a database DATETIME/TIMESTAMP column to a JS `Date` object. */
|
|
3
4
|
export class DateTimeType extends Type {
|
|
4
5
|
getColumnType(prop, platform) {
|
|
@@ -7,6 +8,13 @@ export class DateTimeType extends Type {
|
|
|
7
8
|
compareAsType() {
|
|
8
9
|
return 'Date';
|
|
9
10
|
}
|
|
11
|
+
fromJSON(value) {
|
|
12
|
+
const date = new Date(value);
|
|
13
|
+
if (typeof value !== 'string' || Number.isNaN(date.getTime())) {
|
|
14
|
+
throw ValidationError.invalidType(DateTimeType, value, 'JSON');
|
|
15
|
+
}
|
|
16
|
+
return date;
|
|
17
|
+
}
|
|
10
18
|
get runtimeType() {
|
|
11
19
|
return 'Date';
|
|
12
20
|
}
|
package/types/StringType.d.ts
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { Type } from './Type.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
3
|
import type { EntityProperty } from '../typings.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export interface StringTypeOptions {
|
|
5
|
+
trim?: boolean;
|
|
6
|
+
case?: 'upper' | 'lower';
|
|
7
|
+
}
|
|
8
|
+
/** @internal */
|
|
9
|
+
export declare abstract class BaseStringType extends Type<string | null | undefined, string | null | undefined> {
|
|
10
|
+
readonly options: StringTypeOptions;
|
|
11
|
+
constructor(options?: StringTypeOptions);
|
|
12
|
+
convertToDatabaseValue(value: string | null | undefined): string | null | undefined;
|
|
7
13
|
compareAsType(): string;
|
|
8
14
|
ensureComparable(): boolean;
|
|
15
|
+
private normalize;
|
|
16
|
+
}
|
|
17
|
+
/** Maps a database VARCHAR column to a JS `string`. */
|
|
18
|
+
export declare class StringType extends BaseStringType {
|
|
19
|
+
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
9
20
|
getDefaultLength(platform: Platform): number;
|
|
10
21
|
}
|
package/types/StringType.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { Type } from './Type.js';
|
|
2
|
-
/**
|
|
3
|
-
export class
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/** @internal */
|
|
3
|
+
export class BaseStringType extends Type {
|
|
4
|
+
options;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super();
|
|
7
|
+
this.options = options;
|
|
8
|
+
// a defined `compareValues` replaces the inline `!==` comparator, so only provide it when normalization is configured
|
|
9
|
+
if (options.trim || options.case) {
|
|
10
|
+
this.compareValues = (a, b) => this.normalize(a) === this.normalize(b);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
convertToDatabaseValue(value) {
|
|
14
|
+
return this.normalize(value);
|
|
6
15
|
}
|
|
7
16
|
compareAsType() {
|
|
8
17
|
return 'string';
|
|
@@ -10,6 +19,27 @@ export class StringType extends Type {
|
|
|
10
19
|
ensureComparable() {
|
|
11
20
|
return false;
|
|
12
21
|
}
|
|
22
|
+
normalize(value) {
|
|
23
|
+
if (value == null) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (this.options.trim) {
|
|
27
|
+
value = value.trim();
|
|
28
|
+
}
|
|
29
|
+
if (this.options.case === 'upper') {
|
|
30
|
+
return value.toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
if (this.options.case === 'lower') {
|
|
33
|
+
return value.toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Maps a database VARCHAR column to a JS `string`. */
|
|
39
|
+
export class StringType extends BaseStringType {
|
|
40
|
+
getColumnType(prop, platform) {
|
|
41
|
+
return platform.getVarcharTypeDeclarationSQL(prop);
|
|
42
|
+
}
|
|
13
43
|
getDefaultLength(platform) {
|
|
14
44
|
return platform.getDefaultVarcharLength();
|
|
15
45
|
}
|
package/types/TextType.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseStringType } from './StringType.js';
|
|
2
2
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
3
|
import type { EntityProperty } from '../typings.js';
|
|
4
4
|
/** Maps a database TEXT column (unbounded length) to a JS `string`. */
|
|
5
|
-
export declare class TextType extends
|
|
5
|
+
export declare class TextType extends BaseStringType {
|
|
6
6
|
getColumnType(prop: EntityProperty, platform: Platform): string;
|
|
7
|
-
compareAsType(): string;
|
|
8
|
-
ensureComparable(): boolean;
|
|
9
7
|
}
|
package/types/TextType.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseStringType } from './StringType.js';
|
|
2
2
|
/** Maps a database TEXT column (unbounded length) to a JS `string`. */
|
|
3
|
-
export class TextType extends
|
|
3
|
+
export class TextType extends BaseStringType {
|
|
4
4
|
getColumnType(prop, platform) {
|
|
5
5
|
return platform.getTextTypeDeclarationSQL(prop);
|
|
6
6
|
}
|
|
7
|
-
compareAsType() {
|
|
8
|
-
return 'string';
|
|
9
|
-
}
|
|
10
|
-
ensureComparable() {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
7
|
}
|
package/types/Type.d.ts
CHANGED
|
@@ -59,6 +59,17 @@ export declare abstract class Type<JSType = string, DBType = JSType> {
|
|
|
59
59
|
* By default uses the runtime value.
|
|
60
60
|
*/
|
|
61
61
|
toJSON(value: JSType, platform: Platform): JSType | DBType;
|
|
62
|
+
/**
|
|
63
|
+
* Converts a value from its serialized JSON form back to its JS representation. Used when
|
|
64
|
+
* decoding cursor values. The input is what `toJSON` produced, after a `JSON.parse` round
|
|
65
|
+
* trip, and never an already restored JS value. Cursors are client supplied, so the value
|
|
66
|
+
* can be any JSON shape: validate it and throw for values the type cannot restore, and
|
|
67
|
+
* `findByCursor` surfaces the failure as a `CursorError`.
|
|
68
|
+
* Implementing this method also makes cursor encoding use `toJSON`. Without it, cursors
|
|
69
|
+
* carry the raw JS value, and decoding falls back to `convertToJSValue`, with type-based
|
|
70
|
+
* `Date` restoration for date-like columns.
|
|
71
|
+
*/
|
|
72
|
+
fromJSON?(value: unknown, platform: Platform): JSType;
|
|
62
73
|
/**
|
|
63
74
|
* Gets the SQL declaration snippet for a field of this type.
|
|
64
75
|
*/
|
package/types/Type.js
CHANGED
|
@@ -55,11 +55,11 @@ export class Type {
|
|
|
55
55
|
return prop.columnTypes?.[0] ?? platform.getTextTypeDeclarationSQL(prop);
|
|
56
56
|
}
|
|
57
57
|
static getType(cls) {
|
|
58
|
-
|
|
59
|
-
if (!Type.types.has(
|
|
60
|
-
Type.types.set(
|
|
58
|
+
// keyed by class reference, as minifiers can mangle two classes to the same name
|
|
59
|
+
if (!Type.types.has(cls)) {
|
|
60
|
+
Type.types.set(cls, new cls());
|
|
61
61
|
}
|
|
62
|
-
return Type.types.get(
|
|
62
|
+
return Type.types.get(cls);
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
65
|
* Checks whether the argument is instance of `Type`.
|
package/types/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { IntervalType } from './IntervalType.js';
|
|
|
15
15
|
import { JsonType } from './JsonType.js';
|
|
16
16
|
import { MediumIntType } from './MediumIntType.js';
|
|
17
17
|
import { SmallIntType } from './SmallIntType.js';
|
|
18
|
-
import { StringType } from './StringType.js';
|
|
18
|
+
import { type StringTypeOptions, StringType } from './StringType.js';
|
|
19
19
|
import { TextType } from './TextType.js';
|
|
20
20
|
import { TimeType } from './TimeType.js';
|
|
21
21
|
import { TinyIntType } from './TinyIntType.js';
|
|
@@ -23,7 +23,7 @@ import { type IType, type TransformContext, Type } from './Type.js';
|
|
|
23
23
|
import { Uint8ArrayType } from './Uint8ArrayType.js';
|
|
24
24
|
import { UnknownType } from './UnknownType.js';
|
|
25
25
|
import { UuidType } from './UuidType.js';
|
|
26
|
-
export type { TransformContext, IType };
|
|
26
|
+
export type { TransformContext, IType, StringTypeOptions };
|
|
27
27
|
export { Type, DateType, TimeType, DateTimeType, BigIntType, BlobType, Uint8ArrayType, ArrayType, EnumArrayType, EnumType, JsonType, IntegerType, SmallIntType, TinyIntType, MediumIntType, FloatType, DoubleType, BooleanType, DecimalType, StringType, UuidType, TextType, UnknownType, IntervalType, CharacterType, };
|
|
28
28
|
/** Registry of all built-in type constructors, keyed by their short name (e.g., `types.integer`, `types.uuid`). */
|
|
29
29
|
export declare const types: {
|
package/typings.d.ts
CHANGED
|
@@ -314,6 +314,7 @@ export type OperatorMap<T> = {
|
|
|
314
314
|
$ne?: ExpandScalar<T> | readonly ExpandScalar<T>[] | Subquery;
|
|
315
315
|
$in?: readonly ExpandScalar<T>[] | readonly Primary<T>[] | Raw | Subquery;
|
|
316
316
|
$nin?: readonly ExpandScalar<T>[] | readonly Primary<T>[] | Raw | Subquery;
|
|
317
|
+
$all?: readonly ExpandQuery<T>[];
|
|
317
318
|
$not?: ExpandQuery<T>;
|
|
318
319
|
$none?: ExpandQuery<T>;
|
|
319
320
|
$some?: ExpandQuery<T>;
|
|
@@ -478,12 +479,12 @@ type NonArrayObject = object & {
|
|
|
478
479
|
export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends {
|
|
479
480
|
__runtime?: infer Runtime;
|
|
480
481
|
__raw?: infer Raw;
|
|
481
|
-
} ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? EntityDataNested<U, C> : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends CollectionShape<infer U> ? U | U[] | EntityDataNested<U & object, C> | EntityDataNested<U & object, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
|
|
482
|
+
} ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? EntityDataNested<U, C> : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends CollectionShape<infer U> ? U | U[] | EntityDataNested<U & object, C> | EntityDataNested<U & object, C>[] : T extends readonly (infer U)[] ? [U] extends [NonArrayObject] ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
|
|
482
483
|
/** Like `EntityDataProp` but used in `RequiredEntityData` context with required/optional key distinction. */
|
|
483
484
|
export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends RequiredNullable.Brand ? T | null : T extends Scalar ? T : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends {
|
|
484
485
|
__runtime?: infer Runtime;
|
|
485
486
|
__raw?: infer Raw;
|
|
486
|
-
} ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends CollectionShape<infer U> ? U | U[] | RequiredEntityDataNested<U & object, O, C> | RequiredEntityDataNested<U & object, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
|
|
487
|
+
} ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends CollectionShape<infer U> ? U | U[] | RequiredEntityDataNested<U & object, O, C> | RequiredEntityDataNested<U & object, O, C>[] : T extends readonly (infer U)[] ? [U] extends [NonArrayObject] ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
|
|
487
488
|
/** Nested entity data shape for embedded or related entities within `EntityData`. */
|
|
488
489
|
export type EntityDataNested<T, C extends boolean = false> = T extends undefined ? never : T extends any[] ? Readonly<T> : EntityData<T, C> | ExpandEntityProp<T, C>;
|
|
489
490
|
type UnwrapScalarRef<T> = T extends ScalarReference<infer U> ? U : T;
|
|
@@ -653,6 +654,16 @@ export type SerializeDTO<T, H extends string = never, E extends string = never,
|
|
|
653
654
|
};
|
|
654
655
|
type TargetKeys<T> = T extends EntityClass<infer P> ? keyof P : keyof T;
|
|
655
656
|
type PropertyName<T> = IsUnknown<T> extends false ? TargetKeys<T> : string;
|
|
657
|
+
/** Resolved `through` option of a virtual to-one relation, populated during discovery. */
|
|
658
|
+
export interface ThroughRelation {
|
|
659
|
+
entity: EntityClass;
|
|
660
|
+
where?: FilterQuery<any>;
|
|
661
|
+
orderBy?: QueryOrderMap<any>[];
|
|
662
|
+
/** M:1 property on the `through` entity pointing back to the owner. */
|
|
663
|
+
ownerProperty: string;
|
|
664
|
+
/** M:1 property on the `through` entity pointing to the target, undefined when the target is selected directly. */
|
|
665
|
+
targetProperty?: string;
|
|
666
|
+
}
|
|
656
667
|
/** Table reference object passed to formula callbacks, including alias and schema information. */
|
|
657
668
|
export type FormulaTable = {
|
|
658
669
|
alias: string;
|
|
@@ -710,6 +721,8 @@ export type IndexCallback<T> = (columns: Record<PropertyName<T>, string>, table:
|
|
|
710
721
|
export type FormulaCallback<T> = (columns: FormulaColumns<T>, table: FormulaTable) => string | Raw;
|
|
711
722
|
/** Callback for CHECK constraint expressions. Receives column mappings and table info. */
|
|
712
723
|
export type CheckCallback<T> = (columns: SchemaColumns<T>, table: SchemaTable) => string | Raw;
|
|
724
|
+
/** Callback for row level security policy expressions. Receives column mappings and table info. */
|
|
725
|
+
export type PolicyCallback<T> = (columns: SchemaColumns<T>, table: SchemaTable) => string | Raw;
|
|
713
726
|
/** Callback for trigger body expressions. Receives column mappings and table info. */
|
|
714
727
|
export type TriggerCallback<T> = (columns: Record<PropertyName<T>, string>, table: SchemaTable) => string | Raw;
|
|
715
728
|
/**
|
|
@@ -724,6 +737,28 @@ export interface CheckConstraint<T = any> {
|
|
|
724
737
|
property?: string;
|
|
725
738
|
expression: string | Raw | CheckCallback<T>;
|
|
726
739
|
}
|
|
740
|
+
/** Definition of a PostgreSQL row level security policy on a table. */
|
|
741
|
+
export interface PolicyDef<T = any> {
|
|
742
|
+
/** Policy name. Auto-generated if omitted. */
|
|
743
|
+
name?: string;
|
|
744
|
+
/** DML command the policy applies to. Defaults to `'all'`. */
|
|
745
|
+
command?: 'select' | 'insert' | 'update' | 'delete' | 'all';
|
|
746
|
+
/** Whether the policy is permissive (OR-combined) or restrictive (AND-combined). Defaults to `'permissive'`. */
|
|
747
|
+
type?: 'permissive' | 'restrictive';
|
|
748
|
+
/** Database roles the policy applies to. Defaults to `PUBLIC`. */
|
|
749
|
+
roles?: string[];
|
|
750
|
+
/** `USING` expression filtering visible rows. Can be a string, Raw query, or callback receiving column name mappings. */
|
|
751
|
+
using?: string | Raw | PolicyCallback<T>;
|
|
752
|
+
/** `WITH CHECK` expression validating written rows. Can be a string, Raw query, or callback receiving column name mappings. */
|
|
753
|
+
check?: string | Raw | PolicyCallback<T>;
|
|
754
|
+
}
|
|
755
|
+
/** Per-context database session state applied for row level security (session variables and role). */
|
|
756
|
+
export interface SessionContext {
|
|
757
|
+
/** Session variables set via `set_config`, typically referenced by RLS policies through `current_setting()`. `Date` values are serialized to ISO 8601. */
|
|
758
|
+
variables?: Dictionary<string | number | boolean | Date>;
|
|
759
|
+
/** Database role to switch to for the duration of the context (`set local role` / `set role`). */
|
|
760
|
+
role?: string;
|
|
761
|
+
}
|
|
727
762
|
/** Definition of a database trigger on a table. */
|
|
728
763
|
export interface TriggerDef<T = any> {
|
|
729
764
|
/** Trigger name. Auto-generated if omitted. */
|
|
@@ -1019,6 +1054,7 @@ export interface EntityProperty<Owner = any, Target = any> {
|
|
|
1019
1054
|
fixedOrderColumn?: string;
|
|
1020
1055
|
pivotTable: string;
|
|
1021
1056
|
pivotEntity: EntityClass<Target>;
|
|
1057
|
+
through?: ThroughRelation;
|
|
1022
1058
|
joinColumns: string[];
|
|
1023
1059
|
ownColumns: string[];
|
|
1024
1060
|
inverseJoinColumns: string[];
|
|
@@ -1050,6 +1086,12 @@ export declare class EntityMetadata<Entity = any, Class extends EntityCtor<Entit
|
|
|
1050
1086
|
constructor(meta?: Partial<EntityMetadata>);
|
|
1051
1087
|
addProperty(prop: Partial<EntityProperty<Entity>>): void;
|
|
1052
1088
|
removeProperty(name: string, sync?: boolean): void;
|
|
1089
|
+
/** For TPT entities, the version column exists only on the table of the entity that declares it. */
|
|
1090
|
+
ownsVersionProperty(): boolean;
|
|
1091
|
+
/** For TPT entities, concurrency check columns exist only on the table of the entity that declares them. */
|
|
1092
|
+
getOwnConcurrencyCheckKeys(): EntityKey<Entity>[];
|
|
1093
|
+
/** Whether updates of this table are guarded by a version property or concurrency check columns it owns. */
|
|
1094
|
+
hasOptimisticLock(): boolean;
|
|
1053
1095
|
getPrimaryProps(flatten?: boolean): EntityProperty<Entity>[];
|
|
1054
1096
|
getPrimaryProp(): EntityProperty<Entity>;
|
|
1055
1097
|
/**
|
|
@@ -1165,6 +1207,9 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
|
|
|
1165
1207
|
}[];
|
|
1166
1208
|
checks: CheckConstraint<Entity>[];
|
|
1167
1209
|
triggers: TriggerDef<Entity>[];
|
|
1210
|
+
policies: PolicyDef<Entity>[];
|
|
1211
|
+
/** Enables row level security on the table. `'force'` also enables it for the table owner. Implied by non-empty `policies`, unless set to `false`, which keeps the policies staged but RLS disabled. */
|
|
1212
|
+
rowLevelSecurity?: boolean | 'force';
|
|
1168
1213
|
repositoryClass?: string;
|
|
1169
1214
|
repository: () => EntityClass<EntityRepository<any>>;
|
|
1170
1215
|
hooks: {
|
|
@@ -1470,6 +1515,8 @@ export interface IMigrationGenerator {
|
|
|
1470
1515
|
}
|
|
1471
1516
|
/** Interface that all migration classes must implement. */
|
|
1472
1517
|
export interface Migration {
|
|
1518
|
+
/** Stable migration name, used instead of the class name (which minifiers can mangle). */
|
|
1519
|
+
name?: string;
|
|
1473
1520
|
up(): Promise<void> | void;
|
|
1474
1521
|
down(): Promise<void> | void;
|
|
1475
1522
|
isTransactional(): boolean;
|
|
@@ -1490,6 +1537,15 @@ type FilterDefResolved<T extends object = any> = {
|
|
|
1490
1537
|
entity?: EntityName<T> | EntityName<T>[];
|
|
1491
1538
|
args?: boolean;
|
|
1492
1539
|
strict?: boolean;
|
|
1540
|
+
/**
|
|
1541
|
+
* Also materializes this filter as a PostgreSQL row level security policy on the entity's table, and stages the
|
|
1542
|
+
* matching session variables when its params are enabled via `em.setFilterParams()`. The `cond` must be compilable
|
|
1543
|
+
* to a static expression (no access to `em`/`type`/`options`, not async). Each referenced argument maps to a session
|
|
1544
|
+
* variable named `mikro.<filterName>.<argName>`; pass `{ setting }` to override that name for a single-argument filter.
|
|
1545
|
+
*/
|
|
1546
|
+
rls?: boolean | {
|
|
1547
|
+
setting?: string;
|
|
1548
|
+
};
|
|
1493
1549
|
};
|
|
1494
1550
|
/** Definition of a query filter that can be registered globally or per-entity via `@Filter()`. */
|
|
1495
1551
|
export type FilterDef<T extends EntityName | readonly EntityName[] = any> = FilterDefResolved<EntityFromInput<T>> & {
|
package/typings.js
CHANGED
|
@@ -49,6 +49,7 @@ export class EntityMetadata {
|
|
|
49
49
|
this.uniques = [];
|
|
50
50
|
this.checks = [];
|
|
51
51
|
this.triggers = [];
|
|
52
|
+
this.policies = [];
|
|
52
53
|
this.referencingProperties = [];
|
|
53
54
|
this.concurrencyCheckKeys = new Set();
|
|
54
55
|
Object.assign(this, meta);
|
|
@@ -79,6 +80,25 @@ export class EntityMetadata {
|
|
|
79
80
|
this.sync();
|
|
80
81
|
}
|
|
81
82
|
}
|
|
83
|
+
/** For TPT entities, the version column exists only on the table of the entity that declares it. */
|
|
84
|
+
ownsVersionProperty() {
|
|
85
|
+
if (!this.versionProperty) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return this.inheritanceType !== 'tpt' || !this.ownProps || this.ownProps.some(p => p.name === this.versionProperty);
|
|
89
|
+
}
|
|
90
|
+
/** For TPT entities, concurrency check columns exist only on the table of the entity that declares them. */
|
|
91
|
+
getOwnConcurrencyCheckKeys() {
|
|
92
|
+
const keys = [...this.concurrencyCheckKeys];
|
|
93
|
+
if (this.inheritanceType !== 'tpt' || !this.ownProps) {
|
|
94
|
+
return keys;
|
|
95
|
+
}
|
|
96
|
+
return keys.filter(key => this.ownProps.some(p => p.name === key));
|
|
97
|
+
}
|
|
98
|
+
/** Whether updates of this table are guarded by a version property or concurrency check columns it owns. */
|
|
99
|
+
hasOptimisticLock() {
|
|
100
|
+
return this.ownsVersionProperty() || this.getOwnConcurrencyCheckKeys().length > 0;
|
|
101
|
+
}
|
|
82
102
|
getPrimaryProps(flatten = false) {
|
|
83
103
|
const pks = this.primaryKeys.map(pk => this.properties[pk]);
|
|
84
104
|
if (flatten) {
|
|
@@ -216,7 +236,10 @@ export class EntityMetadata {
|
|
|
216
236
|
if (config) {
|
|
217
237
|
const platform = config.getPlatform();
|
|
218
238
|
for (const prop of this.props) {
|
|
219
|
-
if (prop.enum &&
|
|
239
|
+
if (prop.enum &&
|
|
240
|
+
!prop.nativeEnumName &&
|
|
241
|
+
prop.items?.every(item => typeof item === 'string') &&
|
|
242
|
+
!['json', 'jsonb'].includes(prop.columnTypes?.[0])) {
|
|
220
243
|
const name = platform.getIndexName(this.tableName, prop.fieldNames, 'check');
|
|
221
244
|
const exists = this.checks.findIndex(check => check.name === name);
|
|
222
245
|
if (exists !== -1) {
|
|
@@ -29,15 +29,18 @@ export class ChangeSet {
|
|
|
29
29
|
else {
|
|
30
30
|
this.primaryKey = this.originalEntity[this.meta.primaryKeys[0]];
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
32
|
+
const primaryProp = this.meta.getPrimaryProp();
|
|
33
|
+
const relationPK = !this.meta.compositePK && !!primaryProp.targetMeta?.compositePK;
|
|
34
|
+
// arrays are already the ordered tuple of the target's primary keys
|
|
35
|
+
if (relationPK && Utils.isPlainObject(this.primaryKey)) {
|
|
36
|
+
const pk = this.primaryKey;
|
|
37
|
+
this.primaryKey = primaryProp.targetMeta.primaryKeys.map(childPK => pk[childPK]);
|
|
39
38
|
}
|
|
40
39
|
if (object && this.primaryKey != null) {
|
|
40
|
+
// the whole tuple belongs to the single relation PK, it must not be spread over the (single) PK prop
|
|
41
|
+
if (relationPK) {
|
|
42
|
+
return { [primaryProp.name]: this.primaryKey };
|
|
43
|
+
}
|
|
41
44
|
return Utils.primaryKeyToObject(this.meta, this.primaryKey);
|
|
42
45
|
}
|
|
43
46
|
return this.primaryKey ?? null;
|