fix: Collaboration debounce shared between docs (#3401)

* fix: Collaboration debounce shared between docs

* Rename, Tracing -> Metrics

* Add tracing

* tsc

* fix: Lock document row when loading document in collaboration service incase state needs writing

* fix: Incorrect service name regression
This commit is contained in:
Tom Moor
2022-04-16 14:58:17 -07:00
committed by GitHub
parent 1a8f2c3bb0
commit 4c4b80ba9b
9 changed files with 192 additions and 156 deletions

View File

@@ -51,9 +51,9 @@
"@dnd-kit/core": "^4.0.3", "@dnd-kit/core": "^4.0.3",
"@dnd-kit/modifiers": "^4.0.0", "@dnd-kit/modifiers": "^4.0.0",
"@dnd-kit/sortable": "^5.1.0", "@dnd-kit/sortable": "^5.1.0",
"@getoutline/y-prosemirror": "^1.0.16", "@getoutline/y-prosemirror": "^1.0.18",
"@hocuspocus/provider": "^1.0.0-alpha.21", "@hocuspocus/provider": "^1.0.0-alpha.36",
"@hocuspocus/server": "^1.0.0-alpha.78", "@hocuspocus/server": "^1.0.0-alpha.102",
"@outlinewiki/koa-passport": "^4.1.4", "@outlinewiki/koa-passport": "^4.1.4",
"@outlinewiki/passport-azure-ad-oauth2": "^0.1.0", "@outlinewiki/passport-azure-ad-oauth2": "^0.1.0",
"@renderlesskit/react": "^0.6.0", "@renderlesskit/react": "^0.6.0",
@@ -200,7 +200,7 @@
"winston": "^3.3.3", "winston": "^3.3.3",
"ws": "^7.5.3", "ws": "^7.5.3",
"y-indexeddb": "^9.0.6", "y-indexeddb": "^9.0.6",
"yjs": "^13.5.12" "yjs": "^13.5.34"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.10.5", "@babel/cli": "^7.10.5",

View File

@@ -1,10 +1,14 @@
import { onAuthenticatePayload } from "@hocuspocus/server"; import { onAuthenticatePayload, Extension } from "@hocuspocus/server";
import { APM } from "@server/logging/tracing";
import Document from "@server/models/Document"; import Document from "@server/models/Document";
import { can } from "@server/policies"; import { can } from "@server/policies";
import { getUserForJWT } from "@server/utils/jwt"; import { getUserForJWT } from "@server/utils/jwt";
import { AuthenticationError } from "../errors"; import { AuthenticationError } from "../errors";
export default class Authentication { @APM.trace({
spanName: "authentication",
})
export default class AuthenticationExtension implements Extension {
async onAuthenticate({ async onAuthenticate({
connection, connection,
token, token,

View File

@@ -2,13 +2,14 @@ import {
onConnectPayload, onConnectPayload,
onDisconnectPayload, onDisconnectPayload,
onLoadDocumentPayload, onLoadDocumentPayload,
Extension,
} from "@hocuspocus/server"; } from "@hocuspocus/server";
import Logger from "@server/logging/logger"; import Logger from "@server/logging/logger";
export default class CollaborationLogger { export default class LoggerExtension implements Extension {
async onLoadDocument(data: onLoadDocumentPayload) { async onLoadDocument(data: onLoadDocumentPayload) {
Logger.info("hocuspocus", `Loaded document "${data.documentName}"`, { Logger.info("hocuspocus", `Loaded document "${data.documentName}"`, {
userId: data.context.user.id, userId: data.context.user?.id,
}); });
} }
@@ -17,6 +18,8 @@ export default class CollaborationLogger {
} }
async onDisconnect(data: onDisconnectPayload) { async onDisconnect(data: onDisconnectPayload) {
Logger.info("hocuspocus", `Connection to "${data.documentName}" closed `); Logger.info("hocuspocus", `Closed connection to "${data.documentName}"`, {
userId: data.context.user?.id,
});
} }
} }

View File

@@ -3,11 +3,12 @@ import {
onConnectPayload, onConnectPayload,
onDisconnectPayload, onDisconnectPayload,
onLoadDocumentPayload, onLoadDocumentPayload,
Extension,
} from "@hocuspocus/server"; } from "@hocuspocus/server";
import Metrics from "@server/logging/metrics"; import Metrics from "@server/logging/metrics";
export default class Tracing { export default class MetricsExtension implements Extension {
onLoadDocument({ documentName, instance }: onLoadDocumentPayload) { async onLoadDocument({ documentName, instance }: onLoadDocumentPayload) {
Metrics.increment("collaboration.load_document", { Metrics.increment("collaboration.load_document", {
documentName, documentName,
}); });
@@ -23,7 +24,7 @@ export default class Tracing {
}); });
} }
onConnect({ documentName, instance }: onConnectPayload) { async onConnect({ documentName, instance }: onConnectPayload) {
Metrics.increment("collaboration.connect", { Metrics.increment("collaboration.connect", {
documentName, documentName,
}); });
@@ -33,7 +34,7 @@ export default class Tracing {
); );
} }
onDisconnect({ documentName, instance }: onDisconnectPayload) { async onDisconnect({ documentName, instance }: onDisconnectPayload) {
Metrics.increment("collaboration.disconnect", { Metrics.increment("collaboration.disconnect", {
documentName, documentName,
}); });
@@ -47,13 +48,13 @@ export default class Tracing {
); );
} }
onChange({ documentName }: onChangePayload) { async onStoreDocument({ documentName }: onChangePayload) {
Metrics.increment("collaboration.change", { Metrics.increment("collaboration.change", {
documentName, documentName,
}); });
} }
onDestroy() { async onDestroy() {
Metrics.gaugePerInstance("collaboration.connections_count", 0); Metrics.gaugePerInstance("collaboration.connections_count", 0);
Metrics.gaugePerInstance("collaboration.documents_count", 0); Metrics.gaugePerInstance("collaboration.documents_count", 0);
} }

View File

@@ -0,0 +1,86 @@
import {
onStoreDocumentPayload,
onLoadDocumentPayload,
Extension,
} from "@hocuspocus/server";
import invariant from "invariant";
import * as Y from "yjs";
import { sequelize } from "@server/database/sequelize";
import Logger from "@server/logging/logger";
import { APM } from "@server/logging/tracing";
import Document from "@server/models/Document";
import documentUpdater from "../commands/documentUpdater";
import markdownToYDoc from "./utils/markdownToYDoc";
@APM.trace({
spanName: "persistence",
})
export default class PersistenceExtension implements Extension {
async onLoadDocument({ documentName, ...data }: onLoadDocumentPayload) {
const [, documentId] = documentName.split(".");
const fieldName = "default";
// Check if the given field already exists in the given y-doc. This is import
// so we don't import a document fresh if it exists already.
if (!data.document.isEmpty(fieldName)) {
return;
}
return await sequelize.transaction(async (transaction) => {
const document = await Document.scope("withState").findOne({
transaction,
lock: transaction.LOCK.UPDATE,
where: {
id: documentId,
},
});
invariant(document, "Document not found");
if (document.state) {
const ydoc = new Y.Doc();
Logger.info("database", `Document ${documentId} is in database state`);
Y.applyUpdate(ydoc, document.state);
return ydoc;
}
Logger.info(
"database",
`Document ${documentId} is not in state, creating from markdown`
);
const ydoc = markdownToYDoc(document.text, fieldName);
const state = Y.encodeStateAsUpdate(ydoc);
await document.update(
{
state: Buffer.from(state),
},
{
hooks: false,
transaction,
}
);
return ydoc;
});
}
async onStoreDocument({
document,
context,
documentName,
}: onStoreDocumentPayload) {
const [, documentId] = documentName.split(".");
Logger.info("database", `Persisting ${documentId}`);
try {
await documentUpdater({
documentId,
ydoc: document,
userId: context.user?.id,
});
} catch (err) {
Logger.error("Unable to persist document", err, {
documentId,
userId: context.user?.id,
});
}
}
}

View File

@@ -1,77 +0,0 @@
import { onChangePayload, onLoadDocumentPayload } from "@hocuspocus/server";
import invariant from "invariant";
import { debounce } from "lodash";
import * as Y from "yjs";
import Logger from "@server/logging/logger";
import Document from "@server/models/Document";
import documentUpdater from "../commands/documentUpdater";
import markdownToYDoc from "./utils/markdownToYDoc";
const DELAY = 3000;
export default class Persistence {
async onLoadDocument({ documentName, ...data }: onLoadDocumentPayload) {
const [, documentId] = documentName.split(".");
const fieldName = "default";
// Check if the given field already exists in the given y-doc. This is import
// so we don't import a document fresh if it exists already.
if (!data.document.isEmpty(fieldName)) {
return;
}
const document = await Document.scope("withState").findOne({
where: {
id: documentId,
},
});
invariant(document, "Document not found");
if (document.state) {
const ydoc = new Y.Doc();
Logger.info("database", `Document ${documentId} is in database state`);
Y.applyUpdate(ydoc, document.state);
return ydoc;
}
Logger.info(
"database",
`Document ${documentId} is not in state, creating from markdown`
);
const ydoc = markdownToYDoc(document.text, fieldName);
const state = Y.encodeStateAsUpdate(ydoc);
await document.update(
{
state: Buffer.from(state),
},
{
hooks: false,
}
);
return ydoc;
}
onChange = debounce(
async ({ document, context, documentName }: onChangePayload) => {
const [, documentId] = documentName.split(".");
Logger.info("database", `Persisting ${documentId}`);
try {
await documentUpdater({
documentId,
ydoc: document,
userId: context.user?.id,
});
} catch (err) {
Logger.error("Unable to persist document", err, {
documentId,
userId: context.user?.id,
});
}
},
DELAY,
{
maxWait: DELAY * 3,
}
);
}

View File

@@ -10,7 +10,7 @@ if (process.env.DD_API_KEY) {
// SOURCE_COMMIT is used by Docker Hub // SOURCE_COMMIT is used by Docker Hub
// SOURCE_VERSION is used by Heroku // SOURCE_VERSION is used by Heroku
version: process.env.SOURCE_COMMIT || process.env.SOURCE_VERSION, version: process.env.SOURCE_COMMIT || process.env.SOURCE_VERSION,
service: "outline", service: process.env.DD_SERVICE || "outline",
}, },
{ {
useMock: process.env.NODE_ENV === "test", useMock: process.env.NODE_ENV === "test",

View File

@@ -4,26 +4,28 @@ import { Server } from "@hocuspocus/server";
import invariant from "invariant"; import invariant from "invariant";
import Koa from "koa"; import Koa from "koa";
import WebSocket from "ws"; import WebSocket from "ws";
import AuthenticationExtension from "../collaboration/authentication"; import AuthenticationExtension from "../collaboration/AuthenticationExtension";
import LoggerExtension from "../collaboration/logger"; import LoggerExtension from "../collaboration/LoggerExtension";
import PersistenceExtension from "../collaboration/persistence"; import MetricsExtension from "../collaboration/MetricsExtension";
import TracingExtension from "../collaboration/tracing"; import PersistenceExtension from "../collaboration/PersistenceExtension";
export default function init(app: Koa, server: http.Server) { export default function init(app: Koa, server: http.Server) {
const path = "/collaboration"; const path = "/collaboration";
const wss = new WebSocket.Server({ const wss = new WebSocket.Server({
noServer: true, noServer: true,
}); });
const hocuspocus = Server.configure({ const hocuspocus = Server.configure({
debounce: 3000,
maxDebounce: 10000,
extensions: [ extensions: [
new AuthenticationExtension(), new AuthenticationExtension(),
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Persistence' is not assignable to type 'Exte... Remove this comment to see the full error message
new PersistenceExtension(), new PersistenceExtension(),
new LoggerExtension(), new LoggerExtension(),
// @ts-expect-error ts-migrate(2322) FIXME: Type 'Persistence' is not assignable to type 'Exte... Remove this comment to see the full error message new MetricsExtension(),
new TracingExtension(),
], ],
}); });
server.on("upgrade", function (req, socket, head) { server.on("upgrade", function (req, socket, head) {
if (req.url && req.url.indexOf(path) > -1) { if (req.url && req.url.indexOf(path) > -1) {
const documentName = url.parse(req.url).pathname?.split("/").pop(); const documentName = url.parse(req.url).pathname?.split("/").pop();
@@ -34,7 +36,8 @@ export default function init(app: Koa, server: http.Server) {
}); });
} }
}); });
server.on("shutdown", () => { server.on("shutdown", () => {
hocuspocus.destroy(); return hocuspocus.destroy();
}); });
} }

