@strapi/upload 5.60.0 → 5.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js +29 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js.map +1 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs +27 -0
- package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs.map +1 -0
- package/dist/admin/components/Drawer.js +12 -7
- package/dist/admin/components/Drawer.js.map +1 -1
- package/dist/admin/components/Drawer.mjs +12 -7
- package/dist/admin/components/Drawer.mjs.map +1 -1
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js +188 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js.map +1 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs +186 -0
- package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs.map +1 -0
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +5 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +5 -1
- package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +37 -27
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +37 -27
- package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.js +46 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.js.map +1 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs +43 -0
- package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs.map +1 -0
- package/dist/admin/index.js +10 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +10 -1
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js +1 -1
- package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/pages/Assets/AssetsPage.mjs +2 -2
- package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +2 -2
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +181 -65
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +184 -69
- package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
- package/dist/admin/pages/Assets/components/AssetDetails/AssetPreview.mjs +1 -1
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js +2 -6
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -1
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +2 -6
- package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -1
- package/dist/admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.d.ts +19 -0
- package/dist/admin/src/components/Drawer.d.ts +8 -0
- package/dist/admin/src/components/EditorFileDropZone/EditorFileDropZone.d.ts +21 -0
- package/dist/admin/src/components/MediaLibraryInput/AddAssetMenu.d.ts +7 -1
- package/dist/admin/src/components/MediaLibraryInput/openAssetDetails.d.ts +10 -0
- package/dist/admin/src/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +41 -5
- package/dist/admin/src/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +2 -6
- package/dist/admin/translations/en.json.js +4 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +4 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/audit-logs.js +264 -0
- package/dist/server/audit-logs.js.map +1 -0
- package/dist/server/audit-logs.mjs +255 -0
- package/dist/server/audit-logs.mjs.map +1 -0
- package/dist/server/bootstrap.js +4 -0
- package/dist/server/bootstrap.js.map +1 -1
- package/dist/server/bootstrap.mjs +4 -0
- package/dist/server/bootstrap.mjs.map +1 -1
- package/dist/server/controllers/admin-folder-file.js +23 -1
- package/dist/server/controllers/admin-folder-file.js.map +1 -1
- package/dist/server/controllers/admin-folder-file.mjs +23 -1
- package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
- package/dist/server/services/folder.js +48 -1
- package/dist/server/services/folder.js.map +1 -1
- package/dist/server/services/folder.mjs +48 -1
- package/dist/server/services/folder.mjs.map +1 -1
- package/dist/server/services/image-manipulation.js +12 -5
- package/dist/server/services/image-manipulation.js.map +1 -1
- package/dist/server/services/image-manipulation.mjs +12 -5
- package/dist/server/services/image-manipulation.mjs.map +1 -1
- package/dist/server/services/upload.js +100 -16
- package/dist/server/services/upload.js.map +1 -1
- package/dist/server/services/upload.mjs +102 -18
- package/dist/server/services/upload.mjs.map +1 -1
- package/dist/server/src/audit-logs.d.ts +142 -0
- package/dist/server/src/audit-logs.d.ts.map +1 -0
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +3 -3
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
- package/dist/server/src/services/folder.d.ts.map +1 -1
- package/dist/server/src/services/image-manipulation.d.ts +3 -3
- package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +3 -3
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/upload.d.ts.map +1 -1
- package/package.json +9 -16
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
interface AddAssetMenuProps {
|
|
2
2
|
disabled?: boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Ignore requests to open, without looking disabled. The press still reaches
|
|
5
|
+
* whatever listens for presses outside it, so an open drawer closes — or asks
|
|
6
|
+
* about unsaved edits — instead of having the menu stacked over it.
|
|
7
|
+
*/
|
|
8
|
+
preventOpen?: boolean;
|
|
3
9
|
onBrowseLibrary: () => void;
|
|
4
10
|
onUploadFromDevice: () => void;
|
|
5
11
|
onUploadFromUrl: () => void;
|
|
@@ -15,5 +21,5 @@ interface AddAssetMenuProps {
|
|
|
15
21
|
* The drop zone below keeps working whatever this shows — it is a second way
|
|
16
22
|
* in, not the only one.
|
|
17
23
|
*/
|
|
18
|
-
export declare const AddAssetMenu: ({ disabled, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl, }: AddAssetMenuProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
24
|
+
export declare const AddAssetMenu: ({ disabled, preventOpen, onBrowseLibrary, onUploadFromDevice, onUploadFromUrl, }: AddAssetMenuProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
19
25
|
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Read at event time, so a handler never acts on a stale render. */
|
|
2
|
+
export declare const isAnyAssetDetailsOpen: () => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Registers this field's drawer while `isOpen`, and returns whether any field —
|
|
5
|
+
* this one included — has one open.
|
|
6
|
+
*
|
|
7
|
+
* A layout effect, not a plain one: the drawer that a press closes must be gone
|
|
8
|
+
* from the set before that press's click reaches the next field.
|
|
9
|
+
*/
|
|
10
|
+
export declare const useOpenAssetDetails: (isOpen: boolean) => boolean;
|
|
@@ -15,9 +15,6 @@ interface AssetOperations {
|
|
|
15
15
|
export declare const AssetOperationsContext: React.Context<AssetOperations | null>;
|
|
16
16
|
export declare const useAssetDetailsParam: () => {
|
|
17
17
|
assetId: number | null;
|
|
18
|
-
isVisible: boolean;
|
|
19
|
-
shouldRenderDrawer: boolean;
|
|
20
|
-
onCloseAnimationEnd: (event: React.AnimationEvent) => void;
|
|
21
18
|
openDetails: (id: number) => void;
|
|
22
19
|
closeDetails: () => void;
|
|
23
20
|
};
|
|
@@ -40,7 +37,46 @@ export declare const getBusyMessage: (state: {
|
|
|
40
37
|
interface AssetDetailsProps {
|
|
41
38
|
asset: AssetWithPopulatedCreatedBy;
|
|
42
39
|
closeDetails: () => void;
|
|
40
|
+
/** Receives whether the form has unsaved edits. */
|
|
41
|
+
hasUnsavedEditsRef?: React.MutableRefObject<boolean>;
|
|
42
|
+
/** Guard unsaved edits with <Blocker>, for a drawer whose closing is a navigation. */
|
|
43
|
+
guardNavigation?: boolean;
|
|
44
|
+
/** After the asset was updated, replaced or cropped in place, with the server's copy. */
|
|
45
|
+
onAssetChange?: (asset: AssetWithPopulatedCreatedBy) => void;
|
|
46
|
+
/** After the asset was deleted from the library. */
|
|
47
|
+
onAssetDelete?: (id: number) => void;
|
|
48
|
+
/** `location` stamped on the tracking events, for analytics to tell hosts apart. */
|
|
49
|
+
trackedLocation?: string;
|
|
43
50
|
}
|
|
44
|
-
export declare const AssetDetails: ({ asset, closeDetails }: AssetDetailsProps) => import("react/jsx-runtime").JSX.Element;
|
|
45
|
-
|
|
51
|
+
export declare const AssetDetails: ({ asset, closeDetails, hasUnsavedEditsRef, guardNavigation, onAssetChange, onAssetDelete, trackedLocation, }: AssetDetailsProps) => import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
interface AssetDetailsDrawerProps {
|
|
53
|
+
/** The asset to show, or `null` to close the drawer. */
|
|
54
|
+
assetId: number | null;
|
|
55
|
+
onClose: () => void;
|
|
56
|
+
/**
|
|
57
|
+
* Guard unsaved edits with <Blocker> instead of the drawer's own confirmation.
|
|
58
|
+
* Only for a drawer whose closing is itself a navigation, which <Blocker> then
|
|
59
|
+
* covers along with route changes and browser history. Not for a host that
|
|
60
|
+
* has its own <Blocker>: react-router allows one per page.
|
|
61
|
+
*/
|
|
62
|
+
guardNavigation?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* After the asset was updated, replaced or cropped in place, with the server's
|
|
65
|
+
* copy, so a host holding the asset (e.g. a form field) can stay in sync.
|
|
66
|
+
*/
|
|
67
|
+
onAssetChange?: (asset: AssetWithPopulatedCreatedBy) => void;
|
|
68
|
+
/** After the asset was deleted from the library, just before the drawer closes. */
|
|
69
|
+
onAssetDelete?: (id: number) => void;
|
|
70
|
+
/**
|
|
71
|
+
* `location` stamped on the tracking events, for analytics to tell hosts
|
|
72
|
+
* apart. Defaults to the Media Library page's.
|
|
73
|
+
*/
|
|
74
|
+
trackedLocation?: string;
|
|
75
|
+
}
|
|
76
|
+
export declare const AssetDetailsDrawer: ({ assetId, onClose, guardNavigation, onAssetChange, onAssetDelete, trackedLocation, }: AssetDetailsDrawerProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
77
|
+
/**
|
|
78
|
+
* The drawer bound to the URL's `?assetId=`, as the Media Library page uses it:
|
|
79
|
+
* linkable, survives a refresh, and closing is a navigation.
|
|
80
|
+
*/
|
|
81
|
+
export declare const AssetDetailsDrawerFromUrl: () => import("react/jsx-runtime").JSX.Element;
|
|
46
82
|
export {};
|
|
@@ -8,11 +8,7 @@ export declare const ASSET_DETAILS_URL_PARAM = "assetId";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const parseAssetDetailsId: (raw: string | undefined) => number | null;
|
|
10
10
|
/**
|
|
11
|
-
* Whether the asset details drawer is open, derived purely from the URL
|
|
12
|
-
*
|
|
13
|
-
* Deliberately separate from `useAssetDetailsParam`, which additionally owns the
|
|
14
|
-
* drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the
|
|
15
|
-
* component wired to the close animation, so a second caller would strand it at
|
|
16
|
-
* `true`. Anything that just needs the answer reads this instead.
|
|
11
|
+
* Whether the asset details drawer is open, derived purely from the URL — so it
|
|
12
|
+
* turns false as soon as closing starts, while the drawer is still sliding out.
|
|
17
13
|
*/
|
|
18
14
|
export declare const useIsAssetDetailsOpen: () => boolean;
|
|
@@ -12,6 +12,9 @@ var en = {
|
|
|
12
12
|
"asset-picker.tabs.label": "Asset picker tabs",
|
|
13
13
|
"asset-picker.tabs.selected": "Selected assets",
|
|
14
14
|
"asset-picker.title": "Add asset for {fieldName}",
|
|
15
|
+
"asset-picker.title.insert": "Add assets",
|
|
16
|
+
"editor-drop-zone.accept": "Drop to upload and insert",
|
|
17
|
+
"editor-drop-zone.reject": "You don't have permission to upload files",
|
|
15
18
|
"asset-details.title": "File details",
|
|
16
19
|
"asset-details.description": "Displays file information and metadata",
|
|
17
20
|
"asset-details.fileInfo": "File info",
|
|
@@ -34,6 +37,7 @@ var en = {
|
|
|
34
37
|
"asset-details.field.empty": "{label} is currently empty.",
|
|
35
38
|
"asset-details.location.root": "Media Library",
|
|
36
39
|
"asset-details.saveChanges": "Save changes",
|
|
40
|
+
"asset-details.unsaved.confirm": "You have unsaved changes, are you sure you want to close this file's details? Your changes will be lost.",
|
|
37
41
|
"asset-details.delete.trigger": "Delete this file",
|
|
38
42
|
"asset-details.delete.title": "Delete this media file?",
|
|
39
43
|
"asset-details.delete.description": "This file cannot be recovered once deleted. If it is currently in use, linked content will break and image containers will be empty.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.json.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -10,6 +10,9 @@ var en = {
|
|
|
10
10
|
"asset-picker.tabs.label": "Asset picker tabs",
|
|
11
11
|
"asset-picker.tabs.selected": "Selected assets",
|
|
12
12
|
"asset-picker.title": "Add asset for {fieldName}",
|
|
13
|
+
"asset-picker.title.insert": "Add assets",
|
|
14
|
+
"editor-drop-zone.accept": "Drop to upload and insert",
|
|
15
|
+
"editor-drop-zone.reject": "You don't have permission to upload files",
|
|
13
16
|
"asset-details.title": "File details",
|
|
14
17
|
"asset-details.description": "Displays file information and metadata",
|
|
15
18
|
"asset-details.fileInfo": "File info",
|
|
@@ -32,6 +35,7 @@ var en = {
|
|
|
32
35
|
"asset-details.field.empty": "{label} is currently empty.",
|
|
33
36
|
"asset-details.location.root": "Media Library",
|
|
34
37
|
"asset-details.saveChanges": "Save changes",
|
|
38
|
+
"asset-details.unsaved.confirm": "You have unsaved changes, are you sure you want to close this file's details? Your changes will be lost.",
|
|
35
39
|
"asset-details.delete.trigger": "Delete this file",
|
|
36
40
|
"asset-details.delete.title": "Delete this media file?",
|
|
37
41
|
"asset-details.delete.description": "This file cannot be recovered once deleted. If it is currently in use, linked content will break and image containers will be empty.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"en.json.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var utils = require('@strapi/utils');
|
|
4
|
+
|
|
5
|
+
const AUDITED_EVENTS = {
|
|
6
|
+
UPLOAD_SETTINGS_UPDATE: 'upload-settings.update',
|
|
7
|
+
MEDIA_CREATE: 'media.create',
|
|
8
|
+
MEDIA_UPDATE: 'media.update',
|
|
9
|
+
MEDIA_DELETE: 'media.delete',
|
|
10
|
+
MEDIA_MOVE: 'media.move',
|
|
11
|
+
FOLDER_CREATE: 'media-folder.create',
|
|
12
|
+
FOLDER_UPDATE: 'media-folder.update',
|
|
13
|
+
// Audit-only events. media.update is a webhook event that carries neither the file as it was
|
|
14
|
+
// nor its folder; media-folder.delete carries every deleted folder in one event.
|
|
15
|
+
MEDIA_UPDATE_AUDIT: 'media.update.audit',
|
|
16
|
+
FOLDER_DELETE_AUDIT: 'media-folder.delete.audit'
|
|
17
|
+
};
|
|
18
|
+
const AUDITED_SETTINGS = [
|
|
19
|
+
'sizeOptimization',
|
|
20
|
+
'responsiveDimensions',
|
|
21
|
+
'autoOrientation',
|
|
22
|
+
'aiMetadata'
|
|
23
|
+
];
|
|
24
|
+
// The store replaces the whole value, so an option missing from the request is removed and recorded as null.
|
|
25
|
+
const getUploadSettingsChanges = (previous, next)=>{
|
|
26
|
+
const changes = {};
|
|
27
|
+
for (const setting of AUDITED_SETTINGS){
|
|
28
|
+
const before = previous?.[setting] ?? null;
|
|
29
|
+
const after = next[setting] ?? null;
|
|
30
|
+
if (before !== after) {
|
|
31
|
+
changes[setting] = {
|
|
32
|
+
before,
|
|
33
|
+
after
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return changes;
|
|
38
|
+
};
|
|
39
|
+
const toFolderRef = (folder)=>folder ? {
|
|
40
|
+
id: Number(folder.id),
|
|
41
|
+
name: folder.name ?? null
|
|
42
|
+
} : null;
|
|
43
|
+
/** Picks the fields the audit log compares. */ const toAuditedFile = (file)=>({
|
|
44
|
+
id: Number(file.id),
|
|
45
|
+
name: file.name,
|
|
46
|
+
mime: file.mime,
|
|
47
|
+
size: file.size,
|
|
48
|
+
alternativeText: file.alternativeText ?? null,
|
|
49
|
+
caption: file.caption ?? null,
|
|
50
|
+
focalPoint: file.focalPoint ?? null,
|
|
51
|
+
folder: toFolderRef(file.folder)
|
|
52
|
+
});
|
|
53
|
+
const summarizeFile = (file)=>({
|
|
54
|
+
name: file.name,
|
|
55
|
+
mime: file.mime ?? null,
|
|
56
|
+
size: file.size ?? null
|
|
57
|
+
});
|
|
58
|
+
const getMediaChanges = ({ file: media, previous, replaced })=>{
|
|
59
|
+
const changes = {};
|
|
60
|
+
if (previous.name !== media.name) {
|
|
61
|
+
changes.name = {
|
|
62
|
+
before: previous.name,
|
|
63
|
+
after: media.name
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// The admin sends '' for an empty field that is stored as null
|
|
67
|
+
const blankAsNull = (value)=>value || null;
|
|
68
|
+
for (const field of [
|
|
69
|
+
'alternativeText',
|
|
70
|
+
'caption'
|
|
71
|
+
]){
|
|
72
|
+
const before = previous[field] ?? null;
|
|
73
|
+
const after = media[field] ?? null;
|
|
74
|
+
if (blankAsNull(before) !== blankAsNull(after)) {
|
|
75
|
+
changes[field] = {
|
|
76
|
+
before,
|
|
77
|
+
after
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const focalBefore = previous.focalPoint ?? null;
|
|
82
|
+
const focalAfter = media.focalPoint ?? null;
|
|
83
|
+
if (JSON.stringify(focalBefore) !== JSON.stringify(focalAfter)) {
|
|
84
|
+
changes.focalPoint = {
|
|
85
|
+
before: focalBefore,
|
|
86
|
+
after: focalAfter
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const folderBefore = toFolderRef(previous.folder);
|
|
90
|
+
const folderAfter = toFolderRef(media.folder);
|
|
91
|
+
if ((folderBefore?.id ?? null) !== (folderAfter?.id ?? null)) {
|
|
92
|
+
changes.folder = {
|
|
93
|
+
before: folderBefore,
|
|
94
|
+
after: folderAfter
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (replaced) {
|
|
98
|
+
changes.file = {
|
|
99
|
+
before: summarizeFile(previous),
|
|
100
|
+
after: summarizeFile(media)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return changes;
|
|
104
|
+
};
|
|
105
|
+
const getFolderChanges = ({ folder, previous })=>{
|
|
106
|
+
if (!previous) {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
const changes = {};
|
|
110
|
+
if (previous.name !== folder.name) {
|
|
111
|
+
changes.name = {
|
|
112
|
+
before: previous.name,
|
|
113
|
+
after: folder.name
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const parentBefore = toFolderRef(previous.parent);
|
|
117
|
+
const parentAfter = toFolderRef(folder.parent);
|
|
118
|
+
if ((parentBefore?.id ?? null) !== (parentAfter?.id ?? null)) {
|
|
119
|
+
changes.parent = {
|
|
120
|
+
before: parentBefore,
|
|
121
|
+
after: parentAfter
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return changes;
|
|
125
|
+
};
|
|
126
|
+
const readParents = async (read, joinTable, ids)=>{
|
|
127
|
+
if (ids.length === 0) {
|
|
128
|
+
return new Map();
|
|
129
|
+
}
|
|
130
|
+
const { joinColumn, inverseJoinColumn } = joinTable;
|
|
131
|
+
const rows = await read(joinTable.name).select(joinColumn.name, inverseJoinColumn.name).whereIn(joinColumn.name, ids);
|
|
132
|
+
return new Map(rows.map((row)=>[
|
|
133
|
+
Number(row[joinColumn.name]),
|
|
134
|
+
Number(row[inverseJoinColumn.name])
|
|
135
|
+
]));
|
|
136
|
+
};
|
|
137
|
+
const readMediaMoves = async (read, { files, folders, destinationFolderId, fileFolderJoinTable, folderParentJoinTable, folderTable })=>{
|
|
138
|
+
const fileParents = await readParents(read, fileFolderJoinTable, files.map((file)=>Number(file.id)));
|
|
139
|
+
const folderParents = await readParents(read, folderParentJoinTable, folders.map((folder)=>Number(folder.id)));
|
|
140
|
+
const destinationId = destinationFolderId === null ? null : Number(destinationFolderId);
|
|
141
|
+
const referencedIds = new Set([
|
|
142
|
+
...fileParents.values(),
|
|
143
|
+
...folderParents.values()
|
|
144
|
+
]);
|
|
145
|
+
if (destinationId !== null) {
|
|
146
|
+
referencedIds.add(destinationId);
|
|
147
|
+
}
|
|
148
|
+
const folderNames = new Map();
|
|
149
|
+
if (referencedIds.size > 0) {
|
|
150
|
+
const rows = await read(folderTable).select('id', 'name').whereIn('id', [
|
|
151
|
+
...referencedIds
|
|
152
|
+
]);
|
|
153
|
+
rows.forEach((row)=>folderNames.set(Number(row.id), row.name ?? null));
|
|
154
|
+
}
|
|
155
|
+
const toRef = (id)=>id == null ? null : {
|
|
156
|
+
id,
|
|
157
|
+
name: folderNames.get(id) ?? null
|
|
158
|
+
};
|
|
159
|
+
const to = toRef(destinationId);
|
|
160
|
+
return [
|
|
161
|
+
...files.map((file)=>({
|
|
162
|
+
kind: 'file',
|
|
163
|
+
id: Number(file.id),
|
|
164
|
+
name: file.name,
|
|
165
|
+
from: toRef(fileParents.get(Number(file.id))),
|
|
166
|
+
to
|
|
167
|
+
})),
|
|
168
|
+
...folders.map((folder)=>({
|
|
169
|
+
kind: 'folder',
|
|
170
|
+
id: Number(folder.id),
|
|
171
|
+
name: folder.name,
|
|
172
|
+
from: toRef(folderParents.get(Number(folder.id))),
|
|
173
|
+
to
|
|
174
|
+
}))
|
|
175
|
+
];
|
|
176
|
+
};
|
|
177
|
+
const emitMediaMoves = async ({ strapi }, moves)=>{
|
|
178
|
+
for (const move of moves){
|
|
179
|
+
if ((move.from?.id ?? null) !== (move.to?.id ?? null)) {
|
|
180
|
+
await utils.emitAudit({
|
|
181
|
+
strapi
|
|
182
|
+
}, AUDITED_EVENTS.MEDIA_MOVE, move);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const registerAuditEvents = (auditLogsLifecycle)=>{
|
|
187
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.UPLOAD_SETTINGS_UPDATE, (event)=>({
|
|
188
|
+
resource: {
|
|
189
|
+
type: 'upload-settings'
|
|
190
|
+
},
|
|
191
|
+
details: {
|
|
192
|
+
changes: event.changes
|
|
193
|
+
}
|
|
194
|
+
}));
|
|
195
|
+
const fileResource = (file)=>({
|
|
196
|
+
type: 'media',
|
|
197
|
+
id: Number(file.id),
|
|
198
|
+
name: file.name
|
|
199
|
+
});
|
|
200
|
+
const folderResource = (folder)=>({
|
|
201
|
+
type: 'media-folder',
|
|
202
|
+
id: Number(folder.id),
|
|
203
|
+
name: folder.name
|
|
204
|
+
});
|
|
205
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.MEDIA_CREATE, ({ media })=>({
|
|
206
|
+
resource: fileResource(media),
|
|
207
|
+
details: {
|
|
208
|
+
mime: media.mime ?? null,
|
|
209
|
+
size: media.size ?? null
|
|
210
|
+
}
|
|
211
|
+
}));
|
|
212
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.MEDIA_UPDATE_AUDIT, (event)=>({
|
|
213
|
+
resource: fileResource(event.file),
|
|
214
|
+
details: {
|
|
215
|
+
changes: getMediaChanges(event)
|
|
216
|
+
}
|
|
217
|
+
}), {
|
|
218
|
+
action: AUDITED_EVENTS.MEDIA_UPDATE,
|
|
219
|
+
shouldRecord: (event)=>Object.keys(getMediaChanges(event)).length > 0
|
|
220
|
+
});
|
|
221
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.MEDIA_DELETE, ({ media })=>({
|
|
222
|
+
resource: fileResource(media)
|
|
223
|
+
}));
|
|
224
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.FOLDER_CREATE, ({ folder })=>({
|
|
225
|
+
resource: folderResource(folder)
|
|
226
|
+
}), {
|
|
227
|
+
action: AUDITED_EVENTS.MEDIA_CREATE
|
|
228
|
+
});
|
|
229
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.FOLDER_UPDATE, (event)=>({
|
|
230
|
+
resource: folderResource(event.folder),
|
|
231
|
+
details: {
|
|
232
|
+
changes: getFolderChanges(event)
|
|
233
|
+
}
|
|
234
|
+
}), {
|
|
235
|
+
action: AUDITED_EVENTS.MEDIA_UPDATE,
|
|
236
|
+
shouldRecord: (event)=>Object.keys(getFolderChanges(event)).length > 0
|
|
237
|
+
});
|
|
238
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.FOLDER_DELETE_AUDIT, ({ folder })=>({
|
|
239
|
+
resource: folderResource(folder)
|
|
240
|
+
}), {
|
|
241
|
+
action: AUDITED_EVENTS.MEDIA_DELETE
|
|
242
|
+
});
|
|
243
|
+
auditLogsLifecycle.registerEvent(AUDITED_EVENTS.MEDIA_MOVE, (event)=>({
|
|
244
|
+
resource: {
|
|
245
|
+
type: event.kind === 'file' ? 'media' : 'media-folder',
|
|
246
|
+
id: event.id,
|
|
247
|
+
name: event.name
|
|
248
|
+
},
|
|
249
|
+
details: {
|
|
250
|
+
from: event.from,
|
|
251
|
+
to: event.to
|
|
252
|
+
}
|
|
253
|
+
}));
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
exports.AUDITED_EVENTS = AUDITED_EVENTS;
|
|
257
|
+
exports.emitMediaMoves = emitMediaMoves;
|
|
258
|
+
exports.getFolderChanges = getFolderChanges;
|
|
259
|
+
exports.getMediaChanges = getMediaChanges;
|
|
260
|
+
exports.getUploadSettingsChanges = getUploadSettingsChanges;
|
|
261
|
+
exports.readMediaMoves = readMediaMoves;
|
|
262
|
+
exports.registerAuditEvents = registerAuditEvents;
|
|
263
|
+
exports.toAuditedFile = toAuditedFile;
|
|
264
|
+
//# sourceMappingURL=audit-logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-logs.js","sources":["../../server/src/audit-logs.ts"],"sourcesContent":["import { emitAudit } from '@strapi/utils';\nimport type { Core, Modules } from '@strapi/types';\n\nimport type { Settings } from './controllers/validation/admin/settings';\n\nexport const AUDITED_EVENTS = {\n UPLOAD_SETTINGS_UPDATE: 'upload-settings.update',\n MEDIA_CREATE: 'media.create',\n MEDIA_UPDATE: 'media.update',\n MEDIA_DELETE: 'media.delete',\n MEDIA_MOVE: 'media.move',\n FOLDER_CREATE: 'media-folder.create',\n FOLDER_UPDATE: 'media-folder.update',\n // Audit-only events. media.update is a webhook event that carries neither the file as it was\n // nor its folder; media-folder.delete carries every deleted folder in one event.\n MEDIA_UPDATE_AUDIT: 'media.update.audit',\n FOLDER_DELETE_AUDIT: 'media-folder.delete.audit',\n} as const;\n\nconst AUDITED_SETTINGS = [\n 'sizeOptimization',\n 'responsiveDimensions',\n 'autoOrientation',\n 'aiMetadata',\n] as const;\n\nexport type UploadSettingsChanges = Partial<\n Record<(typeof AUDITED_SETTINGS)[number], Modules.AuditLogs.FieldChange<boolean | null>>\n>;\n\nexport interface SettingsUpdateDetails {\n changes: UploadSettingsChanges;\n}\n\n// The store replaces the whole value, so an option missing from the request is removed and recorded as null.\nexport const getUploadSettingsChanges = (\n previous: Partial<Settings> | null,\n next: Partial<Settings>\n): UploadSettingsChanges => {\n const changes: UploadSettingsChanges = {};\n\n for (const setting of AUDITED_SETTINGS) {\n const before = previous?.[setting] ?? null;\n const after = next[setting] ?? null;\n\n if (before !== after) {\n changes[setting] = { before, after };\n }\n }\n\n return changes;\n};\n\ninterface FolderRef {\n id: number;\n name: string | null;\n}\n\nexport interface MediaMoveEvent {\n kind: 'file' | 'folder';\n id: number;\n name: string;\n from: FolderRef | null;\n to: FolderRef | null;\n}\n\nexport interface MoveDetails {\n from: FolderRef | null;\n to: FolderRef | null;\n}\n\nconst toFolderRef = (folder: { id: number | string; name?: string | null } | null | undefined) =>\n folder ? { id: Number(folder.id), name: folder.name ?? null } : null;\n\ninterface MediaFile {\n id: number;\n name: string;\n mime?: string;\n size?: number;\n alternativeText?: string | null;\n caption?: string | null;\n focalPoint?: { x: number; y: number } | null;\n folder?: { id: number; name: string | null } | null;\n}\n\n/** The payload of media.create and media.delete, which are also webhook events. */\nexport interface MediaEvent {\n media: MediaFile;\n}\n\nexport interface MediaUpdateEvent {\n file: MediaFile;\n previous: MediaFile;\n /** The file's content was replaced. It keeps its hash and url, so no field shows it. */\n replaced?: boolean;\n}\n\n/** Picks the fields the audit log compares. */\nexport const toAuditedFile = (file: Record<string, any>): MediaFile => ({\n id: Number(file.id),\n name: file.name,\n mime: file.mime,\n size: file.size,\n alternativeText: file.alternativeText ?? null,\n caption: file.caption ?? null,\n focalPoint: file.focalPoint ?? null,\n folder: toFolderRef(file.folder),\n});\n\ninterface FileSummary {\n name: string;\n mime: string | null;\n size: number | null;\n}\n\nexport interface MediaChanges {\n name?: Modules.AuditLogs.FieldChange<string>;\n alternativeText?: Modules.AuditLogs.FieldChange<string | null>;\n caption?: Modules.AuditLogs.FieldChange<string | null>;\n focalPoint?: Modules.AuditLogs.FieldChange<{ x: number; y: number } | null>;\n folder?: Modules.AuditLogs.FieldChange<FolderRef | null>;\n file?: Modules.AuditLogs.FieldChange<FileSummary>;\n}\n\nconst summarizeFile = (file: MediaFile): FileSummary => ({\n name: file.name,\n mime: file.mime ?? null,\n size: file.size ?? null,\n});\n\nexport const getMediaChanges = ({\n file: media,\n previous,\n replaced,\n}: MediaUpdateEvent): MediaChanges => {\n const changes: MediaChanges = {};\n\n if (previous.name !== media.name) {\n changes.name = { before: previous.name, after: media.name };\n }\n\n // The admin sends '' for an empty field that is stored as null\n const blankAsNull = (value: string | null | undefined) => value || null;\n for (const field of ['alternativeText', 'caption'] as const) {\n const before = previous[field] ?? null;\n const after = media[field] ?? null;\n if (blankAsNull(before) !== blankAsNull(after)) {\n changes[field] = { before, after };\n }\n }\n\n const focalBefore = previous.focalPoint ?? null;\n const focalAfter = media.focalPoint ?? null;\n if (JSON.stringify(focalBefore) !== JSON.stringify(focalAfter)) {\n changes.focalPoint = { before: focalBefore, after: focalAfter };\n }\n\n const folderBefore = toFolderRef(previous.folder);\n const folderAfter = toFolderRef(media.folder);\n if ((folderBefore?.id ?? null) !== (folderAfter?.id ?? null)) {\n changes.folder = { before: folderBefore, after: folderAfter };\n }\n\n if (replaced) {\n changes.file = { before: summarizeFile(previous), after: summarizeFile(media) };\n }\n\n return changes;\n};\n\ninterface MediaFolder {\n id: number;\n name: string;\n parent?: { id: number; name: string | null } | null;\n}\n\n/** The payload of the media-folder.* events. They are not webhook events. */\nexport interface FolderEvent {\n folder: MediaFolder;\n previous?: MediaFolder;\n}\n\nexport interface FolderChanges {\n name?: Modules.AuditLogs.FieldChange<string>;\n parent?: Modules.AuditLogs.FieldChange<FolderRef | null>;\n}\n\nexport const getFolderChanges = ({ folder, previous }: FolderEvent): FolderChanges => {\n if (!previous) {\n return {};\n }\n\n const changes: FolderChanges = {};\n\n if (previous.name !== folder.name) {\n changes.name = { before: previous.name, after: folder.name };\n }\n\n const parentBefore = toFolderRef(previous.parent);\n const parentAfter = toFolderRef(folder.parent);\n if ((parentBefore?.id ?? null) !== (parentAfter?.id ?? null)) {\n changes.parent = { before: parentBefore, after: parentAfter };\n }\n\n return changes;\n};\n\ninterface JoinTable {\n name: string;\n joinColumn: { name: string };\n inverseJoinColumn: { name: string };\n}\n\n/** Reads rows of a table inside the move transaction. */\ntype ReadTable = (table: string | undefined) => {\n select(...columns: string[]): {\n whereIn(column: string, values: readonly number[]): PromiseLike<Record<string, any>[]>;\n };\n};\n\nconst readParents = async (read: ReadTable, joinTable: JoinTable, ids: number[]) => {\n if (ids.length === 0) {\n return new Map<number, number>();\n }\n\n const { joinColumn, inverseJoinColumn } = joinTable;\n const rows = await read(joinTable.name)\n .select(joinColumn.name, inverseJoinColumn.name)\n .whereIn(joinColumn.name, ids);\n\n return new Map(\n rows.map((row) => [Number(row[joinColumn.name]), Number(row[inverseJoinColumn.name])])\n );\n};\n\nexport const readMediaMoves = async (\n read: ReadTable,\n {\n files,\n folders,\n destinationFolderId,\n fileFolderJoinTable,\n folderParentJoinTable,\n folderTable,\n }: {\n files: Array<{ id: number | string; name: string }>;\n folders: Array<{ id: number | string; name: string }>;\n destinationFolderId: number | string | null;\n fileFolderJoinTable: JoinTable;\n folderParentJoinTable: JoinTable;\n folderTable: string | undefined;\n }\n): Promise<MediaMoveEvent[]> => {\n const fileParents = await readParents(\n read,\n fileFolderJoinTable,\n files.map((file) => Number(file.id))\n );\n const folderParents = await readParents(\n read,\n folderParentJoinTable,\n folders.map((folder) => Number(folder.id))\n );\n\n const destinationId = destinationFolderId === null ? null : Number(destinationFolderId);\n const referencedIds = new Set([...fileParents.values(), ...folderParents.values()]);\n if (destinationId !== null) {\n referencedIds.add(destinationId);\n }\n\n const folderNames = new Map<number, string | null>();\n if (referencedIds.size > 0) {\n const rows = await read(folderTable)\n .select('id', 'name')\n .whereIn('id', [...referencedIds]);\n rows.forEach((row) => folderNames.set(Number(row.id), row.name ?? null));\n }\n\n const toRef = (id: number | null | undefined): FolderRef | null =>\n id == null ? null : { id, name: folderNames.get(id) ?? null };\n\n const to = toRef(destinationId);\n\n return [\n ...files.map((file) => ({\n kind: 'file' as const,\n id: Number(file.id),\n name: file.name,\n from: toRef(fileParents.get(Number(file.id))),\n to,\n })),\n ...folders.map((folder) => ({\n kind: 'folder' as const,\n id: Number(folder.id),\n name: folder.name,\n from: toRef(folderParents.get(Number(folder.id))),\n to,\n })),\n ];\n};\n\nexport const emitMediaMoves = async (\n { strapi }: { strapi: Core.Strapi },\n moves: MediaMoveEvent[]\n) => {\n for (const move of moves) {\n if ((move.from?.id ?? null) !== (move.to?.id ?? null)) {\n await emitAudit({ strapi }, AUDITED_EVENTS.MEDIA_MOVE, move);\n }\n }\n};\n\n/**\n * The part of the audit-logs lifecycle service used by this plugin.\n * The full service type lives in the Admin EE package and cannot be imported here.\n */\ninterface AuditLogsLifecycle {\n registerEvent<TDetails>(\n name: string,\n transform: Modules.AuditLogs.EventTransformer<TDetails>,\n options?: { action?: string; shouldRecord?: (...args: any[]) => boolean }\n ): void;\n}\n\nexport const registerAuditEvents = (auditLogsLifecycle: AuditLogsLifecycle) => {\n auditLogsLifecycle.registerEvent<SettingsUpdateDetails>(\n AUDITED_EVENTS.UPLOAD_SETTINGS_UPDATE,\n (event: SettingsUpdateDetails) => ({\n resource: { type: 'upload-settings' },\n details: { changes: event.changes },\n })\n );\n\n const fileResource = (file: MediaFile) => ({\n type: 'media',\n id: Number(file.id),\n name: file.name,\n });\n const folderResource = (folder: MediaFolder) => ({\n type: 'media-folder',\n id: Number(folder.id),\n name: folder.name,\n });\n\n auditLogsLifecycle.registerEvent<{ mime: string | null; size: number | null }>(\n AUDITED_EVENTS.MEDIA_CREATE,\n ({ media }: MediaEvent) => ({\n resource: fileResource(media),\n details: { mime: media.mime ?? null, size: media.size ?? null },\n })\n );\n\n auditLogsLifecycle.registerEvent<{ changes: MediaChanges }>(\n AUDITED_EVENTS.MEDIA_UPDATE_AUDIT,\n (event: MediaUpdateEvent) => ({\n resource: fileResource(event.file),\n details: { changes: getMediaChanges(event) },\n }),\n {\n action: AUDITED_EVENTS.MEDIA_UPDATE,\n shouldRecord: (event: MediaUpdateEvent) => Object.keys(getMediaChanges(event)).length > 0,\n }\n );\n\n auditLogsLifecycle.registerEvent(AUDITED_EVENTS.MEDIA_DELETE, ({ media }: MediaEvent) => ({\n resource: fileResource(media),\n }));\n\n auditLogsLifecycle.registerEvent(\n AUDITED_EVENTS.FOLDER_CREATE,\n ({ folder }: FolderEvent) => ({ resource: folderResource(folder) }),\n { action: AUDITED_EVENTS.MEDIA_CREATE }\n );\n\n auditLogsLifecycle.registerEvent<{ changes: FolderChanges }>(\n AUDITED_EVENTS.FOLDER_UPDATE,\n (event: FolderEvent) => ({\n resource: folderResource(event.folder),\n details: { changes: getFolderChanges(event) },\n }),\n {\n action: AUDITED_EVENTS.MEDIA_UPDATE,\n shouldRecord: (event: FolderEvent) => Object.keys(getFolderChanges(event)).length > 0,\n }\n );\n\n auditLogsLifecycle.registerEvent(\n AUDITED_EVENTS.FOLDER_DELETE_AUDIT,\n ({ folder }: FolderEvent) => ({ resource: folderResource(folder) }),\n { action: AUDITED_EVENTS.MEDIA_DELETE }\n );\n\n auditLogsLifecycle.registerEvent<MoveDetails>(\n AUDITED_EVENTS.MEDIA_MOVE,\n (event: MediaMoveEvent) => ({\n resource: {\n type: event.kind === 'file' ? 'media' : 'media-folder',\n id: event.id,\n name: event.name,\n },\n details: { from: event.from, to: event.to },\n })\n );\n};\n"],"names":["AUDITED_EVENTS","UPLOAD_SETTINGS_UPDATE","MEDIA_CREATE","MEDIA_UPDATE","MEDIA_DELETE","MEDIA_MOVE","FOLDER_CREATE","FOLDER_UPDATE","MEDIA_UPDATE_AUDIT","FOLDER_DELETE_AUDIT","AUDITED_SETTINGS","getUploadSettingsChanges","previous","next","changes","setting","before","after","toFolderRef","folder","id","Number","name","toAuditedFile","file","mime","size","alternativeText","caption","focalPoint","summarizeFile","getMediaChanges","media","replaced","blankAsNull","value","field","focalBefore","focalAfter","JSON","stringify","folderBefore","folderAfter","getFolderChanges","parentBefore","parent","parentAfter","readParents","read","joinTable","ids","length","Map","joinColumn","inverseJoinColumn","rows","select","whereIn","map","row","readMediaMoves","files","folders","destinationFolderId","fileFolderJoinTable","folderParentJoinTable","folderTable","fileParents","folderParents","destinationId","referencedIds","Set","values","add","folderNames","forEach","set","toRef","get","to","kind","from","emitMediaMoves","strapi","moves","move","emitAudit","registerAuditEvents","auditLogsLifecycle","registerEvent","event","resource","type","details","fileResource","folderResource","action","shouldRecord","Object","keys"],"mappings":"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"}
|