@mikro-orm/core 6.5.8-dev.8 → 6.5.8
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.js +8 -39
- package/entity/defineEntity.d.ts +43 -6
- package/entity/defineEntity.js +0 -2
- package/index.mjs +1 -0
- package/package.json +2 -2
- package/platforms/Platform.d.ts +1 -1
- package/platforms/Platform.js +2 -2
- package/unit-of-work/UnitOfWork.js +4 -0
- package/utils/EntityComparator.js +1 -4
- package/utils/upsert-utils.d.ts +6 -1
- package/utils/upsert-utils.js +43 -0
package/EntityManager.js
CHANGED
|
@@ -678,24 +678,7 @@ class EntityManager {
|
|
|
678
678
|
}
|
|
679
679
|
}
|
|
680
680
|
}
|
|
681
|
-
|
|
682
|
-
const propIndex = !utils_1.Utils.isRawSql(unique) && unique.findIndex(p => data[p] != null);
|
|
683
|
-
if (options.onConflictFields || where == null) {
|
|
684
|
-
if (propIndex !== false && propIndex >= 0) {
|
|
685
|
-
where = { [unique[propIndex]]: data[unique[propIndex]] };
|
|
686
|
-
}
|
|
687
|
-
else if (meta.uniques.length > 0) {
|
|
688
|
-
for (const u of meta.uniques) {
|
|
689
|
-
if (utils_1.Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
690
|
-
where = utils_1.Utils.asArray(u.properties).reduce((o, key) => {
|
|
691
|
-
o[key] = data[key];
|
|
692
|
-
return o;
|
|
693
|
-
}, {});
|
|
694
|
-
break;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
681
|
+
where = (0, utils_1.getWhereCondition)(meta, options.onConflictFields, data, where).where;
|
|
699
682
|
data = utils_1.QueryHelper.processObjectParams(data);
|
|
700
683
|
em.validator.validateParams(data, 'insert data');
|
|
701
684
|
if (em.eventManager.hasListeners(enums_1.EventType.beforeUpsert, meta)) {
|
|
@@ -830,31 +813,17 @@ class EntityManager {
|
|
|
830
813
|
}
|
|
831
814
|
}
|
|
832
815
|
}
|
|
833
|
-
const unique = meta.props.filter(p => p.unique).map(p => p.name);
|
|
834
|
-
propIndex = unique.findIndex(p =>
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
where = { [unique[propIndex]]: row[unique[propIndex]] };
|
|
838
|
-
}
|
|
839
|
-
else if (meta.uniques.length > 0) {
|
|
840
|
-
for (const u of meta.uniques) {
|
|
841
|
-
if (utils_1.Utils.asArray(u.properties).every(p => row[p] != null)) {
|
|
842
|
-
where = utils_1.Utils.asArray(u.properties).reduce((o, key) => {
|
|
843
|
-
o[key] = row[key];
|
|
844
|
-
return o;
|
|
845
|
-
}, {});
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
row = utils_1.QueryHelper.processObjectParams(row);
|
|
816
|
+
const unique = options.onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
817
|
+
propIndex = !utils_1.Utils.isRawSql(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
818
|
+
const tmp = (0, utils_1.getWhereCondition)(meta, options.onConflictFields, row, where);
|
|
819
|
+
propIndex = tmp.propIndex;
|
|
852
820
|
where = utils_1.QueryHelper.processWhere({
|
|
853
|
-
where,
|
|
821
|
+
where: tmp.where,
|
|
854
822
|
entityName,
|
|
855
823
|
metadata: this.metadata,
|
|
856
824
|
platform: this.getPlatform(),
|
|
857
825
|
});
|
|
826
|
+
row = utils_1.QueryHelper.processObjectParams(row);
|
|
858
827
|
em.validator.validateParams(row, 'insert data');
|
|
859
828
|
allData.push(row);
|
|
860
829
|
allWhere.push(where);
|
|
@@ -896,7 +865,7 @@ class EntityManager {
|
|
|
896
865
|
const reloadFields = returning.length > 0 && !(this.getPlatform().usesReturningStatement() && res.rows?.length);
|
|
897
866
|
if (options.onConflictAction === 'ignore' || (!res.rows?.length && loadPK.size > 0) || reloadFields) {
|
|
898
867
|
const unique = meta.hydrateProps.filter(p => !p.lazy).map(p => p.name);
|
|
899
|
-
const add = new Set(propIndex >= 0 ? [unique[propIndex]] : []);
|
|
868
|
+
const add = new Set(propIndex !== false && propIndex >= 0 ? [unique[propIndex]] : []);
|
|
900
869
|
for (const cond of loadPK.values()) {
|
|
901
870
|
utils_1.Utils.keys(cond).forEach(key => add.add(key));
|
|
902
871
|
}
|
package/entity/defineEntity.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { ManyToOneOptions } from '../decorators/ManyToOne';
|
|
|
6
6
|
import type { OneToManyOptions } from '../decorators/OneToMany';
|
|
7
7
|
import type { OneToOneOptions } from '../decorators/OneToOne';
|
|
8
8
|
import type { ManyToManyOptions } from '../decorators/ManyToMany';
|
|
9
|
-
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt } from '../typings';
|
|
9
|
+
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt, Primary, EntityClass } from '../typings';
|
|
10
10
|
import type { Reference, ScalarReference } from './Reference';
|
|
11
11
|
import type { SerializeOptions } from '../serialization/EntitySerializer';
|
|
12
12
|
import type { Cascade, DeferMode, LoadStrategy, QueryOrderMap } from '../enums';
|
|
@@ -390,7 +390,11 @@ export declare class ManyToOneOptionsBuilder<TargetValue extends object> extends
|
|
|
390
390
|
/** Point to the inverse side property name. */
|
|
391
391
|
inversedBy(inversedBy: (string & keyof TargetValue) | ((e: TargetValue) => any)): this;
|
|
392
392
|
/** Map this relation to the primary key value instead of an entity. */
|
|
393
|
-
mapToPk(mapToPk?:
|
|
393
|
+
mapToPk<T extends boolean = true>(mapToPk?: T): this & {
|
|
394
|
+
'~options': {
|
|
395
|
+
mapToPk: T;
|
|
396
|
+
};
|
|
397
|
+
};
|
|
394
398
|
/** Override the default database column name on the owning side (see {@doclink naming-strategy | Naming Strategy}). This option is only for simple properties represented by a single column. */
|
|
395
399
|
joinColumn(joinColumn: string): this;
|
|
396
400
|
/** Override the default database column name on the owning side (see {@doclink naming-strategy | Naming Strategy}). This option is suitable for composite keys, where one property is represented by multiple columns. */
|
|
@@ -458,7 +462,11 @@ export declare class OneToOneOptionsBuilder<TargetValue extends object> extends
|
|
|
458
462
|
/** Point to the inverse side property name. */
|
|
459
463
|
inversedBy(inversedBy: (string & keyof TargetValue) | ((e: TargetValue) => any)): this;
|
|
460
464
|
/** Map this relation to the primary key value instead of an entity. */
|
|
461
|
-
mapToPk(mapToPk?:
|
|
465
|
+
mapToPk<T extends boolean = true>(mapToPk?: T): this & {
|
|
466
|
+
'~options': {
|
|
467
|
+
mapToPk: T;
|
|
468
|
+
};
|
|
469
|
+
};
|
|
462
470
|
/** When a part of a composite column is shared in other properties, use this option to specify what columns are considered as owned by this property. This is useful when your composite property is nullable, but parts of it are not. */
|
|
463
471
|
ownColumns(...ownColumns: string[]): this;
|
|
464
472
|
/** What to do when the target entity gets deleted. */
|
|
@@ -506,6 +514,11 @@ export declare function defineEntity<Properties extends Record<string, any>, con
|
|
|
506
514
|
properties: Properties | ((properties: typeof propertyBuilders) => Properties);
|
|
507
515
|
primaryKeys?: PK & InferPrimaryKey<Properties>[];
|
|
508
516
|
}): EntitySchema<InferEntityFromProperties<Properties, PK>, never>;
|
|
517
|
+
export declare function defineEntity<Entity = any, Base = never>(meta: Omit<Partial<EntityMetadata<Entity>>, 'properties'> & {
|
|
518
|
+
class: EntityClass<Entity>;
|
|
519
|
+
extends?: string | EntitySchema<Base>;
|
|
520
|
+
properties: Record<string, any> | ((properties: typeof propertyBuilders) => Record<string, any>);
|
|
521
|
+
}): EntitySchema<Entity, Base>;
|
|
509
522
|
export declare namespace defineEntity {
|
|
510
523
|
var properties: {
|
|
511
524
|
bigint: <Mode extends "bigint" | "number" | "string" = "bigint">(mode?: Mode) => PropertyOptionsBuilder<(Mode extends "bigint" ? bigint : Mode extends "number" ? number : string) & {}>;
|
|
@@ -577,27 +590,38 @@ type InferBuilderValue<Builder> = Builder extends {
|
|
|
577
590
|
'~type'?: {
|
|
578
591
|
value: infer Value;
|
|
579
592
|
};
|
|
580
|
-
} ? MaybeHidden<MaybeOpt<
|
|
593
|
+
} ? MaybeHidden<MaybeOpt<MaybeScalarRef<MaybeNullable<MaybeRelationRef<MaybeMapToPk<MaybeArray<Value, Builder>, Builder>, Builder>, Builder>, Builder>, Builder>, Builder> : never;
|
|
581
594
|
type MaybeArray<Value, Builder> = Builder extends {
|
|
582
595
|
'~options': {
|
|
583
596
|
array: true;
|
|
584
597
|
};
|
|
585
598
|
} ? Value[] : Value;
|
|
599
|
+
type MaybeMapToPk<Value, Builder> = Builder extends {
|
|
600
|
+
'~options': {
|
|
601
|
+
mapToPk: true;
|
|
602
|
+
};
|
|
603
|
+
} ? Primary<Value> : Value;
|
|
586
604
|
type MaybeNullable<Value, Builder> = Builder extends {
|
|
587
605
|
'~options': {
|
|
588
606
|
nullable: true;
|
|
589
607
|
};
|
|
590
608
|
} ? Value | null | undefined : Value;
|
|
591
|
-
type
|
|
609
|
+
type MaybeRelationRef<Value, Builder> = Builder extends {
|
|
610
|
+
'~options': {
|
|
611
|
+
mapToPk: true;
|
|
612
|
+
};
|
|
613
|
+
} ? Value : Builder extends {
|
|
592
614
|
'~options': {
|
|
593
615
|
ref: false;
|
|
594
616
|
};
|
|
595
617
|
} ? Value : Builder extends {
|
|
596
618
|
'~options': {
|
|
619
|
+
ref: true;
|
|
597
620
|
kind: '1:1';
|
|
598
621
|
};
|
|
599
622
|
} ? Value extends object ? Reference<Value> : never : Builder extends {
|
|
600
623
|
'~options': {
|
|
624
|
+
ref: true;
|
|
601
625
|
kind: 'm:1';
|
|
602
626
|
};
|
|
603
627
|
} ? Value extends object ? Reference<Value> : never : Builder extends {
|
|
@@ -608,12 +632,25 @@ type MaybeRef<Value, Builder> = Builder extends {
|
|
|
608
632
|
'~options': {
|
|
609
633
|
kind: 'm:n';
|
|
610
634
|
};
|
|
611
|
-
} ? Value extends object ? Collection<Value> : never :
|
|
635
|
+
} ? Value extends object ? Collection<Value> : never : Value;
|
|
636
|
+
type MaybeScalarRef<Value, Builder> = Builder extends {
|
|
637
|
+
'~options': {
|
|
638
|
+
ref: false;
|
|
639
|
+
};
|
|
640
|
+
} ? Value : Builder extends {
|
|
641
|
+
'~options': {
|
|
642
|
+
kind: '1:1' | 'm:1' | '1:m' | 'm:n';
|
|
643
|
+
};
|
|
644
|
+
} ? Value : Builder extends {
|
|
612
645
|
'~options': {
|
|
613
646
|
ref: true;
|
|
614
647
|
};
|
|
615
648
|
} ? ScalarReference<Value> : Value;
|
|
616
649
|
type MaybeOpt<Value, Builder> = Builder extends {
|
|
650
|
+
'~options': {
|
|
651
|
+
mapToPk: true;
|
|
652
|
+
};
|
|
653
|
+
} ? Value extends Opt<infer OriginalValue> ? OriginalValue : Value : Builder extends {
|
|
617
654
|
'~options': {
|
|
618
655
|
autoincrement: true;
|
|
619
656
|
};
|
package/entity/defineEntity.js
CHANGED
|
@@ -645,7 +645,6 @@ const propertyBuilders = {
|
|
|
645
645
|
manyToOne: (target) => new ManyToOneOptionsBuilder({
|
|
646
646
|
entity: () => target,
|
|
647
647
|
kind: 'm:1',
|
|
648
|
-
ref: true,
|
|
649
648
|
}),
|
|
650
649
|
oneToMany: (target) => new OneToManyOptionsBuilderOnlyMappedBy({
|
|
651
650
|
entity: () => target,
|
|
@@ -654,7 +653,6 @@ const propertyBuilders = {
|
|
|
654
653
|
oneToOne: (target) => new OneToOneOptionsBuilder({
|
|
655
654
|
entity: () => target,
|
|
656
655
|
kind: '1:1',
|
|
657
|
-
ref: true,
|
|
658
656
|
}),
|
|
659
657
|
};
|
|
660
658
|
function getBuilderOptions(builder) {
|
package/index.mjs
CHANGED
|
@@ -204,6 +204,7 @@ export const expandDotPaths = mod.expandDotPaths;
|
|
|
204
204
|
export const getLoadingStrategy = mod.getLoadingStrategy;
|
|
205
205
|
export const getOnConflictFields = mod.getOnConflictFields;
|
|
206
206
|
export const getOnConflictReturningFields = mod.getOnConflictReturningFields;
|
|
207
|
+
export const getWhereCondition = mod.getWhereCondition;
|
|
207
208
|
export const helper = mod.helper;
|
|
208
209
|
export const parseJsonSafe = mod.parseJsonSafe;
|
|
209
210
|
export const quote = mod.quote;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "6.5.8
|
|
3
|
+
"version": "6.5.8",
|
|
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
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"esprima": "4.0.1",
|
|
65
65
|
"fs-extra": "11.3.2",
|
|
66
66
|
"globby": "11.1.0",
|
|
67
|
-
"mikro-orm": "6.5.8
|
|
67
|
+
"mikro-orm": "6.5.8",
|
|
68
68
|
"reflect-metadata": "0.2.2"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/platforms/Platform.d.ts
CHANGED
|
@@ -193,7 +193,7 @@ export declare abstract class Platform {
|
|
|
193
193
|
getDefaultPrimaryName(tableName: string, columns: string[]): string;
|
|
194
194
|
supportsCustomPrimaryKeyNames(): boolean;
|
|
195
195
|
isPopulated<T>(key: string, populate: PopulateOptions<T>[] | boolean): boolean;
|
|
196
|
-
shouldHaveColumn<T>(prop: EntityProperty<T>, populate: PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean): boolean;
|
|
196
|
+
shouldHaveColumn<T>(prop: EntityProperty<T>, populate: PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean, ignoreInlineEmbeddables?: boolean): boolean;
|
|
197
197
|
/**
|
|
198
198
|
* Currently not supported due to how knex does complex sqlite diffing (always based on current schema)
|
|
199
199
|
*/
|
package/platforms/Platform.js
CHANGED
|
@@ -452,7 +452,7 @@ class Platform {
|
|
|
452
452
|
isPopulated(key, populate) {
|
|
453
453
|
return populate === true || (populate !== false && populate.some(p => p.field === key || p.all));
|
|
454
454
|
}
|
|
455
|
-
shouldHaveColumn(prop, populate, exclude, includeFormulas = true) {
|
|
455
|
+
shouldHaveColumn(prop, populate, exclude, includeFormulas = true, ignoreInlineEmbeddables = true) {
|
|
456
456
|
if (exclude?.includes(prop.name)) {
|
|
457
457
|
return false;
|
|
458
458
|
}
|
|
@@ -472,7 +472,7 @@ class Platform {
|
|
|
472
472
|
return true;
|
|
473
473
|
}
|
|
474
474
|
if (prop.kind === enums_1.ReferenceKind.EMBEDDED) {
|
|
475
|
-
return
|
|
475
|
+
return prop.object || ignoreInlineEmbeddables;
|
|
476
476
|
}
|
|
477
477
|
return prop.kind === enums_1.ReferenceKind.ONE_TO_ONE && prop.owner;
|
|
478
478
|
}
|
|
@@ -98,6 +98,10 @@ class UnitOfWork {
|
|
|
98
98
|
}
|
|
99
99
|
data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
|
|
100
100
|
}
|
|
101
|
+
if (prop.hydrate === false && prop.customType?.ensureComparable(wrapped.__meta, prop)) {
|
|
102
|
+
const converted = prop.customType.convertToJSValue(data[key], this.platform);
|
|
103
|
+
data[key] = prop.customType.convertToDatabaseValue(converted, this.platform);
|
|
104
|
+
}
|
|
101
105
|
if (forceUndefined) {
|
|
102
106
|
if (data[key] === null) {
|
|
103
107
|
data[key] = undefined;
|
|
@@ -545,10 +545,7 @@ class EntityComparator {
|
|
|
545
545
|
context.set('compareObjects', Utils_1.compareObjects);
|
|
546
546
|
context.set('equals', Utils_1.equals);
|
|
547
547
|
for (const prop of meta.comparableProps) {
|
|
548
|
-
|
|
549
|
-
if (prop.hydrate !== false) {
|
|
550
|
-
lines.push(this.getPropertyComparator(prop, context));
|
|
551
|
-
}
|
|
548
|
+
lines.push(this.getPropertyComparator(prop, context));
|
|
552
549
|
}
|
|
553
550
|
// also compare 1:1 inverse sides, important for `factory.mergeData`
|
|
554
551
|
lines.push(`if (options?.includeInverseSides) {`);
|
package/utils/upsert-utils.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type { EntityData, EntityMetadata } from '../typings';
|
|
1
|
+
import type { EntityData, EntityMetadata, FilterQuery } from '../typings';
|
|
2
2
|
import type { UpsertOptions } from '../drivers/IDatabaseDriver';
|
|
3
3
|
import type { RawQueryFragment } from '../utils/RawQueryFragment';
|
|
4
4
|
/** @internal */
|
|
5
5
|
export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T>): (keyof T)[];
|
|
6
6
|
/** @internal */
|
|
7
7
|
export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T, P>): (keyof T)[] | '*';
|
|
8
|
+
/** @internal */
|
|
9
|
+
export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] | RawQueryFragment | undefined, data: EntityData<T>, where: FilterQuery<T>): {
|
|
10
|
+
where: FilterQuery<T>;
|
|
11
|
+
propIndex: number | false;
|
|
12
|
+
};
|
package/utils/upsert-utils.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getOnConflictFields = getOnConflictFields;
|
|
4
4
|
exports.getOnConflictReturningFields = getOnConflictReturningFields;
|
|
5
|
+
exports.getWhereCondition = getWhereCondition;
|
|
6
|
+
const Utils_1 = require("./Utils");
|
|
5
7
|
function expandEmbeddedProperties(prop, key) {
|
|
6
8
|
if (prop.object) {
|
|
7
9
|
return [prop.name];
|
|
@@ -100,3 +102,44 @@ function getOnConflictReturningFields(meta, data, uniqueFields, options) {
|
|
|
100
102
|
}
|
|
101
103
|
return keys.filter(key => !(key in data));
|
|
102
104
|
}
|
|
105
|
+
function getPropertyValue(obj, key) {
|
|
106
|
+
if (key.indexOf('.') === -1) {
|
|
107
|
+
return obj[key];
|
|
108
|
+
}
|
|
109
|
+
const parts = key.split('.');
|
|
110
|
+
let curr = obj;
|
|
111
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
112
|
+
curr[parts[i]] ??= {};
|
|
113
|
+
curr = curr[parts[i]];
|
|
114
|
+
}
|
|
115
|
+
return curr[parts[parts.length - 1]];
|
|
116
|
+
}
|
|
117
|
+
/** @internal */
|
|
118
|
+
function getWhereCondition(meta, onConflictFields, data, where) {
|
|
119
|
+
const unique = onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
|
|
120
|
+
const propIndex = !Utils_1.Utils.isRawSql(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
|
|
121
|
+
if (onConflictFields || where == null) {
|
|
122
|
+
if (propIndex !== false && propIndex >= 0) {
|
|
123
|
+
let key = unique[propIndex];
|
|
124
|
+
if (key.includes('.')) {
|
|
125
|
+
const prop = meta.properties[key.substring(0, key.indexOf('.'))];
|
|
126
|
+
if (prop) {
|
|
127
|
+
key = `${prop.fieldNames[0]}${key.substring(key.indexOf('.'))}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
where = { [key]: getPropertyValue(data, unique[propIndex]) };
|
|
131
|
+
}
|
|
132
|
+
else if (meta.uniques.length > 0) {
|
|
133
|
+
for (const u of meta.uniques) {
|
|
134
|
+
if (Utils_1.Utils.asArray(u.properties).every(p => data[p] != null)) {
|
|
135
|
+
where = Utils_1.Utils.asArray(u.properties).reduce((o, key) => {
|
|
136
|
+
o[key] = data[key];
|
|
137
|
+
return o;
|
|
138
|
+
}, {});
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { where, propIndex };
|
|
145
|
+
}
|