@atlaskit/media-client 37.0.0 → 37.1.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 +22 -0
- package/dist/cjs/client/media-store/resolveAuth.js +7 -1
- package/dist/cjs/constants.js +14 -2
- package/dist/cjs/utils/hashing/fileReaderError.js +36 -0
- package/dist/cjs/utils/hashing/sha256SimpleHasher.js +9 -1
- package/dist/cjs/utils/hashing/simpleHasher.js +9 -1
- package/dist/es2019/client/media-store/resolveAuth.js +8 -0
- package/dist/es2019/constants.js +13 -1
- package/dist/es2019/utils/hashing/fileReaderError.js +31 -0
- package/dist/es2019/utils/hashing/sha256SimpleHasher.js +9 -1
- package/dist/es2019/utils/hashing/simpleHasher.js +9 -1
- package/dist/esm/client/media-store/resolveAuth.js +7 -1
- package/dist/esm/constants.js +13 -1
- package/dist/esm/utils/hashing/fileReaderError.js +31 -0
- package/dist/esm/utils/hashing/sha256SimpleHasher.js +9 -1
- package/dist/esm/utils/hashing/simpleHasher.js +9 -1
- package/dist/types/client/media-store/error.d.ts +1 -1
- package/dist/types/constants.d.ts +11 -0
- package/dist/types/models/errors/types.d.ts +1 -1
- package/dist/types/utils/hashing/fileReaderError.d.ts +15 -0
- package/hashing/file-reader-error/package.json +8 -0
- package/package.json +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @atlaskit/media-client
|
|
2
2
|
|
|
3
|
+
## 37.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`8bac4be486285`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8bac4be486285) -
|
|
8
|
+
Adds a client-side validation guard in media-client's resolveAuth that rejects an auth resolving
|
|
9
|
+
to neither a valid clientId nor a valid asapIssuer, behind the platform_media_validate_client_id
|
|
10
|
+
|
|
11
|
+
## 37.1.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [`0a00d74d3fe12`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/0a00d74d3fe12) -
|
|
16
|
+
Surface the underlying `DOMException` (e.g. `NotReadableError`, `NotFoundError`, `SecurityError`)
|
|
17
|
+
when a `FileReader` fails while hashing an upload, instead of leaking the browser `ProgressEvent`
|
|
18
|
+
which analytics serialised to the opaque `{"isTrusted":true}` error detail. Gated behind
|
|
19
|
+
`platform_media_filereader_error_surfacing`.
|
|
20
|
+
|
|
21
|
+
`@atlaskit/media-client` also exposes the `toFileReaderError` helper via a new
|
|
22
|
+
`@atlaskit/media-client/hashing/file-reader-error` entry point so consumers can normalise
|
|
23
|
+
`FileReader`/`DOMException` failures consistently without duplicating the logic.
|
|
24
|
+
|
|
3
25
|
## 37.0.0
|
|
4
26
|
|
|
5
27
|
### Major Changes
|
|
@@ -87,8 +87,14 @@ var resolveAuth = exports.resolveAuth = /*#__PURE__*/function () {
|
|
|
87
87
|
}
|
|
88
88
|
throw new _error.MediaStoreError('emptyAuth');
|
|
89
89
|
case 6:
|
|
90
|
-
|
|
90
|
+
if (!('clientId' in auth && !auth.clientId && (0, _platformFeatureFlags.fg)('platform_media_validate_client_id'))) {
|
|
91
|
+
_context.next = 7;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
throw new _error.MediaStoreError('emptyClientId');
|
|
91
95
|
case 7:
|
|
96
|
+
return _context.abrupt("return", auth);
|
|
97
|
+
case 8:
|
|
92
98
|
case "end":
|
|
93
99
|
return _context.stop();
|
|
94
100
|
}
|
package/dist/cjs/constants.js
CHANGED
|
@@ -3,11 +3,23 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RECENTS_COLLECTION = exports.PROCESSING_BATCH_SIZE = exports.MAX_UPLOAD_FILE_SIZE = exports.MAX_RESOLUTION = exports.FILE_CACHE_MAX_AGE = exports.CHUNK_SIZE = void 0;
|
|
6
|
+
exports.RECENTS_COLLECTION = exports.PROCESSING_BATCH_SIZE = exports.MAX_UPLOAD_FILE_SIZE = exports.MAX_RESOLUTION = exports.FILE_READER_FALLBACK_ERROR_NAME = exports.FILE_CACHE_MAX_AGE = exports.CHUNK_SIZE = void 0;
|
|
7
7
|
var _media = require("./models/media");
|
|
8
8
|
var RECENTS_COLLECTION = exports.RECENTS_COLLECTION = 'recents';
|
|
9
9
|
var FILE_CACHE_MAX_AGE = exports.FILE_CACHE_MAX_AGE = 60 * 60 * 24 * 30; // Retain for 30 days
|
|
10
10
|
var MAX_RESOLUTION = exports.MAX_RESOLUTION = 4096;
|
|
11
11
|
var CHUNK_SIZE = exports.CHUNK_SIZE = 5 * _media.DATA_UNIT.MB;
|
|
12
12
|
var PROCESSING_BATCH_SIZE = exports.PROCESSING_BATCH_SIZE = 1000;
|
|
13
|
-
var MAX_UPLOAD_FILE_SIZE = exports.MAX_UPLOAD_FILE_SIZE = 2 * _media.DATA_UNIT.TB;
|
|
13
|
+
var MAX_UPLOAD_FILE_SIZE = exports.MAX_UPLOAD_FILE_SIZE = 2 * _media.DATA_UNIT.TB;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Sentinel error name used when a `FileReader`/upload failure cannot be attributed
|
|
17
|
+
* to a concrete `DOMException` (e.g. `reader.error` is `null` or has an empty name).
|
|
18
|
+
*
|
|
19
|
+
* This value ends up as the analytics `errorDetail`/`failReason`, so it is an
|
|
20
|
+
* implicit contract that dashboards/SLO filters key off. It lives here (a public
|
|
21
|
+
* `@atlaskit/media-client/constants` entry point) so both media-client and
|
|
22
|
+
* downstream packages (e.g. media-picker) import a single source of truth rather
|
|
23
|
+
* than re-typing the literal and risking analytics bucket drift.
|
|
24
|
+
*/
|
|
25
|
+
var FILE_READER_FALLBACK_ERROR_NAME = exports.FILE_READER_FALLBACK_ERROR_NAME = 'FileReaderError';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.toFileReaderError = void 0;
|
|
7
|
+
var _constants = require("../../constants");
|
|
8
|
+
/**
|
|
9
|
+
* `FileReader.onerror` hands back a `ProgressEvent`, not an `Error`. Rejecting a
|
|
10
|
+
* promise with that event causes downstream analytics (`JSON.stringify`) to
|
|
11
|
+
* collapse it to `{"isTrusted":true}`, hiding the real cause. The actual failure
|
|
12
|
+
* is on `reader.error` as a `DOMException` whose `name` identifies the problem
|
|
13
|
+
* (e.g. `NotReadableError`, `NotFoundError`, `SecurityError`).
|
|
14
|
+
*
|
|
15
|
+
* This converts that `DOMException` into a real `Error` that preserves the name,
|
|
16
|
+
* so the failure stays legible as it travels through the upload pipeline. The
|
|
17
|
+
* original `DOMException` (including its descriptive message) is retained on the
|
|
18
|
+
* `cause` property for debugging — defined non-enumerable, mirroring the native
|
|
19
|
+
* `new Error(message, { cause })` form, so it never leaks into analytics that
|
|
20
|
+
* read `name`/`message` or serialise the error via `JSON.stringify`.
|
|
21
|
+
*/
|
|
22
|
+
var toFileReaderError = exports.toFileReaderError = function toFileReaderError(error) {
|
|
23
|
+
var _error$name;
|
|
24
|
+
var name = error !== null && error !== void 0 && (_error$name = error.name) !== null && _error$name !== void 0 && _error$name.trim() ? error.name : _constants.FILE_READER_FALLBACK_ERROR_NAME;
|
|
25
|
+
var normalized = new Error(name);
|
|
26
|
+
normalized.name = name;
|
|
27
|
+
if (error) {
|
|
28
|
+
Object.defineProperty(normalized, 'cause', {
|
|
29
|
+
value: error,
|
|
30
|
+
enumerable: false,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return normalized;
|
|
36
|
+
};
|
|
@@ -8,6 +8,8 @@ exports.SimpleHasher = void 0;
|
|
|
8
8
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
9
9
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
10
10
|
var _jsSha = require("js-sha256");
|
|
11
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
12
|
+
var _fileReaderError = require("./fileReaderError");
|
|
11
13
|
var SimpleHasher = exports.SimpleHasher = /*#__PURE__*/function () {
|
|
12
14
|
function SimpleHasher() {
|
|
13
15
|
(0, _classCallCheck2.default)(this, SimpleHasher);
|
|
@@ -22,7 +24,13 @@ var SimpleHasher = exports.SimpleHasher = /*#__PURE__*/function () {
|
|
|
22
24
|
var _reader$result;
|
|
23
25
|
resolve("sha256-".concat(_jsSha.sha256.create().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').hex()));
|
|
24
26
|
};
|
|
25
|
-
reader.onerror =
|
|
27
|
+
reader.onerror = function (event) {
|
|
28
|
+
if ((0, _platformFeatureFlags.fg)('platform_media_filereader_error_surfacing')) {
|
|
29
|
+
reject((0, _fileReaderError.toFileReaderError)(reader.error));
|
|
30
|
+
} else {
|
|
31
|
+
reject(event);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
26
34
|
});
|
|
27
35
|
}
|
|
28
36
|
}]);
|
|
@@ -9,6 +9,8 @@ exports.SimpleHasher = void 0;
|
|
|
9
9
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
10
10
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
11
11
|
var Rusha = _interopRequireWildcard(require("rusha"));
|
|
12
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
13
|
+
var _fileReaderError = require("./fileReaderError");
|
|
12
14
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
13
15
|
var SimpleHasher = exports.SimpleHasher = /*#__PURE__*/function () {
|
|
14
16
|
function SimpleHasher() {
|
|
@@ -24,7 +26,13 @@ var SimpleHasher = exports.SimpleHasher = /*#__PURE__*/function () {
|
|
|
24
26
|
var _reader$result;
|
|
25
27
|
resolve(Rusha.createHash().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').digest('hex'));
|
|
26
28
|
};
|
|
27
|
-
reader.onerror =
|
|
29
|
+
reader.onerror = function (event) {
|
|
30
|
+
if ((0, _platformFeatureFlags.fg)('platform_media_filereader_error_surfacing')) {
|
|
31
|
+
reject((0, _fileReaderError.toFileReaderError)(reader.error));
|
|
32
|
+
} else {
|
|
33
|
+
reject(event);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
28
36
|
});
|
|
29
37
|
}
|
|
30
38
|
}]);
|
|
@@ -58,6 +58,14 @@ export const resolveAuth = async (authProvider, authContext, authProviderTimeout
|
|
|
58
58
|
throw new MediaStoreError('emptyAuth');
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/*
|
|
62
|
+
Only client-based auth carries a clientId. Fail fast when it is present but empty, instead of
|
|
63
|
+
letting the request reach dt-auth and get rejected with `Client id "" is invalid`.
|
|
64
|
+
*/
|
|
65
|
+
if ('clientId' in auth && !auth.clientId && fg('platform_media_validate_client_id')) {
|
|
66
|
+
throw new MediaStoreError('emptyClientId');
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
/*
|
|
62
70
|
We added a token expiration check here in the past, and then we had to revert due to edge cases in the client that we can't control.
|
|
63
71
|
Token expiration check in the frontend is a bad idea. Don't do it!
|
package/dist/es2019/constants.js
CHANGED
|
@@ -4,4 +4,16 @@ export const FILE_CACHE_MAX_AGE = 60 * 60 * 24 * 30; // Retain for 30 days
|
|
|
4
4
|
export const MAX_RESOLUTION = 4096;
|
|
5
5
|
export const CHUNK_SIZE = 5 * DATA_UNIT.MB;
|
|
6
6
|
export const PROCESSING_BATCH_SIZE = 1000;
|
|
7
|
-
export const MAX_UPLOAD_FILE_SIZE = 2 * DATA_UNIT.TB;
|
|
7
|
+
export const MAX_UPLOAD_FILE_SIZE = 2 * DATA_UNIT.TB;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sentinel error name used when a `FileReader`/upload failure cannot be attributed
|
|
11
|
+
* to a concrete `DOMException` (e.g. `reader.error` is `null` or has an empty name).
|
|
12
|
+
*
|
|
13
|
+
* This value ends up as the analytics `errorDetail`/`failReason`, so it is an
|
|
14
|
+
* implicit contract that dashboards/SLO filters key off. It lives here (a public
|
|
15
|
+
* `@atlaskit/media-client/constants` entry point) so both media-client and
|
|
16
|
+
* downstream packages (e.g. media-picker) import a single source of truth rather
|
|
17
|
+
* than re-typing the literal and risking analytics bucket drift.
|
|
18
|
+
*/
|
|
19
|
+
export const FILE_READER_FALLBACK_ERROR_NAME = 'FileReaderError';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FILE_READER_FALLBACK_ERROR_NAME } from '../../constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `FileReader.onerror` hands back a `ProgressEvent`, not an `Error`. Rejecting a
|
|
5
|
+
* promise with that event causes downstream analytics (`JSON.stringify`) to
|
|
6
|
+
* collapse it to `{"isTrusted":true}`, hiding the real cause. The actual failure
|
|
7
|
+
* is on `reader.error` as a `DOMException` whose `name` identifies the problem
|
|
8
|
+
* (e.g. `NotReadableError`, `NotFoundError`, `SecurityError`).
|
|
9
|
+
*
|
|
10
|
+
* This converts that `DOMException` into a real `Error` that preserves the name,
|
|
11
|
+
* so the failure stays legible as it travels through the upload pipeline. The
|
|
12
|
+
* original `DOMException` (including its descriptive message) is retained on the
|
|
13
|
+
* `cause` property for debugging — defined non-enumerable, mirroring the native
|
|
14
|
+
* `new Error(message, { cause })` form, so it never leaks into analytics that
|
|
15
|
+
* read `name`/`message` or serialise the error via `JSON.stringify`.
|
|
16
|
+
*/
|
|
17
|
+
export const toFileReaderError = error => {
|
|
18
|
+
var _error$name;
|
|
19
|
+
const name = error !== null && error !== void 0 && (_error$name = error.name) !== null && _error$name !== void 0 && _error$name.trim() ? error.name : FILE_READER_FALLBACK_ERROR_NAME;
|
|
20
|
+
const normalized = new Error(name);
|
|
21
|
+
normalized.name = name;
|
|
22
|
+
if (error) {
|
|
23
|
+
Object.defineProperty(normalized, 'cause', {
|
|
24
|
+
value: error,
|
|
25
|
+
enumerable: false,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { sha256 } from 'js-sha256';
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
|
+
import { toFileReaderError } from './fileReaderError';
|
|
2
4
|
export class SimpleHasher {
|
|
3
5
|
hash(blob) {
|
|
4
6
|
return new Promise((resolve, reject) => {
|
|
@@ -8,7 +10,13 @@ export class SimpleHasher {
|
|
|
8
10
|
var _reader$result;
|
|
9
11
|
resolve(`sha256-${sha256.create().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').hex()}`);
|
|
10
12
|
};
|
|
11
|
-
reader.onerror =
|
|
13
|
+
reader.onerror = event => {
|
|
14
|
+
if (fg('platform_media_filereader_error_surfacing')) {
|
|
15
|
+
reject(toFileReaderError(reader.error));
|
|
16
|
+
} else {
|
|
17
|
+
reject(event);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
12
20
|
});
|
|
13
21
|
}
|
|
14
22
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as Rusha from 'rusha';
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
|
+
import { toFileReaderError } from './fileReaderError';
|
|
2
4
|
export class SimpleHasher {
|
|
3
5
|
hash(blob) {
|
|
4
6
|
return new Promise((resolve, reject) => {
|
|
@@ -8,7 +10,13 @@ export class SimpleHasher {
|
|
|
8
10
|
var _reader$result;
|
|
9
11
|
resolve(Rusha.createHash().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').digest('hex'));
|
|
10
12
|
};
|
|
11
|
-
reader.onerror =
|
|
13
|
+
reader.onerror = event => {
|
|
14
|
+
if (fg('platform_media_filereader_error_surfacing')) {
|
|
15
|
+
reject(toFileReaderError(reader.error));
|
|
16
|
+
} else {
|
|
17
|
+
reject(event);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
12
20
|
});
|
|
13
21
|
}
|
|
14
22
|
}
|
|
@@ -80,8 +80,14 @@ export var resolveAuth = /*#__PURE__*/function () {
|
|
|
80
80
|
}
|
|
81
81
|
throw new MediaStoreError('emptyAuth');
|
|
82
82
|
case 6:
|
|
83
|
-
|
|
83
|
+
if (!('clientId' in auth && !auth.clientId && fg('platform_media_validate_client_id'))) {
|
|
84
|
+
_context.next = 7;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
throw new MediaStoreError('emptyClientId');
|
|
84
88
|
case 7:
|
|
89
|
+
return _context.abrupt("return", auth);
|
|
90
|
+
case 8:
|
|
85
91
|
case "end":
|
|
86
92
|
return _context.stop();
|
|
87
93
|
}
|
package/dist/esm/constants.js
CHANGED
|
@@ -4,4 +4,16 @@ export var FILE_CACHE_MAX_AGE = 60 * 60 * 24 * 30; // Retain for 30 days
|
|
|
4
4
|
export var MAX_RESOLUTION = 4096;
|
|
5
5
|
export var CHUNK_SIZE = 5 * DATA_UNIT.MB;
|
|
6
6
|
export var PROCESSING_BATCH_SIZE = 1000;
|
|
7
|
-
export var MAX_UPLOAD_FILE_SIZE = 2 * DATA_UNIT.TB;
|
|
7
|
+
export var MAX_UPLOAD_FILE_SIZE = 2 * DATA_UNIT.TB;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Sentinel error name used when a `FileReader`/upload failure cannot be attributed
|
|
11
|
+
* to a concrete `DOMException` (e.g. `reader.error` is `null` or has an empty name).
|
|
12
|
+
*
|
|
13
|
+
* This value ends up as the analytics `errorDetail`/`failReason`, so it is an
|
|
14
|
+
* implicit contract that dashboards/SLO filters key off. It lives here (a public
|
|
15
|
+
* `@atlaskit/media-client/constants` entry point) so both media-client and
|
|
16
|
+
* downstream packages (e.g. media-picker) import a single source of truth rather
|
|
17
|
+
* than re-typing the literal and risking analytics bucket drift.
|
|
18
|
+
*/
|
|
19
|
+
export var FILE_READER_FALLBACK_ERROR_NAME = 'FileReaderError';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FILE_READER_FALLBACK_ERROR_NAME } from '../../constants';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `FileReader.onerror` hands back a `ProgressEvent`, not an `Error`. Rejecting a
|
|
5
|
+
* promise with that event causes downstream analytics (`JSON.stringify`) to
|
|
6
|
+
* collapse it to `{"isTrusted":true}`, hiding the real cause. The actual failure
|
|
7
|
+
* is on `reader.error` as a `DOMException` whose `name` identifies the problem
|
|
8
|
+
* (e.g. `NotReadableError`, `NotFoundError`, `SecurityError`).
|
|
9
|
+
*
|
|
10
|
+
* This converts that `DOMException` into a real `Error` that preserves the name,
|
|
11
|
+
* so the failure stays legible as it travels through the upload pipeline. The
|
|
12
|
+
* original `DOMException` (including its descriptive message) is retained on the
|
|
13
|
+
* `cause` property for debugging — defined non-enumerable, mirroring the native
|
|
14
|
+
* `new Error(message, { cause })` form, so it never leaks into analytics that
|
|
15
|
+
* read `name`/`message` or serialise the error via `JSON.stringify`.
|
|
16
|
+
*/
|
|
17
|
+
export var toFileReaderError = function toFileReaderError(error) {
|
|
18
|
+
var _error$name;
|
|
19
|
+
var name = error !== null && error !== void 0 && (_error$name = error.name) !== null && _error$name !== void 0 && _error$name.trim() ? error.name : FILE_READER_FALLBACK_ERROR_NAME;
|
|
20
|
+
var normalized = new Error(name);
|
|
21
|
+
normalized.name = name;
|
|
22
|
+
if (error) {
|
|
23
|
+
Object.defineProperty(normalized, 'cause', {
|
|
24
|
+
value: error,
|
|
25
|
+
enumerable: false,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return normalized;
|
|
31
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
2
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
3
|
import { sha256 } from 'js-sha256';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { toFileReaderError } from './fileReaderError';
|
|
4
6
|
export var SimpleHasher = /*#__PURE__*/function () {
|
|
5
7
|
function SimpleHasher() {
|
|
6
8
|
_classCallCheck(this, SimpleHasher);
|
|
@@ -15,7 +17,13 @@ export var SimpleHasher = /*#__PURE__*/function () {
|
|
|
15
17
|
var _reader$result;
|
|
16
18
|
resolve("sha256-".concat(sha256.create().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').hex()));
|
|
17
19
|
};
|
|
18
|
-
reader.onerror =
|
|
20
|
+
reader.onerror = function (event) {
|
|
21
|
+
if (fg('platform_media_filereader_error_surfacing')) {
|
|
22
|
+
reject(toFileReaderError(reader.error));
|
|
23
|
+
} else {
|
|
24
|
+
reject(event);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
19
27
|
});
|
|
20
28
|
}
|
|
21
29
|
}]);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
2
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
3
|
import * as Rusha from 'rusha';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { toFileReaderError } from './fileReaderError';
|
|
4
6
|
export var SimpleHasher = /*#__PURE__*/function () {
|
|
5
7
|
function SimpleHasher() {
|
|
6
8
|
_classCallCheck(this, SimpleHasher);
|
|
@@ -15,7 +17,13 @@ export var SimpleHasher = /*#__PURE__*/function () {
|
|
|
15
17
|
var _reader$result;
|
|
16
18
|
resolve(Rusha.createHash().update((_reader$result = reader.result) !== null && _reader$result !== void 0 ? _reader$result : '').digest('hex'));
|
|
17
19
|
};
|
|
18
|
-
reader.onerror =
|
|
20
|
+
reader.onerror = function (event) {
|
|
21
|
+
if (fg('platform_media_filereader_error_surfacing')) {
|
|
22
|
+
reject(toFileReaderError(reader.error));
|
|
23
|
+
} else {
|
|
24
|
+
reject(event);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
19
27
|
});
|
|
20
28
|
}
|
|
21
29
|
}]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseMediaClientError } from '../../models/errors';
|
|
2
|
-
export type MediaStoreErrorReason = 'failedAuthProvider' | 'tokenExpired' | 'missingInitialAuth' | 'emptyAuth' | 'authProviderTimedOut';
|
|
2
|
+
export type MediaStoreErrorReason = 'failedAuthProvider' | 'tokenExpired' | 'missingInitialAuth' | 'emptyAuth' | 'emptyClientId' | 'authProviderTimedOut';
|
|
3
3
|
export type MediaStoreErrorAttributes = {
|
|
4
4
|
readonly reason: MediaStoreErrorReason;
|
|
5
5
|
readonly innerError?: Error;
|
|
@@ -4,3 +4,14 @@ export declare const MAX_RESOLUTION = 4096;
|
|
|
4
4
|
export declare const CHUNK_SIZE: number;
|
|
5
5
|
export declare const PROCESSING_BATCH_SIZE = 1000;
|
|
6
6
|
export declare const MAX_UPLOAD_FILE_SIZE: number;
|
|
7
|
+
/**
|
|
8
|
+
* Sentinel error name used when a `FileReader`/upload failure cannot be attributed
|
|
9
|
+
* to a concrete `DOMException` (e.g. `reader.error` is `null` or has an empty name).
|
|
10
|
+
*
|
|
11
|
+
* This value ends up as the analytics `errorDetail`/`failReason`, so it is an
|
|
12
|
+
* implicit contract that dashboards/SLO filters key off. It lives here (a public
|
|
13
|
+
* `@atlaskit/media-client/constants` entry point) so both media-client and
|
|
14
|
+
* downstream packages (e.g. media-picker) import a single source of truth rather
|
|
15
|
+
* than re-typing the literal and risking analytics bucket drift.
|
|
16
|
+
*/
|
|
17
|
+
export declare const FILE_READER_FALLBACK_ERROR_NAME = "FileReaderError";
|
|
@@ -2,7 +2,7 @@ import { type MediaTraceContext } from '@atlaskit/media-common';
|
|
|
2
2
|
export interface SerializableObject {
|
|
3
3
|
[key: string]: string | number | boolean | null | undefined | SerializableObject;
|
|
4
4
|
}
|
|
5
|
-
export type MediaClientErrorReason = 'clientOffline' | 'clientAbortedRequest' | 'clientTimeoutRequest' | 'serverInvalidBody' | 'serverBadRequest' | 'serverUnauthorized' | 'serverForbidden' | 'serverNotFound' | 'serverRateLimited' | 'serverInternalError' | 'serverBadGateway' | 'serverUnexpectedError' | 'serverUnprocessableEntity' | 'serverEntityLocked' | 'failedAuthProvider' | 'tokenExpired' | 'missingInitialAuth' | 'emptyAuth' | 'authProviderTimedOut' | 'invalidFileId' | 'emptyItems' | 'zeroVersionFile' | 'emptyFileName' | 'pollingMaxAttemptsExceeded' | 'fileSizeExceedsLimit' | 'deprecatedEndpoint';
|
|
5
|
+
export type MediaClientErrorReason = 'clientOffline' | 'clientAbortedRequest' | 'clientTimeoutRequest' | 'serverInvalidBody' | 'serverBadRequest' | 'serverUnauthorized' | 'serverForbidden' | 'serverNotFound' | 'serverRateLimited' | 'serverInternalError' | 'serverBadGateway' | 'serverUnexpectedError' | 'serverUnprocessableEntity' | 'serverEntityLocked' | 'failedAuthProvider' | 'tokenExpired' | 'missingInitialAuth' | 'emptyAuth' | 'authProviderTimedOut' | 'emptyClientId' | 'invalidFileId' | 'emptyItems' | 'zeroVersionFile' | 'emptyFileName' | 'pollingMaxAttemptsExceeded' | 'fileSizeExceedsLimit' | 'deprecatedEndpoint';
|
|
6
6
|
export type MediaClientErrorMetadata = SerializableObject & {
|
|
7
7
|
traceContext?: MediaTraceContext;
|
|
8
8
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `FileReader.onerror` hands back a `ProgressEvent`, not an `Error`. Rejecting a
|
|
3
|
+
* promise with that event causes downstream analytics (`JSON.stringify`) to
|
|
4
|
+
* collapse it to `{"isTrusted":true}`, hiding the real cause. The actual failure
|
|
5
|
+
* is on `reader.error` as a `DOMException` whose `name` identifies the problem
|
|
6
|
+
* (e.g. `NotReadableError`, `NotFoundError`, `SecurityError`).
|
|
7
|
+
*
|
|
8
|
+
* This converts that `DOMException` into a real `Error` that preserves the name,
|
|
9
|
+
* so the failure stays legible as it travels through the upload pipeline. The
|
|
10
|
+
* original `DOMException` (including its descriptive message) is retained on the
|
|
11
|
+
* `cause` property for debugging — defined non-enumerable, mirroring the native
|
|
12
|
+
* `new Error(message, { cause })` form, so it never leaks into analytics that
|
|
13
|
+
* read `name`/`message` or serialise the error via `JSON.stringify`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const toFileReaderError: (error: DOMException | null) => Error;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlaskit/media-client/hashing/file-reader-error",
|
|
3
|
+
"main": "../../dist/cjs/utils/hashing/fileReaderError.js",
|
|
4
|
+
"module": "../../dist/esm/utils/hashing/fileReaderError.js",
|
|
5
|
+
"module:es2019": "../../dist/es2019/utils/hashing/fileReaderError.js",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"types": "../../dist/types/utils/hashing/fileReaderError.d.ts"
|
|
8
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/media-client",
|
|
3
|
-
"version": "37.
|
|
3
|
+
"version": "37.1.1",
|
|
4
4
|
"description": "Media API Web Client Library",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@atlaskit/atlassian-context": "^1.0.0",
|
|
25
25
|
"@atlaskit/chunkinator": "^8.0.0",
|
|
26
|
-
"@atlaskit/media-common": "^14.
|
|
26
|
+
"@atlaskit/media-common": "^14.1.0",
|
|
27
27
|
"@atlaskit/platform-feature-flags": "^2.0.0",
|
|
28
28
|
"@babel/runtime": "^7.0.0",
|
|
29
29
|
"dataloader": "^2.1.0",
|
|
@@ -85,11 +85,18 @@
|
|
|
85
85
|
"platform_media_auth_provider_analytics": {
|
|
86
86
|
"type": "boolean"
|
|
87
87
|
},
|
|
88
|
+
"platform_media_validate_client_id": {
|
|
89
|
+
"type": "boolean"
|
|
90
|
+
},
|
|
88
91
|
"confluence_watermark_admin_ui": {
|
|
89
92
|
"type": "boolean"
|
|
90
93
|
},
|
|
91
94
|
"platform_media_upload_expected_size_header": {
|
|
92
95
|
"type": "boolean"
|
|
96
|
+
},
|
|
97
|
+
"platform_media_filereader_error_surfacing": {
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"referenceOnly": true
|
|
93
100
|
}
|
|
94
101
|
}
|
|
95
102
|
}
|