@mikro-orm/core 7.1.16-dev.10 → 7.1.16-dev.12
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 +41 -7
- package/EntityManager.js +202 -42
- package/MikroORM.d.ts +4 -0
- package/MikroORM.js +9 -0
- package/README.md +1 -0
- package/cache/FileCacheAdapter.js +1 -1
- package/connections/Connection.d.ts +10 -1
- package/connections/Connection.js +9 -0
- package/drivers/DatabaseDriver.d.ts +14 -5
- package/drivers/DatabaseDriver.js +145 -55
- package/entity/Collection.js +4 -2
- package/entity/EntityLoader.js +1 -1
- package/entity/EntityRepository.d.ts +4 -5
- package/entity/EntityRepository.js +2 -1
- package/entity/defineEntity.d.ts +17 -1
- package/entity/defineEntity.js +31 -0
- package/enums.d.ts +3 -1
- package/errors.d.ts +35 -0
- package/errors.js +87 -0
- package/exceptions.d.ts +5 -0
- package/exceptions.js +5 -0
- package/index.d.ts +1 -1
- package/metadata/MetadataDiscovery.d.ts +3 -0
- package/metadata/MetadataDiscovery.js +94 -7
- package/metadata/types.d.ts +19 -3
- package/package.json +1 -1
- package/platforms/Platform.d.ts +19 -1
- package/platforms/Platform.js +56 -0
- 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/index.d.ts +2 -2
- package/typings.d.ts +47 -0
- package/typings.js +1 -0
- package/unit-of-work/UnitOfWork.js +1 -0
- package/utils/Configuration.d.ts +21 -1
- package/utils/Configuration.js +12 -1
- package/utils/Cursor.d.ts +2 -0
- package/utils/Cursor.js +43 -33
- package/utils/QueryHelper.d.ts +12 -0
- package/utils/QueryHelper.js +63 -0
- 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.js +1 -1
- package/utils/Utils.d.ts +2 -0
- package/utils/Utils.js +7 -2
- package/utils/env-vars.js +2 -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/errors.js
CHANGED
|
@@ -94,6 +94,30 @@ export class ValidationError extends Error {
|
|
|
94
94
|
static cannotUseGlobalContext() {
|
|
95
95
|
return new ValidationError("Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead.");
|
|
96
96
|
}
|
|
97
|
+
static sessionContextNotSupported() {
|
|
98
|
+
return new ValidationError('Database session context (row level security) is only supported by the PostgreSQL driver. To test a PostgreSQL app without a server, use the `@mikro-orm/pglite` driver instead of sqlite.');
|
|
99
|
+
}
|
|
100
|
+
static sessionContextRequiresImplicitTransactions() {
|
|
101
|
+
return new ValidationError("Cannot set a database session context (row level security) with the 'transaction' strategy while 'implicitTransactions' is disabled. The context is applied on transaction begin, but writes would run without a transaction and silently bypass the policies. Enable 'implicitTransactions', wrap the work in 'em.transactional()', or use the 'connection' session context strategy.");
|
|
102
|
+
}
|
|
103
|
+
static sessionContextWithDisabledTransactions() {
|
|
104
|
+
return new ValidationError("Cannot set a database session context (row level security) with the 'transaction' strategy while transactions are disabled via 'disableTransactions'. The context is applied on transaction begin, so flushes would run without it and silently bypass the policies. Enable transactions, or use the 'connection' session context strategy.");
|
|
105
|
+
}
|
|
106
|
+
static sessionContextInsideTransaction(action = 'set') {
|
|
107
|
+
const advice = action === 'set'
|
|
108
|
+
? "Set the session context before starting the transaction (e.g. via 'em.fork({ session })')."
|
|
109
|
+
: 'Clear the session context outside the transaction.';
|
|
110
|
+
return new ValidationError(`Cannot ${action} a database session context (row level security) inside an active transaction. The context is applied when the transaction begins or the connection is reserved, so the change would never reach an already-open transaction (with the 'connection' strategy the pinned connection was reserved with the previous context). ${advice}`);
|
|
111
|
+
}
|
|
112
|
+
static cannotStageNonScalarSessionVariable(filterName, argName) {
|
|
113
|
+
return new ValidationError(`Cannot stage the '${argName}' argument of filter '${filterName}' as a session variable (row level security) — only scalar values (string, number, boolean, Date) can be mirrored to the database policy backing the filter.`);
|
|
114
|
+
}
|
|
115
|
+
static sessionContextStreamRequiresTransaction() {
|
|
116
|
+
return new ValidationError("Cannot stream under a database session context (row level security) with the 'transaction' strategy outside a transaction. Streaming never opens the implicit transaction that applies the context, so the streamed rows would not be scoped by it (other tenants' rows would leak). Wrap the stream in 'em.transactional()', or use the 'connection' session context strategy.");
|
|
117
|
+
}
|
|
118
|
+
static connectionSessionContextNotSupported() {
|
|
119
|
+
return new ValidationError("The 'connection' session context strategy requires a driver that supports per-acquire connection hooks (the `postgresql` driver). Use the default 'transaction' strategy instead.");
|
|
120
|
+
}
|
|
97
121
|
static cannotUseOperatorsInsideEmbeddables(entityName, propName, payload) {
|
|
98
122
|
return new ValidationError(`Using operators inside embeddables is not allowed, move the operator above. (property: ${Utils.className(entityName)}.${propName}, payload: ${inspect(payload)})`);
|
|
99
123
|
}
|
|
@@ -116,6 +140,11 @@ export class CursorError extends ValidationError {
|
|
|
116
140
|
static missingValue(entityName, prop) {
|
|
117
141
|
return new CursorError(`Invalid cursor condition, value for '${entityName}.${prop}' is missing.`);
|
|
118
142
|
}
|
|
143
|
+
static invalidCursor(entityName, cause) {
|
|
144
|
+
const error = new CursorError(`Invalid cursor for entity ${entityName}: ${cause.message}`);
|
|
145
|
+
error.cause = cause;
|
|
146
|
+
return error;
|
|
147
|
+
}
|
|
119
148
|
}
|
|
120
149
|
/** Error thrown when an optimistic lock conflict is detected during entity persistence. */
|
|
121
150
|
export class OptimisticLockError extends ValidationError {
|
|
@@ -217,6 +246,16 @@ export class MetadataError extends ValidationError {
|
|
|
217
246
|
const suggestion = target.embeddable ? 'Embedded' : 'ManyToOne';
|
|
218
247
|
return this.fromMessage(meta, prop, `is defined as scalar @Property(), but its type is a discovered entity ${target.className}. Maybe you want to use @${suggestion}() decorator instead?`);
|
|
219
248
|
}
|
|
249
|
+
static throughRelationMissingProperty(meta, prop, through, side) {
|
|
250
|
+
const target = side === 'owner' ? meta.className : prop.targetMeta.className;
|
|
251
|
+
return this.fromMessage(meta, prop, `uses 'through' entity ${through.className} which has no ManyToOne property pointing to ${target}`);
|
|
252
|
+
}
|
|
253
|
+
static throughRelationCompositeTarget(meta, prop) {
|
|
254
|
+
return this.fromMessage(meta, prop, `uses 'through' option which is not supported for targets with composite primary key`);
|
|
255
|
+
}
|
|
256
|
+
static throughRelationInvalidKind(meta, prop) {
|
|
257
|
+
return this.fromMessage(meta, prop, `uses 'through' option which is only supported for ManyToOne and OneToOne relations`);
|
|
258
|
+
}
|
|
220
259
|
static fromMissingOption(meta, prop, option) {
|
|
221
260
|
return this.fromMessage(meta, prop, `is missing '${option}' option`);
|
|
222
261
|
}
|
|
@@ -250,6 +289,54 @@ export class MetadataError extends ValidationError {
|
|
|
250
289
|
static triggersNotSupportedByDriver(meta) {
|
|
251
290
|
return new MetadataError(`Entity ${meta.className} defines database triggers which are not supported by the current driver. Triggers are only available with SQL drivers.`);
|
|
252
291
|
}
|
|
292
|
+
/** Thrown when row level security is declared on an entity using a driver that does not support it. */
|
|
293
|
+
static rowLevelSecurityNotSupportedByDriver(meta) {
|
|
294
|
+
return new MetadataError(`Entity ${meta.className} declares row level security which is not supported by the current driver. Row level security is only available with the PostgreSQL driver. To test a PostgreSQL app without a server, use the \`@mikro-orm/pglite\` driver instead of sqlite.`);
|
|
295
|
+
}
|
|
296
|
+
/** Thrown when row level security is declared on a non-root entity of an STI hierarchy. */
|
|
297
|
+
static rowLevelSecurityOnNonRootStiEntity(meta) {
|
|
298
|
+
return new MetadataError(`Entity ${meta.className} declares row level security, but it is part of a single table inheritance hierarchy. Declare policies on the root entity ${meta.root.className} instead, as the whole hierarchy shares a single table.`);
|
|
299
|
+
}
|
|
300
|
+
/** Thrown when two policies on the same entity are given the same explicit name. */
|
|
301
|
+
static duplicatePolicyName(meta, name) {
|
|
302
|
+
return new MetadataError(`Entity ${meta.className} declares multiple row level security policies named '${name}'. Policy names must be unique per table; rename one of them or omit the name to use an auto-generated one.`);
|
|
303
|
+
}
|
|
304
|
+
/** Thrown when a filter flagged with `rls` is declared on a driver that does not support row level security. */
|
|
305
|
+
static rlsFilterNotSupportedByDriver(meta, filterName) {
|
|
306
|
+
return new MetadataError(`Filter '${filterName}' on entity ${meta.className} is flagged with 'rls', which is only supported by the PostgreSQL driver. To test a PostgreSQL app without a server, use the \`@mikro-orm/pglite\` driver instead of sqlite.`);
|
|
307
|
+
}
|
|
308
|
+
/** Thrown when a filter flagged with `rls` is declared on a non-root entity of an STI hierarchy. */
|
|
309
|
+
static rlsFilterOnNonRootStiEntity(meta, filterName) {
|
|
310
|
+
return new MetadataError(`Filter '${filterName}' on entity ${meta.className} is flagged with 'rls', but the entity is part of a single table inheritance hierarchy. Declare the filter on the root entity ${meta.root.className} instead, as the whole hierarchy shares a single table and the policy would never be created otherwise.`);
|
|
311
|
+
}
|
|
312
|
+
/** Thrown when a global (config or EM registered) filter is flagged with `rls`; RLS filters must be entity scoped. */
|
|
313
|
+
static rlsFilterMustBeEntityScoped(filterName) {
|
|
314
|
+
return new MetadataError(`Filter '${filterName}' is a global filter and cannot be flagged with 'rls'. RLS filters must be declared on an entity via @Filter() so a policy can be attached to its table.`);
|
|
315
|
+
}
|
|
316
|
+
/** Thrown when an entity-scoped `rls` filter is registered at runtime via `em.addFilter()` instead of in metadata. */
|
|
317
|
+
static rlsFilterCannotBeRegisteredAtRuntime(filterName) {
|
|
318
|
+
return new MetadataError(`Filter '${filterName}' cannot be flagged with 'rls' when registered at runtime via 'em.addFilter()'. RLS filters must be declared in entity metadata via the @Filter() decorator (or the entity 'filters' option) so a policy can be attached to the table.`);
|
|
319
|
+
}
|
|
320
|
+
/** Thrown when a filter's custom `setting` is used with more than one argument. */
|
|
321
|
+
static rlsFilterMultiArgSetting(filterName, args) {
|
|
322
|
+
return new MetadataError(`Filter '${filterName}' sets a custom 'setting' but references multiple arguments (${args.join(', ')}). A custom 'setting' is only allowed for single-argument RLS filters; remove it to use the default 'mikro.${filterName}.<arg>' names.`);
|
|
323
|
+
}
|
|
324
|
+
/** Thrown when an `rls` filter's condition depends on runtime state and cannot be compiled to a static policy. */
|
|
325
|
+
static rlsFilterDependsOnRuntimeState(filterName) {
|
|
326
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because its condition depends on runtime state (it accesses 'em', 'type', 'options' or 'entityName', or resolves asynchronously). Declare an explicit policy instead.`);
|
|
327
|
+
}
|
|
328
|
+
/** Thrown when an `rls` filter compares against a column whose type has no automatic session-variable cast. */
|
|
329
|
+
static rlsFilterUncastableType(filterName, columnType) {
|
|
330
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because the column type '${columnType}' has no automatic session-variable cast. Declare an explicit policy instead.`);
|
|
331
|
+
}
|
|
332
|
+
/** Thrown when an `rls` filter references an argument outside of a direct comparison, which cannot be compiled. */
|
|
333
|
+
static rlsFilterUnsupportedCond(filterName) {
|
|
334
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because it references an argument outside of a direct comparison (only expressions like { prop: args.x } are supported). Declare an explicit policy instead.`);
|
|
335
|
+
}
|
|
336
|
+
/** Thrown when an `rls` filter compares against a column the schema generator does not manage. */
|
|
337
|
+
static rlsFilterUnmanagedColumn(filterName, column) {
|
|
338
|
+
return new MetadataError(`Filter '${filterName}' cannot be compiled to an RLS policy because column '${column}' is not part of the managed schema (e.g. the property is marked 'persist: false' or the column is excluded via 'skipColumns'). Declare an explicit policy instead.`);
|
|
339
|
+
}
|
|
253
340
|
static fromMessage(meta, prop, message) {
|
|
254
341
|
return new MetadataError(`${meta.className}.${prop.name} ${message}`);
|
|
255
342
|
}
|
package/exceptions.d.ts
CHANGED
|
@@ -102,3 +102,8 @@ export declare class TableNotFoundException extends DatabaseObjectNotFoundExcept
|
|
|
102
102
|
*/
|
|
103
103
|
export declare class UniqueConstraintViolationException extends ConstraintViolationException {
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Exception for a row-level security policy violation (failed `WITH CHECK`) detected in the driver.
|
|
107
|
+
*/
|
|
108
|
+
export declare class RowLevelSecurityViolationException extends ConstraintViolationException {
|
|
109
|
+
}
|
package/exceptions.js
CHANGED
|
@@ -115,3 +115,8 @@ export class TableNotFoundException extends DatabaseObjectNotFoundException {
|
|
|
115
115
|
*/
|
|
116
116
|
export class UniqueConstraintViolationException extends ConstraintViolationException {
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Exception for a row-level security policy violation (failed `WITH CHECK`) detected in the driver.
|
|
120
|
+
*/
|
|
121
|
+
export class RowLevelSecurityViolationException extends ConstraintViolationException {
|
|
122
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
5
|
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, IndexHints, } from './typings.js';
|
|
6
|
-
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, ExtractDefineEntityProperties, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaColumns, FormulaTable, SchemaTable, SchemaColumns, SchemaColumnRef, EntityDataPropValue, SimpleColumnMeta, Rel, Ref, LazyRef, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, RoutineReturns, RoutineBodyCallback, RoutineJsBody, RoutineIgnoreField, RoutineParamConfig, RoutineConfig, RoutineRuntimeType, RoutineArgs, RoutineReturn, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
6
|
+
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, ExtractDefineEntityProperties, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaColumns, FormulaTable, SchemaTable, SchemaColumns, SchemaColumnRef, EntityDataPropValue, SimpleColumnMeta, Rel, Ref, LazyRef, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, PolicyDef, PolicyCallback, SessionContext, RoutineReturns, RoutineBodyCallback, RoutineJsBody, RoutineIgnoreField, RoutineParamConfig, RoutineConfig, RoutineRuntimeType, RoutineArgs, RoutineReturn, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
7
7
|
export * from './enums.js';
|
|
8
8
|
export * from './errors.js';
|
|
9
9
|
export * from './exceptions.js';
|
|
@@ -117,6 +117,7 @@ export declare class MetadataDiscovery {
|
|
|
117
117
|
private createSchemaTable;
|
|
118
118
|
private initCheckConstraints;
|
|
119
119
|
private initTriggers;
|
|
120
|
+
private initPolicies;
|
|
120
121
|
private initGeneratedColumn;
|
|
121
122
|
private getDefaultVersionValue;
|
|
122
123
|
private inferDefaultValue;
|
|
@@ -125,6 +126,8 @@ export declare class MetadataDiscovery {
|
|
|
125
126
|
private initVersionProperty;
|
|
126
127
|
private initCustomType;
|
|
127
128
|
private initRelation;
|
|
129
|
+
/** Resolves the `through` option of a virtual to-one relation into a read-only formula property. */
|
|
130
|
+
private initThroughRelation;
|
|
128
131
|
private initColumnType;
|
|
129
132
|
private getMappedType;
|
|
130
133
|
private getPrefix;
|
|
@@ -165,10 +165,14 @@ export class MetadataDiscovery {
|
|
|
165
165
|
filtered.forEach(meta => this.initAutoincrement(meta)); // once again after we init custom types
|
|
166
166
|
filtered.forEach(meta => this.initCheckConstraints(meta));
|
|
167
167
|
filtered.forEach(meta => this.initTriggers(meta));
|
|
168
|
-
|
|
168
|
+
// filter names are load-bearing for RLS (policy and session variable names), backfill from the dictionary key
|
|
169
|
+
filtered.forEach(meta => Object.entries(meta.filters).forEach(([key, filter]) => (filter.name ??= key)));
|
|
170
|
+
filtered.forEach(meta => this.initPolicies(meta));
|
|
171
|
+
forEachProp((m, p) => {
|
|
169
172
|
this.initDefaultValue(p);
|
|
170
173
|
this.inferTypeFromDefault(p);
|
|
171
174
|
this.initRelation(p);
|
|
175
|
+
this.initThroughRelation(m, p);
|
|
172
176
|
this.initColumnType(p);
|
|
173
177
|
});
|
|
174
178
|
forEachProp((m, p) => this.initIndexes(m, p));
|
|
@@ -220,11 +224,9 @@ export class MetadataDiscovery {
|
|
|
220
224
|
};
|
|
221
225
|
const missing = [];
|
|
222
226
|
this.#discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const target = typeof
|
|
226
|
-
? pivotEntity()
|
|
227
|
-
: pivotEntity;
|
|
227
|
+
const indirect = (prop.kind === ReferenceKind.MANY_TO_MANY ? prop.pivotEntity : prop.through);
|
|
228
|
+
if (indirect) {
|
|
229
|
+
const target = typeof indirect === 'function' && !indirect.prototype ? indirect() : indirect;
|
|
228
230
|
if (!this.#discovered.find(m => m.className === Utils.className(target)) || !discoveredByIdentity(target)) {
|
|
229
231
|
missing.push(target);
|
|
230
232
|
}
|
|
@@ -970,6 +972,11 @@ export class MetadataDiscovery {
|
|
|
970
972
|
return primaryProp;
|
|
971
973
|
}
|
|
972
974
|
definePivotProperty(prop, name, type, inverse, owner, selfReferencing) {
|
|
975
|
+
let index = prop.index ?? this.#platform.indexForeignKeys();
|
|
976
|
+
if (owner && prop.index) {
|
|
977
|
+
// owner join columns are the leading prefix of the composite PK, so an explicit `index` only applies to them with `fixedOrder`; a custom index name always belongs to the inverse side
|
|
978
|
+
index = prop.fixedOrder ? true : this.#platform.indexForeignKeys();
|
|
979
|
+
}
|
|
973
980
|
const ret = {
|
|
974
981
|
name,
|
|
975
982
|
type: Utils.className(type),
|
|
@@ -978,7 +985,7 @@ export class MetadataDiscovery {
|
|
|
978
985
|
cascade: [Cascade.ALL],
|
|
979
986
|
fixedOrder: prop.fixedOrder,
|
|
980
987
|
fixedOrderColumn: prop.fixedOrderColumn,
|
|
981
|
-
index
|
|
988
|
+
index,
|
|
982
989
|
primary: !prop.fixedOrder,
|
|
983
990
|
autoincrement: false,
|
|
984
991
|
updateRule: prop.updateRule,
|
|
@@ -1071,6 +1078,12 @@ export class MetadataDiscovery {
|
|
|
1071
1078
|
meta.checks = Utils.unique([...base.checks, ...meta.checks], Utils.equals);
|
|
1072
1079
|
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers], Utils.equals);
|
|
1073
1080
|
}
|
|
1081
|
+
// Policies pass down only from inlined abstract bases; STI children share the root table
|
|
1082
|
+
// and TPT children own their tables, so both declare policies directly on the root/child.
|
|
1083
|
+
if (base.abstract && base.inheritanceType !== 'sti' && (meta.inheritanceType !== 'tpt' || !meta.tptParent)) {
|
|
1084
|
+
meta.policies = Utils.unique([...base.policies, ...meta.policies]);
|
|
1085
|
+
meta.rowLevelSecurity ??= base.rowLevelSecurity;
|
|
1086
|
+
}
|
|
1074
1087
|
const pks = Object.values(meta.properties)
|
|
1075
1088
|
.filter(p => p.primary)
|
|
1076
1089
|
.map(p => p.name);
|
|
@@ -1738,6 +1751,28 @@ export class MetadataDiscovery {
|
|
|
1738
1751
|
}
|
|
1739
1752
|
meta.hasTriggers = true;
|
|
1740
1753
|
}
|
|
1754
|
+
initPolicies(meta) {
|
|
1755
|
+
if (meta.policies.length === 0) {
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
const columns = meta.createSchemaColumnMappingObject();
|
|
1759
|
+
const table = this.createSchemaTable(meta);
|
|
1760
|
+
// resolve callbacks into a copy — the defs can be shared with siblings via an inlined abstract base,
|
|
1761
|
+
// and resolving in place would bake the first child's table into every other child's policy
|
|
1762
|
+
meta.policies = meta.policies.map(policy => {
|
|
1763
|
+
if (!(policy.using instanceof Function) && !(policy.check instanceof Function)) {
|
|
1764
|
+
return policy;
|
|
1765
|
+
}
|
|
1766
|
+
const resolved = { ...policy };
|
|
1767
|
+
if (resolved.using instanceof Function) {
|
|
1768
|
+
resolved.using = resolved.using(columns, table);
|
|
1769
|
+
}
|
|
1770
|
+
if (resolved.check instanceof Function) {
|
|
1771
|
+
resolved.check = resolved.check(columns, table);
|
|
1772
|
+
}
|
|
1773
|
+
return resolved;
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1741
1776
|
initGeneratedColumn(meta, prop) {
|
|
1742
1777
|
if (!prop.generated && prop.columnTypes) {
|
|
1743
1778
|
const match = /(.*) generated always as (.*)/i.exec(prop.columnTypes[0]);
|
|
@@ -2042,6 +2077,58 @@ export class MetadataDiscovery {
|
|
|
2042
2077
|
}
|
|
2043
2078
|
}
|
|
2044
2079
|
}
|
|
2080
|
+
/** Resolves the `through` option of a virtual to-one relation into a read-only formula property. */
|
|
2081
|
+
initThroughRelation(meta, prop) {
|
|
2082
|
+
// already resolved, or not a through relation at all
|
|
2083
|
+
if (prop.through?.ownerProperty || !prop.through) {
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
if (![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
2087
|
+
throw MetadataError.throughRelationInvalidKind(meta, prop);
|
|
2088
|
+
}
|
|
2089
|
+
const targetMeta = prop.targetMeta;
|
|
2090
|
+
// the subquery selects a single column
|
|
2091
|
+
if (targetMeta.compositePK) {
|
|
2092
|
+
throw MetadataError.throughRelationCompositeTarget(meta, prop);
|
|
2093
|
+
}
|
|
2094
|
+
const through = prop.through;
|
|
2095
|
+
const throughMeta = this.#metadata.get(!through.prototype ? through() : through);
|
|
2096
|
+
// a property is considered to point at an entity when it targets it or one of its parents
|
|
2097
|
+
const pointsTo = (p, m) => {
|
|
2098
|
+
const candidate = this.#metadata.find(p.target);
|
|
2099
|
+
/* v8 ignore next 3 */
|
|
2100
|
+
if (!candidate) {
|
|
2101
|
+
return false;
|
|
2102
|
+
}
|
|
2103
|
+
return candidate.class === m.class || m.class.prototype instanceof candidate.class;
|
|
2104
|
+
};
|
|
2105
|
+
const fks = Object.values(throughMeta.properties).filter(p => p.kind === ReferenceKind.MANY_TO_ONE);
|
|
2106
|
+
const ownerProp = fks.find(p => pointsTo(p, meta));
|
|
2107
|
+
if (!ownerProp) {
|
|
2108
|
+
throw MetadataError.throughRelationMissingProperty(meta, prop, throughMeta, 'owner');
|
|
2109
|
+
}
|
|
2110
|
+
let targetProperty;
|
|
2111
|
+
const selectsTarget = throughMeta.class === targetMeta.class || throughMeta.class.prototype instanceof targetMeta.class;
|
|
2112
|
+
if (!selectsTarget) {
|
|
2113
|
+
const targetProp = fks.find(p => p !== ownerProp && pointsTo(p, targetMeta));
|
|
2114
|
+
if (!targetProp) {
|
|
2115
|
+
throw MetadataError.throughRelationMissingProperty(meta, prop, throughMeta, 'target');
|
|
2116
|
+
}
|
|
2117
|
+
targetProperty = targetProp.name;
|
|
2118
|
+
}
|
|
2119
|
+
prop.through = {
|
|
2120
|
+
entity: throughMeta.class,
|
|
2121
|
+
where: prop.where,
|
|
2122
|
+
orderBy: prop.orderBy ? Utils.asArray(prop.orderBy) : undefined,
|
|
2123
|
+
ownerProperty: ownerProp.name,
|
|
2124
|
+
targetProperty,
|
|
2125
|
+
};
|
|
2126
|
+
// the condition and ordering apply to the `through` entity, not to the target, so they must not leak into the target joins
|
|
2127
|
+
delete prop.where;
|
|
2128
|
+
delete prop.orderBy;
|
|
2129
|
+
prop.persist = false;
|
|
2130
|
+
prop.formula = columns => this.#platform.getThroughRelationFormula(prop, columns);
|
|
2131
|
+
}
|
|
2045
2132
|
initColumnType(prop) {
|
|
2046
2133
|
this.initUnsigned(prop);
|
|
2047
2134
|
// Get the target properties for FK relations - use targetKey property if specified, otherwise PKs
|
package/metadata/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef } from '../typings.js';
|
|
1
|
+
import type { AnyEntity, Constructor, EntityName, AnyString, CheckCallback, GeneratedColumnCallback, FormulaCallback, FilterQuery, Dictionary, AutoPath, EntityClass, IndexCallback, ObjectQuery, Raw, SchemaColumns, TriggerDef, PolicyDef } from '../typings.js';
|
|
2
2
|
import type { Cascade, LoadStrategy, DeferMode, QueryOrderMap, EmbeddedPrefixMode } from '../enums.js';
|
|
3
3
|
import type { Type, types } from '../types/index.js';
|
|
4
4
|
import type { EntityManager } from '../EntityManager.js';
|
|
@@ -104,6 +104,10 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
|
|
|
104
104
|
hasTriggers?: boolean;
|
|
105
105
|
/** Database triggers to create for this entity's table. (SQL drivers only) */
|
|
106
106
|
triggers?: TriggerDef<E>[];
|
|
107
|
+
/** PostgreSQL row level security policies for this entity's table. Declaring policies implicitly enables RLS. */
|
|
108
|
+
policies?: PolicyDef<E>[];
|
|
109
|
+
/** Enables PostgreSQL row level security on this entity's table. `'force'` also enforces it for the table owner. Set to `false` to keep declared policies staged while leaving RLS disabled. */
|
|
110
|
+
rowLevelSecurity?: boolean | 'force';
|
|
107
111
|
/**
|
|
108
112
|
* PostgreSQL partitioning definition for this table.
|
|
109
113
|
*
|
|
@@ -427,9 +431,15 @@ interface PolymorphicOptions {
|
|
|
427
431
|
*/
|
|
428
432
|
discriminatorMap?: Dictionary<string>;
|
|
429
433
|
}
|
|
430
|
-
export interface ManyToOneOptions<Owner, Target> extends ReferenceOptions<Owner, Target>, PolymorphicOptions {
|
|
434
|
+
export interface ManyToOneOptions<Owner, Target, Through = Target> extends ReferenceOptions<Owner, Target>, PolymorphicOptions {
|
|
431
435
|
/** Point to the inverse side property name. */
|
|
432
436
|
inversedBy?: (string & keyof Target) | ((e: Target) => any);
|
|
437
|
+
/** Resolve this read-only relation via a subquery on another entity: a pivot entity with FKs to both sides, or the target itself to pick a single item out of a to-many relation (see {@doclink relationships#to-one-relations-through-another-entity | To-one relations through another entity}). */
|
|
438
|
+
through?: () => EntityName<Through>;
|
|
439
|
+
/** Condition applied on the `through` entity. */
|
|
440
|
+
where?: FilterQuery<Through>;
|
|
441
|
+
/** Ordering applied on the `through` entity, the first matching row is used. */
|
|
442
|
+
orderBy?: QueryOrderMap<Through> | QueryOrderMap<Through>[];
|
|
433
443
|
/** Wrap the entity in {@apilink Reference} wrapper. */
|
|
434
444
|
ref?: boolean;
|
|
435
445
|
/** Use this relation as a primary key. */
|
|
@@ -481,9 +491,15 @@ export interface OneToManyOptions<Owner, Target> extends ReferenceOptions<Owner,
|
|
|
481
491
|
/** Point to the owning side property name. */
|
|
482
492
|
mappedBy: (string & keyof Target) | ((e: Target) => any);
|
|
483
493
|
}
|
|
484
|
-
export interface OneToOneOptions<Owner, Target> extends Partial<Omit<OneToManyOptions<Owner, Target>, 'orderBy'>>, PolymorphicOptions {
|
|
494
|
+
export interface OneToOneOptions<Owner, Target, Through = Target> extends Partial<Omit<OneToManyOptions<Owner, Target>, 'orderBy' | 'where'>>, PolymorphicOptions {
|
|
485
495
|
/** Set this side as owning. Owning side is where the foreign key is defined. This option is not required if you use `inversedBy` or `mappedBy` to distinguish owning and inverse side. */
|
|
486
496
|
owner?: boolean;
|
|
497
|
+
/** Resolve this read-only relation via a subquery on another entity: a pivot entity with FKs to both sides, or the target itself to pick a single item out of a to-many relation (see {@doclink relationships#to-one-relations-through-another-entity | To-one relations through another entity}). */
|
|
498
|
+
through?: () => EntityName<Through>;
|
|
499
|
+
/** Condition for {@doclink collections#declarative-partial-loading | Declarative partial loading}, or the condition applied on the `through` entity. */
|
|
500
|
+
where?: FilterQuery<Through>;
|
|
501
|
+
/** Ordering applied on the `through` entity, the first matching row is used. */
|
|
502
|
+
orderBy?: QueryOrderMap<Through> | QueryOrderMap<Through>[];
|
|
487
503
|
/** Point to the inverse side property name. */
|
|
488
504
|
inversedBy?: (string & keyof Target) | ((e: Target) => any);
|
|
489
505
|
/** Wrap the entity in {@apilink Reference} wrapper. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.1.16-dev.
|
|
3
|
+
"version": "7.1.16-dev.12",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EntityRepository } from '../entity/EntityRepository.js';
|
|
2
2
|
import { type NamingStrategy } from '../naming-strategy/NamingStrategy.js';
|
|
3
|
-
import type { Constructor, EntityMetadata, EntityProperty, IPrimaryKey, ISchemaGenerator, PopulateOptions, Primary, SimpleColumnMeta, FilterQuery, EntityValue, EntityKey } from '../typings.js';
|
|
3
|
+
import type { Constructor, EntityMetadata, EntityProperty, IPrimaryKey, ISchemaGenerator, PopulateOptions, Primary, SimpleColumnMeta, FilterQuery, EntityValue, EntityKey, FormulaColumns } from '../typings.js';
|
|
4
4
|
import { ExceptionConverter } from './ExceptionConverter.js';
|
|
5
5
|
import type { EntityManager } from '../EntityManager.js';
|
|
6
6
|
import type { Configuration } from '../utils/Configuration.js';
|
|
@@ -230,6 +230,10 @@ export declare abstract class Platform {
|
|
|
230
230
|
convertsJsonAutomatically(): boolean;
|
|
231
231
|
/** Whether date values inside JSON documents keep their native type (e.g. BSON dates), instead of being serialized to ISO strings. */
|
|
232
232
|
preservesDatesInsideJson(): boolean;
|
|
233
|
+
/** Whether `nulls first`/`nulls last` can be requested in an `orderBy`. */
|
|
234
|
+
supportsNullsOrdering(): boolean;
|
|
235
|
+
/** Where nulls land when an `orderBy` requests no explicit placement: lowest (`asc` puts them first) or highest. */
|
|
236
|
+
sortsNullsLowest(): boolean;
|
|
233
237
|
/** Converts a JS value to its JSON database representation (typically JSON.stringify). */
|
|
234
238
|
convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
|
|
235
239
|
/** Converts a database JSON value to its JS representation. */
|
|
@@ -275,6 +279,11 @@ export declare abstract class Platform {
|
|
|
275
279
|
formatQuery(sql: string, params: readonly any[]): string;
|
|
276
280
|
/** Deep-clones embeddable data and tags it for JSON serialization. */
|
|
277
281
|
cloneEmbeddable<T>(data: T): T;
|
|
282
|
+
/**
|
|
283
|
+
* Builds the correlated subquery used as the formula of a virtual to-one relation defined via `through`.
|
|
284
|
+
* @internal
|
|
285
|
+
*/
|
|
286
|
+
getThroughRelationFormula(prop: EntityProperty, columns: FormulaColumns<any>): string;
|
|
278
287
|
/** Initializes the platform with the ORM configuration. */
|
|
279
288
|
setConfig(config: Configuration): void;
|
|
280
289
|
/** Returns the current ORM configuration. */
|
|
@@ -316,6 +325,15 @@ export declare abstract class Platform {
|
|
|
316
325
|
supportsDownMigrations(): boolean;
|
|
317
326
|
/** Whether the platform supports deferred unique constraints. */
|
|
318
327
|
supportsDeferredUniqueConstraints(): boolean;
|
|
328
|
+
/** Whether the platform supports row level security (PostgreSQL). */
|
|
329
|
+
supportsRowLevelSecurity(): boolean;
|
|
330
|
+
/** Whether the driver can apply the session context on every pooled connection acquire (`sessionContext: 'connection'`). */
|
|
331
|
+
supportsConnectionSessionContext(): boolean;
|
|
332
|
+
/**
|
|
333
|
+
* SQL cast suffix (e.g. `'::uuid'`, or `''` when none is needed) applied when an RLS filter reads a session
|
|
334
|
+
* variable via `current_setting()` as the given column type, or `null` if the type has no automatic cast.
|
|
335
|
+
*/
|
|
336
|
+
getCurrentSettingCast(mappedType: Type<unknown>): string | null;
|
|
319
337
|
/** Platform-specific validation of entity metadata. */
|
|
320
338
|
validateMetadata(meta: EntityMetadata): void;
|
|
321
339
|
/**
|
package/platforms/Platform.js
CHANGED
|
@@ -471,6 +471,14 @@ export class Platform {
|
|
|
471
471
|
preservesDatesInsideJson() {
|
|
472
472
|
return false;
|
|
473
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
|
+
}
|
|
474
482
|
/** Converts a JS value to its JSON database representation (typically JSON.stringify). */
|
|
475
483
|
convertJsonToDatabaseValue(value, context) {
|
|
476
484
|
return JSON.stringify(value);
|
|
@@ -616,6 +624,14 @@ export class Platform {
|
|
|
616
624
|
Object.defineProperty(copy, JsonProperty, { enumerable: false, value: true });
|
|
617
625
|
return copy;
|
|
618
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
|
+
}
|
|
619
635
|
/** Initializes the platform with the ORM configuration. */
|
|
620
636
|
setConfig(config) {
|
|
621
637
|
this.config = config;
|
|
@@ -722,11 +738,51 @@ export class Platform {
|
|
|
722
738
|
supportsDeferredUniqueConstraints() {
|
|
723
739
|
return true;
|
|
724
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
|
+
}
|
|
725
756
|
/** Platform-specific validation of entity metadata. */
|
|
726
757
|
validateMetadata(meta) {
|
|
727
758
|
if (meta.partitionBy && !this.supportsPartitionedTables()) {
|
|
728
759
|
throw new MetadataError(`Entity ${meta.className} uses partitionBy, but ${this.constructor.name} does not support partitioned tables`);
|
|
729
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
|
+
}
|
|
730
786
|
}
|
|
731
787
|
/**
|
|
732
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
|
}
|