@atlaskit/media-client 37.6.6 → 37.7.1
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/CHANGELOG.md +66 -0
- package/dist/cjs/client/file-fetcher/index.js +3 -3
- package/dist/cjs/client/media-client.js +2 -2
- package/dist/cjs/client/media-store/MediaStore.js +8 -3
- package/dist/cjs/models/ssr-media-item.js +153 -0
- package/dist/cjs/utils/mapToSeedBasedCdnUrl.js +40 -0
- package/dist/cjs/utils/mediaCdn.js +7 -0
- package/dist/es2019/client/file-fetcher/index.js +1 -1
- package/dist/es2019/client/media-client.js +2 -2
- package/dist/es2019/client/media-store/MediaStore.js +8 -3
- package/dist/es2019/models/ssr-media-item.js +138 -0
- package/dist/es2019/utils/mapToSeedBasedCdnUrl.js +22 -0
- package/dist/es2019/utils/mediaCdn.js +2 -1
- package/dist/esm/client/file-fetcher/index.js +1 -1
- package/dist/esm/client/media-client.js +2 -2
- package/dist/esm/client/media-store/MediaStore.js +8 -3
- package/dist/esm/models/ssr-media-item.js +147 -0
- package/dist/esm/utils/mapToSeedBasedCdnUrl.js +33 -0
- package/dist/esm/utils/mediaCdn.js +2 -1
- package/dist/types/client/media-client.d.ts +1 -1
- package/dist/types/client/media-store/MediaStore.d.ts +1 -1
- package/dist/types/client/media-store/types.d.ts +1 -1
- package/dist/types/models/ssr-media-item.d.ts +65 -0
- package/dist/types/utils/mapToSeedBasedCdnUrl.d.ts +10 -0
- package/dist/types/utils/mediaCdn.d.ts +1 -0
- package/media-cdn/package.json +8 -0
- package/package.json +2 -2
- package/ssr-media-item/package.json +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# @atlaskit/media-client
|
|
2
2
|
|
|
3
|
+
## 37.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`0927c3666c010`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0927c3666c010) -
|
|
8
|
+
Upgrade `uuid` from `3.x` to `11.1.1` to remediate GHSA-w5hq-g745-h8pq / SNYK-JS-UUID-16133035.
|
|
9
|
+
|
|
10
|
+
`uuid@11` removed the deep subpath exports (`uuid/v4`, `uuid/v1`, `uuid/v5`) and the default
|
|
11
|
+
export, so all internal call sites were migrated to named imports:
|
|
12
|
+
|
|
13
|
+
```diff
|
|
14
|
+
-import uuid from 'uuid/v4';
|
|
15
|
+
+import { v4 as uuid } from 'uuid';
|
|
16
|
+
|
|
17
|
+
-import uuid from 'uuid';
|
|
18
|
+
+import { v4 as uuid } from 'uuid';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With the exception of `@atlassian/integrations` (below), this is an internal implementation change
|
|
22
|
+
only - no public API, export, or entrypoint changed. UUID generation behaviour is unchanged
|
|
23
|
+
(`uuid@3`'s default export was already `v4`).
|
|
24
|
+
|
|
25
|
+
`@atlassian/integrations` declares `uuid` as a peer dependency, so its declared range moved from
|
|
26
|
+
`^3.1.0` to `^11.1.1`. That is a peer dependency declaration change, hence `minor` rather than
|
|
27
|
+
`patch` for that package.
|
|
28
|
+
|
|
29
|
+
The following `platform/packages/ai-mate` packages were also touched, but are all `private: true`
|
|
30
|
+
and so are intentionally not listed in the frontmatter above:
|
|
31
|
+
- `@atlassian/csm-assistance-service` - bumped its explicit `uuid` dependency from `npm:^9.0.0` to
|
|
32
|
+
`npm:^11.1.1` (`9.0.1` is also within the advisory's affected range).
|
|
33
|
+
- `@atlassian/csm-guidance-config` - example helper only, migrated to the named `uuid` import.
|
|
34
|
+
- `@atlassian/csm-ui-components` - example helper only, migrated to the named `uuid` import.
|
|
35
|
+
|
|
36
|
+
## 37.7.0
|
|
37
|
+
|
|
38
|
+
### Minor Changes
|
|
39
|
+
|
|
40
|
+
- [`be8a71519cbc2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/be8a71519cbc2) -
|
|
41
|
+
Add support for seeding media card file state from SSR media node metadata, behind the
|
|
42
|
+
`platform_media_ssr_data_seed` feature gate.
|
|
43
|
+
|
|
44
|
+
`@atlaskit/media-client` gains a Relay-free `mapSsrMediaItemToFileState`. Malformed, partial, or
|
|
45
|
+
non-array input yields `undefined` rather than throwing.
|
|
46
|
+
|
|
47
|
+
`@atlaskit/media-card` accepts an optional `ssrMediaItem` prop. When `ssrFileState` is absent and
|
|
48
|
+
the gate is on, the card converts `ssrMediaItem` to FileState via `mapSsrMediaItemToFileState` and
|
|
49
|
+
seeds `useFileState`. `ssrFileState` still wins when both are provided (Relay / media-card-relay).
|
|
50
|
+
|
|
51
|
+
`@atlaskit/renderer` extends `MediaSSR` with `ssrMediaItems` — the host's SSR media payload,
|
|
52
|
+
passed through untouched. The renderer finds the matching item by id and forwards it as
|
|
53
|
+
`ssrMediaItem` to Card. Hosts need no knowledge of `FileState` or of media internals. The field is
|
|
54
|
+
optional and additive: hosts that do not supply it, and media ids without an entry, keep the
|
|
55
|
+
current fetch behaviour.
|
|
56
|
+
|
|
57
|
+
`@atlaskit/media-card-relay`'s `MediaCardRelay` / `MediaInlineCardRelay` now call the shared
|
|
58
|
+
`mapSsrMediaItemToFileState` mapper directly (the Relay fragment data is structurally assignable
|
|
59
|
+
to `SsrMediaItem`, so no cast or wrapper is needed). Its public API and behaviour are unchanged.
|
|
60
|
+
|
|
61
|
+
`@atlaskit/media-file-preview` now forwards an SSR-seeded pre-signed `previewCdnUrl` to the new
|
|
62
|
+
optional `MediaClient.getImageUrlSync(id, params, seededCdnUrl)` argument when
|
|
63
|
+
`platform_media_ssr_data_seed` is enabled and CDN delivery is in use. `@atlaskit/media-client`
|
|
64
|
+
preserves the signed CDN asset URL and inserts supported image parameters before the `wm-ari` /
|
|
65
|
+
`wm-v` watermark anchor, avoiding query-string rebuilding or re-encoding that can invalidate
|
|
66
|
+
CloudFront signatures. Path-based routing, isolated cloud, GCP, and callers without a seeded URL
|
|
67
|
+
retain the existing URL-generation behaviour.
|
|
68
|
+
|
|
3
69
|
## 37.6.6
|
|
4
70
|
|
|
5
71
|
### Patch Changes
|
|
@@ -24,7 +24,7 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
|
|
|
24
24
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
25
25
|
var _map = require("rxjs/operators/map");
|
|
26
26
|
var _Subscription = require("rxjs/Subscription");
|
|
27
|
-
var
|
|
27
|
+
var _uuid = require("uuid");
|
|
28
28
|
var _authToOwner = require("@atlaskit/media-core/auth-to-owner");
|
|
29
29
|
var _isValidUuid = require("@atlaskit/media-common/isValidUuid");
|
|
30
30
|
var _downloadUrl = require("@atlaskit/media-common/downloadUrl");
|
|
@@ -551,9 +551,9 @@ var FileFetcherImpl = exports.FileFetcherImpl = /*#__PURE__*/function () {
|
|
|
551
551
|
key: "generateUploadableFileUpfrontIds",
|
|
552
552
|
value: function generateUploadableFileUpfrontIds(collection, traceContext, expectedFileSize) {
|
|
553
553
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
554
|
-
var id = (0,
|
|
554
|
+
var id = (0, _uuid.v4)();
|
|
555
555
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
556
|
-
var occurrenceKey = (0,
|
|
556
|
+
var occurrenceKey = (0, _uuid.v4)();
|
|
557
557
|
var touchFileDescriptor = {
|
|
558
558
|
fileId: id,
|
|
559
559
|
occurrenceKey: occurrenceKey,
|
|
@@ -60,8 +60,8 @@ var MediaClient = exports.MediaClient = /*#__PURE__*/function () {
|
|
|
60
60
|
}
|
|
61
61
|
}, {
|
|
62
62
|
key: "getImageUrlSync",
|
|
63
|
-
value: function getImageUrlSync(id, params) {
|
|
64
|
-
return this.mediaStore.getFileImageURLSync(id, params);
|
|
63
|
+
value: function getImageUrlSync(id, params, seededCdnUrl) {
|
|
64
|
+
return this.mediaStore.getFileImageURLSync(id, params, seededCdnUrl);
|
|
65
65
|
}
|
|
66
66
|
}, {
|
|
67
67
|
key: "getClientId",
|
|
@@ -34,9 +34,11 @@ var _fg = require("@atlaskit/platform-feature-flags/fg");
|
|
|
34
34
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
35
35
|
var _constants = require("../../constants");
|
|
36
36
|
var _artifacts = require("../../models/artifacts");
|
|
37
|
+
var _isCDNEnabled = require("../../utils/isCDNEnabled");
|
|
37
38
|
var _isPathBasedEnabled = require("../../utils/isPathBasedEnabled");
|
|
38
39
|
var _mapToMediaCdnUrl = require("../../utils/mapToMediaCdnUrl");
|
|
39
40
|
var _mapToPathBasedUrl = require("../../utils/mapToPathBasedUrl");
|
|
41
|
+
var _mapToSeedBasedCdnUrl = require("../../utils/mapToSeedBasedCdnUrl");
|
|
40
42
|
var _request3 = require("../../utils/request");
|
|
41
43
|
var _createMapResponseToBlob = require("../../utils/request/createMapResponseToBlob");
|
|
42
44
|
var _createMapResponseToJson = require("../../utils/request/createMapResponseToJson");
|
|
@@ -460,13 +462,13 @@ var MediaStore = exports.MediaStore = /*#__PURE__*/function () {
|
|
|
460
462
|
}() // TODO Create ticket in case Trace Id can be supported through query params
|
|
461
463
|
}, {
|
|
462
464
|
key: "getFileImageURLSync",
|
|
463
|
-
value: function getFileImageURLSync(id, params) {
|
|
465
|
+
value: function getFileImageURLSync(id, params, seededCdnUrl) {
|
|
464
466
|
var auth = this.resolveInitialAuth();
|
|
465
|
-
return this.createFileImageURL(id, auth, params);
|
|
467
|
+
return this.createFileImageURL(id, auth, params, seededCdnUrl);
|
|
466
468
|
}
|
|
467
469
|
}, {
|
|
468
470
|
key: "createFileImageURL",
|
|
469
|
-
value: function createFileImageURL(id, auth, params) {
|
|
471
|
+
value: function createFileImageURL(id, auth, params, seededCdnUrl) {
|
|
470
472
|
var wmv = (0, _fg.fg)('confluence_watermark_admin_ui') ? (0, _watermarkVersion.getWatermarkVersionFromToken)(auth.token) : undefined;
|
|
471
473
|
var options = {
|
|
472
474
|
params: _objectSpread(_objectSpread({}, (0, _extendImageParams.extendImageParams)(params)), wmv ? {
|
|
@@ -475,6 +477,9 @@ var MediaStore = exports.MediaStore = /*#__PURE__*/function () {
|
|
|
475
477
|
auth: auth
|
|
476
478
|
};
|
|
477
479
|
var imageEndpoint = (0, _cdnFeatureFlag.cdnFeatureFlag)('image');
|
|
480
|
+
if (seededCdnUrl && (0, _isCDNEnabled.isCDNEnabled)()) {
|
|
481
|
+
return (0, _mapToSeedBasedCdnUrl.mapToSeedBasedCdnUrl)(seededCdnUrl, options.params);
|
|
482
|
+
}
|
|
478
483
|
if ((0, _isPathBasedEnabled.isPathBasedEnabled)()) {
|
|
479
484
|
return (0, _mapToPathBasedUrl.mapToPathBasedUrl)((0, _createUrl.createUrl)("".concat(auth.baseUrl, "/file/").concat(id, "/").concat(imageEndpoint), options));
|
|
480
485
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.mapSsrMediaItemToFileState = void 0;
|
|
8
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _fileState = require("./file-state");
|
|
10
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
11
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
12
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
13
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
14
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
15
|
+
/**
|
|
16
|
+
* Describes the shape of a plain recorded AGG media item as serialized by Confluence SSR.
|
|
17
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
18
|
+
*
|
|
19
|
+
* This is the shape as returned by dt-api-filestore `media_items` aggregation.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Describes a complete SSR media item with id and details.
|
|
24
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Safely coerce a value to a number, returning undefined if the value is null/undefined.
|
|
29
|
+
*/
|
|
30
|
+
var toNumber = function toNumber(value) {
|
|
31
|
+
return value == null ? undefined : Number(value);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert abuse classification fields to the keyed format expected by FileState,
|
|
36
|
+
* returning undefined if either classification or confidence is missing/falsy.
|
|
37
|
+
*/
|
|
38
|
+
var toAbuseClassification = function toAbuseClassification(value) {
|
|
39
|
+
if (!value || !value.classification || !value.confidence) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
classification: value.classification,
|
|
44
|
+
confidence: value.confidence
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert the `artifactsList` array shape into the keyed `MediaFileArtifacts`
|
|
50
|
+
* dict that `MediaItemDetails` / `FileState` consumers expect.
|
|
51
|
+
*
|
|
52
|
+
* Each artifact is keyed by its `name` (e.g. `'image.png'`, `'thumb_120.jpg'`).
|
|
53
|
+
* Artifacts missing required fields (`name`, `url`, `processingStatus`)
|
|
54
|
+
* are skipped rather than coerced — callers should treat absent artifacts as
|
|
55
|
+
* "not yet available" rather than "failed".
|
|
56
|
+
*
|
|
57
|
+
* NOTE: This assumes AGG returns `name` values matching the canonical
|
|
58
|
+
* `MediaFileArtifacts` key vocabulary. If that assumption is broken, the
|
|
59
|
+
* downstream renderer will silently miss SSR thumbnails — verify with a real
|
|
60
|
+
* payload before relying on SSR previews.
|
|
61
|
+
*/
|
|
62
|
+
var toArtifacts = function toArtifacts(list) {
|
|
63
|
+
var out = {};
|
|
64
|
+
if (!list) {
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
var _iterator = _createForOfIteratorHelper(list),
|
|
68
|
+
_step;
|
|
69
|
+
try {
|
|
70
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
71
|
+
var item = _step.value;
|
|
72
|
+
if (!item.name || !item.url || !item.processingStatus) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
var artifact = _objectSpread(_objectSpread(_objectSpread({
|
|
76
|
+
processingStatus: item.processingStatus,
|
|
77
|
+
url: item.url
|
|
78
|
+
}, item.mimeType ? {
|
|
79
|
+
mimeType: item.mimeType
|
|
80
|
+
} : {}), item.size != null ? {
|
|
81
|
+
size: Number(item.size)
|
|
82
|
+
} : {}), item.createdAt != null ? {
|
|
83
|
+
createdAt: Number(item.createdAt)
|
|
84
|
+
} : {});
|
|
85
|
+
out[item.name] = artifact;
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
_iterator.e(err);
|
|
89
|
+
} finally {
|
|
90
|
+
_iterator.f();
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Map a plain SSR media item (from AGG / dt-api-filestore) to a `FileState`
|
|
97
|
+
* for seeding `<Card />` / `<MediaInlineCard />`.
|
|
98
|
+
*
|
|
99
|
+
* Returns `undefined` when:
|
|
100
|
+
* - `item` is null/undefined or missing `id`/`details`
|
|
101
|
+
* - any of the required `MediaFile` fields (`name`, `mimeType`, `mediaType`,
|
|
102
|
+
* `processingStatus`) are missing or falsy
|
|
103
|
+
* - `size` is missing or cannot be coerced to a number
|
|
104
|
+
*
|
|
105
|
+
* The transformation reshapes `details.artifactsList` (array, AGG-only) into
|
|
106
|
+
* the keyed `details.artifacts` dict expected by `mapMediaItemToFileState`,
|
|
107
|
+
* and coerces `AGG$Long` numerics.
|
|
108
|
+
*
|
|
109
|
+
* MUST NOT throw on malformed input — always return undefined instead.
|
|
110
|
+
*/
|
|
111
|
+
var mapSsrMediaItemToFileState = exports.mapSsrMediaItemToFileState = function mapSsrMediaItemToFileState(item) {
|
|
112
|
+
try {
|
|
113
|
+
var _d$representations, _d$mediaMetadata, _d$preview;
|
|
114
|
+
if (!(item !== null && item !== void 0 && item.id) || !item.details) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
var d = item.details;
|
|
118
|
+
var processingStatus = d.processingStatus;
|
|
119
|
+
var mediaType = d.mediaType;
|
|
120
|
+
var size = toNumber(d.size);
|
|
121
|
+
if (!d.name || !d.mimeType || !mediaType || !processingStatus || size === undefined) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
var abuseClassification = toAbuseClassification(d.abuseClassification);
|
|
125
|
+
var details = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
126
|
+
name: d.name,
|
|
127
|
+
mimeType: d.mimeType,
|
|
128
|
+
mediaType: mediaType,
|
|
129
|
+
processingStatus: processingStatus,
|
|
130
|
+
size: size,
|
|
131
|
+
artifacts: toArtifacts(d.artifactsList),
|
|
132
|
+
representations: (_d$representations = d.representations) !== null && _d$representations !== void 0 && _d$representations.image ? {
|
|
133
|
+
image: {}
|
|
134
|
+
} : {}
|
|
135
|
+
}, d.failReason ? {
|
|
136
|
+
failReason: d.failReason
|
|
137
|
+
} : {}), d.createdAt != null ? {
|
|
138
|
+
createdAt: Number(d.createdAt)
|
|
139
|
+
} : {}), ((_d$mediaMetadata = d.mediaMetadata) === null || _d$mediaMetadata === void 0 ? void 0 : _d$mediaMetadata.duration) != null ? {
|
|
140
|
+
mediaMetadata: {
|
|
141
|
+
duration: d.mediaMetadata.duration
|
|
142
|
+
}
|
|
143
|
+
} : {}), abuseClassification ? {
|
|
144
|
+
abuseClassification: abuseClassification
|
|
145
|
+
} : {}), (_d$preview = d.preview) !== null && _d$preview !== void 0 && _d$preview.cdnUrl ? {
|
|
146
|
+
previewCdnUrl: d.preview.cdnUrl
|
|
147
|
+
} : {});
|
|
148
|
+
return (0, _fileState.mapMediaItemToFileState)(item.id, details);
|
|
149
|
+
} catch (_unused) {
|
|
150
|
+
// Silently catch any unexpected errors and return undefined
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.mapToSeedBasedCdnUrl = void 0;
|
|
8
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
9
|
+
var IMAGE_PARAM_KEYS = new Set(['width', 'height', 'mode', 'allowAnimated', 'upscale', 'version', 'max-age']);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Adds supported image parameters to a pre-signed CDN asset URL without
|
|
13
|
+
* re-parsing or re-encoding its existing query string.
|
|
14
|
+
*
|
|
15
|
+
* Watermarked CloudFront policies anchor `wm-ari` and `wm-v` as a literal
|
|
16
|
+
* suffix, so parameters must be inserted before `wm-ari`. Non-watermarked
|
|
17
|
+
* policies have a trailing wildcard, so parameters can be appended.
|
|
18
|
+
*/
|
|
19
|
+
var mapToSeedBasedCdnUrl = exports.mapToSeedBasedCdnUrl = function mapToSeedBasedCdnUrl(seededCdnUrl, params) {
|
|
20
|
+
var insert = Object.entries(params !== null && params !== void 0 ? params : {}).filter(function (_ref) {
|
|
21
|
+
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
22
|
+
key = _ref2[0],
|
|
23
|
+
value = _ref2[1];
|
|
24
|
+
return value != null && IMAGE_PARAM_KEYS.has(key);
|
|
25
|
+
}).map(function (_ref3) {
|
|
26
|
+
var _ref4 = (0, _slicedToArray2.default)(_ref3, 2),
|
|
27
|
+
key = _ref4[0],
|
|
28
|
+
value = _ref4[1];
|
|
29
|
+
return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(String(value)));
|
|
30
|
+
}).join('&');
|
|
31
|
+
if (!insert) {
|
|
32
|
+
return seededCdnUrl;
|
|
33
|
+
}
|
|
34
|
+
var wm = seededCdnUrl.search(/[?&]wm-ari=/);
|
|
35
|
+
if (wm !== -1) {
|
|
36
|
+
var separator = seededCdnUrl[wm];
|
|
37
|
+
return "".concat(seededCdnUrl.slice(0, wm)).concat(separator).concat(insert, "&").concat(seededCdnUrl.slice(wm + 1));
|
|
38
|
+
}
|
|
39
|
+
return "".concat(seededCdnUrl).concat(seededCdnUrl.includes('?') ? '&' : '?').concat(insert);
|
|
40
|
+
};
|
|
@@ -4,6 +4,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.MEDIA_TOKEN_LENGTH_LIMIT = exports.MEDIA_CDN_MAP = void 0;
|
|
7
|
+
Object.defineProperty(exports, "isCDNEnabled", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function get() {
|
|
10
|
+
return _isCDNEnabled.isCDNEnabled;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
var _isCDNEnabled = require("./isCDNEnabled");
|
|
7
14
|
var MEDIA_CDN_MAP = exports.MEDIA_CDN_MAP = {
|
|
8
15
|
'api.media.atlassian.com': 'media-cdn.atlassian.com',
|
|
9
16
|
'media.staging.atl-paas.net': 'media-cdn.stg.atlassian.com'
|
|
@@ -3,7 +3,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
3
3
|
import { map } from 'rxjs/operators/map';
|
|
4
4
|
import { Subscription } from 'rxjs/Subscription';
|
|
5
5
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
6
|
-
import uuid from 'uuid
|
|
6
|
+
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import { authToOwner } from '@atlaskit/media-core/auth-to-owner';
|
|
8
8
|
import { isValidUuid } from '@atlaskit/media-common/isValidUuid';
|
|
9
9
|
import { downloadUrl } from '@atlaskit/media-common/downloadUrl';
|
|
@@ -36,8 +36,8 @@ export class MediaClient {
|
|
|
36
36
|
getImageUrl(id, params) {
|
|
37
37
|
return this.mediaStore.getFileImageURL(id, params);
|
|
38
38
|
}
|
|
39
|
-
getImageUrlSync(id, params) {
|
|
40
|
-
return this.mediaStore.getFileImageURLSync(id, params);
|
|
39
|
+
getImageUrlSync(id, params, seededCdnUrl) {
|
|
40
|
+
return this.mediaStore.getFileImageURLSync(id, params, seededCdnUrl);
|
|
41
41
|
}
|
|
42
42
|
async getClientId(collectionName) {
|
|
43
43
|
return this.mediaStore.getClientId(collectionName);
|
|
@@ -7,9 +7,11 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
|
7
7
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
8
8
|
import { FILE_CACHE_MAX_AGE } from '../../constants';
|
|
9
9
|
import { getArtifactUrl } from '../../models/artifacts';
|
|
10
|
+
import { isCDNEnabled } from '../../utils/isCDNEnabled';
|
|
10
11
|
import { isPathBasedEnabled } from '../../utils/isPathBasedEnabled';
|
|
11
12
|
import { mapToMediaCdnUrl } from '../../utils/mapToMediaCdnUrl';
|
|
12
13
|
import { mapToPathBasedUrl } from '../../utils/mapToPathBasedUrl';
|
|
14
|
+
import { mapToSeedBasedCdnUrl } from '../../utils/mapToSeedBasedCdnUrl';
|
|
13
15
|
import { isRequestError, request } from '../../utils/request';
|
|
14
16
|
import { createMapResponseToBlob } from '../../utils/request/createMapResponseToBlob';
|
|
15
17
|
import { createMapResponseToJson } from '../../utils/request/createMapResponseToJson';
|
|
@@ -289,11 +291,11 @@ export class MediaStore {
|
|
|
289
291
|
}
|
|
290
292
|
|
|
291
293
|
// TODO Create ticket in case Trace Id can be supported through query params
|
|
292
|
-
getFileImageURLSync(id, params) {
|
|
294
|
+
getFileImageURLSync(id, params, seededCdnUrl) {
|
|
293
295
|
const auth = this.resolveInitialAuth();
|
|
294
|
-
return this.createFileImageURL(id, auth, params);
|
|
296
|
+
return this.createFileImageURL(id, auth, params, seededCdnUrl);
|
|
295
297
|
}
|
|
296
|
-
createFileImageURL(id, auth, params) {
|
|
298
|
+
createFileImageURL(id, auth, params, seededCdnUrl) {
|
|
297
299
|
const wmv = fg('confluence_watermark_admin_ui') ? getWatermarkVersionFromToken(auth.token) : undefined;
|
|
298
300
|
const options = {
|
|
299
301
|
params: {
|
|
@@ -305,6 +307,9 @@ export class MediaStore {
|
|
|
305
307
|
auth
|
|
306
308
|
};
|
|
307
309
|
const imageEndpoint = cdnFeatureFlag('image');
|
|
310
|
+
if (seededCdnUrl && isCDNEnabled()) {
|
|
311
|
+
return mapToSeedBasedCdnUrl(seededCdnUrl, options.params);
|
|
312
|
+
}
|
|
308
313
|
if (isPathBasedEnabled()) {
|
|
309
314
|
return mapToPathBasedUrl(createUrl(`${auth.baseUrl}/file/${id}/${imageEndpoint}`, options));
|
|
310
315
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { mapMediaItemToFileState } from './file-state';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Describes the shape of a plain recorded AGG media item as serialized by Confluence SSR.
|
|
5
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
6
|
+
*
|
|
7
|
+
* This is the shape as returned by dt-api-filestore `media_items` aggregation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Describes a complete SSR media item with id and details.
|
|
12
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Safely coerce a value to a number, returning undefined if the value is null/undefined.
|
|
17
|
+
*/
|
|
18
|
+
const toNumber = value => value == null ? undefined : Number(value);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Convert abuse classification fields to the keyed format expected by FileState,
|
|
22
|
+
* returning undefined if either classification or confidence is missing/falsy.
|
|
23
|
+
*/
|
|
24
|
+
const toAbuseClassification = value => {
|
|
25
|
+
if (!value || !value.classification || !value.confidence) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
classification: value.classification,
|
|
30
|
+
confidence: value.confidence
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Convert the `artifactsList` array shape into the keyed `MediaFileArtifacts`
|
|
36
|
+
* dict that `MediaItemDetails` / `FileState` consumers expect.
|
|
37
|
+
*
|
|
38
|
+
* Each artifact is keyed by its `name` (e.g. `'image.png'`, `'thumb_120.jpg'`).
|
|
39
|
+
* Artifacts missing required fields (`name`, `url`, `processingStatus`)
|
|
40
|
+
* are skipped rather than coerced — callers should treat absent artifacts as
|
|
41
|
+
* "not yet available" rather than "failed".
|
|
42
|
+
*
|
|
43
|
+
* NOTE: This assumes AGG returns `name` values matching the canonical
|
|
44
|
+
* `MediaFileArtifacts` key vocabulary. If that assumption is broken, the
|
|
45
|
+
* downstream renderer will silently miss SSR thumbnails — verify with a real
|
|
46
|
+
* payload before relying on SSR previews.
|
|
47
|
+
*/
|
|
48
|
+
const toArtifacts = list => {
|
|
49
|
+
const out = {};
|
|
50
|
+
if (!list) {
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
for (const item of list) {
|
|
54
|
+
if (!item.name || !item.url || !item.processingStatus) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const artifact = {
|
|
58
|
+
processingStatus: item.processingStatus,
|
|
59
|
+
url: item.url,
|
|
60
|
+
...(item.mimeType ? {
|
|
61
|
+
mimeType: item.mimeType
|
|
62
|
+
} : {}),
|
|
63
|
+
...(item.size != null ? {
|
|
64
|
+
size: Number(item.size)
|
|
65
|
+
} : {}),
|
|
66
|
+
...(item.createdAt != null ? {
|
|
67
|
+
createdAt: Number(item.createdAt)
|
|
68
|
+
} : {})
|
|
69
|
+
};
|
|
70
|
+
out[item.name] = artifact;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Map a plain SSR media item (from AGG / dt-api-filestore) to a `FileState`
|
|
77
|
+
* for seeding `<Card />` / `<MediaInlineCard />`.
|
|
78
|
+
*
|
|
79
|
+
* Returns `undefined` when:
|
|
80
|
+
* - `item` is null/undefined or missing `id`/`details`
|
|
81
|
+
* - any of the required `MediaFile` fields (`name`, `mimeType`, `mediaType`,
|
|
82
|
+
* `processingStatus`) are missing or falsy
|
|
83
|
+
* - `size` is missing or cannot be coerced to a number
|
|
84
|
+
*
|
|
85
|
+
* The transformation reshapes `details.artifactsList` (array, AGG-only) into
|
|
86
|
+
* the keyed `details.artifacts` dict expected by `mapMediaItemToFileState`,
|
|
87
|
+
* and coerces `AGG$Long` numerics.
|
|
88
|
+
*
|
|
89
|
+
* MUST NOT throw on malformed input — always return undefined instead.
|
|
90
|
+
*/
|
|
91
|
+
export const mapSsrMediaItemToFileState = item => {
|
|
92
|
+
try {
|
|
93
|
+
var _d$representations, _d$mediaMetadata, _d$preview;
|
|
94
|
+
if (!(item !== null && item !== void 0 && item.id) || !item.details) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const d = item.details;
|
|
98
|
+
const processingStatus = d.processingStatus;
|
|
99
|
+
const mediaType = d.mediaType;
|
|
100
|
+
const size = toNumber(d.size);
|
|
101
|
+
if (!d.name || !d.mimeType || !mediaType || !processingStatus || size === undefined) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
const abuseClassification = toAbuseClassification(d.abuseClassification);
|
|
105
|
+
const details = {
|
|
106
|
+
name: d.name,
|
|
107
|
+
mimeType: d.mimeType,
|
|
108
|
+
mediaType: mediaType,
|
|
109
|
+
processingStatus: processingStatus,
|
|
110
|
+
size,
|
|
111
|
+
artifacts: toArtifacts(d.artifactsList),
|
|
112
|
+
representations: (_d$representations = d.representations) !== null && _d$representations !== void 0 && _d$representations.image ? {
|
|
113
|
+
image: {}
|
|
114
|
+
} : {},
|
|
115
|
+
...(d.failReason ? {
|
|
116
|
+
failReason: d.failReason
|
|
117
|
+
} : {}),
|
|
118
|
+
...(d.createdAt != null ? {
|
|
119
|
+
createdAt: Number(d.createdAt)
|
|
120
|
+
} : {}),
|
|
121
|
+
...(((_d$mediaMetadata = d.mediaMetadata) === null || _d$mediaMetadata === void 0 ? void 0 : _d$mediaMetadata.duration) != null ? {
|
|
122
|
+
mediaMetadata: {
|
|
123
|
+
duration: d.mediaMetadata.duration
|
|
124
|
+
}
|
|
125
|
+
} : {}),
|
|
126
|
+
...(abuseClassification ? {
|
|
127
|
+
abuseClassification
|
|
128
|
+
} : {}),
|
|
129
|
+
...((_d$preview = d.preview) !== null && _d$preview !== void 0 && _d$preview.cdnUrl ? {
|
|
130
|
+
previewCdnUrl: d.preview.cdnUrl
|
|
131
|
+
} : {})
|
|
132
|
+
};
|
|
133
|
+
return mapMediaItemToFileState(item.id, details);
|
|
134
|
+
} catch {
|
|
135
|
+
// Silently catch any unexpected errors and return undefined
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const IMAGE_PARAM_KEYS = new Set(['width', 'height', 'mode', 'allowAnimated', 'upscale', 'version', 'max-age']);
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds supported image parameters to a pre-signed CDN asset URL without
|
|
5
|
+
* re-parsing or re-encoding its existing query string.
|
|
6
|
+
*
|
|
7
|
+
* Watermarked CloudFront policies anchor `wm-ari` and `wm-v` as a literal
|
|
8
|
+
* suffix, so parameters must be inserted before `wm-ari`. Non-watermarked
|
|
9
|
+
* policies have a trailing wildcard, so parameters can be appended.
|
|
10
|
+
*/
|
|
11
|
+
export const mapToSeedBasedCdnUrl = (seededCdnUrl, params) => {
|
|
12
|
+
const insert = Object.entries(params !== null && params !== void 0 ? params : {}).filter(([key, value]) => value != null && IMAGE_PARAM_KEYS.has(key)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`).join('&');
|
|
13
|
+
if (!insert) {
|
|
14
|
+
return seededCdnUrl;
|
|
15
|
+
}
|
|
16
|
+
const wm = seededCdnUrl.search(/[?&]wm-ari=/);
|
|
17
|
+
if (wm !== -1) {
|
|
18
|
+
const separator = seededCdnUrl[wm];
|
|
19
|
+
return `${seededCdnUrl.slice(0, wm)}${separator}${insert}&${seededCdnUrl.slice(wm + 1)}`;
|
|
20
|
+
}
|
|
21
|
+
return `${seededCdnUrl}${seededCdnUrl.includes('?') ? '&' : '?'}${insert}`;
|
|
22
|
+
};
|
|
@@ -6,4 +6,5 @@ export const MEDIA_CDN_MAP = {
|
|
|
6
6
|
// Cloudfront has a hard limit of 8,192 bytes
|
|
7
7
|
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html
|
|
8
8
|
// Assuming other parts of the URL make up a max of ~1000 (in reality it's lower), the token can be ~7000
|
|
9
|
-
export const MEDIA_TOKEN_LENGTH_LIMIT = 7000;
|
|
9
|
+
export const MEDIA_TOKEN_LENGTH_LIMIT = 7000;
|
|
10
|
+
export { isCDNEnabled } from './isCDNEnabled';
|
|
@@ -9,7 +9,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
|
9
9
|
import { map } from 'rxjs/operators/map';
|
|
10
10
|
import { Subscription } from 'rxjs/Subscription';
|
|
11
11
|
// eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
12
|
-
import uuid from 'uuid
|
|
12
|
+
import { v4 as uuid } from 'uuid';
|
|
13
13
|
import { authToOwner } from '@atlaskit/media-core/auth-to-owner';
|
|
14
14
|
import { isValidUuid } from '@atlaskit/media-common/isValidUuid';
|
|
15
15
|
import { downloadUrl } from '@atlaskit/media-common/downloadUrl';
|
|
@@ -51,8 +51,8 @@ export var MediaClient = /*#__PURE__*/function () {
|
|
|
51
51
|
}
|
|
52
52
|
}, {
|
|
53
53
|
key: "getImageUrlSync",
|
|
54
|
-
value: function getImageUrlSync(id, params) {
|
|
55
|
-
return this.mediaStore.getFileImageURLSync(id, params);
|
|
54
|
+
value: function getImageUrlSync(id, params, seededCdnUrl) {
|
|
55
|
+
return this.mediaStore.getFileImageURLSync(id, params, seededCdnUrl);
|
|
56
56
|
}
|
|
57
57
|
}, {
|
|
58
58
|
key: "getClientId",
|
|
@@ -13,9 +13,11 @@ import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
|
13
13
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
14
14
|
import { FILE_CACHE_MAX_AGE } from '../../constants';
|
|
15
15
|
import { getArtifactUrl } from '../../models/artifacts';
|
|
16
|
+
import { isCDNEnabled } from '../../utils/isCDNEnabled';
|
|
16
17
|
import { isPathBasedEnabled } from '../../utils/isPathBasedEnabled';
|
|
17
18
|
import { mapToMediaCdnUrl } from '../../utils/mapToMediaCdnUrl';
|
|
18
19
|
import { mapToPathBasedUrl } from '../../utils/mapToPathBasedUrl';
|
|
20
|
+
import { mapToSeedBasedCdnUrl } from '../../utils/mapToSeedBasedCdnUrl';
|
|
19
21
|
import { isRequestError, request as _request } from '../../utils/request';
|
|
20
22
|
import { createMapResponseToBlob } from '../../utils/request/createMapResponseToBlob';
|
|
21
23
|
import { createMapResponseToJson } from '../../utils/request/createMapResponseToJson';
|
|
@@ -434,13 +436,13 @@ export var MediaStore = /*#__PURE__*/function () {
|
|
|
434
436
|
}() // TODO Create ticket in case Trace Id can be supported through query params
|
|
435
437
|
}, {
|
|
436
438
|
key: "getFileImageURLSync",
|
|
437
|
-
value: function getFileImageURLSync(id, params) {
|
|
439
|
+
value: function getFileImageURLSync(id, params, seededCdnUrl) {
|
|
438
440
|
var auth = this.resolveInitialAuth();
|
|
439
|
-
return this.createFileImageURL(id, auth, params);
|
|
441
|
+
return this.createFileImageURL(id, auth, params, seededCdnUrl);
|
|
440
442
|
}
|
|
441
443
|
}, {
|
|
442
444
|
key: "createFileImageURL",
|
|
443
|
-
value: function createFileImageURL(id, auth, params) {
|
|
445
|
+
value: function createFileImageURL(id, auth, params, seededCdnUrl) {
|
|
444
446
|
var wmv = fg('confluence_watermark_admin_ui') ? getWatermarkVersionFromToken(auth.token) : undefined;
|
|
445
447
|
var options = {
|
|
446
448
|
params: _objectSpread(_objectSpread({}, extendImageParams(params)), wmv ? {
|
|
@@ -449,6 +451,9 @@ export var MediaStore = /*#__PURE__*/function () {
|
|
|
449
451
|
auth: auth
|
|
450
452
|
};
|
|
451
453
|
var imageEndpoint = cdnFeatureFlag('image');
|
|
454
|
+
if (seededCdnUrl && isCDNEnabled()) {
|
|
455
|
+
return mapToSeedBasedCdnUrl(seededCdnUrl, options.params);
|
|
456
|
+
}
|
|
452
457
|
if (isPathBasedEnabled()) {
|
|
453
458
|
return mapToPathBasedUrl(createUrl("".concat(auth.baseUrl, "/file/").concat(id, "/").concat(imageEndpoint), options));
|
|
454
459
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
5
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
6
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
7
|
+
import { mapMediaItemToFileState } from './file-state';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Describes the shape of a plain recorded AGG media item as serialized by Confluence SSR.
|
|
11
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
12
|
+
*
|
|
13
|
+
* This is the shape as returned by dt-api-filestore `media_items` aggregation.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Describes a complete SSR media item with id and details.
|
|
18
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Safely coerce a value to a number, returning undefined if the value is null/undefined.
|
|
23
|
+
*/
|
|
24
|
+
var toNumber = function toNumber(value) {
|
|
25
|
+
return value == null ? undefined : Number(value);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Convert abuse classification fields to the keyed format expected by FileState,
|
|
30
|
+
* returning undefined if either classification or confidence is missing/falsy.
|
|
31
|
+
*/
|
|
32
|
+
var toAbuseClassification = function toAbuseClassification(value) {
|
|
33
|
+
if (!value || !value.classification || !value.confidence) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
classification: value.classification,
|
|
38
|
+
confidence: value.confidence
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Convert the `artifactsList` array shape into the keyed `MediaFileArtifacts`
|
|
44
|
+
* dict that `MediaItemDetails` / `FileState` consumers expect.
|
|
45
|
+
*
|
|
46
|
+
* Each artifact is keyed by its `name` (e.g. `'image.png'`, `'thumb_120.jpg'`).
|
|
47
|
+
* Artifacts missing required fields (`name`, `url`, `processingStatus`)
|
|
48
|
+
* are skipped rather than coerced — callers should treat absent artifacts as
|
|
49
|
+
* "not yet available" rather than "failed".
|
|
50
|
+
*
|
|
51
|
+
* NOTE: This assumes AGG returns `name` values matching the canonical
|
|
52
|
+
* `MediaFileArtifacts` key vocabulary. If that assumption is broken, the
|
|
53
|
+
* downstream renderer will silently miss SSR thumbnails — verify with a real
|
|
54
|
+
* payload before relying on SSR previews.
|
|
55
|
+
*/
|
|
56
|
+
var toArtifacts = function toArtifacts(list) {
|
|
57
|
+
var out = {};
|
|
58
|
+
if (!list) {
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
var _iterator = _createForOfIteratorHelper(list),
|
|
62
|
+
_step;
|
|
63
|
+
try {
|
|
64
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
65
|
+
var item = _step.value;
|
|
66
|
+
if (!item.name || !item.url || !item.processingStatus) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
var artifact = _objectSpread(_objectSpread(_objectSpread({
|
|
70
|
+
processingStatus: item.processingStatus,
|
|
71
|
+
url: item.url
|
|
72
|
+
}, item.mimeType ? {
|
|
73
|
+
mimeType: item.mimeType
|
|
74
|
+
} : {}), item.size != null ? {
|
|
75
|
+
size: Number(item.size)
|
|
76
|
+
} : {}), item.createdAt != null ? {
|
|
77
|
+
createdAt: Number(item.createdAt)
|
|
78
|
+
} : {});
|
|
79
|
+
out[item.name] = artifact;
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
_iterator.e(err);
|
|
83
|
+
} finally {
|
|
84
|
+
_iterator.f();
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Map a plain SSR media item (from AGG / dt-api-filestore) to a `FileState`
|
|
91
|
+
* for seeding `<Card />` / `<MediaInlineCard />`.
|
|
92
|
+
*
|
|
93
|
+
* Returns `undefined` when:
|
|
94
|
+
* - `item` is null/undefined or missing `id`/`details`
|
|
95
|
+
* - any of the required `MediaFile` fields (`name`, `mimeType`, `mediaType`,
|
|
96
|
+
* `processingStatus`) are missing or falsy
|
|
97
|
+
* - `size` is missing or cannot be coerced to a number
|
|
98
|
+
*
|
|
99
|
+
* The transformation reshapes `details.artifactsList` (array, AGG-only) into
|
|
100
|
+
* the keyed `details.artifacts` dict expected by `mapMediaItemToFileState`,
|
|
101
|
+
* and coerces `AGG$Long` numerics.
|
|
102
|
+
*
|
|
103
|
+
* MUST NOT throw on malformed input — always return undefined instead.
|
|
104
|
+
*/
|
|
105
|
+
export var mapSsrMediaItemToFileState = function mapSsrMediaItemToFileState(item) {
|
|
106
|
+
try {
|
|
107
|
+
var _d$representations, _d$mediaMetadata, _d$preview;
|
|
108
|
+
if (!(item !== null && item !== void 0 && item.id) || !item.details) {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
var d = item.details;
|
|
112
|
+
var processingStatus = d.processingStatus;
|
|
113
|
+
var mediaType = d.mediaType;
|
|
114
|
+
var size = toNumber(d.size);
|
|
115
|
+
if (!d.name || !d.mimeType || !mediaType || !processingStatus || size === undefined) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
var abuseClassification = toAbuseClassification(d.abuseClassification);
|
|
119
|
+
var details = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
|
|
120
|
+
name: d.name,
|
|
121
|
+
mimeType: d.mimeType,
|
|
122
|
+
mediaType: mediaType,
|
|
123
|
+
processingStatus: processingStatus,
|
|
124
|
+
size: size,
|
|
125
|
+
artifacts: toArtifacts(d.artifactsList),
|
|
126
|
+
representations: (_d$representations = d.representations) !== null && _d$representations !== void 0 && _d$representations.image ? {
|
|
127
|
+
image: {}
|
|
128
|
+
} : {}
|
|
129
|
+
}, d.failReason ? {
|
|
130
|
+
failReason: d.failReason
|
|
131
|
+
} : {}), d.createdAt != null ? {
|
|
132
|
+
createdAt: Number(d.createdAt)
|
|
133
|
+
} : {}), ((_d$mediaMetadata = d.mediaMetadata) === null || _d$mediaMetadata === void 0 ? void 0 : _d$mediaMetadata.duration) != null ? {
|
|
134
|
+
mediaMetadata: {
|
|
135
|
+
duration: d.mediaMetadata.duration
|
|
136
|
+
}
|
|
137
|
+
} : {}), abuseClassification ? {
|
|
138
|
+
abuseClassification: abuseClassification
|
|
139
|
+
} : {}), (_d$preview = d.preview) !== null && _d$preview !== void 0 && _d$preview.cdnUrl ? {
|
|
140
|
+
previewCdnUrl: d.preview.cdnUrl
|
|
141
|
+
} : {});
|
|
142
|
+
return mapMediaItemToFileState(item.id, details);
|
|
143
|
+
} catch (_unused) {
|
|
144
|
+
// Silently catch any unexpected errors and return undefined
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
var IMAGE_PARAM_KEYS = new Set(['width', 'height', 'mode', 'allowAnimated', 'upscale', 'version', 'max-age']);
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Adds supported image parameters to a pre-signed CDN asset URL without
|
|
6
|
+
* re-parsing or re-encoding its existing query string.
|
|
7
|
+
*
|
|
8
|
+
* Watermarked CloudFront policies anchor `wm-ari` and `wm-v` as a literal
|
|
9
|
+
* suffix, so parameters must be inserted before `wm-ari`. Non-watermarked
|
|
10
|
+
* policies have a trailing wildcard, so parameters can be appended.
|
|
11
|
+
*/
|
|
12
|
+
export var mapToSeedBasedCdnUrl = function mapToSeedBasedCdnUrl(seededCdnUrl, params) {
|
|
13
|
+
var insert = Object.entries(params !== null && params !== void 0 ? params : {}).filter(function (_ref) {
|
|
14
|
+
var _ref2 = _slicedToArray(_ref, 2),
|
|
15
|
+
key = _ref2[0],
|
|
16
|
+
value = _ref2[1];
|
|
17
|
+
return value != null && IMAGE_PARAM_KEYS.has(key);
|
|
18
|
+
}).map(function (_ref3) {
|
|
19
|
+
var _ref4 = _slicedToArray(_ref3, 2),
|
|
20
|
+
key = _ref4[0],
|
|
21
|
+
value = _ref4[1];
|
|
22
|
+
return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(String(value)));
|
|
23
|
+
}).join('&');
|
|
24
|
+
if (!insert) {
|
|
25
|
+
return seededCdnUrl;
|
|
26
|
+
}
|
|
27
|
+
var wm = seededCdnUrl.search(/[?&]wm-ari=/);
|
|
28
|
+
if (wm !== -1) {
|
|
29
|
+
var separator = seededCdnUrl[wm];
|
|
30
|
+
return "".concat(seededCdnUrl.slice(0, wm)).concat(separator).concat(insert, "&").concat(seededCdnUrl.slice(wm + 1));
|
|
31
|
+
}
|
|
32
|
+
return "".concat(seededCdnUrl).concat(seededCdnUrl.includes('?') ? '&' : '?').concat(insert);
|
|
33
|
+
};
|
|
@@ -6,4 +6,5 @@ export var MEDIA_CDN_MAP = {
|
|
|
6
6
|
// Cloudfront has a hard limit of 8,192 bytes
|
|
7
7
|
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html
|
|
8
8
|
// Assuming other parts of the URL make up a max of ~1000 (in reality it's lower), the token can be ~7000
|
|
9
|
-
export var MEDIA_TOKEN_LENGTH_LIMIT = 7000;
|
|
9
|
+
export var MEDIA_TOKEN_LENGTH_LIMIT = 7000;
|
|
10
|
+
export { isCDNEnabled } from './isCDNEnabled';
|
|
@@ -23,7 +23,7 @@ export declare class MediaClient {
|
|
|
23
23
|
__DO_NOT_USE__getMediaStore(): MediaStore;
|
|
24
24
|
getImage(id: string, params?: MediaStoreGetFileImageParams, controller?: AbortController, fetchMaxRes?: boolean, traceContext?: MediaTraceContext): Promise<Blob>;
|
|
25
25
|
getImageUrl(id: string, params?: MediaStoreGetFileImageParams): Promise<string>;
|
|
26
|
-
getImageUrlSync(id: string, params?: MediaStoreGetFileImageParams): string;
|
|
26
|
+
getImageUrlSync(id: string, params?: MediaStoreGetFileImageParams, seededCdnUrl?: string): string;
|
|
27
27
|
getClientId(collectionName?: string): Promise<string | undefined>;
|
|
28
28
|
getClientIdSync(): string | undefined;
|
|
29
29
|
getImageMetadata(id: string, params?: MediaStoreGetFileImageParams): Promise<ImageMetadata>;
|
|
@@ -26,7 +26,7 @@ export declare class MediaStore implements MediaApi {
|
|
|
26
26
|
}): Promise<MediaStoreResponse<TouchedFiles>>;
|
|
27
27
|
getFile(fileId: string, params?: MediaStoreGetFileParams, traceContext?: MediaTraceContext): Promise<MediaStoreResponse<MediaFile>>;
|
|
28
28
|
getFileImageURL(id: string, params?: MediaStoreGetFileImageParams): Promise<string>;
|
|
29
|
-
getFileImageURLSync(id: string, params?: MediaStoreGetFileImageParams): string;
|
|
29
|
+
getFileImageURLSync(id: string, params?: MediaStoreGetFileImageParams, seededCdnUrl?: string): string;
|
|
30
30
|
private createFileImageURL;
|
|
31
31
|
getFileBinary(id: string, collectionName?: string, abortController?: AbortController, maxAge?: number): Promise<Blob>;
|
|
32
32
|
getFileBinaryURL(id: string, collectionName?: string, maxAge?: number, name?: string): Promise<string>;
|
|
@@ -177,7 +177,7 @@ export interface MediaApi {
|
|
|
177
177
|
}) => Promise<MediaStoreResponse<TouchedFiles>>;
|
|
178
178
|
getFile: (fileId: string, params: MediaStoreGetFileParams, traceContext?: MediaTraceContext) => Promise<MediaStoreResponse<MediaFile>>;
|
|
179
179
|
getFileImageURL: (id: string, params?: MediaStoreGetFileImageParams) => Promise<string>;
|
|
180
|
-
getFileImageURLSync: (id: string, params?: MediaStoreGetFileImageParams) => string;
|
|
180
|
+
getFileImageURLSync: (id: string, params?: MediaStoreGetFileImageParams, seededCdnUrl?: string) => string;
|
|
181
181
|
getFileBinary: (id: string, collectionName?: string, abortController?: AbortController) => Promise<Blob>;
|
|
182
182
|
getFileBinaryURL: (id: string, collectionName?: string, maxAge?: number, name?: string) => Promise<string>;
|
|
183
183
|
getArtifactURL: (artifacts: MediaFileArtifacts, artifactName: keyof MediaFileArtifacts, collectionName?: string) => Promise<string>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { FileState } from '@atlaskit/media-state/file-state';
|
|
2
|
+
/**
|
|
3
|
+
* Describes the shape of a plain recorded AGG media item as serialized by Confluence SSR.
|
|
4
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
5
|
+
*
|
|
6
|
+
* This is the shape as returned by dt-api-filestore `media_items` aggregation.
|
|
7
|
+
*/
|
|
8
|
+
export interface SsrMediaItemDetails {
|
|
9
|
+
readonly name?: string | null;
|
|
10
|
+
readonly size?: number | null;
|
|
11
|
+
readonly mimeType?: string | null;
|
|
12
|
+
readonly mediaType?: string | null;
|
|
13
|
+
readonly processingStatus?: string | null;
|
|
14
|
+
readonly failReason?: string | null;
|
|
15
|
+
readonly createdAt?: number | null;
|
|
16
|
+
readonly preview?: {
|
|
17
|
+
readonly cdnUrl?: string | null;
|
|
18
|
+
} | null;
|
|
19
|
+
readonly artifactsList?: ReadonlyArray<{
|
|
20
|
+
readonly createdAt?: number | null;
|
|
21
|
+
readonly mimeType?: string | null;
|
|
22
|
+
readonly name?: string;
|
|
23
|
+
readonly processingStatus?: string | null;
|
|
24
|
+
readonly size?: number | null;
|
|
25
|
+
readonly url?: string | null;
|
|
26
|
+
}> | null;
|
|
27
|
+
readonly representations?: {
|
|
28
|
+
readonly image?: {
|
|
29
|
+
readonly _empty?: boolean | null;
|
|
30
|
+
} | null;
|
|
31
|
+
} | null;
|
|
32
|
+
readonly mediaMetadata?: {
|
|
33
|
+
readonly duration?: number | null;
|
|
34
|
+
} | null;
|
|
35
|
+
readonly abuseClassification?: {
|
|
36
|
+
readonly classification?: string | null;
|
|
37
|
+
readonly confidence?: string | null;
|
|
38
|
+
} | null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Describes a complete SSR media item with id and details.
|
|
42
|
+
* All fields are optional/nullable to tolerate untrusted runtime JSON from window globals.
|
|
43
|
+
*/
|
|
44
|
+
export interface SsrMediaItem {
|
|
45
|
+
readonly id?: string;
|
|
46
|
+
readonly type?: string;
|
|
47
|
+
readonly details?: SsrMediaItemDetails | null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Map a plain SSR media item (from AGG / dt-api-filestore) to a `FileState`
|
|
51
|
+
* for seeding `<Card />` / `<MediaInlineCard />`.
|
|
52
|
+
*
|
|
53
|
+
* Returns `undefined` when:
|
|
54
|
+
* - `item` is null/undefined or missing `id`/`details`
|
|
55
|
+
* - any of the required `MediaFile` fields (`name`, `mimeType`, `mediaType`,
|
|
56
|
+
* `processingStatus`) are missing or falsy
|
|
57
|
+
* - `size` is missing or cannot be coerced to a number
|
|
58
|
+
*
|
|
59
|
+
* The transformation reshapes `details.artifactsList` (array, AGG-only) into
|
|
60
|
+
* the keyed `details.artifacts` dict expected by `mapMediaItemToFileState`,
|
|
61
|
+
* and coerces `AGG$Long` numerics.
|
|
62
|
+
*
|
|
63
|
+
* MUST NOT throw on malformed input — always return undefined instead.
|
|
64
|
+
*/
|
|
65
|
+
export declare const mapSsrMediaItemToFileState: (item: SsrMediaItem | null | undefined) => FileState | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MediaStoreGetFileImageParams } from '../client/media-store/types';
|
|
2
|
+
/**
|
|
3
|
+
* Adds supported image parameters to a pre-signed CDN asset URL without
|
|
4
|
+
* re-parsing or re-encoding its existing query string.
|
|
5
|
+
*
|
|
6
|
+
* Watermarked CloudFront policies anchor `wm-ari` and `wm-v` as a literal
|
|
7
|
+
* suffix, so parameters must be inserted before `wm-ari`. Non-watermarked
|
|
8
|
+
* policies have a trailing wildcard, so parameters can be appended.
|
|
9
|
+
*/
|
|
10
|
+
export declare const mapToSeedBasedCdnUrl: (seededCdnUrl: string, params?: MediaStoreGetFileImageParams) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/media-client",
|
|
3
|
-
"version": "37.
|
|
3
|
+
"version": "37.7.1",
|
|
4
4
|
"description": "Media API Web Client Library",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"lru_map": "^0.4.1",
|
|
35
35
|
"rusha": "^0.8.13",
|
|
36
36
|
"rxjs": "^5.5.0",
|
|
37
|
-
"uuid": "^
|
|
37
|
+
"uuid": "^11.1.1",
|
|
38
38
|
"xstate": "4.20.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/media-client/ssr-media-item",
|
|
3
|
+
"main": "../dist/cjs/models/ssr-media-item.js",
|
|
4
|
+
"module": "../dist/esm/models/ssr-media-item.js",
|
|
5
|
+
"module:es2019": "../dist/es2019/models/ssr-media-item.js",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"types": "../dist/types/models/ssr-media-item.d.ts"
|
|
8
|
+
}
|