mirror of
https://github.com/outline/outline.git
synced 2026-06-13 03:14:59 +03:00
Add revision deletion endpoints (#9240)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import copy from "copy-to-clipboard";
|
||||
import { LinkIcon, RestoreIcon } from "outline-icons";
|
||||
import { LinkIcon, RestoreIcon, TrashIcon } from "outline-icons";
|
||||
import { matchPath } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import stores from "~/stores";
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from "~/utils/routeHelpers";
|
||||
|
||||
export const restoreRevision = createAction({
|
||||
name: ({ t }) => t("Restore revision"),
|
||||
name: ({ t }) => t("Restore"),
|
||||
analyticsName: "Restore revision",
|
||||
icon: <RestoreIcon />,
|
||||
section: RevisionSection,
|
||||
@@ -41,6 +41,38 @@ export const restoreRevision = createAction({
|
||||
},
|
||||
});
|
||||
|
||||
export const deleteRevision = createAction({
|
||||
name: ({ t }) => t("Delete"),
|
||||
analyticsName: "Delete revision",
|
||||
icon: <TrashIcon />,
|
||||
section: RevisionSection,
|
||||
dangerous: true,
|
||||
visible: ({ activeDocumentId }) =>
|
||||
!!activeDocumentId && stores.policies.abilities(activeDocumentId).update,
|
||||
perform: async ({ t, event, location, activeDocumentId }) => {
|
||||
event?.preventDefault();
|
||||
if (!activeDocumentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const document = stores.documents.get(activeDocumentId);
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = matchPath<{ revisionId: string }>(location.pathname, {
|
||||
path: matchDocumentHistory,
|
||||
});
|
||||
const revisionId = match?.params.revisionId;
|
||||
if (revisionId) {
|
||||
const revision = stores.revisions.get(revisionId);
|
||||
await revision?.delete();
|
||||
toast.success(t("This version of the document was deleted"));
|
||||
history.push(documentHistoryPath(document));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const copyLinkToRevision = createAction({
|
||||
name: ({ t }) => t("Copy link"),
|
||||
analyticsName: "Copy link to revision",
|
||||
|
||||
@@ -48,10 +48,12 @@ export type DocumentEvent = {
|
||||
userId: string;
|
||||
};
|
||||
|
||||
export type Event = { id: string; actorId: string; createdAt: string } & (
|
||||
| RevisionEvent
|
||||
| DocumentEvent
|
||||
);
|
||||
export type Event = {
|
||||
id: string;
|
||||
actorId: string;
|
||||
createdAt: string;
|
||||
deletedAt?: string;
|
||||
} & (RevisionEvent | DocumentEvent);
|
||||
|
||||
type Props = {
|
||||
document: Document;
|
||||
@@ -85,6 +87,7 @@ const EventListItem = ({ event, document, ...rest }: Props) => {
|
||||
if (
|
||||
!document.isDeleted &&
|
||||
event.name === "revisions.create" &&
|
||||
!event.deletedAt &&
|
||||
!isDerivedFromDocument &&
|
||||
!revisionLoadedRef.current
|
||||
) {
|
||||
@@ -95,24 +98,31 @@ const EventListItem = ({ event, document, ...rest }: Props) => {
|
||||
|
||||
switch (event.name) {
|
||||
case "revisions.create":
|
||||
icon = <EditIcon size={16} />;
|
||||
meta = event.latest ? (
|
||||
<>
|
||||
{t("Current version")} · {actor?.name}
|
||||
</>
|
||||
) : (
|
||||
t("{{userName}} edited", opts)
|
||||
);
|
||||
to = {
|
||||
pathname: documentHistoryPath(
|
||||
document,
|
||||
isDerivedFromDocument ? "latest" : event.id
|
||||
),
|
||||
state: {
|
||||
sidebarContext,
|
||||
retainScrollPosition: true,
|
||||
},
|
||||
};
|
||||
{
|
||||
if (event.deletedAt) {
|
||||
icon = <TrashIcon />;
|
||||
meta = t("Revision deleted");
|
||||
} else {
|
||||
icon = <EditIcon size={16} />;
|
||||
meta = event.latest ? (
|
||||
<>
|
||||
{t("Current version")} · {actor?.name}
|
||||
</>
|
||||
) : (
|
||||
t("{{userName}} edited", opts)
|
||||
);
|
||||
to = {
|
||||
pathname: documentHistoryPath(
|
||||
document,
|
||||
isDerivedFromDocument ? "latest" : event.id
|
||||
),
|
||||
state: {
|
||||
sidebarContext,
|
||||
retainScrollPosition: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "documents.archive":
|
||||
@@ -181,7 +191,7 @@ const EventListItem = ({ event, document, ...rest }: Props) => {
|
||||
to = undefined;
|
||||
}
|
||||
|
||||
return event.name === "revisions.create" ? (
|
||||
return event.name === "revisions.create" && !event.deletedAt ? (
|
||||
<RevisionItem
|
||||
small
|
||||
exact
|
||||
@@ -218,7 +228,12 @@ const EventListItem = ({ event, document, ...rest }: Props) => {
|
||||
</IconWrapper>
|
||||
<Text size="xsmall" type="secondary">
|
||||
{meta} ·{" "}
|
||||
<Time dateTime={event.createdAt} relative shorten addSuffix />
|
||||
<Time
|
||||
dateTime={event.deletedAt ?? event.createdAt}
|
||||
relative
|
||||
shorten
|
||||
addSuffix
|
||||
/>
|
||||
</Text>
|
||||
</EventItem>
|
||||
);
|
||||
|
||||
@@ -3,11 +3,11 @@ import { ProsemirrorData } from "@shared/types";
|
||||
import { isRTL } from "@shared/utils/rtl";
|
||||
import Document from "./Document";
|
||||
import User from "./User";
|
||||
import Model from "./base/Model";
|
||||
import ParanoidModel from "./base/ParanoidModel";
|
||||
import Field from "./decorators/Field";
|
||||
import Relation from "./decorators/Relation";
|
||||
|
||||
class Revision extends Model {
|
||||
class Revision extends ParanoidModel {
|
||||
static modelName = "Revision";
|
||||
|
||||
/** The document ID that the revision is related to */
|
||||
|
||||
@@ -50,6 +50,7 @@ function History() {
|
||||
name: "revisions.create",
|
||||
actorId: data.createdBy.id,
|
||||
createdAt: data.createdAt,
|
||||
deletedAt: data.deletedAt,
|
||||
latest: false,
|
||||
} satisfies Event;
|
||||
}
|
||||
@@ -70,7 +71,7 @@ function History() {
|
||||
return [];
|
||||
}
|
||||
|
||||
const [revisionsArr, eventsArr] = await Promise.all([
|
||||
const [revisionsPage, eventsPage] = await Promise.all([
|
||||
revisions.fetchPage({
|
||||
documentId: document.id,
|
||||
offset: offset.revisions,
|
||||
@@ -85,7 +86,7 @@ function History() {
|
||||
]);
|
||||
|
||||
const pageEvents = orderBy(
|
||||
[...revisionsArr, ...eventsArr].map(toEvent),
|
||||
[...revisionsPage, ...eventsPage].map(toEvent),
|
||||
"createdAt",
|
||||
"desc"
|
||||
).slice(0, Pagination.defaultLimit);
|
||||
@@ -110,11 +111,8 @@ function History() {
|
||||
|
||||
const latestRevisionId = RevisionHelper.latestId(document.id);
|
||||
return revisions
|
||||
.filter(
|
||||
(revision: Revision) =>
|
||||
revision.id !== latestRevisionId &&
|
||||
revision.documentId === document.id
|
||||
)
|
||||
.getByDocumentId(document.id)
|
||||
.filter((revision: Revision) => revision.id !== latestRevisionId)
|
||||
.slice(0, offset.revisions)
|
||||
.map(toEvent);
|
||||
}, [document, revisions, offset.revisions, toEvent]);
|
||||
@@ -123,7 +121,7 @@ function History() {
|
||||
() =>
|
||||
document
|
||||
? events
|
||||
.filter({ documentId: document.id })
|
||||
.getByDocumentId(document.id)
|
||||
.slice(0, offset.events)
|
||||
.map(toEvent)
|
||||
: [],
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import orderBy from "lodash/orderBy";
|
||||
import { computed } from "mobx";
|
||||
import Event from "~/models/Event";
|
||||
import RootStore from "./RootStore";
|
||||
import Store, { RPCAction } from "./base/Store";
|
||||
@@ -11,8 +9,12 @@ export default class EventsStore extends Store<Event<any>> {
|
||||
super(rootStore, Event);
|
||||
}
|
||||
|
||||
@computed
|
||||
get orderedData(): Event<any>[] {
|
||||
return orderBy(Array.from(this.data.values()), "createdAt", "desc");
|
||||
}
|
||||
/**
|
||||
* Retrieves all events for a given document ID
|
||||
*
|
||||
* @param documentId - The ID of the document to retrieve events for
|
||||
* @returns An array of events for the specified document ID
|
||||
*/
|
||||
getByDocumentId = (documentId: string): Event<any>[] =>
|
||||
this.orderedData.filter((event) => event.documentId === documentId);
|
||||
}
|
||||
|
||||
@@ -1,50 +1,21 @@
|
||||
import invariant from "invariant";
|
||||
import filter from "lodash/filter";
|
||||
import { action, runInAction } from "mobx";
|
||||
import RootStore from "~/stores/RootStore";
|
||||
import Store, { RPCAction } from "~/stores/base/Store";
|
||||
import Store from "~/stores/base/Store";
|
||||
import Revision from "~/models/Revision";
|
||||
import { PaginationParams } from "~/types";
|
||||
import { client } from "~/utils/ApiClient";
|
||||
|
||||
export default class RevisionsStore extends Store<Revision> {
|
||||
actions = [RPCAction.List, RPCAction.Update, RPCAction.Info];
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
super(rootStore, Revision);
|
||||
}
|
||||
|
||||
getDocumentRevisions(documentId: string): Revision[] {
|
||||
const revisions = filter(this.orderedData, {
|
||||
documentId,
|
||||
});
|
||||
const latestRevision = revisions[0];
|
||||
const document = this.rootStore.documents.get(documentId);
|
||||
|
||||
// There is no guarantee that we have a revision that represents the latest
|
||||
// state of the document. This pushes a fake revision in at the top if there
|
||||
// isn't one
|
||||
if (
|
||||
latestRevision &&
|
||||
document &&
|
||||
latestRevision.createdAt !== document.updatedAt
|
||||
) {
|
||||
revisions.unshift(
|
||||
new Revision(
|
||||
{
|
||||
id: "latest",
|
||||
documentId: document.id,
|
||||
title: document.title,
|
||||
createdAt: document.updatedAt,
|
||||
createdBy: document.createdBy,
|
||||
},
|
||||
this
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return revisions;
|
||||
}
|
||||
/**
|
||||
* Retrieves all revisions for a given document ID
|
||||
*
|
||||
* @param documentId - The ID of the document to retrieve revisions for
|
||||
* @returns An array of revisions for the specified document ID
|
||||
*/
|
||||
getByDocumentId = (documentId: string): Revision[] =>
|
||||
this.orderedData.filter((revision) => revision.documentId === documentId);
|
||||
|
||||
/**
|
||||
* Fetches the latest revision for the given document.
|
||||
@@ -55,25 +26,4 @@ export default class RevisionsStore extends Store<Revision> {
|
||||
const res = await client.post(`/revisions.info`, { documentId });
|
||||
return this.add(res.data);
|
||||
};
|
||||
|
||||
@action
|
||||
fetchPage = async (
|
||||
options: { documentId: string } & (PaginationParams | undefined)
|
||||
): Promise<Revision[]> => {
|
||||
this.isFetching = true;
|
||||
|
||||
try {
|
||||
const res = await client.post("/revisions.list", options);
|
||||
invariant(res?.data, "Document revisions not available");
|
||||
|
||||
let models: Revision[] = [];
|
||||
runInAction("RevisionsStore#fetchPage", () => {
|
||||
models = res.data.map(this.add);
|
||||
this.isLoaded = true;
|
||||
});
|
||||
return models;
|
||||
} finally {
|
||||
this.isFetching = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn("revisions", "deletedAt", {
|
||||
type: Sequelize.DATE,
|
||||
allowNull: true
|
||||
});
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
await queryInterface.removeColumn("revisions", "deletedAt");
|
||||
}
|
||||
};
|
||||
@@ -13,12 +13,13 @@ import {
|
||||
Table,
|
||||
IsNumeric,
|
||||
Length as SimpleLength,
|
||||
BeforeDestroy,
|
||||
} from "sequelize-typescript";
|
||||
import type { ProsemirrorData } from "@shared/types";
|
||||
import { DocumentValidation, RevisionValidation } from "@shared/validations";
|
||||
import Document from "./Document";
|
||||
import User from "./User";
|
||||
import IdModel from "./base/IdModel";
|
||||
import ParanoidModel from "./base/ParanoidModel";
|
||||
import Fix from "./decorators/Fix";
|
||||
import IsHexColor from "./validators/IsHexColor";
|
||||
import Length from "./validators/Length";
|
||||
@@ -34,7 +35,7 @@ import Length from "./validators/Length";
|
||||
}))
|
||||
@Table({ tableName: "revisions", modelName: "revision" })
|
||||
@Fix
|
||||
class Revision extends IdModel<
|
||||
class Revision extends ParanoidModel<
|
||||
InferAttributes<Revision>,
|
||||
Partial<InferCreationAttributes<Revision>>
|
||||
> {
|
||||
@@ -74,7 +75,7 @@ class Revision extends IdModel<
|
||||
* and is no longer being written.
|
||||
*/
|
||||
@Column(DataType.TEXT)
|
||||
text: string;
|
||||
text: string | null;
|
||||
|
||||
/** The content of the revision as JSON. */
|
||||
@Column(DataType.JSONB)
|
||||
@@ -109,6 +110,15 @@ class Revision extends IdModel<
|
||||
@Column(DataType.UUID)
|
||||
userId: string;
|
||||
|
||||
// hooks
|
||||
|
||||
@BeforeDestroy
|
||||
static async clearData(model: Revision) {
|
||||
model.content = null;
|
||||
model.text = null;
|
||||
model.title = "";
|
||||
}
|
||||
|
||||
// static methods
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,7 +104,7 @@ export class DocumentHelper {
|
||||
} else if (document instanceof Collection) {
|
||||
doc = parser.parse(document.description ?? "");
|
||||
} else {
|
||||
doc = parser.parse(document.text);
|
||||
doc = parser.parse(document.text ?? "");
|
||||
}
|
||||
|
||||
if (doc && options?.signedUrls && options?.teamId) {
|
||||
|
||||
@@ -2,10 +2,18 @@ import { User, Revision } from "@server/models";
|
||||
import { allow } from "./cancan";
|
||||
import { and, isTeamMutable, or } from "./utils";
|
||||
|
||||
allow(User, ["update"], Revision, (actor, revision) =>
|
||||
allow(User, "update", Revision, (actor, revision) =>
|
||||
and(
|
||||
//
|
||||
or(actor.id === revision?.userId, actor.isAdmin),
|
||||
isTeamMutable(actor)
|
||||
)
|
||||
);
|
||||
|
||||
allow(User, "delete", Revision, (actor) =>
|
||||
and(
|
||||
//
|
||||
actor.isAdmin,
|
||||
isTeamMutable(actor)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ async function presentRevision(revision: Revision, diff?: string) {
|
||||
html: diff,
|
||||
createdAt: revision.createdAt,
|
||||
createdBy: presentUser(revision.user),
|
||||
deletedAt: revision.deletedAt,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Router from "koa-router";
|
||||
import { Op } from "sequelize";
|
||||
import { UserRole } from "@shared/types";
|
||||
import { RevisionHelper } from "@shared/utils/RevisionHelper";
|
||||
import slugify from "@shared/utils/slugify";
|
||||
import { ValidationError } from "@server/errors";
|
||||
@@ -92,6 +93,37 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
"revisions.delete",
|
||||
auth({ role: UserRole.Admin }),
|
||||
validate(T.RevisionsDeleteSchema),
|
||||
transaction(),
|
||||
async (ctx: APIContext<T.RevisionsDeleteReq>) => {
|
||||
const { id } = ctx.input.body;
|
||||
const { user } = ctx.state.auth;
|
||||
const { transaction } = ctx.state;
|
||||
|
||||
const revision = await Revision.findByPk(id, {
|
||||
rejectOnEmpty: true,
|
||||
lock: {
|
||||
of: Revision,
|
||||
level: transaction.LOCK.UPDATE,
|
||||
},
|
||||
});
|
||||
const document = await Document.findByPk(revision.documentId, {
|
||||
userId: user.id,
|
||||
});
|
||||
authorize(user, "read", document);
|
||||
authorize(user, "delete", revision);
|
||||
|
||||
await revision.destroyWithCtx(ctx);
|
||||
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
router.post(
|
||||
"revisions.diff",
|
||||
auth(),
|
||||
@@ -168,6 +200,7 @@ router.post(
|
||||
order: [[sort, direction]],
|
||||
offset: ctx.state.pagination.offset,
|
||||
limit: ctx.state.pagination.limit,
|
||||
paranoid: false,
|
||||
});
|
||||
const data = await Promise.all(
|
||||
revisions.map((revision) => presentRevision(revision))
|
||||
|
||||
@@ -59,3 +59,11 @@ export const RevisionsListSchema = z.object({
|
||||
});
|
||||
|
||||
export type RevisionsListReq = z.infer<typeof RevisionsListSchema>;
|
||||
|
||||
export const RevisionsDeleteSchema = BaseSchema.extend({
|
||||
body: z.object({
|
||||
id: z.string().uuid(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type RevisionsDeleteReq = z.infer<typeof RevisionsDeleteSchema>;
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
"Archive all notifications": "Archive all notifications",
|
||||
"New App": "New App",
|
||||
"New Application": "New Application",
|
||||
"Restore revision": "Restore revision",
|
||||
"This version of the document was deleted": "This version of the document was deleted",
|
||||
"Link copied": "Link copied",
|
||||
"Dark": "Dark",
|
||||
"Light": "Light",
|
||||
@@ -247,6 +247,7 @@
|
||||
"Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch.": "Sorry, an unrecoverable error occurred{{notified}}. Please try reloading the page, it may have been a temporary glitch.",
|
||||
"our engineers have been notified": "our engineers have been notified",
|
||||
"Show detail": "Show detail",
|
||||
"Revision deleted": "Revision deleted",
|
||||
"Current version": "Current version",
|
||||
"{{userName}} edited": "{{userName}} edited",
|
||||
"{{userName}} archived": "{{userName}} archived",
|
||||
|
||||
@@ -55,6 +55,7 @@ export class EventHelper {
|
||||
"pins.update",
|
||||
"pins.delete",
|
||||
"revisions.create",
|
||||
"revisions.delete",
|
||||
"shares.create",
|
||||
"shares.update",
|
||||
"shares.revoke",
|
||||
|
||||
@@ -4167,18 +4167,7 @@
|
||||
"@smithy/util-utf8" "^4.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/credential-provider-imds@^4.0.4":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.4.tgz#01315ab90c4cb3e017c1ee2c6e5f958aeaa7cf78"
|
||||
integrity sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==
|
||||
dependencies:
|
||||
"@smithy/node-config-provider" "^4.1.1"
|
||||
"@smithy/property-provider" "^4.0.2"
|
||||
"@smithy/types" "^4.2.0"
|
||||
"@smithy/url-parser" "^4.0.2"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/credential-provider-imds@^4.0.5":
|
||||
"@smithy/credential-provider-imds@^4.0.4", "@smithy/credential-provider-imds@^4.0.5":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.5.tgz#d44989d783300af37b2be2fc4ec29cdb67540c32"
|
||||
integrity sha512-saEAGwrIlkb9XxX/m5S5hOtzjoJPEK6Qw2f9pYTbIsMPOFyGSXBBTw95WbOyru8A1vIS2jVCCU1Qhz50QWG3IA==
|
||||
@@ -4381,15 +4370,7 @@
|
||||
"@smithy/types" "^4.3.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/property-provider@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.2.tgz#4572c10415c9d4215f3df1530ba61b0319b17b55"
|
||||
integrity sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==
|
||||
dependencies:
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/property-provider@^4.0.3":
|
||||
"@smithy/property-provider@^4.0.2", "@smithy/property-provider@^4.0.3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.0.3.tgz#cefeb7bc7a8baaeec9f68e82c3164141703a15d5"
|
||||
integrity sha512-Wcn17QNdawJZcZZPBuMuzyBENVi1AXl4TdE0jvzo4vWX2x5df/oMlmr/9M5XAAC6+yae4kWZlOYIsNsgDrMU9A==
|
||||
@@ -4405,16 +4386,7 @@
|
||||
"@smithy/types" "^4.3.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/querystring-builder@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.2.tgz#834cea95bf413ab417bf9c166d60fd80d2cb3016"
|
||||
integrity sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==
|
||||
dependencies:
|
||||
"@smithy/types" "^4.2.0"
|
||||
"@smithy/util-uri-escape" "^4.0.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/querystring-builder@^4.0.3":
|
||||
"@smithy/querystring-builder@^4.0.2", "@smithy/querystring-builder@^4.0.3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.0.3.tgz#056a17082e0a0ab10c817380d96321a8bba588fd"
|
||||
integrity sha512-UUzIWMVfPmDZcOutk2/r1vURZqavvQW0OHvgsyNV0cKupChvqg+/NKPRMaMEe+i8tP96IthMFeZOZWpV+E4RAw==
|
||||
@@ -4438,15 +4410,7 @@
|
||||
dependencies:
|
||||
"@smithy/types" "^4.3.0"
|
||||
|
||||
"@smithy/shared-ini-file-loader@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.2.tgz#15043f0516fe09ff4b22982bc5f644dc701ebae5"
|
||||
integrity sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==
|
||||
dependencies:
|
||||
"@smithy/types" "^4.2.0"
|
||||
tslib "^2.6.2"
|
||||
|
||||
"@smithy/shared-ini-file-loader@^4.0.3":
|
||||
"@smithy/shared-ini-file-loader@^4.0.2", "@smithy/shared-ini-file-loader@^4.0.3":
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.3.tgz#23fab0e773630b0817846c52c54b435ac32a4dd0"
|
||||
integrity sha512-vHwlrqhZGIoLwaH8vvIjpHnloShqdJ7SUPNM2EQtEox+yEDFTVQ7E+DLZ+6OhnYEgFUwPByJyz6UZaOu2tny6A==
|
||||
|
||||
Reference in New Issue
Block a user