@mikro-orm/core
Advanced tools
@@ -67,2 +67,8 @@ import { type CountOptions, type DeleteOptions, type DriverMethodOptions, EntityManagerType, type FindOneOptions, type FindOptions, type IDatabaseDriver, type LockOptions, type NativeInsertUpdateManyOptions, type NativeInsertUpdateOptions, type OrderDefinition, type StreamOptions } from './IDatabaseDriver.js'; | ||
| }; | ||
| /** | ||
| * Restores the JS value of a single cursor offset: ISO strings become `Date` instances based on the | ||
| * property type (never based on the string shape alone), and custom types are restored via | ||
| * `convertToJSValue`. Values compared against a JSON document keep their serialized form instead. | ||
| */ | ||
| private mapCursorOffset; | ||
| protected createCursorCondition<T extends object>(definition: (readonly [keyof T & string, QueryOrder])[], offsets: Dictionary[], inverse: boolean, meta: EntityMetadata<T>): FilterQuery<T>; | ||
@@ -69,0 +75,0 @@ /** @internal */ |
@@ -10,4 +10,7 @@ import { EntityManagerType, } from './IDatabaseDriver.js'; | ||
| import { helper } from '../entity/wrap.js'; | ||
| import { Reference } from '../entity/Reference.js'; | ||
| import { PolymorphicRef } from '../entity/PolymorphicRef.js'; | ||
| import { JsonType } from '../types/JsonType.js'; | ||
| import { DateTimeType } from '../types/DateTimeType.js'; | ||
| import { QueryHelper } from '../utils/QueryHelper.js'; | ||
| import { MikroORM } from '../MikroORM.js'; | ||
@@ -145,9 +148,20 @@ /** Abstract base class for all database drivers, implementing common driver logic. */ | ||
| const createCursor = (val, key, inverse = false) => { | ||
| let def = isCursor(val, key) ? val[key] : val; | ||
| if (Utils.isPlainObject(def)) { | ||
| def = Cursor.for(meta, def, orderBy); | ||
| const def = Reference.unwrapReference((isCursor(val, key) ? val[key] : val)); | ||
| let offsets; | ||
| // entity (and reference) instances are supported as cursors too, their properties are read the same way | ||
| if (Utils.isPlainObject(def) || Utils.isEntity(def)) { | ||
| // POJO values are already JS values, extract them ordered per the definition, | ||
| // without the JSON round trip `Cursor.for` + `Cursor.decode` would impose | ||
| offsets = definition.map(([key]) => { | ||
| if (def[key] === undefined) { | ||
| throw CursorError.missingValue(meta.className, key); | ||
| } | ||
| return def[key]; | ||
| }); | ||
| } | ||
| /* v8 ignore next */ | ||
| const offsets = def ? Cursor.decode(def) : []; | ||
| if (definition.length === offsets.length) { | ||
| else { | ||
| /* v8 ignore next */ | ||
| offsets = def ? Cursor.decode(def) : []; | ||
| } | ||
| if (definition.length > 0 && definition.length === offsets.length) { | ||
| return this.createCursorCondition(definition, offsets, inverse, meta); | ||
@@ -179,9 +193,51 @@ } | ||
| }; | ||
| // the cursor condition is created at the driver level, after the EM already converted custom types | ||
| // in the user `where`, so we need to run the same conversion over it explicitly | ||
| const where = QueryHelper.processWhere({ | ||
| where: ($and.length > 1 ? { $and } : { ...$and[0] }), | ||
| entityName: meta.class, | ||
| metadata: this.metadata, | ||
| platform: this.platform, | ||
| convertCustomTypes: options.convertCustomTypes, | ||
| }); | ||
| return { | ||
| orderBy: definition.map(([prop, direction]) => createOrderBy(prop, direction)), | ||
| where: ($and.length > 1 ? { $and } : { ...$and[0] }), | ||
| where, | ||
| }; | ||
| } | ||
| /** | ||
| * Restores the JS value of a single cursor offset: ISO strings become `Date` instances based on the | ||
| * property type (never based on the string shape alone), and custom types are restored via | ||
| * `convertToJSValue`. Values compared against a JSON document keep their serialized form instead. | ||
| */ | ||
| mapCursorOffset(prop, value, insideJson) { | ||
| if (Utils.isScalarReference(value)) { | ||
| value = value.unwrap(); | ||
| } | ||
| // scalar direction on a relation orders by its primary key | ||
| if (Utils.isEntity(value, true)) { | ||
| value = helper(value).getPrimaryKey(); | ||
| } | ||
| if (value == null) { | ||
| return value; | ||
| } | ||
| if (insideJson) { | ||
| // compared against the JSON document, which holds the serialized form | ||
| if (value instanceof Date) { | ||
| return value.toISOString(); | ||
| } | ||
| // restore the JS value from the serialized form, `processWhere` then converts it to | ||
| // the database form, which is what the JSON document holds for custom typed props | ||
| return prop?.customType ? prop.customType.convertToJSValue(value, this.platform) : value; | ||
| } | ||
| if (typeof value === 'string' && | ||
| (prop?.runtimeType === 'Date' || | ||
| (prop?.customType && this.platform.getMappedType(prop.columnTypes?.[0] ?? '') instanceof DateTimeType))) { | ||
| value = new Date(value); | ||
| } | ||
| return prop?.customType ? prop.customType.convertToJSValue(value, this.platform) : value; | ||
| } | ||
| createCursorCondition(definition, offsets, inverse, meta) { | ||
| const createCondition = (prop, direction, offset, eq = false, path = prop) => { | ||
| const createCondition = (prop, direction, offset, eq = false, path = prop, properties = meta.properties, insideJson = false) => { | ||
| const propMeta = properties[prop]; | ||
| if (Utils.isPlainObject(direction)) { | ||
@@ -191,4 +247,9 @@ if (offset === undefined) { | ||
| } | ||
| // POJO cursors can carry entity, reference or embeddable class instances, read their properties directly | ||
| offset = Reference.unwrapReference(offset); | ||
| const childProps = propMeta?.kind === ReferenceKind.EMBEDDED ? propMeta.embeddedProps : propMeta?.targetMeta?.properties; | ||
| insideJson ||= | ||
| (propMeta?.kind === ReferenceKind.EMBEDDED && !!propMeta.object) || propMeta?.customType instanceof JsonType; | ||
| const value = Utils.keys(direction).reduce((o, key) => { | ||
| Object.assign(o, createCondition(key, direction[key], offset?.[key], eq, `${path}.${key}`)); | ||
| Object.assign(o, createCondition(key, direction[key], offset?.[key], eq, `${path}.${key}`, childProps ?? {}, insideJson)); | ||
| return o; | ||
@@ -216,2 +277,3 @@ }, {}); | ||
| } | ||
| offset = this.mapCursorOffset(propMeta, offset, insideJson); | ||
| // Handle null offset (intentional null cursor value) | ||
@@ -218,0 +280,0 @@ if (offset === null) { |
+1
-1
| { | ||
| "name": "@mikro-orm/core", | ||
| "version": "7.1.10-dev.8", | ||
| "version": "7.1.10-dev.9", | ||
| "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,0 +5,0 @@ "keywords": [ |
+1
-6
@@ -153,8 +153,3 @@ import { Utils } from './Utils.js'; | ||
| static decode(value) { | ||
| return JSON.parse(Buffer.from(value, 'base64url').toString('utf8')).map((value) => { | ||
| if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}/.exec(value)) { | ||
| return new Date(value); | ||
| } | ||
| return value; | ||
| }); | ||
| return JSON.parse(Buffer.from(value, 'base64url').toString('utf8')); | ||
| } | ||
@@ -161,0 +156,0 @@ static getDefinition(meta, orderBy) { |
+1
-1
@@ -156,3 +156,3 @@ import { clone } from './clone.js'; | ||
| static PK_SEPARATOR = '~~~'; | ||
| static #ORM_VERSION = '7.1.10-dev.8'; | ||
| static #ORM_VERSION = '7.1.10-dev.9'; | ||
| /** | ||
@@ -159,0 +159,0 @@ * Checks if the argument is instance of `Object`. Returns false for arrays. |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
1580376
0.23%33748
0.19%