diff --git a/package.json b/package.json index a7de38c39..4a93e79ce 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "autotrack": "^2.4.1", "aws-sdk": "^2.1189.0", "babel-plugin-lodash": "^3.3.4", - "babel-plugin-styled-components": "^1.11.1", + "babel-plugin-styled-components": "^2.0.7", "babel-plugin-transform-class-properties": "^6.24.1", "body-scroll-lock": "^4.0.0-beta.0", "bull": "^4.10.2", @@ -92,7 +92,7 @@ "email-providers": "^1.13.1", "emoji-regex": "^10.0.0", "es6-error": "^4.1.1", - "exports-loader": "^0.6.4", + "exports-loader": "^1.1.1", "fetch-retry": "^5.0.3", "fetch-with-proxy": "^3.0.1", "file-loader": "^1.1.6", @@ -112,7 +112,7 @@ "is-printable-key-event": "^1.0.0", "jsdom": "^20.0.3", "json-loader": "0.5.7", - "jsonwebtoken": "^8.5.0", + "jsonwebtoken": "^9.0.0", "jszip": "^3.10.1", "katex": "^0.16.3", "kbar": "0.1.0-beta.28", @@ -152,7 +152,7 @@ "pg-tsquery": "^8.4.0", "polished": "^4.2.2", "prosemirror-commands": "1.2.2", - "prosemirror-dropcursor": "^1.4.0", + "prosemirror-dropcursor": "^1.6.1", "prosemirror-gapcursor": "^1.3.1", "prosemirror-history": "^1.2.0", "prosemirror-inputrules": "^1.1.3", @@ -266,7 +266,7 @@ "@types/nodemailer": "^6.4.4", "@types/passport-oauth2": "^1.4.11", "@types/prosemirror-commands": "^1.0.4", - "@types/prosemirror-dropcursor": "^1.0.0", + "@types/prosemirror-dropcursor": "^1.5.0", "@types/prosemirror-gapcursor": "^1.0.1", "@types/prosemirror-history": "^1.0.1", "@types/prosemirror-inputrules": "^1.0.2", diff --git a/server/models/helpers/SearchHelper.test.ts b/server/models/helpers/SearchHelper.test.ts index 7f1ddfc05..f72cb5e01 100644 --- a/server/models/helpers/SearchHelper.test.ts +++ b/server/models/helpers/SearchHelper.test.ts @@ -333,3 +333,139 @@ describe("#searchForUser", () => { expect(totalCount).toBe("0"); }); }); + +describe("#searchTitlesForUser", () => { + test("should return search results from collections", async () => { + const team = await buildTeam(); + const user = await buildUser({ + teamId: team.id, + }); + const collection = await buildCollection({ + userId: user.id, + teamId: team.id, + }); + const document = await buildDocument({ + userId: user.id, + teamId: team.id, + collectionId: collection.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test"); + expect(documents.length).toBe(1); + expect(documents[0]?.id).toBe(document.id); + }); + + test("should filter to specific collection", async () => { + const team = await buildTeam(); + const user = await buildUser({ + teamId: team.id, + }); + const collection = await buildCollection({ + userId: user.id, + teamId: team.id, + }); + const collection1 = await buildCollection({ + userId: user.id, + teamId: team.id, + }); + const document = await buildDocument({ + userId: user.id, + teamId: team.id, + collectionId: collection.id, + title: "test", + }); + await buildDraftDocument({ + teamId: team.id, + userId: user.id, + title: "test", + }); + await buildDocument({ + userId: user.id, + teamId: team.id, + collectionId: collection1.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test", { + collectionId: collection.id, + }); + expect(documents.length).toBe(1); + expect(documents[0]?.id).toBe(document.id); + }); + + test("should handle no collections", async () => { + const team = await buildTeam(); + const user = await buildUser({ + teamId: team.id, + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test"); + expect(documents.length).toBe(0); + }); + + test("should search only drafts created by user", async () => { + const user = await buildUser(); + await buildDraftDocument({ + teamId: user.teamId, + userId: user.id, + createdById: user.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test", { + includeDrafts: true, + }); + expect(documents.length).toBe(1); + }); + + test("should not include drafts", async () => { + const user = await buildUser(); + await buildDraftDocument({ + teamId: user.teamId, + userId: user.id, + createdById: user.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test", { + includeDrafts: false, + }); + expect(documents.length).toBe(0); + }); + + test("should include results from drafts as well", async () => { + const user = await buildUser(); + await buildDocument({ + userId: user.id, + teamId: user.teamId, + createdById: user.id, + title: "not test", + }); + await buildDraftDocument({ + teamId: user.teamId, + userId: user.id, + createdById: user.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test", { + includeDrafts: true, + }); + expect(documents.length).toBe(2); + }); + + test("should not include results from drafts", async () => { + const user = await buildUser(); + await buildDocument({ + userId: user.id, + teamId: user.teamId, + createdById: user.id, + title: "not test", + }); + await buildDraftDocument({ + teamId: user.teamId, + userId: user.id, + createdById: user.id, + title: "test", + }); + const documents = await SearchHelper.searchTitlesForUser(user, "test", { + includeDrafts: false, + }); + expect(documents.length).toBe(1); + }); +}); diff --git a/server/models/helpers/SearchHelper.ts b/server/models/helpers/SearchHelper.ts index 1e57bbdbd..ba19a6021 100644 --- a/server/models/helpers/SearchHelper.ts +++ b/server/models/helpers/SearchHelper.ts @@ -2,7 +2,7 @@ import removeMarkdown from "@tommoor/remove-markdown"; import invariant from "invariant"; import { find, map } from "lodash"; import queryParser from "pg-tsquery"; -import { Op, QueryTypes } from "sequelize"; +import { Op, QueryTypes, WhereOptions } from "sequelize"; import { DateFilter } from "@shared/types"; import unescape from "@shared/utils/unescape"; import { sequelize } from "@server/database/sequelize"; @@ -170,6 +170,128 @@ export default class SearchHelper { return SearchHelper.buildResponse(results, documents, count); } + public static async searchTitlesForUser( + user: User, + query: string, + options: SearchOptions = {} + ): Promise { + const { limit = 15, offset = 0 } = options; + + let where: WhereOptions = { + title: { + [Op.iLike]: `%${query}%`, + }, + }; + + // Ensure we're filtering by the users accessible collections. If + // collectionId is passed as an option it is assumed that the authorization + // has already been done in the router + if (options.collectionId) { + where = { + ...where, + collectionId: options.collectionId, + }; + } else { + // @ts-expect-error doesn't like OR null + where = { + ...where, + [Op.or]: [ + { + collectionId: { + [Op.in]: await user.collectionIds(), + }, + }, + { + collectionId: { + [Op.is]: null, + }, + createdById: user.id, + }, + ], + }; + } + + if (options.dateFilter) { + where = { + ...where, + updatedAt: { + [Op.gt]: sequelize.literal( + `now() - interval '1 ${options.dateFilter}'` + ), + }, + }; + } + + if (!options.includeArchived) { + where = { + ...where, + archivedAt: { + [Op.is]: null, + }, + }; + } + + if (options.includeDrafts) { + where = { + ...where, + [Op.or]: [ + { + publishedAt: { + [Op.ne]: null, + }, + }, + { + createdById: user.id, + }, + ], + }; + } else { + where = { + ...where, + publishedAt: { + [Op.ne]: null, + }, + }; + } + + if (options.collaboratorIds) { + where = { + ...where, + collaboratorIds: { + [Op.contains]: options.collaboratorIds, + }, + }; + } + + return await Document.scope([ + "withoutState", + "withDrafts", + { + method: ["withViews", user.id], + }, + { + method: ["withCollectionPermissions", user.id], + }, + ]).findAll({ + where, + order: [["updatedAt", "DESC"]], + include: [ + { + model: User, + as: "createdBy", + paranoid: false, + }, + { + model: User, + as: "updatedBy", + paranoid: false, + }, + ], + offset, + limit, + }); + } + public static async searchForUser( user: User, query: string, diff --git a/server/routes/api/documents/documents.test.ts b/server/routes/api/documents/documents.test.ts index fbdb43c13..bde267871 100644 --- a/server/routes/api/documents/documents.test.ts +++ b/server/routes/api/documents/documents.test.ts @@ -1,3 +1,4 @@ +import { subDays } from "date-fns"; import { CollectionPermission } from "@shared/types"; import { Document, @@ -1048,6 +1049,88 @@ describe("#documents.search_titles", () => { expect(body.data[0].id).toEqual(document.id); }); + it("should allow filtering of results by date", async () => { + const user = await buildUser(); + await buildDocument({ + userId: user.id, + teamId: user.teamId, + title: "Super secret", + createdAt: subDays(new Date(), 365), + updatedAt: subDays(new Date(), 365), + }); + const res = await server.post("/api/documents.search_titles", { + body: { + token: user.getJwtToken(), + query: "SECRET", + dateFilter: "day", + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.length).toEqual(0); + }); + + it("should allow filtering to include archived", async () => { + const user = await buildUser(); + const document = await buildDocument({ + userId: user.id, + teamId: user.teamId, + title: "Super secret", + archivedAt: new Date(), + }); + const res = await server.post("/api/documents.search_titles", { + body: { + token: user.getJwtToken(), + query: "SECRET", + includeArchived: true, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.length).toEqual(1); + expect(body.data[0].id).toEqual(document.id); + }); + + it("should allow filtering to include drafts", async () => { + const user = await buildUser(); + const document = await buildDraftDocument({ + userId: user.id, + teamId: user.teamId, + title: "Super secret", + }); + const res = await server.post("/api/documents.search_titles", { + body: { + token: user.getJwtToken(), + query: "SECRET", + includeDrafts: true, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.length).toEqual(1); + expect(body.data[0].id).toEqual(document.id); + }); + + it("should allow filtering to include a user", async () => { + const user = await buildUser(); + const document = await buildDocument({ + userId: user.id, + teamId: user.teamId, + title: "Super secret", + }); + const res = await server.post("/api/documents.search_titles", { + body: { + token: user.getJwtToken(), + query: "SECRET", + userId: user.id, + }, + }); + const body = await res.json(); + expect(res.status).toEqual(200); + expect(body.data.length).toEqual(1); + expect(body.data[0].id).toEqual(document.id); + }); + it("should not include archived or deleted documents", async () => { const user = await buildUser(); await buildDocument({ diff --git a/server/routes/api/documents/documents.ts b/server/routes/api/documents/documents.ts index 7430a3b2a..9e5626ca6 100644 --- a/server/routes/api/documents/documents.ts +++ b/server/routes/api/documents/documents.ts @@ -588,43 +588,37 @@ router.post( "documents.search_titles", auth(), pagination(), - validate(T.DocumentsSearchTitlesSchema), - async (ctx: APIContext) => { - const { query } = ctx.input; + validate(T.DocumentsSearchSchema), + async (ctx: APIContext) => { + const { + query, + includeArchived, + includeDrafts, + dateFilter, + collectionId, + userId, + } = ctx.input; const { offset, limit } = ctx.state.pagination; const { user } = ctx.state; + let collaboratorIds = undefined; - const collectionIds = await user.collectionIds(); - const documents = await Document.scope([ - { - method: ["withViews", user.id], - }, - { - method: ["withCollectionPermissions", user.id], - }, - ]).findAll({ - where: { - title: { - [Op.iLike]: `%${query}%`, - }, - collectionId: collectionIds, - archivedAt: { - [Op.is]: null, - }, - }, - order: [["updatedAt", "DESC"]], - include: [ - { - model: User, - as: "createdBy", - paranoid: false, - }, - { - model: User, - as: "updatedBy", - paranoid: false, - }, - ], + if (collectionId) { + const collection = await Collection.scope({ + method: ["withMembership", user.id], + }).findByPk(collectionId); + authorize(user, "read", collection); + } + + if (userId) { + collaboratorIds = [userId]; + } + + const documents = await SearchHelper.searchTitlesForUser(user, query, { + includeArchived, + includeDrafts, + dateFilter, + collectionId, + collaboratorIds, offset, limit, }); diff --git a/server/routes/api/documents/schema.ts b/server/routes/api/documents/schema.ts index 565684f14..192542697 100644 --- a/server/routes/api/documents/schema.ts +++ b/server/routes/api/documents/schema.ts @@ -138,12 +138,6 @@ export const DocumentsRestoreSchema = BaseIdSchema.extend({ export type DocumentsRestoreReq = z.infer; -export const DocumentsSearchTitlesSchema = SearchQuerySchema.extend({}); - -export type DocumentsSearchTitlesReq = z.infer< - typeof DocumentsSearchTitlesSchema ->; - export const DocumentsSearchSchema = SearchQuerySchema.merge( DateFilterSchema ).extend({ diff --git a/server/test/factories.ts b/server/test/factories.ts index e6f8c18b0..f5cfd1cb6 100644 --- a/server/test/factories.ts +++ b/server/test/factories.ts @@ -365,14 +365,19 @@ export async function buildDocument( } count++; - return Document.create({ - title: `Document ${count}`, - text: "This is the text in an example document", - publishedAt: isNull(overrides.collectionId) ? null : new Date(), - lastModifiedById: overrides.userId, - createdById: overrides.userId, - ...overrides, - }); + return Document.create( + { + title: `Document ${count}`, + text: "This is the text in an example document", + publishedAt: isNull(overrides.collectionId) ? null : new Date(), + lastModifiedById: overrides.userId, + createdById: overrides.userId, + ...overrides, + }, + { + silent: overrides.createdAt || overrides.updatedAt ? true : false, + } + ); } export async function buildFileOperation( diff --git a/yarn.lock b/yarn.lock index c1885186a..3e07f7e0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -85,7 +85,7 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": +"@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== @@ -3179,12 +3179,12 @@ "@types/prosemirror-state" "*" "@types/prosemirror-view" "*" -"@types/prosemirror-dropcursor@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz#49250849b8a0b86e8c29eb1ba70a463e53e46947" - integrity sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg== +"@types/prosemirror-dropcursor@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz#5633fb20e7c1bf27edbdb64781b09bfb9f150f25" + integrity sha512-Xa13THoY0YkvYP/peH995ahT79w3ErdsmFUIaTY21nshxxnn5mdSgG+RTpkqXwZ85v+n28MvNfLF2gm+c8RZ1A== dependencies: - "@types/prosemirror-state" "*" + prosemirror-dropcursor "*" "@types/prosemirror-gapcursor@^1.0.1": version "1.0.4" @@ -4456,15 +4456,16 @@ babel-plugin-polyfill-regenerator@^0.2.2, babel-plugin-polyfill-regenerator@^0.2 dependencies: "@babel/helper-define-polyfill-provider" "^0.2.4" -"babel-plugin-styled-components@>= 1.12.0", babel-plugin-styled-components@^1.11.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz#1dec1676512177de6b827211e9eda5a30db4f9b9" - integrity sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA== +"babel-plugin-styled-components@>= 1.12.0", babel-plugin-styled-components@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz#c81ef34b713f9da2b7d3f5550df0d1e19e798086" + integrity sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA== dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-annotate-as-pure" "^7.16.0" + "@babel/helper-module-imports" "^7.16.0" babel-plugin-syntax-jsx "^6.18.0" lodash "^4.17.11" + picomatch "^2.3.0" babel-plugin-syntax-class-properties@^6.8.0: version "6.13.0" @@ -7475,13 +7476,14 @@ expect@^28.1.3: jest-message-util "^28.1.3" jest-util "^28.1.3" -exports-loader@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754be2740fc886" - integrity sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY= +exports-loader@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-1.1.1.tgz#88c9a6877ee6a5519d7c41a016bdd99148421e69" + integrity sha512-CmyhIR2sJ3KOfVsHjsR0Yvo+0lhRhRMAevCbB8dhTVLHsZPs0lCQTvRmR9YNvBXDBxUuhmCE2f54KqEjZUaFrg== dependencies: - loader-utils "^1.0.2" - source-map "0.5.x" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + source-map "^0.6.1" express-useragent@^1.0.15: version "1.0.15" @@ -10036,21 +10038,15 @@ jsonpointer@^5.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.0.tgz#f802669a524ec4805fa7389eadbc9921d5dc8072" integrity sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg== -jsonwebtoken@^8.5.0: - version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== +jsonwebtoken@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" + integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== dependencies: jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" + lodash "^4.17.21" ms "^2.1.1" - semver "^5.6.0" + semver "^7.3.8" "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0: version "3.1.0" @@ -10562,46 +10558,16 @@ lodash.flattendeep@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= - lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= - lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= - lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -10617,11 +10583,6 @@ lodash.mergewith@4.6.2: resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - lodash.pick@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" @@ -12089,10 +12050,10 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pidtree@^0.5.0: version "0.5.0" @@ -12377,10 +12338,10 @@ prosemirror-commands@1.2.2: prosemirror-state "^1.0.0" prosemirror-transform "^1.0.0" -prosemirror-dropcursor@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz#91a859d4ee79c99b1c0ba6ee61c093b195c0d9f0" - integrity sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ== +prosemirror-dropcursor@*, prosemirror-dropcursor@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.6.1.tgz#31f696172105f232bd17543ccf305e0f33e59d1d" + integrity sha512-LtyqQpkIknaT7NnZl3vDr3TpkNcG4ABvGRXx37XJ8tJNUGtcrZBh40A0344rDwlRTfUEmynQS/grUsoSWz+HgA== dependencies: prosemirror-state "^1.0.0" prosemirror-transform "^1.1.0" @@ -13624,10 +13585,10 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -14050,7 +14011,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.6: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=