perf: Remove documentStructure from default query select (#9141)

* perf: Remove documentStructure from default query select

* test
This commit is contained in:
Tom Moor
2025-05-07 07:47:57 -04:00
committed by GitHub
parent 810b7908e4
commit b9387734c7
15 changed files with 161 additions and 144 deletions

View File

@@ -228,6 +228,7 @@
"sequelize": "^6.37.3",
"sequelize-cli": "^6.6.2",
"sequelize-encrypted": "^1.0.0",
"sequelize-strict-attributes": "^1.0.2",
"sequelize-typescript": "^2.1.6",
"slug": "^5.3.0",
"slugify": "^1.6.6",

View File

@@ -1,4 +1,3 @@
import invariant from "invariant";
import { Op, WhereOptions } from "sequelize";
import isUUID from "validator/lib/isUUID";
import { UrlHelper } from "@shared/utils/UrlHelper";
@@ -22,8 +21,8 @@ type Props = {
type Result = {
document: Document;
share?: Share;
collection?: Collection | null;
share: Share | null;
collection: Collection | null;
};
export default async function loadDocument({
@@ -33,9 +32,9 @@ export default async function loadDocument({
user,
includeState,
}: Props): Promise<Result> {
let document;
let collection;
let share;
let document: Document | null = null;
let collection: Collection | null = null;
let share: Share | null = null;
if (!shareId && !(id && user)) {
throw AuthenticationError(`Authentication or shareId required`);
@@ -72,20 +71,7 @@ export default async function loadDocument({
where: whereClause,
include: [
{
// unscoping here allows us to return unpublished documents
model: Document.unscoped(),
include: [
{
model: User,
as: "createdBy",
paranoid: false,
},
{
model: User,
as: "updatedBy",
paranoid: false,
},
],
model: Document.scope("withDrafts"),
required: true,
as: "document",
},
@@ -129,14 +115,13 @@ export default async function loadDocument({
const canReadDocument = user && can(user, "read", document);
if (canReadDocument) {
// Cannot use document.collection here as it does not include the
// documentStructure by default through the relationship.
if (document.collectionId) {
collection = await Collection.findByPk(document.collectionId);
if (!collection) {
throw NotFoundError("Collection could not be found for document");
}
collection = await Collection.scope("withDocumentStructure").findByPk(
document.collectionId,
{
rejectOnEmpty: true,
}
);
}
return {
@@ -155,11 +140,15 @@ export default async function loadDocument({
// It is possible to disable sharing at the collection so we must check
if (document.collectionId) {
collection = await Collection.findByPk(document.collectionId);
collection = await Collection.scope("withDocumentStructure").findByPk(
document.collectionId,
{
rejectOnEmpty: true,
}
);
}
invariant(collection, "collection not found");
if (!collection.sharing) {
if (!collection?.sharing) {
throw AuthorizationError();
}

View File

@@ -1,4 +1,3 @@
import invariant from "invariant";
import { Transaction } from "sequelize";
import { createContext } from "@server/context";
import { traceFunction } from "@server/logging/tracing";
@@ -66,16 +65,21 @@ async function documentMover({
result.documents.push(document);
} else {
// Load the current and the next collection upfront and lock them
const collection = await Collection.findByPk(document.collectionId!, {
transaction,
lock: Transaction.LOCK.UPDATE,
paranoid: false,
});
const collection = await Collection.scope("withDocumentStructure").findByPk(
document.collectionId!,
{
transaction,
lock: Transaction.LOCK.UPDATE,
paranoid: false,
}
);
let newCollection = collection;
if (collectionChanged) {
if (collectionId) {
newCollection = await Collection.findByPk(collectionId, {
newCollection = await Collection.scope(
"withDocumentStructure"
).findByPk(collectionId, {
transaction,
lock: Transaction.LOCK.UPDATE,
});
@@ -144,12 +148,14 @@ async function documentMover({
if (collectionId) {
// Reload the collection to get relationship data
newCollection = await Collection.scope({
method: ["withMembership", user.id],
}).findByPk(collectionId, {
newCollection = await Collection.scope([
{
method: ["withMembership", user.id],
},
]).findByPk(collectionId, {
transaction,
rejectOnEmpty: true,
});
invariant(newCollection, "Collection not found");
result.collections.push(newCollection);

View File

@@ -16,7 +16,7 @@ beforeEach(() => {
});
describe("#url", () => {
test("should return correct url for the collection", () => {
it("should return correct url for the collection", () => {
const collection = new Collection({
id: "1234",
});
@@ -25,7 +25,7 @@ describe("#url", () => {
});
describe("getDocumentParents", () => {
test("should return array of parent document ids", async () => {
it("should return array of parent document ids", async () => {
const parent = await buildDocument();
const document = await buildDocument();
const collection = await buildCollection({
@@ -41,7 +41,7 @@ describe("getDocumentParents", () => {
expect(result ? result[0] : undefined).toBe(parent.id);
});
test("should return array of parent document ids", async () => {
it("should return array of parent document ids", async () => {
const parent = await buildDocument();
const document = await buildDocument();
const collection = await buildCollection({
@@ -56,7 +56,7 @@ describe("getDocumentParents", () => {
expect(result?.length).toBe(0);
});
test("should not error if documentStructure is empty", async () => {
it("should not error if documentStructure is empty", async () => {
const parent = await buildDocument();
await buildDocument();
const collection = await buildCollection();
@@ -66,7 +66,7 @@ describe("getDocumentParents", () => {
});
describe("getDocumentTree", () => {
test("should return document tree", async () => {
it("should return document tree", async () => {
const document = await buildDocument();
const collection = await buildCollection({
documentStructure: [await document.toNavigationNode()],
@@ -76,7 +76,7 @@ describe("getDocumentTree", () => {
);
});
test("should return nested documents in tree", async () => {
it("should return nested documents in tree", async () => {
const parent = await buildDocument();
const document = await buildDocument();
const collection = await buildCollection({
@@ -99,7 +99,7 @@ describe("getDocumentTree", () => {
});
describe("#addDocumentToStructure", () => {
test("should add as last element without index", async () => {
it("should add as last element without index", async () => {
const collection = await buildCollection();
const id = uuidv4();
const newDocument = await buildDocument({
@@ -117,7 +117,7 @@ describe("#addDocumentToStructure", () => {
expect(collection.documentStructure!.length).toBe(1);
});
test("should add with an index", async () => {
it("should add with an index", async () => {
const collection = await buildCollection();
const id = uuidv4();
const newDocument = await buildDocument({
@@ -131,7 +131,7 @@ describe("#addDocumentToStructure", () => {
expect(collection.documentStructure![0].id).toBe(id);
});
test("should add as a child if with parent", async () => {
it("should add as a child if with parent", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -150,7 +150,7 @@ describe("#addDocumentToStructure", () => {
expect(collection.documentStructure![0].children[0].id).toBe(id);
});
test("should add as a child if with parent with index", async () => {
it("should add as a child if with parent with index", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -176,7 +176,7 @@ describe("#addDocumentToStructure", () => {
expect(collection.documentStructure![0].children[0].id).toBe(id);
});
test("should add the document along with its nested document(s)", async () => {
it("should add the document along with its nested document(s)", async () => {
const collection = await buildCollection();
const document = await buildDocument({
@@ -204,7 +204,7 @@ describe("#addDocumentToStructure", () => {
);
});
test("should add the document along with its archived nested document(s)", async () => {
it("should add the document along with its archived nested document(s)", async () => {
const collection = await buildCollection();
const document = await buildDocument({
@@ -237,7 +237,7 @@ describe("#addDocumentToStructure", () => {
);
});
describe("options: documentJson", () => {
test("should append supplied json over document's own", async () => {
it("should append supplied json over document's own", async () => {
const collection = await buildCollection();
const id = uuidv4();
const newDocument = await buildDocument({
@@ -268,7 +268,7 @@ describe("#addDocumentToStructure", () => {
});
describe("#updateDocument", () => {
test("should update root document's data", async () => {
it("should update root document's data", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -279,7 +279,7 @@ describe("#updateDocument", () => {
expect(collection.documentStructure![0].title).toBe("Updated title");
});
test("should update child document's data", async () => {
it("should update child document's data", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -297,7 +297,7 @@ describe("#updateDocument", () => {
newDocument.title = "Updated title";
await newDocument.save();
await collection.updateDocument(newDocument);
const reloaded = await Collection.findByPk(collection.id);
const reloaded = await collection.reload();
expect(reloaded!.documentStructure![0].children[0].title).toBe(
"Updated title"
);
@@ -305,7 +305,7 @@ describe("#updateDocument", () => {
});
describe("#removeDocument", () => {
test("should save if removing", async () => {
it("should save if removing", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -315,7 +315,7 @@ describe("#removeDocument", () => {
expect(collection.save).toBeCalled();
});
test("should remove documents from root", async () => {
it("should remove documents from root", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -331,7 +331,7 @@ describe("#removeDocument", () => {
expect(collectionDocuments.count).toBe(0);
});
test("should remove a document with child documents", async () => {
it("should remove a document with child documents", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -359,7 +359,7 @@ describe("#removeDocument", () => {
expect(collectionDocuments.count).toBe(0);
});
test("should remove a child document", async () => {
it("should remove a child document", async () => {
const collection = await buildCollection();
const document = await buildDocument({ collectionId: collection.id });
await collection.reload();
@@ -380,7 +380,7 @@ describe("#removeDocument", () => {
expect(collection.documentStructure![0].children.length).toBe(1);
// Remove the document
await collection.deleteDocument(newDocument);
const reloaded = await Collection.findByPk(collection.id);
const reloaded = await collection.reload();
expect(reloaded!.documentStructure!.length).toBe(1);
expect(reloaded!.documentStructure![0].children.length).toBe(0);
const collectionDocuments = await Document.findAndCountAll({
@@ -393,7 +393,7 @@ describe("#removeDocument", () => {
});
describe("#membershipUserIds", () => {
test("should return collection and group memberships", async () => {
it("should return collection and group memberships", async () => {
const team = await buildTeam();
const teamId = team.id;
// Make 6 users
@@ -464,47 +464,53 @@ describe("#membershipUserIds", () => {
});
describe("#findByPk", () => {
test("should return collection with collection Id", async () => {
it("should return collection with collection Id", async () => {
const collection = await buildCollection();
const response = await Collection.findByPk(collection.id);
expect(response!.id).toBe(collection.id);
});
test("should return collection when urlId is present", async () => {
it("should not return documentStructure by default", async () => {
const collection = await buildCollection();
const response = await Collection.findByPk(collection.id);
expect(() => response!.documentStructure).toThrow();
});
it("should return collection when urlId is present", async () => {
const collection = await buildCollection();
const id = `${slugify(collection.name)}-${collection.urlId}`;
const response = await Collection.findByPk(id);
expect(response!.id).toBe(collection.id);
});
test("should return collection when urlId is present, but missing slug", async () => {
it("should return collection when urlId is present, but missing slug", async () => {
const collection = await buildCollection();
const id = collection.urlId;
const response = await Collection.findByPk(id);
expect(response!.id).toBe(collection.id);
});
test("should return null when incorrect uuid type", async () => {
it("should return null when incorrect uuid type", async () => {
const collection = await buildCollection();
const response = await Collection.findByPk(collection.id + "-incorrect");
expect(response).toBe(null);
});
test("should return null when incorrect urlId length", async () => {
it("should return null when incorrect urlId length", async () => {
const collection = await buildCollection();
const id = `${slugify(collection.name)}-${collection.urlId}incorrect`;
const response = await Collection.findByPk(id);
expect(response).toBe(null);
});
test("should return null when no collection is found with uuid", async () => {
it("should return null when no collection is found with uuid", async () => {
const response = await Collection.findByPk(
"a9e71a81-7342-4ea3-9889-9b9cc8f667da"
);
expect(response).toBe(null);
});
test("should return null when no collection is found with urlId", async () => {
it("should return null when no collection is found with urlId", async () => {
const id = `${slugify("test collection")}-${randomstring.generate(15)}`;
const response = await Collection.findByPk(id);
expect(response).toBe(null);

View File

@@ -37,6 +37,7 @@ import {
AllowNull,
BeforeCreate,
BeforeUpdate,
DefaultScope,
} from "sequelize-typescript";
import isUUID from "validator/lib/isUUID";
import type { CollectionSort, ProsemirrorData } from "@shared/types";
@@ -69,6 +70,11 @@ type AdditionalFindOptions = {
rejectOnEmpty?: boolean | Error;
};
@DefaultScope(() => ({
attributes: {
exclude: ["documentStructure"],
},
}))
@Scopes(() => ({
withAllMemberships: {
include: [
@@ -121,6 +127,12 @@ type AdditionalFindOptions = {
},
],
}),
withDocumentStructure: () => ({
attributes: {
// resets to include the documentStructure column
exclude: [],
},
}),
withMembership: (userId: string) => {
if (!userId) {
return {};
@@ -238,6 +250,7 @@ class Collection extends ParanoidModel<
@Column
maintainerApprovalRequired: boolean;
@Default(null)
@Column(DataType.JSONB)
documentStructure: NavigationNode[] | null;

View File

@@ -11,7 +11,6 @@ import {
buildUser,
buildGuestUser,
} from "@server/test/factories";
import Collection from "./Collection";
import UserMembership from "./UserMembership";
beforeEach(() => {
@@ -96,10 +95,8 @@ describe("#delete", () => {
await document.delete(user);
const [newDocument, newCollection] = await Promise.all([
Document.findByPk(document.id, {
paranoid: false,
}),
Collection.findByPk(collection.id),
document.reload({ paranoid: false }),
collection.reload(),
]);
expect(newDocument?.lastModifiedById).toEqual(user.id);

View File

@@ -162,11 +162,13 @@ type AdditionalFindOptions = {
return {
include: [
{
attributes: ["id", "permission", "sharing", "teamId", "deletedAt"],
model: userId
? Collection.scope({
method: ["withMembership", userId],
})
? Collection.scope([
"defaultScope",
{
method: ["withMembership", userId],
},
])
: Collection,
as: "collection",
paranoid,
@@ -414,10 +416,13 @@ class Document extends ArchivableModel<
return;
}
const collection = await Collection.findByPk(model.collectionId, {
transaction,
lock: Transaction.LOCK.UPDATE,
});
const collection = await Collection.scope("withDocumentStructure").findByPk(
model.collectionId,
{
transaction,
lock: Transaction.LOCK.UPDATE,
}
);
if (!collection) {
return;
}
@@ -438,7 +443,9 @@ class Document extends ArchivableModel<
}
return this.sequelize!.transaction(async (transaction: Transaction) => {
const collection = await Collection.findByPk(model.collectionId!, {
const collection = await Collection.scope(
"withDocumentStructure"
).findByPk(model.collectionId!, {
transaction,
lock: transaction.LOCK.UPDATE,
});
@@ -926,7 +933,9 @@ class Document extends ArchivableModel<
}
if (!this.template && this.collectionId) {
const collection = await Collection.findByPk(this.collectionId, {
const collection = await Collection.scope(
"withDocumentStructure"
).findByPk(this.collectionId, {
transaction,
lock: Transaction.LOCK.UPDATE,
});
@@ -993,10 +1002,13 @@ class Document extends ArchivableModel<
await this.sequelize.transaction(async (transaction: Transaction) => {
const collection = this.collectionId
? await Collection.findByPk(this.collectionId, {
transaction,
lock: transaction.LOCK.UPDATE,
})
? await Collection.scope("withDocumentStructure").findByPk(
this.collectionId,
{
transaction,
lock: transaction.LOCK.UPDATE,
}
)
: undefined;
if (collection) {
@@ -1027,10 +1039,13 @@ class Document extends ArchivableModel<
archive = async (user: User, options?: FindOptions) => {
const { transaction } = { ...options };
const collection = this.collectionId
? await Collection.findByPk(this.collectionId, {
transaction,
lock: transaction?.LOCK.UPDATE,
})
? await Collection.scope("withDocumentStructure").findByPk(
this.collectionId,
{
transaction,
lock: transaction?.LOCK.UPDATE,
}
)
: undefined;
if (collection) {
@@ -1051,7 +1066,7 @@ class Document extends ArchivableModel<
) => {
const { transaction } = { ...options };
const collection = collectionId
? await Collection.findByPk(collectionId, {
? await Collection.scope("withDocumentStructure").findByPk(collectionId, {
transaction,
lock: transaction?.LOCK.UPDATE,
})
@@ -1103,7 +1118,9 @@ class Document extends ArchivableModel<
let deleted = false;
if (!this.template && this.collectionId) {
const collection = await Collection.findByPk(this.collectionId!, {
const collection = await Collection.scope(
"withDocumentStructure"
).findByPk(this.collectionId!, {
transaction,
lock: transaction.LOCK.UPDATE,
paranoid: false,

View File

@@ -140,9 +140,11 @@ router.post(
async (ctx: APIContext<T.CollectionsDocumentsReq>) => {
const { id } = ctx.input.body;
const { user } = ctx.state.auth;
const collection = await Collection.scope({
method: ["withMembership", user.id],
}).findByPk(id);
const collection = await Collection.scope([
{
method: ["withMembership", user.id],
},
]).findByPk(id);
authorize(user, "readDocument", collection);

View File

@@ -977,7 +977,7 @@ describe("#documents.list", () => {
const res = await server.post("/api/documents.list", {
body: {
token: user.getJwtToken(),
collection: document.collectionId,
collectionId: document.collectionId,
},
});
const body = await res.json();
@@ -1013,7 +1013,7 @@ describe("#documents.list", () => {
const res = await server.post("/api/documents.list", {
body: {
token: user.getJwtToken(),
collection: collection.id,
collectionId: collection.id,
},
});
const body = await res.json();

View File

@@ -133,15 +133,19 @@ router.post(
// if a specific collection is passed then we need to check auth to view it
if (collectionId) {
where[Op.and].push({ collectionId: [collectionId] });
const collection = await Collection.scope({
method: ["withMembership", user.id],
}).findByPk(collectionId);
const collection = await Collection.scope([
sort === "index" ? "withDocumentStructure" : "defaultScope",
{
method: ["withMembership", user.id],
},
]).findByPk(collectionId);
authorize(user, "readDocument", collection);
// index sort is special because it uses the order of the documents in the
// collection.documentStructure rather than a database column
if (sort === "index") {
documentIds = (collection?.documentStructure || [])
documentIds = (collection.documentStructure || [])
.map((node) => node.id)
.slice(ctx.state.pagination.offset, ctx.state.pagination.limit);
where[Op.and].push({ id: documentIds });

View File

@@ -54,7 +54,11 @@ router.post(
});
authorize(user, "read", document);
const collection = await document.$get("collection");
const collection = document.collectionId
? await Collection.scope("withDocumentStructure").findByPk(
document.collectionId
)
: undefined;
const parentIds = collection?.getDocumentParents(documentId);
const parentShare = parentIds
? await Share.scope({

View File

@@ -1,5 +1,6 @@
import path from "path";
import { InferAttributes, InferCreationAttributes } from "sequelize";
import sequelizeStrictAttributes from "sequelize-strict-attributes";
import { Sequelize } from "sequelize-typescript";
import { Umzug, SequelizeStorage, MigrationError } from "umzug";
import env from "@server/env";
@@ -23,7 +24,7 @@ export function createDatabaseInstance(
}
): Sequelize {
try {
return new Sequelize(databaseUrl, {
const instance = new Sequelize(databaseUrl, {
logging: (msg) =>
process.env.DEBUG?.includes("database") &&
Logger.debug("database", msg),
@@ -47,6 +48,8 @@ export function createDatabaseInstance(
},
schema,
});
sequelizeStrictAttributes(instance);
return instance;
} catch (error) {
Logger.fatal(
"Could not connect to database",

View File

@@ -310,7 +310,7 @@ export async function buildCollection(
overrides.permission = CollectionPermission.ReadWrite;
}
return Collection.create({
return Collection.scope("withDocumentStructure").create({
name: faker.lorem.words(2),
description: faker.lorem.words(4),
createdById: overrides.userId,
@@ -416,7 +416,9 @@ export async function buildDocument(
if (overrides.collectionId && overrides.publishedAt !== null) {
collection = collection
? await Collection.findByPk(overrides.collectionId)
? await Collection.scope("withDocumentStructure").findByPk(
overrides.collectionId
)
: undefined;
await collection?.addDocumentToStructure(document, 0);

View File

@@ -11,7 +11,6 @@ export async function collectionIndexing(
where: {
teamId,
},
attributes: ["id", "index", "name", "teamId"],
transaction,
});

View File

@@ -737,14 +737,7 @@
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2"
"@babel/helper-annotate-as-pure@^7.22.5", "@babel/helper-annotate-as-pure@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4"
integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==
dependencies:
"@babel/types" "^7.25.9"
"@babel/helper-annotate-as-pure@^7.27.1":
"@babel/helper-annotate-as-pure@^7.22.5", "@babel/helper-annotate-as-pure@^7.25.9", "@babel/helper-annotate-as-pure@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz#4345d81a9a46a6486e24d069469f13e60445c05d"
integrity sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==
@@ -775,16 +768,7 @@
"@babel/traverse" "^7.27.1"
semver "^6.3.1"
"@babel/helper-create-regexp-features-plugin@^7.18.6":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz#5169756ecbe1d95f7866b90bb555b022595302a0"
integrity sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==
dependencies:
"@babel/helper-annotate-as-pure" "^7.25.9"
regexpu-core "^6.2.0"
semver "^6.3.1"
"@babel/helper-create-regexp-features-plugin@^7.27.1":
"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz#05b0882d97ba1d4d03519e4bce615d70afa18c53"
integrity sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==
@@ -826,15 +810,7 @@
"@babel/traverse" "^7.27.1"
"@babel/types" "^7.27.1"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.22.5":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715"
integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==
dependencies:
"@babel/traverse" "^7.25.9"
"@babel/types" "^7.25.9"
"@babel/helper-module-imports@^7.27.1":
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5", "@babel/helper-module-imports@^7.22.5", "@babel/helper-module-imports@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204"
integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
@@ -1093,20 +1069,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
"@babel/plugin-syntax-typescript@^7.27.1":
"@babel/plugin-syntax-typescript@^7.27.1", "@babel/plugin-syntax-typescript@^7.7.2":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18"
integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==
dependencies:
"@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-syntax-typescript@^7.7.2":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399"
integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==
dependencies:
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357"
@@ -14294,6 +14263,11 @@ sequelize-pool@^7.1.0:
resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768"
integrity "sha1-IQs5GvQAJ2L4IxiP1uz8dBMCB2g= sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg=="
sequelize-strict-attributes@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/sequelize-strict-attributes/-/sequelize-strict-attributes-1.0.2.tgz#d51c5322551abe59e95249011fdb335c513ade53"
integrity sha512-FAzGrB4BAQzLD0xkGvAhWQf0Y8IRSn8ydaG6j8D8eMxT2z8iKIMzDdxtm9YFhHrTkLd+ILRktQLhEmGm921QvA==
sequelize-typescript@^2.1.6:
version "2.1.6"
resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-2.1.6.tgz#9476c8a2510114ed1c3a26b424c47e05c2e6284e"