diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index a7ca647b..48469103 100644
--- a/util/src/entities/Channel.ts
+++ b/util/src/entities/Channel.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId} from "typeorm";
+import { OrmUtils } from "@fosscord/util";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
import { PublicUserProjection, User } from "./User";
@@ -222,7 +223,7 @@ export class Channel extends BaseClass {
};
await Promise.all([
- Object.assign(new Channel(),channel).save(),
+ OrmUtils.mergeDeep(new Channel(),channel).save(),
!opts?.skipEventEmit
? emitEvent({
event: "CHANNEL_CREATE",
@@ -263,7 +264,7 @@ export class Channel extends BaseClass {
if (containsAll(re, channelRecipients)) {
if (channel == null) {
channel = ur.channel;
- ur = Object.assign(ur, { closed: false });
+ ur = OrmUtils.mergeDeep(ur, { closed: false });
await ur.save();
}
}
@@ -273,7 +274,7 @@ export class Channel extends BaseClass {
if (channel == null) {
name = trimSpecial(name);
- channel = await (Object.assign(new Channel(), {
+ channel = await (OrmUtils.mergeDeep(new Channel(), {
name,
type,
owner_id: type === ChannelType.DM ? undefined : null, // 1:1 DMs are ownerless in fosscord-server
@@ -281,7 +282,7 @@ export class Channel extends BaseClass {
last_message_id: null,
recipients: channelRecipients.map(
(x) =>
- Object.assign(new Recipient(), { user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })
+ OrmUtils.mergeDeep(new Recipient(), { user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })
),
}) as Channel).save();
}
diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index 058033e8..da8a2c2f 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId} from "typeorm";
+import { OrmUtils } from "@fosscord/util";
import { Config, handleFile, Snowflake } from "..";
import { Ban } from "./Ban";
import { BaseClass } from "./BaseClass";
@@ -285,7 +286,7 @@ export class Guild extends BaseClass {
}) {
const guild_id = Snowflake.generate();
- const guild: Guild = Object.assign(new Guild(),{
+ const guild: Guild = OrmUtils.mergeDeep(new Guild(),{
name: body.name || "Fosscord",
icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
region: Config.get().regions.default,
@@ -321,7 +322,7 @@ export class Guild extends BaseClass {
// we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error
// TODO: make the @everyone a pseudorole that is dynamically generated at runtime so we can save storage
- let role: Role = Object.assign(new Role(), {
+ let role: Role = OrmUtils.mergeDeep(new Role(), {
id: guild_id,
guild_id: guild_id,
color: 0,
diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts
index e4aa8331..f0c361d0 100644
--- a/util/src/entities/Member.ts
+++ b/util/src/entities/Member.ts
@@ -25,6 +25,7 @@ import { Role } from "./Role";
import { BaseClassWithoutId } from "./BaseClass";
import { Ban, PublicGuildRelations } from ".";
import { DiscordApiErrors } from "../util/Constants";
+import { OrmUtils } from "@fosscord/util";
export const MemberPrivateProjection: (keyof Member)[] = [
"id",
@@ -161,7 +162,7 @@ export class Member extends BaseClassWithoutId {
}),
Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] }),
]);
- member.roles.push(Object.assign(new Role(), { id: role_id }));
+ member.roles.push(OrmUtils.mergeDeep(new Role(), { id: role_id }));
await Promise.all([
member.save(),
@@ -256,7 +257,7 @@ export class Member extends BaseClassWithoutId {
nick: undefined,
roles: [guild_id], // @everyone role
joined_at: new Date(),
- premium_since: (new Date()).getTime(),
+ premium_since: new Date(),
deaf: false,
mute: false,
pending: false,
@@ -264,9 +265,9 @@ export class Member extends BaseClassWithoutId {
//TODO: check for bugs
if(guild.member_count) guild.member_count++;
await Promise.all([
- Object.assign(new Member(), {
+ OrmUtils.mergeDeep(new Member(), {
...member,
- roles: [Object.assign(new Role(), { id: guild_id })],
+ roles: [OrmUtils.mergeDeep(new Role(), { id: guild_id })],
// read_state: {},
settings: {
channel_overrides: [],
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index 81017c2d..d023780b 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm";
+import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, ManyToMany, OneToMany, RelationId} from "typeorm";
+import { OrmUtils } from "@fosscord/util";
import { BaseClass } from "./BaseClass";
import { BitField } from "../util/BitField";
import { Relationship } from "./Relationship";
@@ -255,7 +256,7 @@ export class User extends BaseClass {
// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
const language = req.language === "en" ? "en-US" : req.language || "en-US";
- const user = Object.assign(new User(), {
+ const user = OrmUtils.mergeDeep(new User(), {
created_at: new Date(),
username: username,
discriminator,
diff --git a/util/src/util/imports/OrmUtils.ts b/util/src/util/imports/OrmUtils.ts
new file mode 100644
index 00000000..91d88172
--- /dev/null
+++ b/util/src/util/imports/OrmUtils.ts
@@ -0,0 +1,113 @@
+//source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts
+export class OrmUtils {
+ // Checks if it's an object made by Object.create(null), {} or new Object()
+ private static isPlainObject(item: any) {
+ if (item === null || item === undefined) {
+ return false
+ }
+
+ return !item.constructor || item.constructor === Object
+ }
+
+ private static mergeArrayKey(
+ target: any,
+ key: number,
+ value: any,
+ memo: Map<any, any>,
+ ) {
+ // Have we seen this before? Prevent infinite recursion.
+ if (memo.has(value)) {
+ target[key] = memo.get(value)
+ return
+ }
+
+ if (value instanceof Promise) {
+ // Skip promises entirely.
+ // This is a hold-over from the old code & is because we don't want to pull in
+ // the lazy fields. Ideally we'd remove these promises via another function first
+ // but for now we have to do it here.
+ return
+ }
+
+ if (!this.isPlainObject(value) && !Array.isArray(value)) {
+ target[key] = value
+ return
+ }
+
+ if (!target[key]) {
+ target[key] = Array.isArray(value) ? [] : {}
+ }
+
+ memo.set(value, target[key])
+ this.merge(target[key], value, memo)
+ memo.delete(value)
+ }
+
+ private static mergeObjectKey(
+ target: any,
+ key: string,
+ value: any,
+ memo: Map<any, any>,
+ ) {
+ // Have we seen this before? Prevent infinite recursion.
+ if (memo.has(value)) {
+ Object.assign(target, { [key]: memo.get(value) })
+ return
+ }
+
+ if (value instanceof Promise) {
+ // Skip promises entirely.
+ // This is a hold-over from the old code & is because we don't want to pull in
+ // the lazy fields. Ideally we'd remove these promises via another function first
+ // but for now we have to do it here.
+ return
+ }
+
+ if (!this.isPlainObject(value) && !Array.isArray(value)) {
+ Object.assign(target, { [key]: value })
+ return
+ }
+
+ if (!target[key]) {
+ Object.assign(target, { [key]: value })
+ }
+
+ memo.set(value, target[key])
+ this.merge(target[key], value, memo)
+ memo.delete(value)
+ }
+
+ private static merge(
+ target: any,
+ source: any,
+ memo: Map<any, any> = new Map(),
+ ): any {
+ if (Array.isArray(target) && Array.isArray(source)) {
+ for (let key = 0; key < source.length; key++) {
+ this.mergeArrayKey(target, key, source[key], memo)
+ }
+ }
+ else {
+ for (const key of Object.keys(source)) {
+ this.mergeObjectKey(target, key, source[key], memo)
+ }
+ }
+
+
+ }
+
+ /**
+ * Deep Object.assign.
+ */
+ static mergeDeep(target: any, ...sources: any[]): any {
+ if (!sources.length) {
+ return target
+ }
+
+ for (const source of sources) {
+ OrmUtils.merge(target, source)
+ }
+
+ return target
+ }
+}
\ No newline at end of file
diff --git a/util/src/util/imports/index.ts b/util/src/util/imports/index.ts
index 4a4448ba..18c47a3b 100644
--- a/util/src/util/imports/index.ts
+++ b/util/src/util/imports/index.ts
@@ -1,2 +1,3 @@
export * from './Checks';
export * from './HTTPError';
+export * from './OrmUtils';
\ No newline at end of file
|