@atlaskit/editor-synced-block-provider 4.3.5 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/cjs/clients/block-service/blockSubscription.js +106 -5
- package/dist/cjs/hooks/useHandleContentChanges.js +1 -1
- package/dist/cjs/index.js +13 -0
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +12 -7
- package/dist/cjs/store-manager/sourceSyncBlockStoreManager.js +16 -6
- package/dist/cjs/store-manager/syncBlockStoreManager.js +7 -2
- package/dist/es2019/clients/block-service/blockSubscription.js +102 -3
- package/dist/es2019/hooks/useHandleContentChanges.js +1 -1
- package/dist/es2019/index.js +1 -0
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +3 -0
- package/dist/es2019/store-manager/sourceSyncBlockStoreManager.js +16 -6
- package/dist/es2019/store-manager/syncBlockStoreManager.js +5 -2
- package/dist/esm/clients/block-service/blockSubscription.js +104 -4
- package/dist/esm/hooks/useHandleContentChanges.js +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +12 -7
- package/dist/esm/store-manager/sourceSyncBlockStoreManager.js +16 -6
- package/dist/esm/store-manager/syncBlockStoreManager.js +7 -2
- package/dist/types/clients/block-service/blockSubscription.d.ts +12 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/store-manager/referenceSyncBlockStoreManager.d.ts +1 -0
- package/dist/types/store-manager/sourceSyncBlockStoreManager.d.ts +3 -2
- package/dist/types/store-manager/syncBlockStoreManager.d.ts +3 -1
- package/dist/types-ts4.5/clients/block-service/blockSubscription.d.ts +12 -0
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/store-manager/referenceSyncBlockStoreManager.d.ts +1 -0
- package/dist/types-ts4.5/store-manager/sourceSyncBlockStoreManager.d.ts +3 -2
- package/dist/types-ts4.5/store-manager/syncBlockStoreManager.d.ts +3 -1
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 4.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`dd9c0778c3832`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dd9c0778c3832) -
|
|
8
|
+
Add \_\_livePage option to syncedBlock plugin and store managers. Add isReferenceBlock/isSyncBlock
|
|
9
|
+
methods. Use predicate functions instead of hardcoded node type names. Fix isDirty logic for
|
|
10
|
+
remote transactions. Fix flush() view-mode return value.
|
|
11
|
+
|
|
12
|
+
## 4.3.6
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [`540d72eba7200`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/540d72eba7200) -
|
|
17
|
+
Editor-5503: Add connection diagnostics to GraphQL WebSocket errors for improved debugging of
|
|
18
|
+
opaque browser error events
|
|
19
|
+
|
|
3
20
|
## 4.3.5
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
|
@@ -1,14 +1,60 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
|
-
exports.subscribeToBlockUpdates = void 0;
|
|
7
|
+
exports.subscribeToBlockUpdates = exports.getConnectionDiagnosticsSummary = exports.extractGraphQLWSErrorMessage = void 0;
|
|
8
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
7
9
|
var _graphqlWs = require("graphql-ws");
|
|
8
10
|
var _coreUtils = require("@atlaskit/editor-common/core-utils");
|
|
11
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
9
12
|
var _utils = require("../../utils/utils");
|
|
10
13
|
var GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
11
14
|
var blockServiceClient = null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Tracks the last known WebSocket connection context for diagnostics.
|
|
18
|
+
* Since browser WebSocket errors are intentionally opaque ({isTrusted: true}),
|
|
19
|
+
* we capture lifecycle events to provide meaningful context when errors occur.
|
|
20
|
+
*/
|
|
21
|
+
var connectionDiagnostics = {
|
|
22
|
+
/** Whether the last connection attempt was a retry */
|
|
23
|
+
wasRetry: false,
|
|
24
|
+
/** Timestamp of the last successful connection */
|
|
25
|
+
lastConnectedAt: 0,
|
|
26
|
+
/** The close code from the most recent WebSocket CloseEvent, see (https://websocket.org/reference/close-codes/)*/
|
|
27
|
+
lastCloseCode: 0,
|
|
28
|
+
/** The close reason from the most recent WebSocket CloseEvent */
|
|
29
|
+
lastCloseReason: '',
|
|
30
|
+
/** Whether the last close was clean */
|
|
31
|
+
lastCloseWasClean: true,
|
|
32
|
+
/** Current connection state */
|
|
33
|
+
state: 'idle',
|
|
34
|
+
/** Number of consecutive connection failures */
|
|
35
|
+
consecutiveFailures: 0
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns a diagnostic summary string from the last known connection state.
|
|
40
|
+
* This provides context that the opaque WebSocket error events cannot.
|
|
41
|
+
*/
|
|
42
|
+
var getConnectionDiagnosticsSummary = exports.getConnectionDiagnosticsSummary = function getConnectionDiagnosticsSummary() {
|
|
43
|
+
var parts = [];
|
|
44
|
+
if (connectionDiagnostics.lastCloseCode !== 0) {
|
|
45
|
+
parts.push("lastCloseCode=".concat(connectionDiagnostics.lastCloseCode));
|
|
46
|
+
parts.push("lastCloseReason=\"".concat(connectionDiagnostics.lastCloseReason || 'none', "\""));
|
|
47
|
+
parts.push("wasClean=".concat(connectionDiagnostics.lastCloseWasClean));
|
|
48
|
+
}
|
|
49
|
+
parts.push("state=".concat(connectionDiagnostics.state));
|
|
50
|
+
parts.push("wasRetry=".concat(connectionDiagnostics.wasRetry));
|
|
51
|
+
parts.push("consecutiveFailures=".concat(connectionDiagnostics.consecutiveFailures));
|
|
52
|
+
if (connectionDiagnostics.lastConnectedAt > 0) {
|
|
53
|
+
var elapsed = Date.now() - connectionDiagnostics.lastConnectedAt;
|
|
54
|
+
parts.push("timeSinceLastConnection=".concat(elapsed, "ms"));
|
|
55
|
+
}
|
|
56
|
+
return parts.join(', ');
|
|
57
|
+
};
|
|
12
58
|
var getBlockServiceClient = function getBlockServiceClient() {
|
|
13
59
|
// Don't create client during SSR
|
|
14
60
|
if ((0, _coreUtils.isSSR)()) {
|
|
@@ -20,12 +66,63 @@ var getBlockServiceClient = function getBlockServiceClient() {
|
|
|
20
66
|
blockServiceClient = (0, _graphqlWs.createClient)({
|
|
21
67
|
url: wsUrl,
|
|
22
68
|
lazy: true,
|
|
23
|
-
retryAttempts: 3
|
|
69
|
+
retryAttempts: 3,
|
|
70
|
+
on: (0, _platformFeatureFlags.fg)('platform_synced_block_add_info_web_socket_error') ? {
|
|
71
|
+
connecting: function connecting(isRetry) {
|
|
72
|
+
connectionDiagnostics.wasRetry = isRetry;
|
|
73
|
+
connectionDiagnostics.state = 'connecting';
|
|
74
|
+
},
|
|
75
|
+
connected: function connected(_socket, _payload, wasRetry) {
|
|
76
|
+
connectionDiagnostics.state = 'connected';
|
|
77
|
+
connectionDiagnostics.lastConnectedAt = Date.now();
|
|
78
|
+
connectionDiagnostics.wasRetry = wasRetry;
|
|
79
|
+
connectionDiagnostics.consecutiveFailures = 0;
|
|
80
|
+
},
|
|
81
|
+
closed: function closed(event) {
|
|
82
|
+
var _closeEvent$code, _closeEvent$reason, _closeEvent$wasClean;
|
|
83
|
+
connectionDiagnostics.state = 'closed';
|
|
84
|
+
var closeEvent = event;
|
|
85
|
+
connectionDiagnostics.lastCloseCode = (_closeEvent$code = closeEvent.code) !== null && _closeEvent$code !== void 0 ? _closeEvent$code : 0;
|
|
86
|
+
connectionDiagnostics.lastCloseReason = (_closeEvent$reason = closeEvent.reason) !== null && _closeEvent$reason !== void 0 ? _closeEvent$reason : '';
|
|
87
|
+
connectionDiagnostics.lastCloseWasClean = (_closeEvent$wasClean = closeEvent.wasClean) !== null && _closeEvent$wasClean !== void 0 ? _closeEvent$wasClean : false;
|
|
88
|
+
},
|
|
89
|
+
error: function error() {
|
|
90
|
+
connectionDiagnostics.state = 'error';
|
|
91
|
+
connectionDiagnostics.consecutiveFailures += 1;
|
|
92
|
+
}
|
|
93
|
+
} : undefined
|
|
24
94
|
});
|
|
25
95
|
}
|
|
26
96
|
return blockServiceClient;
|
|
27
97
|
};
|
|
28
98
|
var SUBSCRIPTION_QUERY = "\nsubscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {\n\tblockService_onBlockUpdated(resourceId: $resourceId) {\n\t\tblockAri\n\t\tblockInstanceId\n\t\tcontent\n\t\tcontentUpdatedAt\n\t\tcreatedAt\n\t\tcreatedBy\n\t\tdeletionReason\n\t\tproduct\n\t\tsourceAri\n\t\tstatus\n\t}\n}\n";
|
|
99
|
+
/**
|
|
100
|
+
* Extracts a meaningful error message from the error: GraphQL WebSocket Error: {"isTrusted":true}
|
|
101
|
+
*
|
|
102
|
+
* @param error - The error passed to the sink's error callback
|
|
103
|
+
* @returns A descriptive error message string
|
|
104
|
+
*/
|
|
105
|
+
var extractGraphQLWSErrorMessage = exports.extractGraphQLWSErrorMessage = function extractGraphQLWSErrorMessage(error) {
|
|
106
|
+
var diagnostics = getConnectionDiagnosticsSummary();
|
|
107
|
+
|
|
108
|
+
// Raw Event from WebSocket.onerror — browsers don't expose error details
|
|
109
|
+
// for security reasons, so {isTrusted: true} is all we get.
|
|
110
|
+
if ((0, _typeof2.default)(error) === 'object' && error !== null && 'isTrusted' in error) {
|
|
111
|
+
return "GraphQL WebSocket connection error (browser restricted error details). Diagnostics: ".concat(diagnostics);
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
return error.message;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Fallback: try to stringify whatever we got
|
|
118
|
+
try {
|
|
119
|
+
var serialized = JSON.stringify(error);
|
|
120
|
+
return "GraphQL subscription error: ".concat(serialized);
|
|
121
|
+
} catch (_unused) {
|
|
122
|
+
return 'GraphQL subscription error: unknown error';
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
29
126
|
/**
|
|
30
127
|
* Extracts the resourceId from a block ARI.
|
|
31
128
|
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
@@ -65,7 +162,7 @@ var parseSubscriptionPayload = function parseSubscriptionPayload(payload) {
|
|
|
65
162
|
sourceAri: payload.sourceAri,
|
|
66
163
|
status: payload.status
|
|
67
164
|
};
|
|
68
|
-
} catch (
|
|
165
|
+
} catch (_unused2) {
|
|
69
166
|
return null;
|
|
70
167
|
}
|
|
71
168
|
};
|
|
@@ -111,8 +208,12 @@ var subscribeToBlockUpdates = exports.subscribeToBlockUpdates = function subscri
|
|
|
111
208
|
};
|
|
112
209
|
return error;
|
|
113
210
|
}(function (error) {
|
|
114
|
-
|
|
115
|
-
|
|
211
|
+
if ((0, _platformFeatureFlags.fg)('platform_synced_block_add_info_web_socket_error')) {
|
|
212
|
+
onError === null || onError === void 0 || onError(new Error(extractGraphQLWSErrorMessage(error)));
|
|
213
|
+
} else {
|
|
214
|
+
var errorMessage = error instanceof Error ? error.message : 'GraphQL subscription error';
|
|
215
|
+
onError === null || onError === void 0 || onError(new Error(errorMessage));
|
|
216
|
+
}
|
|
116
217
|
}),
|
|
117
218
|
complete: function complete() {
|
|
118
219
|
// Subscription completed
|
|
@@ -10,6 +10,6 @@ var useHandleContentChanges = exports.useHandleContentChanges = function useHand
|
|
|
10
10
|
// node content is modified by a ProseMirror transaction, which is exactly
|
|
11
11
|
// when the source manager cache needs to be updated.
|
|
12
12
|
(0, _react.useEffect)(function () {
|
|
13
|
-
manager.sourceManager.updateSyncBlockData(syncBlockNode);
|
|
13
|
+
manager.sourceManager.updateSyncBlockData(syncBlockNode, false);
|
|
14
14
|
}, [manager, syncBlockNode]);
|
|
15
15
|
};
|
package/dist/cjs/index.js
CHANGED
|
@@ -87,6 +87,12 @@ Object.defineProperty(exports, "createSyncBlockNode", {
|
|
|
87
87
|
return _utils.createSyncBlockNode;
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
|
+
Object.defineProperty(exports, "extractGraphQLWSErrorMessage", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
get: function get() {
|
|
93
|
+
return _blockSubscription.extractGraphQLWSErrorMessage;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
90
96
|
Object.defineProperty(exports, "extractResourceIdFromBlockAri", {
|
|
91
97
|
enumerable: true,
|
|
92
98
|
get: function get() {
|
|
@@ -135,6 +141,12 @@ Object.defineProperty(exports, "getConfluencePageAri", {
|
|
|
135
141
|
return _ari2.getConfluencePageAri;
|
|
136
142
|
}
|
|
137
143
|
});
|
|
144
|
+
Object.defineProperty(exports, "getConnectionDiagnosticsSummary", {
|
|
145
|
+
enumerable: true,
|
|
146
|
+
get: function get() {
|
|
147
|
+
return _blockSubscription.getConnectionDiagnosticsSummary;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
138
150
|
Object.defineProperty(exports, "getContentIdAndProductFromResourceId", {
|
|
139
151
|
enumerable: true,
|
|
140
152
|
get: function get() {
|
|
@@ -262,6 +274,7 @@ var _useFetchSyncBlockTitle = require("./hooks/useFetchSyncBlockTitle");
|
|
|
262
274
|
var _useHandleContentChanges = require("./hooks/useHandleContentChanges");
|
|
263
275
|
var _ari = require("./clients/block-service/ari");
|
|
264
276
|
var _blockService = require("./clients/block-service/blockService");
|
|
277
|
+
var _blockSubscription = require("./clients/block-service/blockSubscription");
|
|
265
278
|
var _ari2 = require("./clients/confluence/ari");
|
|
266
279
|
var _fetchMediaToken = require("./clients/confluence/fetchMediaToken");
|
|
267
280
|
var _ari3 = require("./clients/jira/ari");
|
|
@@ -109,14 +109,19 @@ var ReferenceSyncBlockStoreManager = exports.ReferenceSyncBlockStoreManager = /*
|
|
|
109
109
|
// The provider might have SSR data cache already set, so we need to update the cache in session memory storage
|
|
110
110
|
this.setSSRDataInSessionCache((_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getNodeDataCacheKeys());
|
|
111
111
|
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
115
|
-
* When enabled, the store manager will subscribe to real-time updates
|
|
116
|
-
* instead of relying on polling.
|
|
117
|
-
* @param enabled - Whether to enable real-time subscriptions
|
|
118
|
-
*/
|
|
119
112
|
return (0, _createClass2.default)(ReferenceSyncBlockStoreManager, [{
|
|
113
|
+
key: "isReferenceBlock",
|
|
114
|
+
value: function isReferenceBlock(node) {
|
|
115
|
+
return node.type.name === 'syncBlock';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
120
|
+
* When enabled, the store manager will subscribe to real-time updates
|
|
121
|
+
* instead of relying on polling.
|
|
122
|
+
* @param enabled - Whether to enable real-time subscriptions
|
|
123
|
+
*/
|
|
124
|
+
}, {
|
|
120
125
|
key: "setRealTimeSubscriptionsEnabled",
|
|
121
126
|
value: function setRealTimeSubscriptionsEnabled(enabled) {
|
|
122
127
|
this._subscriptionManager.setRealTimeSubscriptionsEnabled(enabled);
|
|
@@ -26,7 +26,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
26
26
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
27
27
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
28
28
|
var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
29
|
-
function SourceSyncBlockStoreManager(dataProvider, viewMode) {
|
|
29
|
+
function SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
|
|
30
30
|
var _this = this;
|
|
31
31
|
(0, _classCallCheck2.default)(this, SourceSyncBlockStoreManager);
|
|
32
32
|
(0, _defineProperty2.default)(this, "hasReceivedContentChange", false);
|
|
@@ -41,6 +41,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
41
41
|
});
|
|
42
42
|
this.dataProvider = dataProvider;
|
|
43
43
|
this.viewMode = viewMode;
|
|
44
|
+
this.isLivePage = isLivePage;
|
|
44
45
|
this.syncBlockCache = new Map();
|
|
45
46
|
this.creationCompletionCallbacks = new Map();
|
|
46
47
|
}
|
|
@@ -76,7 +77,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
76
77
|
*/
|
|
77
78
|
}, {
|
|
78
79
|
key: "updateSyncBlockData",
|
|
79
|
-
value: function updateSyncBlockData(syncBlockNode) {
|
|
80
|
+
value: function updateSyncBlockData(syncBlockNode, isRemote) {
|
|
80
81
|
try {
|
|
81
82
|
if (this.viewMode === 'view' && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_8')) {
|
|
82
83
|
return false;
|
|
@@ -99,11 +100,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
99
100
|
return true;
|
|
100
101
|
}
|
|
101
102
|
var syncBlockData = (0, _utils.convertSyncBlockPMNodeToSyncBlockData)(syncBlockNode);
|
|
102
|
-
if (cachedBlock) {
|
|
103
|
+
if (cachedBlock && !isRemote) {
|
|
103
104
|
this.hasReceivedContentChange = true;
|
|
104
105
|
}
|
|
106
|
+
var isDirty = !isRemote || !cachedBlock; // if the change is not remote, or the block is not in the cache yet, it's dirty
|
|
105
107
|
this.syncBlockCache.set(resourceId, _objectSpread(_objectSpread({}, syncBlockData), {}, {
|
|
106
|
-
isDirty:
|
|
108
|
+
isDirty: isDirty,
|
|
109
|
+
// if the change is from remote, it's not dirty
|
|
107
110
|
contentFragment: syncBlockNode.content
|
|
108
111
|
}));
|
|
109
112
|
} else {
|
|
@@ -145,7 +148,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
145
148
|
_context.next = 3;
|
|
146
149
|
break;
|
|
147
150
|
}
|
|
148
|
-
return _context.abrupt("return",
|
|
151
|
+
return _context.abrupt("return", true);
|
|
149
152
|
case 3:
|
|
150
153
|
bodiedSyncBlockNodes = [];
|
|
151
154
|
bodiedSyncBlockData = [];
|
|
@@ -256,6 +259,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
256
259
|
if (this.viewMode === 'view' && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_8')) {
|
|
257
260
|
return false;
|
|
258
261
|
}
|
|
262
|
+
|
|
263
|
+
// Only track unsaved changes in source synced block for live pages
|
|
264
|
+
// classic pages's draft don't publish synced block content to block service and the content itself is saved as part of the draft
|
|
265
|
+
// classic page's publish flow will trigger a flush which only uses the isDirty flag to determine if there are unsaved changes
|
|
266
|
+
if (!this.isLivePage) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
259
269
|
return this.hasReceivedContentChange && Array.from(this.syncBlockCache.values()).some(function (syncBlockData) {
|
|
260
270
|
return syncBlockData.isDirty;
|
|
261
271
|
});
|
|
@@ -356,7 +366,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
356
366
|
}
|
|
357
367
|
if ((0, _platformFeatureFlags.fg)('platform_synced_block_update_refactor')) {
|
|
358
368
|
// add the node to the cache
|
|
359
|
-
this.updateSyncBlockData(node);
|
|
369
|
+
this.updateSyncBlockData(node, false);
|
|
360
370
|
}
|
|
361
371
|
this.creationCompletionCallbacks.set(resourceId, onCompletion);
|
|
362
372
|
(_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
|
|
@@ -27,16 +27,21 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
27
27
|
// SourceSyncBlockStoreManager is responsible for the lifecycle and state management of source sync blocks in an editor instance.
|
|
28
28
|
// Can be used in both editor and renderer contexts.
|
|
29
29
|
var SyncBlockStoreManager = exports.SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
30
|
-
function SyncBlockStoreManager(dataProvider, viewMode) {
|
|
30
|
+
function SyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
|
|
31
31
|
(0, _classCallCheck2.default)(this, SyncBlockStoreManager);
|
|
32
32
|
// In future, if reference manager needs to reach to source manager and read its current in memory cache
|
|
33
33
|
// we can pass the source manager as a parameter to the reference manager constructor
|
|
34
|
-
this.sourceSyncBlockStoreManager = new _sourceSyncBlockStoreManager.SourceSyncBlockStoreManager(dataProvider, viewMode);
|
|
34
|
+
this.sourceSyncBlockStoreManager = new _sourceSyncBlockStoreManager.SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
|
|
35
35
|
this.referenceSyncBlockStoreManager = new _referenceSyncBlockStoreManager.ReferenceSyncBlockStoreManager(dataProvider, viewMode);
|
|
36
36
|
this.dataProvider = dataProvider;
|
|
37
37
|
this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
|
|
38
38
|
}
|
|
39
39
|
return (0, _createClass2.default)(SyncBlockStoreManager, [{
|
|
40
|
+
key: "isSyncBlock",
|
|
41
|
+
value: function isSyncBlock(node) {
|
|
42
|
+
return this.sourceSyncBlockStoreManager.isSourceBlock(node) || this.referenceSyncBlockStoreManager.isReferenceBlock(node);
|
|
43
|
+
}
|
|
44
|
+
}, {
|
|
40
45
|
key: "fetchReferencesSourceInfo",
|
|
41
46
|
value: function () {
|
|
42
47
|
var _fetchReferencesSourceInfo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(resourceId, blockInstanceId, isSourceSyncBlock) {
|
|
@@ -1,8 +1,52 @@
|
|
|
1
1
|
import { createClient } from 'graphql-ws';
|
|
2
2
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
3
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
4
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
4
5
|
const GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
5
6
|
let blockServiceClient = null;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tracks the last known WebSocket connection context for diagnostics.
|
|
10
|
+
* Since browser WebSocket errors are intentionally opaque ({isTrusted: true}),
|
|
11
|
+
* we capture lifecycle events to provide meaningful context when errors occur.
|
|
12
|
+
*/
|
|
13
|
+
const connectionDiagnostics = {
|
|
14
|
+
/** Whether the last connection attempt was a retry */
|
|
15
|
+
wasRetry: false,
|
|
16
|
+
/** Timestamp of the last successful connection */
|
|
17
|
+
lastConnectedAt: 0,
|
|
18
|
+
/** The close code from the most recent WebSocket CloseEvent, see (https://websocket.org/reference/close-codes/)*/
|
|
19
|
+
lastCloseCode: 0,
|
|
20
|
+
/** The close reason from the most recent WebSocket CloseEvent */
|
|
21
|
+
lastCloseReason: '',
|
|
22
|
+
/** Whether the last close was clean */
|
|
23
|
+
lastCloseWasClean: true,
|
|
24
|
+
/** Current connection state */
|
|
25
|
+
state: 'idle',
|
|
26
|
+
/** Number of consecutive connection failures */
|
|
27
|
+
consecutiveFailures: 0
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns a diagnostic summary string from the last known connection state.
|
|
32
|
+
* This provides context that the opaque WebSocket error events cannot.
|
|
33
|
+
*/
|
|
34
|
+
export const getConnectionDiagnosticsSummary = () => {
|
|
35
|
+
const parts = [];
|
|
36
|
+
if (connectionDiagnostics.lastCloseCode !== 0) {
|
|
37
|
+
parts.push(`lastCloseCode=${connectionDiagnostics.lastCloseCode}`);
|
|
38
|
+
parts.push(`lastCloseReason="${connectionDiagnostics.lastCloseReason || 'none'}"`);
|
|
39
|
+
parts.push(`wasClean=${connectionDiagnostics.lastCloseWasClean}`);
|
|
40
|
+
}
|
|
41
|
+
parts.push(`state=${connectionDiagnostics.state}`);
|
|
42
|
+
parts.push(`wasRetry=${connectionDiagnostics.wasRetry}`);
|
|
43
|
+
parts.push(`consecutiveFailures=${connectionDiagnostics.consecutiveFailures}`);
|
|
44
|
+
if (connectionDiagnostics.lastConnectedAt > 0) {
|
|
45
|
+
const elapsed = Date.now() - connectionDiagnostics.lastConnectedAt;
|
|
46
|
+
parts.push(`timeSinceLastConnection=${elapsed}ms`);
|
|
47
|
+
}
|
|
48
|
+
return parts.join(', ');
|
|
49
|
+
};
|
|
6
50
|
const getBlockServiceClient = () => {
|
|
7
51
|
// Don't create client during SSR
|
|
8
52
|
if (isSSR()) {
|
|
@@ -14,7 +58,31 @@ const getBlockServiceClient = () => {
|
|
|
14
58
|
blockServiceClient = createClient({
|
|
15
59
|
url: wsUrl,
|
|
16
60
|
lazy: true,
|
|
17
|
-
retryAttempts: 3
|
|
61
|
+
retryAttempts: 3,
|
|
62
|
+
on: fg('platform_synced_block_add_info_web_socket_error') ? {
|
|
63
|
+
connecting: isRetry => {
|
|
64
|
+
connectionDiagnostics.wasRetry = isRetry;
|
|
65
|
+
connectionDiagnostics.state = 'connecting';
|
|
66
|
+
},
|
|
67
|
+
connected: (_socket, _payload, wasRetry) => {
|
|
68
|
+
connectionDiagnostics.state = 'connected';
|
|
69
|
+
connectionDiagnostics.lastConnectedAt = Date.now();
|
|
70
|
+
connectionDiagnostics.wasRetry = wasRetry;
|
|
71
|
+
connectionDiagnostics.consecutiveFailures = 0;
|
|
72
|
+
},
|
|
73
|
+
closed: event => {
|
|
74
|
+
var _closeEvent$code, _closeEvent$reason, _closeEvent$wasClean;
|
|
75
|
+
connectionDiagnostics.state = 'closed';
|
|
76
|
+
const closeEvent = event;
|
|
77
|
+
connectionDiagnostics.lastCloseCode = (_closeEvent$code = closeEvent.code) !== null && _closeEvent$code !== void 0 ? _closeEvent$code : 0;
|
|
78
|
+
connectionDiagnostics.lastCloseReason = (_closeEvent$reason = closeEvent.reason) !== null && _closeEvent$reason !== void 0 ? _closeEvent$reason : '';
|
|
79
|
+
connectionDiagnostics.lastCloseWasClean = (_closeEvent$wasClean = closeEvent.wasClean) !== null && _closeEvent$wasClean !== void 0 ? _closeEvent$wasClean : false;
|
|
80
|
+
},
|
|
81
|
+
error: () => {
|
|
82
|
+
connectionDiagnostics.state = 'error';
|
|
83
|
+
connectionDiagnostics.consecutiveFailures += 1;
|
|
84
|
+
}
|
|
85
|
+
} : undefined
|
|
18
86
|
});
|
|
19
87
|
}
|
|
20
88
|
return blockServiceClient;
|
|
@@ -35,6 +103,33 @@ subscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {
|
|
|
35
103
|
}
|
|
36
104
|
}
|
|
37
105
|
`;
|
|
106
|
+
/**
|
|
107
|
+
* Extracts a meaningful error message from the error: GraphQL WebSocket Error: {"isTrusted":true}
|
|
108
|
+
*
|
|
109
|
+
* @param error - The error passed to the sink's error callback
|
|
110
|
+
* @returns A descriptive error message string
|
|
111
|
+
*/
|
|
112
|
+
export const extractGraphQLWSErrorMessage = error => {
|
|
113
|
+
const diagnostics = getConnectionDiagnosticsSummary();
|
|
114
|
+
|
|
115
|
+
// Raw Event from WebSocket.onerror — browsers don't expose error details
|
|
116
|
+
// for security reasons, so {isTrusted: true} is all we get.
|
|
117
|
+
if (typeof error === 'object' && error !== null && 'isTrusted' in error) {
|
|
118
|
+
return `GraphQL WebSocket connection error (browser restricted error details). Diagnostics: ${diagnostics}`;
|
|
119
|
+
}
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
return error.message;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fallback: try to stringify whatever we got
|
|
125
|
+
try {
|
|
126
|
+
const serialized = JSON.stringify(error);
|
|
127
|
+
return `GraphQL subscription error: ${serialized}`;
|
|
128
|
+
} catch {
|
|
129
|
+
return 'GraphQL subscription error: unknown error';
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
38
133
|
/**
|
|
39
134
|
* Extracts the resourceId from a block ARI.
|
|
40
135
|
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
@@ -112,8 +207,12 @@ export const subscribeToBlockUpdates = (blockAri, onData, onError) => {
|
|
|
112
207
|
}
|
|
113
208
|
},
|
|
114
209
|
error: error => {
|
|
115
|
-
|
|
116
|
-
|
|
210
|
+
if (fg('platform_synced_block_add_info_web_socket_error')) {
|
|
211
|
+
onError === null || onError === void 0 ? void 0 : onError(new Error(extractGraphQLWSErrorMessage(error)));
|
|
212
|
+
} else {
|
|
213
|
+
const errorMessage = error instanceof Error ? error.message : 'GraphQL subscription error';
|
|
214
|
+
onError === null || onError === void 0 ? void 0 : onError(new Error(errorMessage));
|
|
215
|
+
}
|
|
117
216
|
},
|
|
118
217
|
complete: () => {
|
|
119
218
|
// Subscription completed
|
|
@@ -4,6 +4,6 @@ export const useHandleContentChanges = (manager, syncBlockNode) => {
|
|
|
4
4
|
// node content is modified by a ProseMirror transaction, which is exactly
|
|
5
5
|
// when the source manager cache needs to be updated.
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
manager.sourceManager.updateSyncBlockData(syncBlockNode);
|
|
7
|
+
manager.sourceManager.updateSyncBlockData(syncBlockNode, false);
|
|
8
8
|
}, [manager, syncBlockNode]);
|
|
9
9
|
};
|
package/dist/es2019/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { useHandleContentChanges } from './hooks/useHandleContentChanges';
|
|
|
11
11
|
// clients
|
|
12
12
|
export { generateBlockAri, generateBlockAriFromReference, getLocalIdFromBlockResourceId } from './clients/block-service/ari';
|
|
13
13
|
export { BlockError, updateSyncedBlocks } from './clients/block-service/blockService';
|
|
14
|
+
export { extractGraphQLWSErrorMessage, getConnectionDiagnosticsSummary } from './clients/block-service/blockSubscription';
|
|
14
15
|
export { getConfluencePageAri, getPageIdAndTypeFromConfluencePageAri } from './clients/confluence/ari';
|
|
15
16
|
export { fetchMediaToken } from './clients/confluence/fetchMediaToken';
|
|
16
17
|
export { getJiraWorkItemAri, getJiraWorkItemIdFromAri } from './clients/jira/ari';
|
|
@@ -64,6 +64,9 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
64
64
|
// The provider might have SSR data cache already set, so we need to update the cache in session memory storage
|
|
65
65
|
this.setSSRDataInSessionCache((_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getNodeDataCacheKeys());
|
|
66
66
|
}
|
|
67
|
+
isReferenceBlock(node) {
|
|
68
|
+
return node.type.name === 'syncBlock';
|
|
69
|
+
}
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
@@ -13,7 +13,7 @@ import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
|
13
13
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
14
14
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
15
15
|
export class SourceSyncBlockStoreManager {
|
|
16
|
-
constructor(dataProvider, viewMode) {
|
|
16
|
+
constructor(dataProvider, viewMode, isLivePage) {
|
|
17
17
|
_defineProperty(this, "hasReceivedContentChange", false);
|
|
18
18
|
_defineProperty(this, "setPendingDeletion", (Ids, value) => {
|
|
19
19
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
@@ -26,6 +26,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
26
26
|
});
|
|
27
27
|
this.dataProvider = dataProvider;
|
|
28
28
|
this.viewMode = viewMode;
|
|
29
|
+
this.isLivePage = isLivePage;
|
|
29
30
|
this.syncBlockCache = new Map();
|
|
30
31
|
this.creationCompletionCallbacks = new Map();
|
|
31
32
|
}
|
|
@@ -53,7 +54,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
53
54
|
* Add/update a sync block node to/from the local cache
|
|
54
55
|
* @param syncBlockNode - The sync block node to update
|
|
55
56
|
*/
|
|
56
|
-
updateSyncBlockData(syncBlockNode) {
|
|
57
|
+
updateSyncBlockData(syncBlockNode, isRemote) {
|
|
57
58
|
try {
|
|
58
59
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
59
60
|
return false;
|
|
@@ -77,12 +78,14 @@ export class SourceSyncBlockStoreManager {
|
|
|
77
78
|
return true;
|
|
78
79
|
}
|
|
79
80
|
const syncBlockData = convertSyncBlockPMNodeToSyncBlockData(syncBlockNode);
|
|
80
|
-
if (cachedBlock) {
|
|
81
|
+
if (cachedBlock && !isRemote) {
|
|
81
82
|
this.hasReceivedContentChange = true;
|
|
82
83
|
}
|
|
84
|
+
const isDirty = !isRemote || !cachedBlock; // if the change is not remote, or the block is not in the cache yet, it's dirty
|
|
83
85
|
this.syncBlockCache.set(resourceId, {
|
|
84
86
|
...syncBlockData,
|
|
85
|
-
isDirty:
|
|
87
|
+
isDirty: isDirty,
|
|
88
|
+
// if the change is from remote, it's not dirty
|
|
86
89
|
contentFragment: syncBlockNode.content
|
|
87
90
|
});
|
|
88
91
|
} else {
|
|
@@ -115,7 +118,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
115
118
|
try {
|
|
116
119
|
var _this$saveExperience;
|
|
117
120
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
118
|
-
return
|
|
121
|
+
return true;
|
|
119
122
|
}
|
|
120
123
|
const bodiedSyncBlockNodes = [];
|
|
121
124
|
const bodiedSyncBlockData = [];
|
|
@@ -198,6 +201,13 @@ export class SourceSyncBlockStoreManager {
|
|
|
198
201
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
199
202
|
return false;
|
|
200
203
|
}
|
|
204
|
+
|
|
205
|
+
// Only track unsaved changes in source synced block for live pages
|
|
206
|
+
// classic pages's draft don't publish synced block content to block service and the content itself is saved as part of the draft
|
|
207
|
+
// classic page's publish flow will trigger a flush which only uses the isDirty flag to determine if there are unsaved changes
|
|
208
|
+
if (!this.isLivePage) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
201
211
|
return this.hasReceivedContentChange && Array.from(this.syncBlockCache.values()).some(syncBlockData => syncBlockData.isDirty);
|
|
202
212
|
}
|
|
203
213
|
isPendingCreation(resourceId) {
|
|
@@ -285,7 +295,7 @@ export class SourceSyncBlockStoreManager {
|
|
|
285
295
|
}
|
|
286
296
|
if (fg('platform_synced_block_update_refactor')) {
|
|
287
297
|
// add the node to the cache
|
|
288
|
-
this.updateSyncBlockData(node);
|
|
298
|
+
this.updateSyncBlockData(node, false);
|
|
289
299
|
}
|
|
290
300
|
this.creationCompletionCallbacks.set(resourceId, onCompletion);
|
|
291
301
|
(_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 ? void 0 : _this$createExperienc.start({});
|
|
@@ -14,14 +14,17 @@ import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
|
|
|
14
14
|
// SourceSyncBlockStoreManager is responsible for the lifecycle and state management of source sync blocks in an editor instance.
|
|
15
15
|
// Can be used in both editor and renderer contexts.
|
|
16
16
|
export class SyncBlockStoreManager {
|
|
17
|
-
constructor(dataProvider, viewMode) {
|
|
17
|
+
constructor(dataProvider, viewMode, isLivePage) {
|
|
18
18
|
// In future, if reference manager needs to reach to source manager and read its current in memory cache
|
|
19
19
|
// we can pass the source manager as a parameter to the reference manager constructor
|
|
20
|
-
this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode);
|
|
20
|
+
this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
|
|
21
21
|
this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode);
|
|
22
22
|
this.dataProvider = dataProvider;
|
|
23
23
|
this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
|
|
24
24
|
}
|
|
25
|
+
isSyncBlock(node) {
|
|
26
|
+
return this.sourceSyncBlockStoreManager.isSourceBlock(node) || this.referenceSyncBlockStoreManager.isReferenceBlock(node);
|
|
27
|
+
}
|
|
25
28
|
async fetchReferencesSourceInfo(resourceId, blockInstanceId, isSourceSyncBlock) {
|
|
26
29
|
try {
|
|
27
30
|
var _this$fetchReferences, _response$references, _this$fetchReferences5, _response$references2;
|
|
@@ -1,8 +1,53 @@
|
|
|
1
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
1
2
|
import { createClient } from 'graphql-ws';
|
|
2
3
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
5
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
4
6
|
var GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
5
7
|
var blockServiceClient = null;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks the last known WebSocket connection context for diagnostics.
|
|
11
|
+
* Since browser WebSocket errors are intentionally opaque ({isTrusted: true}),
|
|
12
|
+
* we capture lifecycle events to provide meaningful context when errors occur.
|
|
13
|
+
*/
|
|
14
|
+
var connectionDiagnostics = {
|
|
15
|
+
/** Whether the last connection attempt was a retry */
|
|
16
|
+
wasRetry: false,
|
|
17
|
+
/** Timestamp of the last successful connection */
|
|
18
|
+
lastConnectedAt: 0,
|
|
19
|
+
/** The close code from the most recent WebSocket CloseEvent, see (https://websocket.org/reference/close-codes/)*/
|
|
20
|
+
lastCloseCode: 0,
|
|
21
|
+
/** The close reason from the most recent WebSocket CloseEvent */
|
|
22
|
+
lastCloseReason: '',
|
|
23
|
+
/** Whether the last close was clean */
|
|
24
|
+
lastCloseWasClean: true,
|
|
25
|
+
/** Current connection state */
|
|
26
|
+
state: 'idle',
|
|
27
|
+
/** Number of consecutive connection failures */
|
|
28
|
+
consecutiveFailures: 0
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns a diagnostic summary string from the last known connection state.
|
|
33
|
+
* This provides context that the opaque WebSocket error events cannot.
|
|
34
|
+
*/
|
|
35
|
+
export var getConnectionDiagnosticsSummary = function getConnectionDiagnosticsSummary() {
|
|
36
|
+
var parts = [];
|
|
37
|
+
if (connectionDiagnostics.lastCloseCode !== 0) {
|
|
38
|
+
parts.push("lastCloseCode=".concat(connectionDiagnostics.lastCloseCode));
|
|
39
|
+
parts.push("lastCloseReason=\"".concat(connectionDiagnostics.lastCloseReason || 'none', "\""));
|
|
40
|
+
parts.push("wasClean=".concat(connectionDiagnostics.lastCloseWasClean));
|
|
41
|
+
}
|
|
42
|
+
parts.push("state=".concat(connectionDiagnostics.state));
|
|
43
|
+
parts.push("wasRetry=".concat(connectionDiagnostics.wasRetry));
|
|
44
|
+
parts.push("consecutiveFailures=".concat(connectionDiagnostics.consecutiveFailures));
|
|
45
|
+
if (connectionDiagnostics.lastConnectedAt > 0) {
|
|
46
|
+
var elapsed = Date.now() - connectionDiagnostics.lastConnectedAt;
|
|
47
|
+
parts.push("timeSinceLastConnection=".concat(elapsed, "ms"));
|
|
48
|
+
}
|
|
49
|
+
return parts.join(', ');
|
|
50
|
+
};
|
|
6
51
|
var getBlockServiceClient = function getBlockServiceClient() {
|
|
7
52
|
// Don't create client during SSR
|
|
8
53
|
if (isSSR()) {
|
|
@@ -14,12 +59,63 @@ var getBlockServiceClient = function getBlockServiceClient() {
|
|
|
14
59
|
blockServiceClient = createClient({
|
|
15
60
|
url: wsUrl,
|
|
16
61
|
lazy: true,
|
|
17
|
-
retryAttempts: 3
|
|
62
|
+
retryAttempts: 3,
|
|
63
|
+
on: fg('platform_synced_block_add_info_web_socket_error') ? {
|
|
64
|
+
connecting: function connecting(isRetry) {
|
|
65
|
+
connectionDiagnostics.wasRetry = isRetry;
|
|
66
|
+
connectionDiagnostics.state = 'connecting';
|
|
67
|
+
},
|
|
68
|
+
connected: function connected(_socket, _payload, wasRetry) {
|
|
69
|
+
connectionDiagnostics.state = 'connected';
|
|
70
|
+
connectionDiagnostics.lastConnectedAt = Date.now();
|
|
71
|
+
connectionDiagnostics.wasRetry = wasRetry;
|
|
72
|
+
connectionDiagnostics.consecutiveFailures = 0;
|
|
73
|
+
},
|
|
74
|
+
closed: function closed(event) {
|
|
75
|
+
var _closeEvent$code, _closeEvent$reason, _closeEvent$wasClean;
|
|
76
|
+
connectionDiagnostics.state = 'closed';
|
|
77
|
+
var closeEvent = event;
|
|
78
|
+
connectionDiagnostics.lastCloseCode = (_closeEvent$code = closeEvent.code) !== null && _closeEvent$code !== void 0 ? _closeEvent$code : 0;
|
|
79
|
+
connectionDiagnostics.lastCloseReason = (_closeEvent$reason = closeEvent.reason) !== null && _closeEvent$reason !== void 0 ? _closeEvent$reason : '';
|
|
80
|
+
connectionDiagnostics.lastCloseWasClean = (_closeEvent$wasClean = closeEvent.wasClean) !== null && _closeEvent$wasClean !== void 0 ? _closeEvent$wasClean : false;
|
|
81
|
+
},
|
|
82
|
+
error: function error() {
|
|
83
|
+
connectionDiagnostics.state = 'error';
|
|
84
|
+
connectionDiagnostics.consecutiveFailures += 1;
|
|
85
|
+
}
|
|
86
|
+
} : undefined
|
|
18
87
|
});
|
|
19
88
|
}
|
|
20
89
|
return blockServiceClient;
|
|
21
90
|
};
|
|
22
91
|
var SUBSCRIPTION_QUERY = "\nsubscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {\n\tblockService_onBlockUpdated(resourceId: $resourceId) {\n\t\tblockAri\n\t\tblockInstanceId\n\t\tcontent\n\t\tcontentUpdatedAt\n\t\tcreatedAt\n\t\tcreatedBy\n\t\tdeletionReason\n\t\tproduct\n\t\tsourceAri\n\t\tstatus\n\t}\n}\n";
|
|
92
|
+
/**
|
|
93
|
+
* Extracts a meaningful error message from the error: GraphQL WebSocket Error: {"isTrusted":true}
|
|
94
|
+
*
|
|
95
|
+
* @param error - The error passed to the sink's error callback
|
|
96
|
+
* @returns A descriptive error message string
|
|
97
|
+
*/
|
|
98
|
+
export var extractGraphQLWSErrorMessage = function extractGraphQLWSErrorMessage(error) {
|
|
99
|
+
var diagnostics = getConnectionDiagnosticsSummary();
|
|
100
|
+
|
|
101
|
+
// Raw Event from WebSocket.onerror — browsers don't expose error details
|
|
102
|
+
// for security reasons, so {isTrusted: true} is all we get.
|
|
103
|
+
if (_typeof(error) === 'object' && error !== null && 'isTrusted' in error) {
|
|
104
|
+
return "GraphQL WebSocket connection error (browser restricted error details). Diagnostics: ".concat(diagnostics);
|
|
105
|
+
}
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
return error.message;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Fallback: try to stringify whatever we got
|
|
111
|
+
try {
|
|
112
|
+
var serialized = JSON.stringify(error);
|
|
113
|
+
return "GraphQL subscription error: ".concat(serialized);
|
|
114
|
+
} catch (_unused) {
|
|
115
|
+
return 'GraphQL subscription error: unknown error';
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
23
119
|
/**
|
|
24
120
|
* Extracts the resourceId from a block ARI.
|
|
25
121
|
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
@@ -59,7 +155,7 @@ var parseSubscriptionPayload = function parseSubscriptionPayload(payload) {
|
|
|
59
155
|
sourceAri: payload.sourceAri,
|
|
60
156
|
status: payload.status
|
|
61
157
|
};
|
|
62
|
-
} catch (
|
|
158
|
+
} catch (_unused2) {
|
|
63
159
|
return null;
|
|
64
160
|
}
|
|
65
161
|
};
|
|
@@ -105,8 +201,12 @@ export var subscribeToBlockUpdates = function subscribeToBlockUpdates(blockAri,
|
|
|
105
201
|
};
|
|
106
202
|
return error;
|
|
107
203
|
}(function (error) {
|
|
108
|
-
|
|
109
|
-
|
|
204
|
+
if (fg('platform_synced_block_add_info_web_socket_error')) {
|
|
205
|
+
onError === null || onError === void 0 || onError(new Error(extractGraphQLWSErrorMessage(error)));
|
|
206
|
+
} else {
|
|
207
|
+
var errorMessage = error instanceof Error ? error.message : 'GraphQL subscription error';
|
|
208
|
+
onError === null || onError === void 0 || onError(new Error(errorMessage));
|
|
209
|
+
}
|
|
110
210
|
}),
|
|
111
211
|
complete: function complete() {
|
|
112
212
|
// Subscription completed
|
|
@@ -4,6 +4,6 @@ export var useHandleContentChanges = function useHandleContentChanges(manager, s
|
|
|
4
4
|
// node content is modified by a ProseMirror transaction, which is exactly
|
|
5
5
|
// when the source manager cache needs to be updated.
|
|
6
6
|
useEffect(function () {
|
|
7
|
-
manager.sourceManager.updateSyncBlockData(syncBlockNode);
|
|
7
|
+
manager.sourceManager.updateSyncBlockData(syncBlockNode, false);
|
|
8
8
|
}, [manager, syncBlockNode]);
|
|
9
9
|
};
|
package/dist/esm/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export { useHandleContentChanges } from './hooks/useHandleContentChanges';
|
|
|
11
11
|
// clients
|
|
12
12
|
export { generateBlockAri, generateBlockAriFromReference, getLocalIdFromBlockResourceId } from './clients/block-service/ari';
|
|
13
13
|
export { BlockError, updateSyncedBlocks } from './clients/block-service/blockService';
|
|
14
|
+
export { extractGraphQLWSErrorMessage, getConnectionDiagnosticsSummary } from './clients/block-service/blockSubscription';
|
|
14
15
|
export { getConfluencePageAri, getPageIdAndTypeFromConfluencePageAri } from './clients/confluence/ari';
|
|
15
16
|
export { fetchMediaToken } from './clients/confluence/fetchMediaToken';
|
|
16
17
|
export { getJiraWorkItemAri, getJiraWorkItemIdFromAri } from './clients/jira/ari';
|
|
@@ -102,14 +102,19 @@ export var ReferenceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
102
102
|
// The provider might have SSR data cache already set, so we need to update the cache in session memory storage
|
|
103
103
|
this.setSSRDataInSessionCache((_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getNodeDataCacheKeys());
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
108
|
-
* When enabled, the store manager will subscribe to real-time updates
|
|
109
|
-
* instead of relying on polling.
|
|
110
|
-
* @param enabled - Whether to enable real-time subscriptions
|
|
111
|
-
*/
|
|
112
105
|
return _createClass(ReferenceSyncBlockStoreManager, [{
|
|
106
|
+
key: "isReferenceBlock",
|
|
107
|
+
value: function isReferenceBlock(node) {
|
|
108
|
+
return node.type.name === 'syncBlock';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
113
|
+
* When enabled, the store manager will subscribe to real-time updates
|
|
114
|
+
* instead of relying on polling.
|
|
115
|
+
* @param enabled - Whether to enable real-time subscriptions
|
|
116
|
+
*/
|
|
117
|
+
}, {
|
|
113
118
|
key: "setRealTimeSubscriptionsEnabled",
|
|
114
119
|
value: function setRealTimeSubscriptionsEnabled(enabled) {
|
|
115
120
|
this._subscriptionManager.setRealTimeSubscriptionsEnabled(enabled);
|
|
@@ -19,7 +19,7 @@ import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
|
|
|
19
19
|
// Handles caching, debouncing updates, and publish/subscribe for local changes.
|
|
20
20
|
// Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
|
|
21
21
|
export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
22
|
-
function SourceSyncBlockStoreManager(dataProvider, viewMode) {
|
|
22
|
+
function SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
|
|
23
23
|
var _this = this;
|
|
24
24
|
_classCallCheck(this, SourceSyncBlockStoreManager);
|
|
25
25
|
_defineProperty(this, "hasReceivedContentChange", false);
|
|
@@ -34,6 +34,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
34
34
|
});
|
|
35
35
|
this.dataProvider = dataProvider;
|
|
36
36
|
this.viewMode = viewMode;
|
|
37
|
+
this.isLivePage = isLivePage;
|
|
37
38
|
this.syncBlockCache = new Map();
|
|
38
39
|
this.creationCompletionCallbacks = new Map();
|
|
39
40
|
}
|
|
@@ -69,7 +70,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
69
70
|
*/
|
|
70
71
|
}, {
|
|
71
72
|
key: "updateSyncBlockData",
|
|
72
|
-
value: function updateSyncBlockData(syncBlockNode) {
|
|
73
|
+
value: function updateSyncBlockData(syncBlockNode, isRemote) {
|
|
73
74
|
try {
|
|
74
75
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
75
76
|
return false;
|
|
@@ -92,11 +93,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
92
93
|
return true;
|
|
93
94
|
}
|
|
94
95
|
var syncBlockData = convertSyncBlockPMNodeToSyncBlockData(syncBlockNode);
|
|
95
|
-
if (cachedBlock) {
|
|
96
|
+
if (cachedBlock && !isRemote) {
|
|
96
97
|
this.hasReceivedContentChange = true;
|
|
97
98
|
}
|
|
99
|
+
var isDirty = !isRemote || !cachedBlock; // if the change is not remote, or the block is not in the cache yet, it's dirty
|
|
98
100
|
this.syncBlockCache.set(resourceId, _objectSpread(_objectSpread({}, syncBlockData), {}, {
|
|
99
|
-
isDirty:
|
|
101
|
+
isDirty: isDirty,
|
|
102
|
+
// if the change is from remote, it's not dirty
|
|
100
103
|
contentFragment: syncBlockNode.content
|
|
101
104
|
}));
|
|
102
105
|
} else {
|
|
@@ -138,7 +141,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
138
141
|
_context.next = 3;
|
|
139
142
|
break;
|
|
140
143
|
}
|
|
141
|
-
return _context.abrupt("return",
|
|
144
|
+
return _context.abrupt("return", true);
|
|
142
145
|
case 3:
|
|
143
146
|
bodiedSyncBlockNodes = [];
|
|
144
147
|
bodiedSyncBlockData = [];
|
|
@@ -249,6 +252,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
249
252
|
if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
|
|
250
253
|
return false;
|
|
251
254
|
}
|
|
255
|
+
|
|
256
|
+
// Only track unsaved changes in source synced block for live pages
|
|
257
|
+
// classic pages's draft don't publish synced block content to block service and the content itself is saved as part of the draft
|
|
258
|
+
// classic page's publish flow will trigger a flush which only uses the isDirty flag to determine if there are unsaved changes
|
|
259
|
+
if (!this.isLivePage) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
252
262
|
return this.hasReceivedContentChange && Array.from(this.syncBlockCache.values()).some(function (syncBlockData) {
|
|
253
263
|
return syncBlockData.isDirty;
|
|
254
264
|
});
|
|
@@ -349,7 +359,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
349
359
|
}
|
|
350
360
|
if (fg('platform_synced_block_update_refactor')) {
|
|
351
361
|
// add the node to the cache
|
|
352
|
-
this.updateSyncBlockData(node);
|
|
362
|
+
this.updateSyncBlockData(node, false);
|
|
353
363
|
}
|
|
354
364
|
this.creationCompletionCallbacks.set(resourceId, onCompletion);
|
|
355
365
|
(_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
|
|
@@ -21,16 +21,21 @@ import { SourceSyncBlockStoreManager } from './sourceSyncBlockStoreManager';
|
|
|
21
21
|
// SourceSyncBlockStoreManager is responsible for the lifecycle and state management of source sync blocks in an editor instance.
|
|
22
22
|
// Can be used in both editor and renderer contexts.
|
|
23
23
|
export var SyncBlockStoreManager = /*#__PURE__*/function () {
|
|
24
|
-
function SyncBlockStoreManager(dataProvider, viewMode) {
|
|
24
|
+
function SyncBlockStoreManager(dataProvider, viewMode, isLivePage) {
|
|
25
25
|
_classCallCheck(this, SyncBlockStoreManager);
|
|
26
26
|
// In future, if reference manager needs to reach to source manager and read its current in memory cache
|
|
27
27
|
// we can pass the source manager as a parameter to the reference manager constructor
|
|
28
|
-
this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode);
|
|
28
|
+
this.sourceSyncBlockStoreManager = new SourceSyncBlockStoreManager(dataProvider, viewMode, isLivePage);
|
|
29
29
|
this.referenceSyncBlockStoreManager = new ReferenceSyncBlockStoreManager(dataProvider, viewMode);
|
|
30
30
|
this.dataProvider = dataProvider;
|
|
31
31
|
this.referenceSyncBlockStoreManager.setRealTimeSubscriptionsEnabled(true);
|
|
32
32
|
}
|
|
33
33
|
return _createClass(SyncBlockStoreManager, [{
|
|
34
|
+
key: "isSyncBlock",
|
|
35
|
+
value: function isSyncBlock(node) {
|
|
36
|
+
return this.sourceSyncBlockStoreManager.isSourceBlock(node) || this.referenceSyncBlockStoreManager.isReferenceBlock(node);
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
34
39
|
key: "fetchReferencesSourceInfo",
|
|
35
40
|
value: function () {
|
|
36
41
|
var _fetchReferencesSourceInfo = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(resourceId, blockInstanceId, isSourceSyncBlock) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ADFEntity } from '@atlaskit/adf-utils/types';
|
|
2
2
|
import type { SyncBlockProduct } from '../../common/types';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a diagnostic summary string from the last known connection state.
|
|
5
|
+
* This provides context that the opaque WebSocket error events cannot.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getConnectionDiagnosticsSummary: () => string;
|
|
3
8
|
export type BlockSubscriptionPayload = {
|
|
4
9
|
blockAri: string;
|
|
5
10
|
blockInstanceId: string;
|
|
@@ -27,6 +32,13 @@ export type ParsedBlockSubscriptionData = {
|
|
|
27
32
|
type SubscriptionCallback = (data: ParsedBlockSubscriptionData) => void;
|
|
28
33
|
type ErrorCallback = (error: Error) => void;
|
|
29
34
|
type Unsubscribe = () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Extracts a meaningful error message from the error: GraphQL WebSocket Error: {"isTrusted":true}
|
|
37
|
+
*
|
|
38
|
+
* @param error - The error passed to the sink's error callback
|
|
39
|
+
* @returns A descriptive error message string
|
|
40
|
+
*/
|
|
41
|
+
export declare const extractGraphQLWSErrorMessage: (error: unknown) => string;
|
|
30
42
|
/**
|
|
31
43
|
* Creates a GraphQL subscription to block updates using the shared graphql-ws client.
|
|
32
44
|
*
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export { useHandleContentChanges } from './hooks/useHandleContentChanges';
|
|
|
7
7
|
export { generateBlockAri, generateBlockAriFromReference, getLocalIdFromBlockResourceId, } from './clients/block-service/ari';
|
|
8
8
|
export type { BlockContentResponse, BatchRetrieveSyncedBlocksResponse, ErrorResponse, } from './clients/block-service/blockService';
|
|
9
9
|
export { BlockError, updateSyncedBlocks } from './clients/block-service/blockService';
|
|
10
|
+
export { extractGraphQLWSErrorMessage, getConnectionDiagnosticsSummary, } from './clients/block-service/blockSubscription';
|
|
10
11
|
export { getConfluencePageAri, getPageIdAndTypeFromConfluencePageAri, } from './clients/confluence/ari';
|
|
11
12
|
export { fetchMediaToken, type TokenData, type ConfigData, } from './clients/confluence/fetchMediaToken';
|
|
12
13
|
export { getJiraWorkItemAri, getJiraWorkItemIdFromAri } from './clients/jira/ari';
|
|
@@ -25,6 +25,7 @@ export declare class ReferenceSyncBlockStoreManager {
|
|
|
25
25
|
private _providerFactoryManager;
|
|
26
26
|
private _batchFetcher;
|
|
27
27
|
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
28
|
+
isReferenceBlock(node: PMNode): boolean;
|
|
28
29
|
/**
|
|
29
30
|
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
30
31
|
* When enabled, the store manager will subscribe to real-time updates
|
|
@@ -9,6 +9,7 @@ type OnCompletion = (success: boolean) => void;
|
|
|
9
9
|
type DestroyCallback = () => void;
|
|
10
10
|
export declare class SourceSyncBlockStoreManager {
|
|
11
11
|
private viewMode?;
|
|
12
|
+
private isLivePage?;
|
|
12
13
|
private dataProvider?;
|
|
13
14
|
private fireAnalyticsEvent?;
|
|
14
15
|
private syncBlockCache;
|
|
@@ -21,7 +22,7 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
21
22
|
private saveExperience;
|
|
22
23
|
private deleteExperience;
|
|
23
24
|
private fetchSourceInfoExperience;
|
|
24
|
-
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
25
|
+
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode, isLivePage?: boolean);
|
|
25
26
|
/**
|
|
26
27
|
* Register a callback to be invoked after flush() completes.
|
|
27
28
|
* Used by the pm-plugin to dispatch a transaction so that
|
|
@@ -34,7 +35,7 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
34
35
|
* Add/update a sync block node to/from the local cache
|
|
35
36
|
* @param syncBlockNode - The sync block node to update
|
|
36
37
|
*/
|
|
37
|
-
updateSyncBlockData(syncBlockNode: PMNode): boolean;
|
|
38
|
+
updateSyncBlockData(syncBlockNode: PMNode, isRemote: boolean): boolean;
|
|
38
39
|
/**
|
|
39
40
|
* Save content of bodiedSyncBlock nodes in local cache to backend
|
|
40
41
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SyncBlockEventPayload } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { BlockInstanceId, ReferencesSourceInfo, ResourceId } from '../common/types';
|
|
4
5
|
import type { SyncBlockDataProviderInterface } from '../providers/types';
|
|
5
6
|
import { ReferenceSyncBlockStoreManager } from './referenceSyncBlockStoreManager';
|
|
@@ -11,7 +12,8 @@ export declare class SyncBlockStoreManager {
|
|
|
11
12
|
private fireAnalyticsEvent?;
|
|
12
13
|
private fetchReferencesExperience;
|
|
13
14
|
private fetchSourceInfoExperience;
|
|
14
|
-
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
15
|
+
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode, isLivePage?: boolean);
|
|
16
|
+
isSyncBlock(node: PMNode): boolean;
|
|
15
17
|
fetchReferencesSourceInfo(resourceId: ResourceId, blockInstanceId: BlockInstanceId, isSourceSyncBlock: boolean): Promise<ReferencesSourceInfo>;
|
|
16
18
|
setFireAnalyticsEvent(fireAnalyticsEvent?: (payload: SyncBlockEventPayload) => void): void;
|
|
17
19
|
get referenceManager(): ReferenceSyncBlockStoreManager;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ADFEntity } from '@atlaskit/adf-utils/types';
|
|
2
2
|
import type { SyncBlockProduct } from '../../common/types';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a diagnostic summary string from the last known connection state.
|
|
5
|
+
* This provides context that the opaque WebSocket error events cannot.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getConnectionDiagnosticsSummary: () => string;
|
|
3
8
|
export type BlockSubscriptionPayload = {
|
|
4
9
|
blockAri: string;
|
|
5
10
|
blockInstanceId: string;
|
|
@@ -27,6 +32,13 @@ export type ParsedBlockSubscriptionData = {
|
|
|
27
32
|
type SubscriptionCallback = (data: ParsedBlockSubscriptionData) => void;
|
|
28
33
|
type ErrorCallback = (error: Error) => void;
|
|
29
34
|
type Unsubscribe = () => void;
|
|
35
|
+
/**
|
|
36
|
+
* Extracts a meaningful error message from the error: GraphQL WebSocket Error: {"isTrusted":true}
|
|
37
|
+
*
|
|
38
|
+
* @param error - The error passed to the sink's error callback
|
|
39
|
+
* @returns A descriptive error message string
|
|
40
|
+
*/
|
|
41
|
+
export declare const extractGraphQLWSErrorMessage: (error: unknown) => string;
|
|
30
42
|
/**
|
|
31
43
|
* Creates a GraphQL subscription to block updates using the shared graphql-ws client.
|
|
32
44
|
*
|
|
@@ -7,6 +7,7 @@ export { useHandleContentChanges } from './hooks/useHandleContentChanges';
|
|
|
7
7
|
export { generateBlockAri, generateBlockAriFromReference, getLocalIdFromBlockResourceId, } from './clients/block-service/ari';
|
|
8
8
|
export type { BlockContentResponse, BatchRetrieveSyncedBlocksResponse, ErrorResponse, } from './clients/block-service/blockService';
|
|
9
9
|
export { BlockError, updateSyncedBlocks } from './clients/block-service/blockService';
|
|
10
|
+
export { extractGraphQLWSErrorMessage, getConnectionDiagnosticsSummary, } from './clients/block-service/blockSubscription';
|
|
10
11
|
export { getConfluencePageAri, getPageIdAndTypeFromConfluencePageAri, } from './clients/confluence/ari';
|
|
11
12
|
export { fetchMediaToken, type TokenData, type ConfigData, } from './clients/confluence/fetchMediaToken';
|
|
12
13
|
export { getJiraWorkItemAri, getJiraWorkItemIdFromAri } from './clients/jira/ari';
|
|
@@ -25,6 +25,7 @@ export declare class ReferenceSyncBlockStoreManager {
|
|
|
25
25
|
private _providerFactoryManager;
|
|
26
26
|
private _batchFetcher;
|
|
27
27
|
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
28
|
+
isReferenceBlock(node: PMNode): boolean;
|
|
28
29
|
/**
|
|
29
30
|
* Enables or disables real-time GraphQL subscriptions for block updates.
|
|
30
31
|
* When enabled, the store manager will subscribe to real-time updates
|
|
@@ -9,6 +9,7 @@ type OnCompletion = (success: boolean) => void;
|
|
|
9
9
|
type DestroyCallback = () => void;
|
|
10
10
|
export declare class SourceSyncBlockStoreManager {
|
|
11
11
|
private viewMode?;
|
|
12
|
+
private isLivePage?;
|
|
12
13
|
private dataProvider?;
|
|
13
14
|
private fireAnalyticsEvent?;
|
|
14
15
|
private syncBlockCache;
|
|
@@ -21,7 +22,7 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
21
22
|
private saveExperience;
|
|
22
23
|
private deleteExperience;
|
|
23
24
|
private fetchSourceInfoExperience;
|
|
24
|
-
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
25
|
+
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode, isLivePage?: boolean);
|
|
25
26
|
/**
|
|
26
27
|
* Register a callback to be invoked after flush() completes.
|
|
27
28
|
* Used by the pm-plugin to dispatch a transaction so that
|
|
@@ -34,7 +35,7 @@ export declare class SourceSyncBlockStoreManager {
|
|
|
34
35
|
* Add/update a sync block node to/from the local cache
|
|
35
36
|
* @param syncBlockNode - The sync block node to update
|
|
36
37
|
*/
|
|
37
|
-
updateSyncBlockData(syncBlockNode: PMNode): boolean;
|
|
38
|
+
updateSyncBlockData(syncBlockNode: PMNode, isRemote: boolean): boolean;
|
|
38
39
|
/**
|
|
39
40
|
* Save content of bodiedSyncBlock nodes in local cache to backend
|
|
40
41
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SyncBlockEventPayload } from '@atlaskit/editor-common/analytics';
|
|
2
2
|
import type { ViewMode } from '@atlaskit/editor-plugin-editor-viewmode';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
3
4
|
import type { BlockInstanceId, ReferencesSourceInfo, ResourceId } from '../common/types';
|
|
4
5
|
import type { SyncBlockDataProviderInterface } from '../providers/types';
|
|
5
6
|
import { ReferenceSyncBlockStoreManager } from './referenceSyncBlockStoreManager';
|
|
@@ -11,7 +12,8 @@ export declare class SyncBlockStoreManager {
|
|
|
11
12
|
private fireAnalyticsEvent?;
|
|
12
13
|
private fetchReferencesExperience;
|
|
13
14
|
private fetchSourceInfoExperience;
|
|
14
|
-
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode);
|
|
15
|
+
constructor(dataProvider?: SyncBlockDataProviderInterface, viewMode?: ViewMode, isLivePage?: boolean);
|
|
16
|
+
isSyncBlock(node: PMNode): boolean;
|
|
15
17
|
fetchReferencesSourceInfo(resourceId: ResourceId, blockInstanceId: BlockInstanceId, isSourceSyncBlock: boolean): Promise<ReferencesSourceInfo>;
|
|
16
18
|
setFireAnalyticsEvent(fireAnalyticsEvent?: (payload: SyncBlockEventPayload) => void): void;
|
|
17
19
|
get referenceManager(): ReferenceSyncBlockStoreManager;
|
package/package.json
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"name": "@atlaskit/editor-synced-block-provider",
|
|
84
|
-
"version": "4.
|
|
84
|
+
"version": "4.4.0",
|
|
85
85
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
86
86
|
"author": "Atlassian Pty Ltd",
|
|
87
87
|
"license": "Apache-2.0",
|
|
@@ -97,6 +97,9 @@
|
|
|
97
97
|
},
|
|
98
98
|
"platform_synced_block_patch_8": {
|
|
99
99
|
"type": "boolean"
|
|
100
|
+
},
|
|
101
|
+
"platform_synced_block_add_info_web_socket_error": {
|
|
102
|
+
"type": "boolean"
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
}
|