@atlaskit/collab-provider 9.7.2 → 9.7.4
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 +12 -0
- package/dist/cjs/channel.js +95 -3
- package/dist/cjs/document/catchup.js +3 -3
- package/dist/cjs/errors/error-code-mapper.js +7 -1
- package/dist/cjs/errors/error-types.js +5 -0
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/channel.js +66 -4
- package/dist/es2019/document/catchup.js +1 -1
- package/dist/es2019/errors/error-code-mapper.js +7 -1
- package/dist/es2019/errors/error-types.js +5 -0
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/channel.js +96 -4
- package/dist/esm/document/catchup.js +1 -1
- package/dist/esm/errors/error-code-mapper.js +7 -1
- package/dist/esm/errors/error-types.js +5 -0
- package/dist/esm/version-wrapper.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/channel.d.ts +12 -2
- package/dist/types/document/catchup.d.ts +1 -1
- package/dist/types/document/document-service.d.ts +2 -2
- package/dist/types/errors/error-types.d.ts +20 -2
- package/dist/types/helpers/utils.d.ts +1 -1
- package/dist/types/provider/commit-step.d.ts +1 -1
- package/dist/types/provider/index.d.ts +2 -2
- package/dist/types/types.d.ts +10 -1
- package/dist/types-ts4.5/channel.d.ts +12 -2
- package/dist/types-ts4.5/document/catchup.d.ts +1 -1
- package/dist/types-ts4.5/document/document-service.d.ts +2 -2
- package/dist/types-ts4.5/errors/error-types.d.ts +20 -2
- package/dist/types-ts4.5/helpers/utils.d.ts +1 -1
- package/dist/types-ts4.5/provider/commit-step.d.ts +1 -1
- package/dist/types-ts4.5/provider/index.d.ts +2 -2
- package/dist/types-ts4.5/types.d.ts +10 -1
- package/package.json +4 -6
- package/report.api.md +10 -3
- package/tmp/api-report-tmp.d.ts +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/collab-provider
|
|
2
2
|
|
|
3
|
+
## 9.7.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`4e6f1bf8511`](https://bitbucket.org/atlassian/atlassian-frontend/commits/4e6f1bf8511) - [ED-19233] Import prosemirror libraries from internal facade package
|
|
8
|
+
|
|
9
|
+
## 9.7.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`04fa8eb5246`](https://bitbucket.org/atlassian/atlassian-frontend/commits/04fa8eb5246) - Added rate limiting options to collab provider
|
|
14
|
+
|
|
3
15
|
## 9.7.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/cjs/channel.js
CHANGED
|
@@ -26,6 +26,9 @@ var _network = _interopRequireDefault(require("./connectivity/network"));
|
|
|
26
26
|
var _errorTypes = require("./errors/error-types");
|
|
27
27
|
var _socketMessageMetrics = require("./helpers/socket-message-metrics");
|
|
28
28
|
var _featureFlags = require("./feature-flags");
|
|
29
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, 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 normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
30
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
31
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
29
32
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
30
33
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
31
34
|
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; }
|
|
@@ -38,11 +41,19 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
38
41
|
var _this;
|
|
39
42
|
(0, _classCallCheck2.default)(this, Channel);
|
|
40
43
|
_this = _super.call(this);
|
|
44
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "RATE_LIMIT_TYPE_NONE", 0);
|
|
45
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "RATE_LIMIT_TYPE_SOFT", 1);
|
|
46
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "RATE_LIMIT_TYPE_HARD", 2);
|
|
41
47
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "connected", false);
|
|
42
48
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "socket", null);
|
|
43
49
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "reconnectHelper", null);
|
|
44
50
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "initialized", false);
|
|
45
51
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "network", null);
|
|
52
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "rateLimitWindowDurationMs", 60000);
|
|
53
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "rateLimitWindowStartMs", 0);
|
|
54
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "stepCounter", 0);
|
|
55
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "stepSizeCounter", 0);
|
|
56
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "maxStepSize", 0);
|
|
46
57
|
// read-only getters used for tests
|
|
47
58
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getInitialized", function () {
|
|
48
59
|
return _this.initialized;
|
|
@@ -72,7 +83,7 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
72
83
|
reason: data.reason,
|
|
73
84
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
74
85
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
75
|
-
usedCachedToken: _this.token
|
|
86
|
+
usedCachedToken: !!_this.token
|
|
76
87
|
});
|
|
77
88
|
_this.unsetToken();
|
|
78
89
|
});
|
|
@@ -83,7 +94,7 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
83
94
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
84
95
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
85
96
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
86
|
-
usedCachedToken: _this.token
|
|
97
|
+
usedCachedToken: !!_this.token
|
|
87
98
|
});
|
|
88
99
|
(_this$analyticsHelper3 = _this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while establishing connection');
|
|
89
100
|
// If error received with `data`, it means the connection is rejected
|
|
@@ -137,7 +148,7 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
137
148
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
138
149
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
139
150
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
140
|
-
usedCachedToken: _this.token
|
|
151
|
+
usedCachedToken: !!_this.token
|
|
141
152
|
});
|
|
142
153
|
_this.emit('connected', {
|
|
143
154
|
sid: _this.socket.id,
|
|
@@ -577,6 +588,12 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
577
588
|
// Ensure the error emit to the provider has the same structure, so we can handle them unified.
|
|
578
589
|
this.socket.on('connect_error', this.onConnectError);
|
|
579
590
|
this.socket.on('permission:invalidateToken', this.handlePermissionInvalidateToken);
|
|
591
|
+
this.socket.onAnyOutgoing(function (event) {
|
|
592
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
593
|
+
args[_key - 1] = arguments[_key];
|
|
594
|
+
}
|
|
595
|
+
return _this2.onAnyOutgoingHandler(Date.now(), args);
|
|
596
|
+
});
|
|
580
597
|
|
|
581
598
|
// To trigger reconnection when browser comes back online
|
|
582
599
|
if (!this.network) {
|
|
@@ -590,6 +607,81 @@ var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
590
607
|
// Fired upon a reconnection attempt error (from Socket.IO Manager)
|
|
591
608
|
this.socket.io.on('reconnect_error', this.onReconnectError);
|
|
592
609
|
}
|
|
610
|
+
}, {
|
|
611
|
+
key: "onAnyOutgoingHandler",
|
|
612
|
+
value: function onAnyOutgoingHandler(currentTimeMs, args) {
|
|
613
|
+
var rateLimitType = this.config.rateLimitType || this.RATE_LIMIT_TYPE_NONE;
|
|
614
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_NONE) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
var stepLimit = this.config.rateLimitStepCount || 0;
|
|
618
|
+
var stepSizeLimit = this.config.rateLimitTotalStepSize || 0;
|
|
619
|
+
var maxStepSizeLimit = this.config.rateLimitMaxStepSize || 0;
|
|
620
|
+
var _iterator = _createForOfIteratorHelper(args),
|
|
621
|
+
_step;
|
|
622
|
+
try {
|
|
623
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
624
|
+
var arg = _step.value;
|
|
625
|
+
if (arg.type === 'steps:commit') {
|
|
626
|
+
if (currentTimeMs - this.rateLimitWindowStartMs > this.rateLimitWindowDurationMs) {
|
|
627
|
+
// Start a new window
|
|
628
|
+
this.rateLimitWindowStartMs = currentTimeMs;
|
|
629
|
+
this.stepCounter = 0;
|
|
630
|
+
this.stepSizeCounter = 0;
|
|
631
|
+
this.maxStepSize = 0;
|
|
632
|
+
}
|
|
633
|
+
this.stepCounter += arg.steps.length;
|
|
634
|
+
var _iterator2 = _createForOfIteratorHelper(arg.steps),
|
|
635
|
+
_step2;
|
|
636
|
+
try {
|
|
637
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
638
|
+
var step = _step2.value;
|
|
639
|
+
var stepSize = JSON.stringify(step).length;
|
|
640
|
+
this.stepSizeCounter += stepSize;
|
|
641
|
+
if (stepSize > this.maxStepSize) {
|
|
642
|
+
this.maxStepSize = stepSize;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
} catch (err) {
|
|
646
|
+
_iterator2.e(err);
|
|
647
|
+
} finally {
|
|
648
|
+
_iterator2.f();
|
|
649
|
+
}
|
|
650
|
+
if (this.isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit)) {
|
|
651
|
+
var rateLimitError = {
|
|
652
|
+
message: 'Rate limited',
|
|
653
|
+
data: {
|
|
654
|
+
code: _errorTypes.NCS_ERROR_CODE.RATE_LIMIT_ERROR,
|
|
655
|
+
status: 500,
|
|
656
|
+
meta: {
|
|
657
|
+
rateLimitType: rateLimitType,
|
|
658
|
+
maxStepSize: this.maxStepSize,
|
|
659
|
+
stepSizeCounter: this.stepSizeCounter,
|
|
660
|
+
stepCounter: this.stepCounter
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_HARD) {
|
|
665
|
+
this.emit('error', rateLimitError);
|
|
666
|
+
throw new Error();
|
|
667
|
+
} else if (rateLimitType === this.RATE_LIMIT_TYPE_SOFT) {
|
|
668
|
+
var _this$analyticsHelper7;
|
|
669
|
+
(_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(rateLimitError, 'Rate limited');
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
} catch (err) {
|
|
675
|
+
_iterator.e(err);
|
|
676
|
+
} finally {
|
|
677
|
+
_iterator.f();
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}, {
|
|
681
|
+
key: "isLimitExceeded",
|
|
682
|
+
value: function isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
|
|
683
|
+
return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
|
|
684
|
+
}
|
|
593
685
|
}, {
|
|
594
686
|
key: "disconnect",
|
|
595
687
|
value: function disconnect() {
|
|
@@ -10,7 +10,7 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
|
|
|
10
10
|
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
11
11
|
var _const = require("../helpers/const");
|
|
12
12
|
var _utils = require("../helpers/utils");
|
|
13
|
-
var
|
|
13
|
+
var _transform = require("@atlaskit/editor-prosemirror/transform");
|
|
14
14
|
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, 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 normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
15
15
|
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
16
16
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
@@ -114,11 +114,11 @@ var catchup = /*#__PURE__*/function () {
|
|
|
114
114
|
inverted = _ref2.inverted;
|
|
115
115
|
// Due to @types/prosemirror-transform mismatch with the actual
|
|
116
116
|
// constructor, hack to set the `inverted`.
|
|
117
|
-
var stepMap = new
|
|
117
|
+
var stepMap = new _transform.StepMap(ranges);
|
|
118
118
|
stepMap.inverted = inverted;
|
|
119
119
|
return stepMap;
|
|
120
120
|
}); // create Mapping used for Step.map
|
|
121
|
-
mapping = new
|
|
121
|
+
mapping = new _transform.Mapping(stepMaps);
|
|
122
122
|
logger("".concat(_unconfirmedSteps.length, " unconfirmed steps before rebased: ").concat(JSON.stringify(_unconfirmedSteps)));
|
|
123
123
|
newUnconfirmedSteps = rebaseSteps(_unconfirmedSteps, mapping);
|
|
124
124
|
if ((newUnconfirmedSteps === null || newUnconfirmedSteps === void 0 ? void 0 : newUnconfirmedSteps.length) < _unconfirmedSteps.length) {
|
|
@@ -103,7 +103,7 @@ var errorCodeMapper = function errorCodeMapper(error) {
|
|
|
103
103
|
return {
|
|
104
104
|
code: _collab.PROVIDER_ERROR_CODE.INTERNAL_SERVICE_ERROR,
|
|
105
105
|
message: 'Collab Provider experienced an unrecoverable error',
|
|
106
|
-
recoverable:
|
|
106
|
+
recoverable: true,
|
|
107
107
|
reason: (_error$data3 = error.data) === null || _error$data3 === void 0 ? void 0 : _error$data3.code,
|
|
108
108
|
status: 500
|
|
109
109
|
};
|
|
@@ -115,6 +115,12 @@ var errorCodeMapper = function errorCodeMapper(error) {
|
|
|
115
115
|
reason: (_error$data4 = error.data) === null || _error$data4 === void 0 ? void 0 : _error$data4.code,
|
|
116
116
|
status: 500
|
|
117
117
|
};
|
|
118
|
+
case _errorTypes.NCS_ERROR_CODE.RATE_LIMIT_ERROR:
|
|
119
|
+
return {
|
|
120
|
+
code: _collab.PROVIDER_ERROR_CODE.FAIL_TO_SAVE,
|
|
121
|
+
message: 'Document rate limit',
|
|
122
|
+
recoverable: false
|
|
123
|
+
};
|
|
118
124
|
default:
|
|
119
125
|
return;
|
|
120
126
|
}
|
|
@@ -46,6 +46,7 @@ var NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
46
46
|
NCS_ERROR_CODE["INVALID_ACTIVATION_ID"] = "INVALID_ACTIVATION_ID";
|
|
47
47
|
NCS_ERROR_CODE["INVALID_DOCUMENT_ARI"] = "INVALID_DOCUMENT_ARI";
|
|
48
48
|
NCS_ERROR_CODE["INVALID_CLOUD_ID"] = "INVALID_CLOUD_ID";
|
|
49
|
+
NCS_ERROR_CODE["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
49
50
|
return NCS_ERROR_CODE;
|
|
50
51
|
}({}); // TODO: Import emitted error codes from NCS
|
|
51
52
|
// NCS Errors
|
|
@@ -60,6 +61,10 @@ var NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
60
61
|
* When we try to apply state updates to the editor, if that fails to apply the user can enter an invalid state where no
|
|
61
62
|
* changes can be saved to NCS.
|
|
62
63
|
*/
|
|
64
|
+
/**
|
|
65
|
+
* The client is trying to send too many messages or messages that are too large. This not expected to be a standard
|
|
66
|
+
* operating condition and should only ever indicate a frontend bug.
|
|
67
|
+
*/
|
|
63
68
|
/**
|
|
64
69
|
* A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
|
|
65
70
|
*/
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.version = exports.nextMajorVersion = exports.name = void 0;
|
|
7
7
|
var name = "@atlaskit/collab-provider";
|
|
8
8
|
exports.name = name;
|
|
9
|
-
var version = "9.7.
|
|
9
|
+
var version = "9.7.4";
|
|
10
10
|
exports.version = version;
|
|
11
11
|
var nextMajorVersion = function nextMajorVersion() {
|
|
12
12
|
return [Number(version.split('.')[0]) + 1, 0, 0].join('.');
|
package/dist/cjs/version.json
CHANGED
package/dist/es2019/channel.js
CHANGED
|
@@ -8,18 +8,26 @@ import ReconnectHelper from './connectivity/reconnect-helper';
|
|
|
8
8
|
import { createDocInitExp } from './analytics/ufo';
|
|
9
9
|
import { socketIOReasons } from './disconnected-reason-mapper';
|
|
10
10
|
import Network from './connectivity/network';
|
|
11
|
-
import { NotConnectedError, NotInitializedError, INTERNAL_ERROR_CODE } from './errors/error-types';
|
|
11
|
+
import { NotConnectedError, NotInitializedError, INTERNAL_ERROR_CODE, NCS_ERROR_CODE } from './errors/error-types';
|
|
12
12
|
import { SocketMessageMetrics } from './helpers/socket-message-metrics';
|
|
13
13
|
import { getCollabProviderFeatureFlag } from './feature-flags';
|
|
14
14
|
const logger = createLogger('Channel', 'green');
|
|
15
15
|
export class Channel extends Emitter {
|
|
16
16
|
constructor(config, analyticsHelper) {
|
|
17
17
|
super();
|
|
18
|
+
_defineProperty(this, "RATE_LIMIT_TYPE_NONE", 0);
|
|
19
|
+
_defineProperty(this, "RATE_LIMIT_TYPE_SOFT", 1);
|
|
20
|
+
_defineProperty(this, "RATE_LIMIT_TYPE_HARD", 2);
|
|
18
21
|
_defineProperty(this, "connected", false);
|
|
19
22
|
_defineProperty(this, "socket", null);
|
|
20
23
|
_defineProperty(this, "reconnectHelper", null);
|
|
21
24
|
_defineProperty(this, "initialized", false);
|
|
22
25
|
_defineProperty(this, "network", null);
|
|
26
|
+
_defineProperty(this, "rateLimitWindowDurationMs", 60000);
|
|
27
|
+
_defineProperty(this, "rateLimitWindowStartMs", 0);
|
|
28
|
+
_defineProperty(this, "stepCounter", 0);
|
|
29
|
+
_defineProperty(this, "stepSizeCounter", 0);
|
|
30
|
+
_defineProperty(this, "maxStepSize", 0);
|
|
23
31
|
// read-only getters used for tests
|
|
24
32
|
_defineProperty(this, "getInitialized", () => this.initialized);
|
|
25
33
|
_defineProperty(this, "getConnected", () => this.connected);
|
|
@@ -39,7 +47,7 @@ export class Channel extends Emitter {
|
|
|
39
47
|
reason: data.reason,
|
|
40
48
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
41
49
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
42
|
-
usedCachedToken: this.token
|
|
50
|
+
usedCachedToken: !!this.token
|
|
43
51
|
});
|
|
44
52
|
this.unsetToken();
|
|
45
53
|
});
|
|
@@ -50,7 +58,7 @@ export class Channel extends Emitter {
|
|
|
50
58
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
51
59
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
52
60
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
53
|
-
usedCachedToken: this.token
|
|
61
|
+
usedCachedToken: !!this.token
|
|
54
62
|
});
|
|
55
63
|
(_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while establishing connection');
|
|
56
64
|
// If error received with `data`, it means the connection is rejected
|
|
@@ -105,7 +113,7 @@ export class Channel extends Emitter {
|
|
|
105
113
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
106
114
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
107
115
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
108
|
-
usedCachedToken: this.token
|
|
116
|
+
usedCachedToken: !!this.token
|
|
109
117
|
});
|
|
110
118
|
this.emit('connected', {
|
|
111
119
|
sid: this.socket.id,
|
|
@@ -444,6 +452,7 @@ export class Channel extends Emitter {
|
|
|
444
452
|
// Ensure the error emit to the provider has the same structure, so we can handle them unified.
|
|
445
453
|
this.socket.on('connect_error', this.onConnectError);
|
|
446
454
|
this.socket.on('permission:invalidateToken', this.handlePermissionInvalidateToken);
|
|
455
|
+
this.socket.onAnyOutgoing((event, ...args) => this.onAnyOutgoingHandler(Date.now(), args));
|
|
447
456
|
|
|
448
457
|
// To trigger reconnection when browser comes back online
|
|
449
458
|
if (!this.network) {
|
|
@@ -457,6 +466,59 @@ export class Channel extends Emitter {
|
|
|
457
466
|
// Fired upon a reconnection attempt error (from Socket.IO Manager)
|
|
458
467
|
this.socket.io.on('reconnect_error', this.onReconnectError);
|
|
459
468
|
}
|
|
469
|
+
onAnyOutgoingHandler(currentTimeMs, args) {
|
|
470
|
+
const rateLimitType = this.config.rateLimitType || this.RATE_LIMIT_TYPE_NONE;
|
|
471
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_NONE) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const stepLimit = this.config.rateLimitStepCount || 0;
|
|
475
|
+
const stepSizeLimit = this.config.rateLimitTotalStepSize || 0;
|
|
476
|
+
const maxStepSizeLimit = this.config.rateLimitMaxStepSize || 0;
|
|
477
|
+
for (const arg of args) {
|
|
478
|
+
if (arg.type === 'steps:commit') {
|
|
479
|
+
if (currentTimeMs - this.rateLimitWindowStartMs > this.rateLimitWindowDurationMs) {
|
|
480
|
+
// Start a new window
|
|
481
|
+
this.rateLimitWindowStartMs = currentTimeMs;
|
|
482
|
+
this.stepCounter = 0;
|
|
483
|
+
this.stepSizeCounter = 0;
|
|
484
|
+
this.maxStepSize = 0;
|
|
485
|
+
}
|
|
486
|
+
this.stepCounter += arg.steps.length;
|
|
487
|
+
for (const step of arg.steps) {
|
|
488
|
+
const stepSize = JSON.stringify(step).length;
|
|
489
|
+
this.stepSizeCounter += stepSize;
|
|
490
|
+
if (stepSize > this.maxStepSize) {
|
|
491
|
+
this.maxStepSize = stepSize;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (this.isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit)) {
|
|
495
|
+
const rateLimitError = {
|
|
496
|
+
message: 'Rate limited',
|
|
497
|
+
data: {
|
|
498
|
+
code: NCS_ERROR_CODE.RATE_LIMIT_ERROR,
|
|
499
|
+
status: 500,
|
|
500
|
+
meta: {
|
|
501
|
+
rateLimitType,
|
|
502
|
+
maxStepSize: this.maxStepSize,
|
|
503
|
+
stepSizeCounter: this.stepSizeCounter,
|
|
504
|
+
stepCounter: this.stepCounter
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_HARD) {
|
|
509
|
+
this.emit('error', rateLimitError);
|
|
510
|
+
throw new Error();
|
|
511
|
+
} else if (rateLimitType === this.RATE_LIMIT_TYPE_SOFT) {
|
|
512
|
+
var _this$analyticsHelper8;
|
|
513
|
+
(_this$analyticsHelper8 = this.analyticsHelper) === null || _this$analyticsHelper8 === void 0 ? void 0 : _this$analyticsHelper8.sendErrorEvent(rateLimitError, 'Rate limited');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
|
|
520
|
+
return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
|
|
521
|
+
}
|
|
460
522
|
disconnect() {
|
|
461
523
|
var _this$network;
|
|
462
524
|
this.unsubscribeAll();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
|
|
2
2
|
import { createLogger } from '../helpers/utils';
|
|
3
|
-
import { StepMap, Mapping } from 'prosemirror
|
|
3
|
+
import { StepMap, Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
4
4
|
const logger = createLogger('Catchup', 'red');
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -99,7 +99,7 @@ export const errorCodeMapper = error => {
|
|
|
99
99
|
return {
|
|
100
100
|
code: PROVIDER_ERROR_CODE.INTERNAL_SERVICE_ERROR,
|
|
101
101
|
message: 'Collab Provider experienced an unrecoverable error',
|
|
102
|
-
recoverable:
|
|
102
|
+
recoverable: true,
|
|
103
103
|
reason: (_error$data3 = error.data) === null || _error$data3 === void 0 ? void 0 : _error$data3.code,
|
|
104
104
|
status: 500
|
|
105
105
|
};
|
|
@@ -111,6 +111,12 @@ export const errorCodeMapper = error => {
|
|
|
111
111
|
reason: (_error$data4 = error.data) === null || _error$data4 === void 0 ? void 0 : _error$data4.code,
|
|
112
112
|
status: 500
|
|
113
113
|
};
|
|
114
|
+
case NCS_ERROR_CODE.RATE_LIMIT_ERROR:
|
|
115
|
+
return {
|
|
116
|
+
code: PROVIDER_ERROR_CODE.FAIL_TO_SAVE,
|
|
117
|
+
message: 'Document rate limit',
|
|
118
|
+
recoverable: false
|
|
119
|
+
};
|
|
114
120
|
default:
|
|
115
121
|
return;
|
|
116
122
|
}
|
|
@@ -32,6 +32,7 @@ export let NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
32
32
|
NCS_ERROR_CODE["INVALID_ACTIVATION_ID"] = "INVALID_ACTIVATION_ID";
|
|
33
33
|
NCS_ERROR_CODE["INVALID_DOCUMENT_ARI"] = "INVALID_DOCUMENT_ARI";
|
|
34
34
|
NCS_ERROR_CODE["INVALID_CLOUD_ID"] = "INVALID_CLOUD_ID";
|
|
35
|
+
NCS_ERROR_CODE["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
35
36
|
return NCS_ERROR_CODE;
|
|
36
37
|
}({});
|
|
37
38
|
|
|
@@ -49,6 +50,10 @@ export let NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
49
50
|
* When we try to apply state updates to the editor, if that fails to apply the user can enter an invalid state where no
|
|
50
51
|
* changes can be saved to NCS.
|
|
51
52
|
*/
|
|
53
|
+
/**
|
|
54
|
+
* The client is trying to send too many messages or messages that are too large. This not expected to be a standard
|
|
55
|
+
* operating condition and should only ever indicate a frontend bug.
|
|
56
|
+
*/
|
|
52
57
|
/**
|
|
53
58
|
* A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
|
|
54
59
|
*/
|
package/dist/es2019/version.json
CHANGED
package/dist/esm/channel.js
CHANGED
|
@@ -6,6 +6,9 @@ import _inherits from "@babel/runtime/helpers/inherits";
|
|
|
6
6
|
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
7
7
|
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
8
8
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
9
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, 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 normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
10
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
11
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
9
12
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
10
13
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
11
14
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
@@ -20,7 +23,7 @@ import ReconnectHelper from './connectivity/reconnect-helper';
|
|
|
20
23
|
import { createDocInitExp } from './analytics/ufo';
|
|
21
24
|
import { socketIOReasons } from './disconnected-reason-mapper';
|
|
22
25
|
import Network from './connectivity/network';
|
|
23
|
-
import { NotConnectedError, NotInitializedError, INTERNAL_ERROR_CODE } from './errors/error-types';
|
|
26
|
+
import { NotConnectedError, NotInitializedError, INTERNAL_ERROR_CODE, NCS_ERROR_CODE } from './errors/error-types';
|
|
24
27
|
import { SocketMessageMetrics } from './helpers/socket-message-metrics';
|
|
25
28
|
import { getCollabProviderFeatureFlag } from './feature-flags';
|
|
26
29
|
var logger = createLogger('Channel', 'green');
|
|
@@ -31,11 +34,19 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
31
34
|
var _this;
|
|
32
35
|
_classCallCheck(this, Channel);
|
|
33
36
|
_this = _super.call(this);
|
|
37
|
+
_defineProperty(_assertThisInitialized(_this), "RATE_LIMIT_TYPE_NONE", 0);
|
|
38
|
+
_defineProperty(_assertThisInitialized(_this), "RATE_LIMIT_TYPE_SOFT", 1);
|
|
39
|
+
_defineProperty(_assertThisInitialized(_this), "RATE_LIMIT_TYPE_HARD", 2);
|
|
34
40
|
_defineProperty(_assertThisInitialized(_this), "connected", false);
|
|
35
41
|
_defineProperty(_assertThisInitialized(_this), "socket", null);
|
|
36
42
|
_defineProperty(_assertThisInitialized(_this), "reconnectHelper", null);
|
|
37
43
|
_defineProperty(_assertThisInitialized(_this), "initialized", false);
|
|
38
44
|
_defineProperty(_assertThisInitialized(_this), "network", null);
|
|
45
|
+
_defineProperty(_assertThisInitialized(_this), "rateLimitWindowDurationMs", 60000);
|
|
46
|
+
_defineProperty(_assertThisInitialized(_this), "rateLimitWindowStartMs", 0);
|
|
47
|
+
_defineProperty(_assertThisInitialized(_this), "stepCounter", 0);
|
|
48
|
+
_defineProperty(_assertThisInitialized(_this), "stepSizeCounter", 0);
|
|
49
|
+
_defineProperty(_assertThisInitialized(_this), "maxStepSize", 0);
|
|
39
50
|
// read-only getters used for tests
|
|
40
51
|
_defineProperty(_assertThisInitialized(_this), "getInitialized", function () {
|
|
41
52
|
return _this.initialized;
|
|
@@ -65,7 +76,7 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
65
76
|
reason: data.reason,
|
|
66
77
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
67
78
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
68
|
-
usedCachedToken: _this.token
|
|
79
|
+
usedCachedToken: !!_this.token
|
|
69
80
|
});
|
|
70
81
|
_this.unsetToken();
|
|
71
82
|
});
|
|
@@ -76,7 +87,7 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
76
87
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
77
88
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
78
89
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
79
|
-
usedCachedToken: _this.token
|
|
90
|
+
usedCachedToken: !!_this.token
|
|
80
91
|
});
|
|
81
92
|
(_this$analyticsHelper3 = _this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(error, 'Error while establishing connection');
|
|
82
93
|
// If error received with `data`, it means the connection is rejected
|
|
@@ -130,7 +141,7 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
130
141
|
latency: measure === null || measure === void 0 ? void 0 : measure.duration,
|
|
131
142
|
// Potentially incorrect when value of token changes between connecting and connect.
|
|
132
143
|
// See: https://bitbucket.org/atlassian/%7Bc8e2f021-38d2-46d0-9b7a-b3f7b428f724%7D/pull-requests/29905#comment-375308874
|
|
133
|
-
usedCachedToken: _this.token
|
|
144
|
+
usedCachedToken: !!_this.token
|
|
134
145
|
});
|
|
135
146
|
_this.emit('connected', {
|
|
136
147
|
sid: _this.socket.id,
|
|
@@ -570,6 +581,12 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
570
581
|
// Ensure the error emit to the provider has the same structure, so we can handle them unified.
|
|
571
582
|
this.socket.on('connect_error', this.onConnectError);
|
|
572
583
|
this.socket.on('permission:invalidateToken', this.handlePermissionInvalidateToken);
|
|
584
|
+
this.socket.onAnyOutgoing(function (event) {
|
|
585
|
+
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
586
|
+
args[_key - 1] = arguments[_key];
|
|
587
|
+
}
|
|
588
|
+
return _this2.onAnyOutgoingHandler(Date.now(), args);
|
|
589
|
+
});
|
|
573
590
|
|
|
574
591
|
// To trigger reconnection when browser comes back online
|
|
575
592
|
if (!this.network) {
|
|
@@ -583,6 +600,81 @@ export var Channel = /*#__PURE__*/function (_Emitter) {
|
|
|
583
600
|
// Fired upon a reconnection attempt error (from Socket.IO Manager)
|
|
584
601
|
this.socket.io.on('reconnect_error', this.onReconnectError);
|
|
585
602
|
}
|
|
603
|
+
}, {
|
|
604
|
+
key: "onAnyOutgoingHandler",
|
|
605
|
+
value: function onAnyOutgoingHandler(currentTimeMs, args) {
|
|
606
|
+
var rateLimitType = this.config.rateLimitType || this.RATE_LIMIT_TYPE_NONE;
|
|
607
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_NONE) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
var stepLimit = this.config.rateLimitStepCount || 0;
|
|
611
|
+
var stepSizeLimit = this.config.rateLimitTotalStepSize || 0;
|
|
612
|
+
var maxStepSizeLimit = this.config.rateLimitMaxStepSize || 0;
|
|
613
|
+
var _iterator = _createForOfIteratorHelper(args),
|
|
614
|
+
_step;
|
|
615
|
+
try {
|
|
616
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
617
|
+
var arg = _step.value;
|
|
618
|
+
if (arg.type === 'steps:commit') {
|
|
619
|
+
if (currentTimeMs - this.rateLimitWindowStartMs > this.rateLimitWindowDurationMs) {
|
|
620
|
+
// Start a new window
|
|
621
|
+
this.rateLimitWindowStartMs = currentTimeMs;
|
|
622
|
+
this.stepCounter = 0;
|
|
623
|
+
this.stepSizeCounter = 0;
|
|
624
|
+
this.maxStepSize = 0;
|
|
625
|
+
}
|
|
626
|
+
this.stepCounter += arg.steps.length;
|
|
627
|
+
var _iterator2 = _createForOfIteratorHelper(arg.steps),
|
|
628
|
+
_step2;
|
|
629
|
+
try {
|
|
630
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
631
|
+
var step = _step2.value;
|
|
632
|
+
var stepSize = JSON.stringify(step).length;
|
|
633
|
+
this.stepSizeCounter += stepSize;
|
|
634
|
+
if (stepSize > this.maxStepSize) {
|
|
635
|
+
this.maxStepSize = stepSize;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} catch (err) {
|
|
639
|
+
_iterator2.e(err);
|
|
640
|
+
} finally {
|
|
641
|
+
_iterator2.f();
|
|
642
|
+
}
|
|
643
|
+
if (this.isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit)) {
|
|
644
|
+
var rateLimitError = {
|
|
645
|
+
message: 'Rate limited',
|
|
646
|
+
data: {
|
|
647
|
+
code: NCS_ERROR_CODE.RATE_LIMIT_ERROR,
|
|
648
|
+
status: 500,
|
|
649
|
+
meta: {
|
|
650
|
+
rateLimitType: rateLimitType,
|
|
651
|
+
maxStepSize: this.maxStepSize,
|
|
652
|
+
stepSizeCounter: this.stepSizeCounter,
|
|
653
|
+
stepCounter: this.stepCounter
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
if (rateLimitType === this.RATE_LIMIT_TYPE_HARD) {
|
|
658
|
+
this.emit('error', rateLimitError);
|
|
659
|
+
throw new Error();
|
|
660
|
+
} else if (rateLimitType === this.RATE_LIMIT_TYPE_SOFT) {
|
|
661
|
+
var _this$analyticsHelper7;
|
|
662
|
+
(_this$analyticsHelper7 = this.analyticsHelper) === null || _this$analyticsHelper7 === void 0 ? void 0 : _this$analyticsHelper7.sendErrorEvent(rateLimitError, 'Rate limited');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
} catch (err) {
|
|
668
|
+
_iterator.e(err);
|
|
669
|
+
} finally {
|
|
670
|
+
_iterator.f();
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}, {
|
|
674
|
+
key: "isLimitExceeded",
|
|
675
|
+
value: function isLimitExceeded(stepLimit, stepSizeLimit, maxStepSizeLimit) {
|
|
676
|
+
return stepLimit > 0 && this.stepCounter > stepLimit || stepSizeLimit > 0 && this.stepSizeCounter > stepSizeLimit || maxStepSizeLimit > 0 && this.maxStepSize > maxStepSizeLimit;
|
|
677
|
+
}
|
|
586
678
|
}, {
|
|
587
679
|
key: "disconnect",
|
|
588
680
|
value: function disconnect() {
|
|
@@ -5,7 +5,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
|
|
|
5
5
|
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
6
6
|
import { EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
|
|
7
7
|
import { createLogger } from '../helpers/utils';
|
|
8
|
-
import { StepMap, Mapping } from 'prosemirror
|
|
8
|
+
import { StepMap, Mapping } from '@atlaskit/editor-prosemirror/transform';
|
|
9
9
|
var logger = createLogger('Catchup', 'red');
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -99,7 +99,7 @@ export var errorCodeMapper = function errorCodeMapper(error) {
|
|
|
99
99
|
return {
|
|
100
100
|
code: PROVIDER_ERROR_CODE.INTERNAL_SERVICE_ERROR,
|
|
101
101
|
message: 'Collab Provider experienced an unrecoverable error',
|
|
102
|
-
recoverable:
|
|
102
|
+
recoverable: true,
|
|
103
103
|
reason: (_error$data3 = error.data) === null || _error$data3 === void 0 ? void 0 : _error$data3.code,
|
|
104
104
|
status: 500
|
|
105
105
|
};
|
|
@@ -111,6 +111,12 @@ export var errorCodeMapper = function errorCodeMapper(error) {
|
|
|
111
111
|
reason: (_error$data4 = error.data) === null || _error$data4 === void 0 ? void 0 : _error$data4.code,
|
|
112
112
|
status: 500
|
|
113
113
|
};
|
|
114
|
+
case NCS_ERROR_CODE.RATE_LIMIT_ERROR:
|
|
115
|
+
return {
|
|
116
|
+
code: PROVIDER_ERROR_CODE.FAIL_TO_SAVE,
|
|
117
|
+
message: 'Document rate limit',
|
|
118
|
+
recoverable: false
|
|
119
|
+
};
|
|
114
120
|
default:
|
|
115
121
|
return;
|
|
116
122
|
}
|
|
@@ -41,6 +41,7 @@ export var NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
41
41
|
NCS_ERROR_CODE["INVALID_ACTIVATION_ID"] = "INVALID_ACTIVATION_ID";
|
|
42
42
|
NCS_ERROR_CODE["INVALID_DOCUMENT_ARI"] = "INVALID_DOCUMENT_ARI";
|
|
43
43
|
NCS_ERROR_CODE["INVALID_CLOUD_ID"] = "INVALID_CLOUD_ID";
|
|
44
|
+
NCS_ERROR_CODE["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
|
|
44
45
|
return NCS_ERROR_CODE;
|
|
45
46
|
}({});
|
|
46
47
|
|
|
@@ -58,6 +59,10 @@ export var NCS_ERROR_CODE = /*#__PURE__*/function (NCS_ERROR_CODE) {
|
|
|
58
59
|
* When we try to apply state updates to the editor, if that fails to apply the user can enter an invalid state where no
|
|
59
60
|
* changes can be saved to NCS.
|
|
60
61
|
*/
|
|
62
|
+
/**
|
|
63
|
+
* The client is trying to send too many messages or messages that are too large. This not expected to be a standard
|
|
64
|
+
* operating condition and should only ever indicate a frontend bug.
|
|
65
|
+
*/
|
|
61
66
|
/**
|
|
62
67
|
* A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
|
|
63
68
|
*/
|
package/dist/esm/version.json
CHANGED
package/dist/types/channel.d.ts
CHANGED
|
@@ -4,16 +4,24 @@ import type { Socket } from 'socket.io-client';
|
|
|
4
4
|
import AnalyticsHelper from './analytics/analytics-helper';
|
|
5
5
|
import type { Metadata } from '@atlaskit/editor-common/collab';
|
|
6
6
|
export declare class Channel extends Emitter<ChannelEvent> {
|
|
7
|
+
private readonly RATE_LIMIT_TYPE_NONE;
|
|
8
|
+
private readonly RATE_LIMIT_TYPE_SOFT;
|
|
9
|
+
private readonly RATE_LIMIT_TYPE_HARD;
|
|
7
10
|
private connected;
|
|
8
|
-
private config;
|
|
11
|
+
private readonly config;
|
|
9
12
|
private socket;
|
|
10
13
|
private reconnectHelper?;
|
|
11
14
|
private initialized;
|
|
12
|
-
private analyticsHelper?;
|
|
15
|
+
private readonly analyticsHelper?;
|
|
13
16
|
private initExperience?;
|
|
14
17
|
private token?;
|
|
15
18
|
private network;
|
|
16
19
|
private socketMessageMetrics?;
|
|
20
|
+
private readonly rateLimitWindowDurationMs;
|
|
21
|
+
private rateLimitWindowStartMs;
|
|
22
|
+
private stepCounter;
|
|
23
|
+
private stepSizeCounter;
|
|
24
|
+
private maxStepSize;
|
|
17
25
|
constructor(config: Config, analyticsHelper: AnalyticsHelper);
|
|
18
26
|
getInitialized: () => boolean;
|
|
19
27
|
getConnected: () => boolean;
|
|
@@ -25,6 +33,8 @@ export declare class Channel extends Emitter<ChannelEvent> {
|
|
|
25
33
|
* Connect to collab service using websockets
|
|
26
34
|
*/
|
|
27
35
|
connect(shouldInitialize?: boolean): void;
|
|
36
|
+
onAnyOutgoingHandler(currentTimeMs: number, args: any[]): void;
|
|
37
|
+
private isLimitExceeded;
|
|
28
38
|
private handlePermissionInvalidateToken;
|
|
29
39
|
private onConnectError;
|
|
30
40
|
private onReconnectError;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CatchupOptions } from '../types';
|
|
2
|
-
import { Mapping, Step } from 'prosemirror
|
|
2
|
+
import { Mapping, Step } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
/**
|
|
4
4
|
* Rebase the steps based on the mapping pipeline.
|
|
5
5
|
* Some steps could be lost, if they are no longer
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import AnalyticsHelper from '../analytics/analytics-helper';
|
|
3
3
|
import { CatchupResponse, ChannelEvent, StepsPayload } from '../types';
|
|
4
4
|
import type { SyncUpErrorFunction, ResolvedEditorState } from '@atlaskit/editor-common/collab';
|
|
5
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
5
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
6
|
import type { MetadataService } from '../metadata/metadata-service';
|
|
7
7
|
import type { CollabEvents, CollabInitPayload } from '@atlaskit/editor-common/collab';
|
|
8
|
-
import type { EditorState, Transaction } from 'prosemirror
|
|
8
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
9
9
|
import { ParticipantsService } from '../participants/participants-service';
|
|
10
10
|
import type { InternalError } from '../errors/error-types';
|
|
11
11
|
export declare class DocumentService {
|
|
@@ -26,7 +26,8 @@ export declare enum NCS_ERROR_CODE {
|
|
|
26
26
|
DYNAMO_ERROR = "DYNAMO_ERROR",
|
|
27
27
|
INVALID_ACTIVATION_ID = "INVALID_ACTIVATION_ID",
|
|
28
28
|
INVALID_DOCUMENT_ARI = "INVALID_DOCUMENT_ARI",
|
|
29
|
-
INVALID_CLOUD_ID = "INVALID_CLOUD_ID"
|
|
29
|
+
INVALID_CLOUD_ID = "INVALID_CLOUD_ID",
|
|
30
|
+
RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR"
|
|
30
31
|
}
|
|
31
32
|
type HeadVersionUpdateFailedError = {
|
|
32
33
|
message: string;
|
|
@@ -236,10 +237,27 @@ export type InternalDocumentUpdateFailure = {
|
|
|
236
237
|
status: 500;
|
|
237
238
|
};
|
|
238
239
|
};
|
|
240
|
+
/**
|
|
241
|
+
* The client is trying to send too many messages or messages that are too large. This not expected to be a standard
|
|
242
|
+
* operating condition and should only ever indicate a frontend bug.
|
|
243
|
+
*/
|
|
244
|
+
export type RateLimitError = {
|
|
245
|
+
message: string;
|
|
246
|
+
data: {
|
|
247
|
+
code: NCS_ERROR_CODE.RATE_LIMIT_ERROR;
|
|
248
|
+
meta: {
|
|
249
|
+
rateLimitType: number;
|
|
250
|
+
maxStepSize: number;
|
|
251
|
+
stepSizeCounter: number;
|
|
252
|
+
stepCounter: number;
|
|
253
|
+
};
|
|
254
|
+
status: 500;
|
|
255
|
+
};
|
|
256
|
+
};
|
|
239
257
|
/**
|
|
240
258
|
* A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
|
|
241
259
|
*/
|
|
242
|
-
export type InternalError = NCSErrors | DocumentRecoveryError | AddStepsError | CatchUpFailedError | TokenPermissionError | ReconnectionError | ConnectionError | ReconnectionNetworkError | DocumentNotFoundError | InternalDocumentUpdateFailure;
|
|
260
|
+
export type InternalError = NCSErrors | DocumentRecoveryError | AddStepsError | CatchUpFailedError | TokenPermissionError | ReconnectionError | ConnectionError | ReconnectionNetworkError | DocumentNotFoundError | InternalDocumentUpdateFailure | RateLimitError;
|
|
243
261
|
type ValidEventAttributeType = boolean | string | number;
|
|
244
262
|
export declare class CustomError extends Error {
|
|
245
263
|
extraEventAttributes?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProductInformation } from '../types';
|
|
2
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
2
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
export declare const createLogger: (prefix: string, color?: string) => (msg: string, data?: any) => void;
|
|
4
4
|
export declare function sleep(ms: number): Promise<unknown>;
|
|
5
5
|
export declare const getProduct: (productInfo?: ProductInformation) => string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="lodash" />
|
|
2
2
|
import { ChannelEvent, StepsPayload } from '../types';
|
|
3
3
|
import type { CollabCommitStatusEventPayload, CollabEvents } from '@atlaskit/editor-common/collab';
|
|
4
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
4
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
5
5
|
import AnalyticsHelper from '../analytics/analytics-helper';
|
|
6
6
|
import type { InternalError } from '../errors/error-types';
|
|
7
7
|
export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EditorState, Transaction } from 'prosemirror
|
|
2
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
1
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
import { Emitter } from '../emitter';
|
|
4
4
|
import type { Config } from '../types';
|
|
5
5
|
import type { CollabEditProvider, CollabEvents, CollabTelepointerPayload, ResolvedEditorState, Metadata, SyncUpErrorFunction } from '@atlaskit/editor-common/collab';
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Step } from 'prosemirror
|
|
1
|
+
import type { Step } from '@atlaskit/editor-prosemirror/transform';
|
|
2
2
|
import type { AnalyticsWebClient } from '@atlaskit/analytics-listeners';
|
|
3
3
|
import type { Manager } from 'socket.io-client';
|
|
4
4
|
import type { InternalError } from './errors/error-types';
|
|
@@ -63,6 +63,15 @@ export interface Config {
|
|
|
63
63
|
* throwing a non-recoverable error if it's detected.
|
|
64
64
|
*/
|
|
65
65
|
enableErrorOnFailedDocumentApply?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Configure the client side circuit breaker in the event that abnormal behaviour causes the client to flood
|
|
68
|
+
* NCS with too many steps or too large a volume of data. This can result in either a soft fail or a hard (fatal) fail
|
|
69
|
+
* depending on the configured rate limit type.
|
|
70
|
+
*/
|
|
71
|
+
rateLimitMaxStepSize?: number;
|
|
72
|
+
rateLimitStepCount?: number;
|
|
73
|
+
rateLimitTotalStepSize?: number;
|
|
74
|
+
rateLimitType?: number;
|
|
66
75
|
}
|
|
67
76
|
export interface InitAndAuthData {
|
|
68
77
|
initialized: boolean;
|
|
@@ -4,16 +4,24 @@ import type { Socket } from 'socket.io-client';
|
|
|
4
4
|
import AnalyticsHelper from './analytics/analytics-helper';
|
|
5
5
|
import type { Metadata } from '@atlaskit/editor-common/collab';
|
|
6
6
|
export declare class Channel extends Emitter<ChannelEvent> {
|
|
7
|
+
private readonly RATE_LIMIT_TYPE_NONE;
|
|
8
|
+
private readonly RATE_LIMIT_TYPE_SOFT;
|
|
9
|
+
private readonly RATE_LIMIT_TYPE_HARD;
|
|
7
10
|
private connected;
|
|
8
|
-
private config;
|
|
11
|
+
private readonly config;
|
|
9
12
|
private socket;
|
|
10
13
|
private reconnectHelper?;
|
|
11
14
|
private initialized;
|
|
12
|
-
private analyticsHelper?;
|
|
15
|
+
private readonly analyticsHelper?;
|
|
13
16
|
private initExperience?;
|
|
14
17
|
private token?;
|
|
15
18
|
private network;
|
|
16
19
|
private socketMessageMetrics?;
|
|
20
|
+
private readonly rateLimitWindowDurationMs;
|
|
21
|
+
private rateLimitWindowStartMs;
|
|
22
|
+
private stepCounter;
|
|
23
|
+
private stepSizeCounter;
|
|
24
|
+
private maxStepSize;
|
|
17
25
|
constructor(config: Config, analyticsHelper: AnalyticsHelper);
|
|
18
26
|
getInitialized: () => boolean;
|
|
19
27
|
getConnected: () => boolean;
|
|
@@ -25,6 +33,8 @@ export declare class Channel extends Emitter<ChannelEvent> {
|
|
|
25
33
|
* Connect to collab service using websockets
|
|
26
34
|
*/
|
|
27
35
|
connect(shouldInitialize?: boolean): void;
|
|
36
|
+
onAnyOutgoingHandler(currentTimeMs: number, args: any[]): void;
|
|
37
|
+
private isLimitExceeded;
|
|
28
38
|
private handlePermissionInvalidateToken;
|
|
29
39
|
private onConnectError;
|
|
30
40
|
private onReconnectError;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CatchupOptions } from '../types';
|
|
2
|
-
import { Mapping, Step } from 'prosemirror
|
|
2
|
+
import { Mapping, Step } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
/**
|
|
4
4
|
* Rebase the steps based on the mapping pipeline.
|
|
5
5
|
* Some steps could be lost, if they are no longer
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import AnalyticsHelper from '../analytics/analytics-helper';
|
|
3
3
|
import { CatchupResponse, ChannelEvent, StepsPayload } from '../types';
|
|
4
4
|
import type { SyncUpErrorFunction, ResolvedEditorState } from '@atlaskit/editor-common/collab';
|
|
5
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
5
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
6
6
|
import type { MetadataService } from '../metadata/metadata-service';
|
|
7
7
|
import type { CollabEvents, CollabInitPayload } from '@atlaskit/editor-common/collab';
|
|
8
|
-
import type { EditorState, Transaction } from 'prosemirror
|
|
8
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
9
9
|
import { ParticipantsService } from '../participants/participants-service';
|
|
10
10
|
import type { InternalError } from '../errors/error-types';
|
|
11
11
|
export declare class DocumentService {
|
|
@@ -26,7 +26,8 @@ export declare enum NCS_ERROR_CODE {
|
|
|
26
26
|
DYNAMO_ERROR = "DYNAMO_ERROR",
|
|
27
27
|
INVALID_ACTIVATION_ID = "INVALID_ACTIVATION_ID",
|
|
28
28
|
INVALID_DOCUMENT_ARI = "INVALID_DOCUMENT_ARI",
|
|
29
|
-
INVALID_CLOUD_ID = "INVALID_CLOUD_ID"
|
|
29
|
+
INVALID_CLOUD_ID = "INVALID_CLOUD_ID",
|
|
30
|
+
RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR"
|
|
30
31
|
}
|
|
31
32
|
type HeadVersionUpdateFailedError = {
|
|
32
33
|
message: string;
|
|
@@ -236,10 +237,27 @@ export type InternalDocumentUpdateFailure = {
|
|
|
236
237
|
status: 500;
|
|
237
238
|
};
|
|
238
239
|
};
|
|
240
|
+
/**
|
|
241
|
+
* The client is trying to send too many messages or messages that are too large. This not expected to be a standard
|
|
242
|
+
* operating condition and should only ever indicate a frontend bug.
|
|
243
|
+
*/
|
|
244
|
+
export type RateLimitError = {
|
|
245
|
+
message: string;
|
|
246
|
+
data: {
|
|
247
|
+
code: NCS_ERROR_CODE.RATE_LIMIT_ERROR;
|
|
248
|
+
meta: {
|
|
249
|
+
rateLimitType: number;
|
|
250
|
+
maxStepSize: number;
|
|
251
|
+
stepSizeCounter: number;
|
|
252
|
+
stepCounter: number;
|
|
253
|
+
};
|
|
254
|
+
status: 500;
|
|
255
|
+
};
|
|
256
|
+
};
|
|
239
257
|
/**
|
|
240
258
|
* A union of all possible internal errors, that are mapped to another error if being emitted to the editor.
|
|
241
259
|
*/
|
|
242
|
-
export type InternalError = NCSErrors | DocumentRecoveryError | AddStepsError | CatchUpFailedError | TokenPermissionError | ReconnectionError | ConnectionError | ReconnectionNetworkError | DocumentNotFoundError | InternalDocumentUpdateFailure;
|
|
260
|
+
export type InternalError = NCSErrors | DocumentRecoveryError | AddStepsError | CatchUpFailedError | TokenPermissionError | ReconnectionError | ConnectionError | ReconnectionNetworkError | DocumentNotFoundError | InternalDocumentUpdateFailure | RateLimitError;
|
|
243
261
|
type ValidEventAttributeType = boolean | string | number;
|
|
244
262
|
export declare class CustomError extends Error {
|
|
245
263
|
extraEventAttributes?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProductInformation } from '../types';
|
|
2
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
2
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
export declare const createLogger: (prefix: string, color?: string) => (msg: string, data?: any) => void;
|
|
4
4
|
export declare function sleep(ms: number): Promise<unknown>;
|
|
5
5
|
export declare const getProduct: (productInfo?: ProductInformation) => string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="lodash" />
|
|
2
2
|
import { ChannelEvent, StepsPayload } from '../types';
|
|
3
3
|
import type { CollabCommitStatusEventPayload, CollabEvents } from '@atlaskit/editor-common/collab';
|
|
4
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
4
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
5
5
|
import AnalyticsHelper from '../analytics/analytics-helper';
|
|
6
6
|
import type { InternalError } from '../errors/error-types';
|
|
7
7
|
export declare const commitStep: ({ broadcast, steps, version, userId, clientId, onStepsAdded, onErrorHandled, analyticsHelper, emit, }: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EditorState, Transaction } from 'prosemirror
|
|
2
|
-
import type { Step as ProseMirrorStep } from 'prosemirror
|
|
1
|
+
import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
2
|
+
import type { Step as ProseMirrorStep } from '@atlaskit/editor-prosemirror/transform';
|
|
3
3
|
import { Emitter } from '../emitter';
|
|
4
4
|
import type { Config } from '../types';
|
|
5
5
|
import type { CollabEditProvider, CollabEvents, CollabTelepointerPayload, ResolvedEditorState, Metadata, SyncUpErrorFunction } from '@atlaskit/editor-common/collab';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Step } from 'prosemirror
|
|
1
|
+
import type { Step } from '@atlaskit/editor-prosemirror/transform';
|
|
2
2
|
import type { AnalyticsWebClient } from '@atlaskit/analytics-listeners';
|
|
3
3
|
import type { Manager } from 'socket.io-client';
|
|
4
4
|
import type { InternalError } from './errors/error-types';
|
|
@@ -63,6 +63,15 @@ export interface Config {
|
|
|
63
63
|
* throwing a non-recoverable error if it's detected.
|
|
64
64
|
*/
|
|
65
65
|
enableErrorOnFailedDocumentApply?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Configure the client side circuit breaker in the event that abnormal behaviour causes the client to flood
|
|
68
|
+
* NCS with too many steps or too large a volume of data. This can result in either a soft fail or a hard (fatal) fail
|
|
69
|
+
* depending on the configured rate limit type.
|
|
70
|
+
*/
|
|
71
|
+
rateLimitMaxStepSize?: number;
|
|
72
|
+
rateLimitStepCount?: number;
|
|
73
|
+
rateLimitTotalStepSize?: number;
|
|
74
|
+
rateLimitType?: number;
|
|
66
75
|
}
|
|
67
76
|
export interface InitAndAuthData {
|
|
68
77
|
initialized: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/collab-provider",
|
|
3
|
-
"version": "9.7.
|
|
3
|
+
"version": "9.7.4",
|
|
4
4
|
"description": "A provider for collaborative editing.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -36,15 +36,15 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@atlaskit/analytics-gas-types": "^5.1.0",
|
|
38
38
|
"@atlaskit/analytics-listeners": "^8.7.0",
|
|
39
|
-
"@atlaskit/editor-common": "^74.
|
|
39
|
+
"@atlaskit/editor-common": "^74.29.0",
|
|
40
40
|
"@atlaskit/editor-json-transformer": "^8.10.0",
|
|
41
|
+
"@atlaskit/editor-prosemirror": "1.0.2",
|
|
41
42
|
"@atlaskit/prosemirror-collab": "^0.2.0",
|
|
42
43
|
"@atlaskit/ufo": "^0.2.0",
|
|
43
44
|
"@atlaskit/util-service-support": "^6.2.0",
|
|
44
45
|
"@babel/runtime": "^7.0.0",
|
|
45
46
|
"eventemitter2": "^4.1.0",
|
|
46
47
|
"lodash": "^4.17.21",
|
|
47
|
-
"prosemirror-transform": "1.3.2",
|
|
48
48
|
"socket.io-client": "^4.7.1"
|
|
49
49
|
},
|
|
50
50
|
"techstack": {
|
|
@@ -62,12 +62,10 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@atlaskit/adf-schema": "^26.
|
|
65
|
+
"@atlaskit/adf-schema": "^26.4.0",
|
|
66
66
|
"@atlaskit/analytics-listeners": "^8.7.0",
|
|
67
67
|
"@atlaskit/editor-test-helpers": "^18.10.0",
|
|
68
68
|
"@atlassian/atlassian-frontend-prettier-config-1.0.1": "npm:@atlassian/atlassian-frontend-prettier-config@1.0.1",
|
|
69
|
-
"prosemirror-model": "1.16.0",
|
|
70
|
-
"prosemirror-state": "1.3.4",
|
|
71
69
|
"typescript": "~4.9.5"
|
|
72
70
|
},
|
|
73
71
|
"prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.1"
|
package/report.api.md
CHANGED
|
@@ -34,7 +34,7 @@ import { CollabParticipant } from '@atlaskit/editor-common/collab';
|
|
|
34
34
|
import { CollabPresencePayload } from '@atlaskit/editor-common/collab';
|
|
35
35
|
import { CollabSendableSelection } from '@atlaskit/editor-common/collab';
|
|
36
36
|
import { CollabTelepointerPayload } from '@atlaskit/editor-common/collab';
|
|
37
|
-
import type { EditorState } from 'prosemirror
|
|
37
|
+
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
38
38
|
import { JSONDocNode } from '@atlaskit/editor-json-transformer';
|
|
39
39
|
import type { Manager } from 'socket.io-client';
|
|
40
40
|
import type { Metadata as Metadata_2 } from '@atlaskit/editor-common/collab';
|
|
@@ -43,9 +43,9 @@ import { PROVIDER_ERROR_CODE } from '@atlaskit/editor-common/collab';
|
|
|
43
43
|
import { ProviderError } from '@atlaskit/editor-common/collab';
|
|
44
44
|
import { ProviderParticipant } from '@atlaskit/editor-common/collab';
|
|
45
45
|
import { ResolvedEditorState } from '@atlaskit/editor-common/collab';
|
|
46
|
-
import type { Step } from 'prosemirror
|
|
46
|
+
import type { Step } from '@atlaskit/editor-prosemirror/transform';
|
|
47
47
|
import { SyncUpErrorFunction } from '@atlaskit/editor-common/collab';
|
|
48
|
-
import type { Transaction } from 'prosemirror
|
|
48
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
49
49
|
|
|
50
50
|
// @public (undocumented)
|
|
51
51
|
type AuthCallback = (cb: (data: InitAndAuthData) => void) => void;
|
|
@@ -144,6 +144,13 @@ interface Config {
|
|
|
144
144
|
permissionTokenRefresh?: () => Promise<null | string>;
|
|
145
145
|
// (undocumented)
|
|
146
146
|
productInfo?: ProductInformation;
|
|
147
|
+
rateLimitMaxStepSize?: number;
|
|
148
|
+
// (undocumented)
|
|
149
|
+
rateLimitStepCount?: number;
|
|
150
|
+
// (undocumented)
|
|
151
|
+
rateLimitTotalStepSize?: number;
|
|
152
|
+
// (undocumented)
|
|
153
|
+
rateLimitType?: number;
|
|
147
154
|
// (undocumented)
|
|
148
155
|
storage?: Storage_2;
|
|
149
156
|
throwOnNotConnected?: boolean;
|
package/tmp/api-report-tmp.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { CollabParticipant } from '@atlaskit/editor-common/collab';
|
|
|
23
23
|
import { CollabPresencePayload } from '@atlaskit/editor-common/collab';
|
|
24
24
|
import { CollabSendableSelection } from '@atlaskit/editor-common/collab';
|
|
25
25
|
import { CollabTelepointerPayload } from '@atlaskit/editor-common/collab';
|
|
26
|
-
import type { EditorState } from 'prosemirror
|
|
26
|
+
import type { EditorState } from '@atlaskit/editor-prosemirror/state';
|
|
27
27
|
import { JSONDocNode } from '@atlaskit/editor-json-transformer';
|
|
28
28
|
import type { Manager } from 'socket.io-client';
|
|
29
29
|
import type { Metadata as Metadata_2 } from '@atlaskit/editor-common/collab';
|
|
@@ -32,9 +32,9 @@ import { PROVIDER_ERROR_CODE } from '@atlaskit/editor-common/collab';
|
|
|
32
32
|
import { ProviderError } from '@atlaskit/editor-common/collab';
|
|
33
33
|
import { ProviderParticipant } from '@atlaskit/editor-common/collab';
|
|
34
34
|
import { ResolvedEditorState } from '@atlaskit/editor-common/collab';
|
|
35
|
-
import type { Step } from 'prosemirror
|
|
35
|
+
import type { Step } from '@atlaskit/editor-prosemirror/transform';
|
|
36
36
|
import { SyncUpErrorFunction } from '@atlaskit/editor-common/collab';
|
|
37
|
-
import type { Transaction } from 'prosemirror
|
|
37
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
38
38
|
|
|
39
39
|
// @public (undocumented)
|
|
40
40
|
type AuthCallback = (cb: (data: InitAndAuthData) => void) => void;
|
|
@@ -120,6 +120,13 @@ interface Config {
|
|
|
120
120
|
permissionTokenRefresh?: () => Promise<null | string>;
|
|
121
121
|
// (undocumented)
|
|
122
122
|
productInfo?: ProductInformation;
|
|
123
|
+
rateLimitMaxStepSize?: number;
|
|
124
|
+
// (undocumented)
|
|
125
|
+
rateLimitStepCount?: number;
|
|
126
|
+
// (undocumented)
|
|
127
|
+
rateLimitTotalStepSize?: number;
|
|
128
|
+
// (undocumented)
|
|
129
|
+
rateLimitType?: number;
|
|
123
130
|
// (undocumented)
|
|
124
131
|
storage?: Storage_2;
|
|
125
132
|
throwOnNotConnected?: boolean;
|