126
yarn.lock
View File

@@ -1276,36 +1276,47 @@
dependencies: dependencies:
tslib "^2.1.0" tslib "^2.1.0"
"@getoutline/y-prosemirror@^1.0.16": "@getoutline/y-prosemirror@^1.0.18":
version "1.0.16" version "1.0.18"
resolved "https://registry.yarnpkg.com/@getoutline/y-prosemirror/-/y-prosemirror-1.0.16.tgz#34a29966208113bceb8c46bbf8417853e8885e09" resolved "https://registry.yarnpkg.com/@getoutline/y-prosemirror/-/y-prosemirror-1.0.18.tgz#17245c0362d30adb85131c86fb9a59358884b234"
integrity sha512-rJ3NF3Qk9v1/dNKQ25k3aud7TcV8s5175U9stagCDdvwsI67JufgzaWhT0jBhX8tEaZPdQVmXoqe8bf6OC72Cg== integrity sha512-nLxqUHEHJDBwbcMWhlPWlJ4VpdjtajkmKSAWeVTsIEa5HTo1JQSdnADdS/HFSVSkESW8b6TRrOJylyHDn46uYQ==
dependencies: dependencies:
lib0 "^0.2.42" lib0 "^0.2.42"
"@hocuspocus/provider@^1.0.0-alpha.21": "@hocuspocus/common@^1.0.0-alpha.11":
version "1.0.0-alpha.21" version "1.0.0-alpha.11"
resolved "https://registry.yarnpkg.com/@hocuspocus/provider/-/provider-1.0.0-alpha.21.tgz#369869e33a7c138041a03bbb80acd61dd77e3632" resolved "https://registry.yarnpkg.com/@hocuspocus/common/-/common-1.0.0-alpha.11.tgz#d598d221465338c1d912251105519e88f8486805"
integrity sha512-24brZ0OIeUInbMBTN2weL06Xmt4KUn3Sj7BbNkxgt7QwHjIqpcqlZrHE8IYg46HTbp8k0KKnc1CCMxb1ui0fRA== integrity sha512-oOddSLUTr8KrC58KSs5YHzr99ZSI4HZdIkYRoqmrusViF8M850uLXgYce7eG7Xaq4KlvXCSDG+wioQRTFXkCaA==
dependencies: dependencies:
"@lifeomic/attempt" "^3.0.0" lib0 "^0.2.47"
lib0 "^0.2.42"
"@hocuspocus/provider@^1.0.0-alpha.36":
version "1.0.0-alpha.36"
resolved "https://registry.yarnpkg.com/@hocuspocus/provider/-/provider-1.0.0-alpha.36.tgz#a09dd42baa9c88cbd63027a18edcd3f79823a0cf"
integrity sha512-vmrbaS2Si408Gau1vv/xH7ln/QbVJtUZsgLz0DwYYWWGcAJffaU1f89B2+sOL8IGRLclybldrVfZR+tFKBMzRw==
dependencies:
"@hocuspocus/common" "^1.0.0-alpha.11"
"@lifeomic/attempt" "^3.0.2"
lib0 "^0.2.46"
y-protocols "^1.0.5" y-protocols "^1.0.5"
yjs "^13.5.0" yjs "^13.5.29"
"@hocuspocus/server@^1.0.0-alpha.78": "@hocuspocus/server@^1.0.0-alpha.102":
version "1.0.0-alpha.78" version "1.0.0-alpha.102"
resolved "https://registry.yarnpkg.com/@hocuspocus/server/-/server-1.0.0-alpha.78.tgz#06597ae871e3cfc68dd4fda5e6fbc696c8baf452" resolved "https://registry.yarnpkg.com/@hocuspocus/server/-/server-1.0.0-alpha.102.tgz#6c478032b3b30b45d96cbb744d8f61a6b9a71cf1"
integrity sha512-78HbOiJLo2b130UjJk7Z/Olue/qKBeL/CPn2B5/FrDRa41SClftAXipQTSa6J7JYbMi5tUyDP8cO2gckBC2Q3Q== integrity sha512-U82HAy9S9gNuPShsUrefJH2Bdv71+6gjIueNW39oLiWjR87Nmuenjzu1gbVcC6sJwjlsj3JJ0E1NDPu0xTDfxQ==
dependencies: dependencies:
"@types/async-lock" "^1.1.2" "@hocuspocus/common" "^1.0.0-alpha.11"
"@types/uuid" "^8.3.0" "@types/async-lock" "^1.1.3"
"@types/ws" "^8.2.0" "@types/uuid" "^8.3.4"
async-lock "^1.2.8" "@types/ws" "^8.5.3"
lib0 "^0.2.41" async-lock "^1.3.1"
kleur "^4.1.4"
lib0 "^0.2.46"
uuid "^8.3.2" uuid "^8.3.2"
ws "^8.2.3" ws "^8.5.0"
yjs "^13.5.0" y-protocols "^1.0.5"
yjs "^13.5.29"
"@icons/material@^0.2.4": "@icons/material@^0.2.4":
version "0.2.4" version "0.2.4"
@@ -1802,10 +1813,10 @@
"@babel/runtime" "^7.7.2" "@babel/runtime" "^7.7.2"
regenerator-runtime "^0.13.3" regenerator-runtime "^0.13.3"
"@lifeomic/attempt@^3.0.0": "@lifeomic/attempt@^3.0.2":
version "3.0.0" version "3.0.3"
resolved "https://registry.yarnpkg.com/@lifeomic/attempt/-/attempt-3.0.0.tgz#75fecc204f8b0ac18b5363b4404bb32450f01859" resolved "https://registry.yarnpkg.com/@lifeomic/attempt/-/attempt-3.0.3.tgz#e742a5b85eb673e2f1746b0f39cb932cbc6145bb"
integrity sha512-Ibk4Vfl46dSrhtH5fHsrTA4waAuyP7/qcr3uo0mO70azRc6LWgJILlMy3B1oOvyiN9jQcdqwsThaQkPKLiYKTg== integrity sha512-GlM2AbzrErd/TmLL3E8hAHmb5Q7VhDJp35vIbyPVA5Rz55LZuRr8pwL3qrwwkVNo05gMX1J44gURKb4MHQZo7w==
"@nicolo-ribaudo/chokidar-2@^2.1.8": "@nicolo-ribaudo/chokidar-2@^2.1.8":
version "2.1.8" version "2.1.8"
@@ -2582,10 +2593,10 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/async-lock@^1.1.2": "@types/async-lock@^1.1.3":
version "1.1.3" version "1.1.5"
resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.3.tgz#0d86017cf87abbcb941c55360e533d37a3f23b3d" resolved "https://registry.yarnpkg.com/@types/async-lock/-/async-lock-1.1.5.tgz#a82f33e09aef451d6ded7bffae73f9d254723124"
integrity sha512-UpeDcjGKsYEQMeqEbfESm8OWJI305I7b9KE4ji3aBjoKWyN5CTdn8izcA1FM1DVDne30R5fNEnIy89vZw5LXJQ== integrity sha512-A9ClUfmj6wwZMLRz0NaYzb98YH1exlHdf/cdDSKBfMQJnPOdO8xlEW0Eh2QsTTntGzOFWURcEjYElkZ1IY4GCQ==
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
version "7.1.17" version "7.1.17"
@@ -3452,10 +3463,10 @@
resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.0.tgz#8f4875063d2ea966c57a34a25c11333520e83980" resolved "https://registry.yarnpkg.com/@types/utf8/-/utf8-3.0.0.tgz#8f4875063d2ea966c57a34a25c11333520e83980"
integrity sha512-QrhvCktdm5wD48axAnjqSzPH9lOj0MiCYfMX6MSqGs2Jv+txwvdxviXiCEj8zSCWIEDU9SIJ7g9pU5KtxRgYSg== integrity sha512-QrhvCktdm5wD48axAnjqSzPH9lOj0MiCYfMX6MSqGs2Jv+txwvdxviXiCEj8zSCWIEDU9SIJ7g9pU5KtxRgYSg==
"@types/uuid@^8.3.0": "@types/uuid@^8.3.4":
version "8.3.1" version "8.3.4"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
"@types/validator@*", "@types/validator@^13.7.1": "@types/validator@*", "@types/validator@^13.7.1":
version "13.7.1" version "13.7.1"
@@ -3483,10 +3494,10 @@
anymatch "^3.0.0" anymatch "^3.0.0"
source-map "^0.6.0" source-map "^0.6.0"
"@types/ws@^8.2.0": "@types/ws@^8.5.3":
version "8.2.0" version "8.5.3"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.0.tgz#75faefbe2328f3b833cb8dc640658328990d04f3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
integrity sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg== integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
@@ -4130,10 +4141,10 @@ async-each@^1.0.1:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async-lock@^1.2.8: async-lock@^1.3.1:
version "1.3.0" version "1.3.1"
resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.3.0.tgz#0fba111bea8b9693020857eba4f9adca173df3e5" resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.3.1.tgz#f2301c200600cde97acc386453b7126fa8aced3c"
integrity sha512-8A7SkiisnEgME2zEedtDYPxUPzdv3x//E7n5IFktPAtMYSEAV7eNJF0rMwrVyUFj6d/8rgajLantbjcNRQYXIg== integrity sha512-zK7xap9UnttfbE23JmcrNIyueAn6jWshihJqA33U/hEnKprF/lVGBDsBv/bqLm2YMMl1DnpHhUY044eA0t1TUw==
async@0.9.x: async@0.9.x:
version "0.9.2" version "0.9.2"
@@ -9693,6 +9704,11 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
kleur@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
koa-body@^4.2.0: koa-body@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-4.2.0.tgz#37229208b820761aca5822d14c5fc55cee31b26f" resolved "https://registry.yarnpkg.com/koa-body/-/koa-body-4.2.0.tgz#37229208b820761aca5822d14c5fc55cee31b26f"
@@ -9975,10 +9991,10 @@ levn@~0.3.0:
prelude-ls "~1.1.2" prelude-ls "~1.1.2"
type-check "~0.3.2" type-check "~0.3.2"
lib0@^0.2.35, lib0@^0.2.41, lib0@^0.2.42: lib0@^0.2.35, lib0@^0.2.42, lib0@^0.2.46, lib0@^0.2.47, lib0@^0.2.49:
version "0.2.42" version "0.2.49"
resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.42.tgz#6d8bf1fb8205dec37a953c521c5ee403fd8769b0" resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.49.tgz#7addb5075063d66ea2c55749e5aeaa48e36278c8"
integrity sha512-8BNM4MiokEKzMvSxTOC3gnCBisJH+jL67CnSnqzHv3jli3pUvGC8wz+0DQ2YvGr4wVQdb2R2uNNPw9LEpVvJ4Q== integrity sha512-ziwYLe/pmI9bjHsAehm4ApuVfZ+q+sbC+vO6Z5+KM+0Fe0MrTLwZSDkJ+cElnhFNQ0P6z/wVkRmc5+vTmImJ9A==
dependencies: dependencies:
isomorphic.js "^0.2.4" isomorphic.js "^0.2.4"
@@ -15390,10 +15406,10 @@ ws@^7.4.6, ws@^7.5.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
ws@^8.2.3: ws@^8.5.0:
version "8.2.3" version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
ws@~7.4.2: ws@~7.4.2:
version "7.4.6" version "7.4.6"
@@ -15614,12 +15630,12 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=
yjs@^13.5.0, yjs@^13.5.12: yjs@^13.5.29, yjs@^13.5.34:
version "13.5.12" version "13.5.34"
resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.5.12.tgz#7a0cf3119fb368c07243825e989a55de164b3f9c" resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.5.34.tgz#ad9ddb8b6c0806e15b289ff0eabc4f06ba238952"
integrity sha512-/buy1kh8Ls+t733Lgov9hiNxCsjHSCymTuZNahj2hsPNoGbvnSdDmCz9Z4F19Yr1eUAAXQLJF3q7fiBcvPC6Qg== integrity sha512-w/XTk5vhCzbyd6uKKJWE6rPUBf9+heOTzgq8DBkcVgBMv7oeJVFQw2sRqY0YvuLZxURd/XVD2dcNnw8qeFH7Tw==
dependencies: dependencies:
lib0 "^0.2.41" lib0 "^0.2.49"
ylru@^1.2.0: ylru@^1.2.0:
version "1.2.1" version "1.2.1"