@atlaskit/editor-synced-block-provider 3.30.3 → 3.30.5
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 +16 -0
- package/dist/cjs/clients/block-service/blockSubscription.js +3 -53
- package/dist/cjs/clients/block-service/sharedSubscriptionUtils.js +82 -0
- package/dist/cjs/index.js +33 -0
- package/dist/cjs/providers/block-service/blockServiceAPI.js +22 -6
- package/dist/cjs/store-manager/referenceSyncBlockStoreManager.js +52 -85
- package/dist/cjs/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +94 -0
- package/dist/cjs/utils/relayResponseConverter.js +76 -0
- package/dist/cjs/utils/relaySubscriptionUtils.js +130 -0
- package/dist/es2019/clients/block-service/blockSubscription.js +2 -67
- package/dist/es2019/clients/block-service/sharedSubscriptionUtils.js +91 -0
- package/dist/es2019/index.js +4 -0
- package/dist/es2019/providers/block-service/blockServiceAPI.js +22 -6
- package/dist/es2019/store-manager/referenceSyncBlockStoreManager.js +41 -62
- package/dist/es2019/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +88 -0
- package/dist/es2019/utils/relayResponseConverter.js +69 -0
- package/dist/es2019/utils/relaySubscriptionUtils.js +125 -0
- package/dist/esm/clients/block-service/blockSubscription.js +2 -52
- package/dist/esm/clients/block-service/sharedSubscriptionUtils.js +76 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/providers/block-service/blockServiceAPI.js +22 -6
- package/dist/esm/store-manager/referenceSyncBlockStoreManager.js +52 -85
- package/dist/esm/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.js +88 -0
- package/dist/esm/utils/relayResponseConverter.js +69 -0
- package/dist/esm/utils/relaySubscriptionUtils.js +123 -0
- package/dist/types/clients/block-service/blockSubscription.d.ts +1 -26
- package/dist/types/clients/block-service/sharedSubscriptionUtils.d.ts +61 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/providers/block-service/blockServiceAPI.d.ts +7 -2
- package/dist/types/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.d.ts +31 -0
- package/dist/types/utils/relayResponseConverter.d.ts +47 -0
- package/dist/types/utils/relaySubscriptionUtils.d.ts +61 -0
- package/dist/types-ts4.5/clients/block-service/blockSubscription.d.ts +1 -26
- package/dist/types-ts4.5/clients/block-service/sharedSubscriptionUtils.d.ts +61 -0
- package/dist/types-ts4.5/index.d.ts +4 -0
- package/dist/types-ts4.5/providers/block-service/blockServiceAPI.d.ts +7 -2
- package/dist/types-ts4.5/utils/__generated__/relaySubscriptionUtilsSubscription.graphql.d.ts +31 -0
- package/dist/types-ts4.5/utils/relayResponseConverter.d.ts +47 -0
- package/dist/types-ts4.5/utils/relaySubscriptionUtils.d.ts +61 -0
- package/package.json +3 -5
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.convertParsedDataToSyncBlockInstance = convertParsedDataToSyncBlockInstance;
|
|
7
|
+
exports.convertRelayResponseToSyncBlockInstance = convertRelayResponseToSyncBlockInstance;
|
|
8
|
+
var _sharedSubscriptionUtils = require("../clients/block-service/sharedSubscriptionUtils");
|
|
9
|
+
var _validValue = require("./validValue");
|
|
10
|
+
/**
|
|
11
|
+
* Converts parsed subscription data to SyncBlockInstance format.
|
|
12
|
+
*
|
|
13
|
+
* @param parsed - The parsed subscription data
|
|
14
|
+
* @param resourceId - The resource ID for the block
|
|
15
|
+
* @returns A SyncBlockInstance
|
|
16
|
+
*/
|
|
17
|
+
function convertParsedDataToSyncBlockInstance(parsed, resourceId) {
|
|
18
|
+
return {
|
|
19
|
+
data: {
|
|
20
|
+
content: parsed.content,
|
|
21
|
+
contentUpdatedAt: parsed.contentUpdatedAt,
|
|
22
|
+
resourceId: parsed.blockAri,
|
|
23
|
+
blockInstanceId: parsed.blockInstanceId,
|
|
24
|
+
sourceAri: parsed.sourceAri,
|
|
25
|
+
product: parsed.product,
|
|
26
|
+
createdAt: parsed.createdAt,
|
|
27
|
+
createdBy: parsed.createdBy,
|
|
28
|
+
status: (0, _validValue.normaliseSyncBlockStatus)(parsed.status)
|
|
29
|
+
},
|
|
30
|
+
resourceId: resourceId
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Converts a Relay subscription response to SyncBlockInstance format using shared parsing logic.
|
|
36
|
+
|
|
37
|
+
* @param response - The Relay subscription response containing block update data
|
|
38
|
+
* @param resourceId - The resource ID for the block
|
|
39
|
+
* @returns A SyncBlockInstance or null if parsing fails
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* // In a Relay subscription handler
|
|
44
|
+
* onNext: (response) => {
|
|
45
|
+
* if (response?.blockService_onBlockUpdated) {
|
|
46
|
+
* const syncBlockInstance = convertRelayResponseToSyncBlockInstance(
|
|
47
|
+
* response.blockService_onBlockUpdated,
|
|
48
|
+
* resourceId,
|
|
49
|
+
* );
|
|
50
|
+
* if (syncBlockInstance) {
|
|
51
|
+
* onUpdate(syncBlockInstance);
|
|
52
|
+
* }
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
function convertRelayResponseToSyncBlockInstance(response, resourceId) {
|
|
58
|
+
var _response$contentUpda, _response$deletionRea;
|
|
59
|
+
var payload = {
|
|
60
|
+
blockAri: response.blockAri,
|
|
61
|
+
blockInstanceId: response.blockInstanceId,
|
|
62
|
+
content: response.content,
|
|
63
|
+
contentUpdatedAt: (_response$contentUpda = response.contentUpdatedAt) !== null && _response$contentUpda !== void 0 ? _response$contentUpda : undefined,
|
|
64
|
+
createdAt: response.createdAt,
|
|
65
|
+
createdBy: response.createdBy,
|
|
66
|
+
deletionReason: (_response$deletionRea = response.deletionReason) !== null && _response$deletionRea !== void 0 ? _response$deletionRea : undefined,
|
|
67
|
+
product: response.product,
|
|
68
|
+
sourceAri: response.sourceAri,
|
|
69
|
+
status: response.status
|
|
70
|
+
};
|
|
71
|
+
var parsed = (0, _sharedSubscriptionUtils.parseSubscriptionPayload)(payload);
|
|
72
|
+
if (!parsed) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return convertParsedDataToSyncBlockInstance(parsed, resourceId);
|
|
76
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.createRelayBlockSubscription = createRelayBlockSubscription;
|
|
8
|
+
exports.createRelaySubscriptionFunction = createRelaySubscriptionFunction;
|
|
9
|
+
var _relayRuntime = require("relay-runtime");
|
|
10
|
+
var _ari = require("../clients/block-service/ari");
|
|
11
|
+
var _blockSubscription = require("../clients/block-service/blockSubscription");
|
|
12
|
+
var _relaySubscriptionUtilsSubscription = _interopRequireDefault(require("./__generated__/relaySubscriptionUtilsSubscription.graphql"));
|
|
13
|
+
var _relayResponseConverter = require("./relayResponseConverter");
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for creating a Relay block subscription.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a Relay-based block subscription without needing to wrap providers.
|
|
20
|
+
* This is a clean utility function that can be used directly in components or hooks.
|
|
21
|
+
*
|
|
22
|
+
* @param config - Configuration for the subscription
|
|
23
|
+
* @returns An unsubscribe function to dispose the subscription
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const unsubscribe = createRelayBlockSubscription({
|
|
28
|
+
* relayEnvironment: environment,
|
|
29
|
+
* cloudId: 'my-cloud-id',
|
|
30
|
+
* resourceId: 'my-resource-id',
|
|
31
|
+
* onUpdate: (blockInstance) => {
|
|
32
|
+
* console.log('Block updated:', blockInstance);
|
|
33
|
+
* },
|
|
34
|
+
* onError: (error) => {
|
|
35
|
+
* console.error('Subscription error:', error);
|
|
36
|
+
* }
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* // Later, when component unmounts or subscription is no longer needed
|
|
40
|
+
* unsubscribe();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
function createRelayBlockSubscription(config) {
|
|
44
|
+
var relayEnvironment = config.relayEnvironment,
|
|
45
|
+
cloudId = config.cloudId,
|
|
46
|
+
resourceId = config.resourceId,
|
|
47
|
+
onUpdate = config.onUpdate,
|
|
48
|
+
_onError = config.onError;
|
|
49
|
+
|
|
50
|
+
// Convert resourceId to blockAri for the subscription
|
|
51
|
+
var blockAri = (0, _ari.generateBlockAriFromReference)({
|
|
52
|
+
cloudId: cloudId,
|
|
53
|
+
resourceId: resourceId
|
|
54
|
+
});
|
|
55
|
+
var subscriptionQuery = _relaySubscriptionUtilsSubscription.default;
|
|
56
|
+
|
|
57
|
+
// Try to use Relay subscription first
|
|
58
|
+
try {
|
|
59
|
+
var disposable = (0, _relayRuntime.requestSubscription)(relayEnvironment, {
|
|
60
|
+
subscription: subscriptionQuery,
|
|
61
|
+
variables: {
|
|
62
|
+
resourceId: blockAri
|
|
63
|
+
},
|
|
64
|
+
onNext: function onNext(response) {
|
|
65
|
+
if (response !== null && response !== void 0 && response.blockService_onBlockUpdated) {
|
|
66
|
+
var syncBlockInstance = (0, _relayResponseConverter.convertRelayResponseToSyncBlockInstance)(response.blockService_onBlockUpdated, resourceId);
|
|
67
|
+
if (syncBlockInstance) {
|
|
68
|
+
onUpdate(syncBlockInstance);
|
|
69
|
+
} else {
|
|
70
|
+
_onError === null || _onError === void 0 || _onError(new Error('Failed to parse Relay block subscription payload'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
onError: function onError(error) {
|
|
75
|
+
_onError === null || _onError === void 0 || _onError(error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// If subscription was successfully created, return the unsubscribe function
|
|
80
|
+
if (disposable) {
|
|
81
|
+
return function () {
|
|
82
|
+
disposable.dispose();
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// If requestSubscription throws, fall back to WebSocket
|
|
87
|
+
_onError === null || _onError === void 0 || _onError(error instanceof Error ? error : new Error('Relay subscription failed'));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Fallback to WebSocket subscription when Relay subscriptions aren't available
|
|
91
|
+
return (0, _blockSubscription.subscribeToBlockUpdates)(blockAri, function (parsedData) {
|
|
92
|
+
var syncBlockInstance = (0, _relayResponseConverter.convertParsedDataToSyncBlockInstance)(parsedData, parsedData.resourceId);
|
|
93
|
+
onUpdate(syncBlockInstance);
|
|
94
|
+
}, function (error) {
|
|
95
|
+
_onError === null || _onError === void 0 || _onError(error);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Hook-like function to create a subscription function that can be passed to providers.
|
|
101
|
+
* This creates a function with the same signature as subscribeToBlockUpdates that uses Relay.
|
|
102
|
+
*
|
|
103
|
+
* @param cloudId - Cloud ID for generating block ARIs
|
|
104
|
+
* @param relayEnvironment - Optional Relay environment. If not provided, will attempt to use global environment
|
|
105
|
+
* @returns A function that can be used as subscribeToBlockUpdates in provider configurations
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const relaySubscribeToBlockUpdates = createRelaySubscriptionFunction(cloudId, environment);
|
|
110
|
+
*
|
|
111
|
+
* // Can be used directly as a subscribeToBlockUpdates replacement
|
|
112
|
+
* const unsubscribe = relaySubscribeToBlockUpdates(resourceId, onUpdate, onError);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
function createRelaySubscriptionFunction(cloudId, relayEnvironment) {
|
|
116
|
+
return function (resourceId, onUpdate, onError) {
|
|
117
|
+
var environment = relayEnvironment;
|
|
118
|
+
if (!environment) {
|
|
119
|
+
onError === null || onError === void 0 || onError(new Error('Relay environment not available'));
|
|
120
|
+
return function () {};
|
|
121
|
+
}
|
|
122
|
+
return createRelayBlockSubscription({
|
|
123
|
+
relayEnvironment: environment,
|
|
124
|
+
cloudId: cloudId,
|
|
125
|
+
resourceId: resourceId,
|
|
126
|
+
onUpdate: onUpdate,
|
|
127
|
+
onError: onError
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createClient } from 'graphql-ws';
|
|
2
2
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
3
|
-
import {
|
|
3
|
+
import { BLOCK_SERVICE_SUBSCRIPTION_QUERY, parseSubscriptionPayload } from './sharedSubscriptionUtils';
|
|
4
4
|
const GRAPHQL_WS_ENDPOINT = '/gateway/api/graphql/subscriptions';
|
|
5
5
|
let blockServiceClient = null;
|
|
6
6
|
const getBlockServiceClient = () => {
|
|
@@ -19,71 +19,6 @@ const getBlockServiceClient = () => {
|
|
|
19
19
|
}
|
|
20
20
|
return blockServiceClient;
|
|
21
21
|
};
|
|
22
|
-
const SUBSCRIPTION_QUERY = `
|
|
23
|
-
subscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {
|
|
24
|
-
blockService_onBlockUpdated(resourceId: $resourceId) {
|
|
25
|
-
blockAri
|
|
26
|
-
blockInstanceId
|
|
27
|
-
content
|
|
28
|
-
contentUpdatedAt
|
|
29
|
-
createdAt
|
|
30
|
-
createdBy
|
|
31
|
-
deletionReason
|
|
32
|
-
product
|
|
33
|
-
sourceAri
|
|
34
|
-
status
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
`;
|
|
38
|
-
/**
|
|
39
|
-
* Extracts the resourceId from a block ARI.
|
|
40
|
-
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
41
|
-
* @param blockAri - The block ARI string
|
|
42
|
-
* @returns The resourceId portion of the ARI
|
|
43
|
-
*/
|
|
44
|
-
const extractResourceIdFromBlockAri = blockAri => {
|
|
45
|
-
// eslint-disable-next-line require-unicode-regexp
|
|
46
|
-
const match = blockAri.match(/ari:cloud:blocks:[^:]+:synced-block\/(.+)$/);
|
|
47
|
-
return (match === null || match === void 0 ? void 0 : match[1]) || null;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Parses the subscription payload into a standardized format.
|
|
52
|
-
* @param payload - The raw subscription payload
|
|
53
|
-
* @returns Parsed block data or null if parsing fails
|
|
54
|
-
*/
|
|
55
|
-
const parseSubscriptionPayload = payload => {
|
|
56
|
-
try {
|
|
57
|
-
const resourceId = extractResourceIdFromBlockAri(payload.blockAri);
|
|
58
|
-
if (!resourceId) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
let createdAt;
|
|
62
|
-
if (payload.createdAt !== undefined && payload.createdAt !== null) {
|
|
63
|
-
try {
|
|
64
|
-
// BE returns microseconds, convert to milliseconds
|
|
65
|
-
createdAt = new Date(payload.createdAt / 1000).toISOString();
|
|
66
|
-
} catch {
|
|
67
|
-
createdAt = undefined;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
blockAri: payload.blockAri,
|
|
72
|
-
blockInstanceId: payload.blockInstanceId,
|
|
73
|
-
content: JSON.parse(payload.content),
|
|
74
|
-
contentUpdatedAt: convertContentUpdatedAt(payload.contentUpdatedAt),
|
|
75
|
-
createdAt,
|
|
76
|
-
createdBy: payload.createdBy,
|
|
77
|
-
product: payload.product,
|
|
78
|
-
resourceId,
|
|
79
|
-
sourceAri: payload.sourceAri,
|
|
80
|
-
status: payload.status
|
|
81
|
-
};
|
|
82
|
-
} catch {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
22
|
/**
|
|
88
23
|
* Creates a GraphQL subscription to block updates using the shared graphql-ws client.
|
|
89
24
|
*
|
|
@@ -99,7 +34,7 @@ export const subscribeToBlockUpdates = (blockAri, onData, onError) => {
|
|
|
99
34
|
return () => {};
|
|
100
35
|
}
|
|
101
36
|
const unsubscribe = client.subscribe({
|
|
102
|
-
query:
|
|
37
|
+
query: BLOCK_SERVICE_SUBSCRIPTION_QUERY,
|
|
103
38
|
variables: {
|
|
104
39
|
resourceId: blockAri
|
|
105
40
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Shared GraphQL subscription query for block updates.
|
|
4
|
+
* This is the canonical subscription query used across all implementations.
|
|
5
|
+
*/
|
|
6
|
+
export const BLOCK_SERVICE_SUBSCRIPTION_QUERY = `
|
|
7
|
+
subscription EDITOR_SYNCED_BLOCK_ON_BLOCK_UPDATED($resourceId: ID!) {
|
|
8
|
+
blockService_onBlockUpdated(resourceId: $resourceId) {
|
|
9
|
+
blockAri
|
|
10
|
+
blockInstanceId
|
|
11
|
+
content
|
|
12
|
+
contentUpdatedAt
|
|
13
|
+
createdAt
|
|
14
|
+
createdBy
|
|
15
|
+
deletionReason
|
|
16
|
+
product
|
|
17
|
+
sourceAri
|
|
18
|
+
status
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Raw subscription payload from the GraphQL subscription.
|
|
25
|
+
* This represents the exact shape returned by the blockService_onBlockUpdated subscription.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parsed and normalized block subscription data.
|
|
30
|
+
* This is the standardized format used across different subscription implementations.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extracts the resourceId from a block ARI.
|
|
35
|
+
* Block ARI format: ari:cloud:blocks:<cloudId>:synced-block/<resourceId>
|
|
36
|
+
* @param blockAri - The block ARI string
|
|
37
|
+
* @returns The resourceId portion of the ARI
|
|
38
|
+
*/
|
|
39
|
+
export const extractResourceIdFromBlockAri = blockAri => {
|
|
40
|
+
// eslint-disable-next-line require-unicode-regexp
|
|
41
|
+
const match = blockAri.match(/ari:cloud:blocks:[^:]+:synced-block\/(.+)$/);
|
|
42
|
+
return (match === null || match === void 0 ? void 0 : match[1]) || null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Converts a timestamp to ISO string.
|
|
47
|
+
* @param timestamp - Timestamp in milliseconds
|
|
48
|
+
* @returns ISO string or undefined if conversion fails
|
|
49
|
+
*/
|
|
50
|
+
export const convertTimestampToISOString = timestamp => {
|
|
51
|
+
if (timestamp === undefined || timestamp === null) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return new Date(timestamp).toISOString();
|
|
56
|
+
} catch {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parses the raw subscription payload into a standardized format.
|
|
63
|
+
* This function handles all the data transformation and error handling consistently
|
|
64
|
+
* across different subscription implementations.
|
|
65
|
+
*
|
|
66
|
+
* @param payload - The raw subscription payload
|
|
67
|
+
* @returns Parsed block data or null if parsing fails
|
|
68
|
+
*/
|
|
69
|
+
export const parseSubscriptionPayload = payload => {
|
|
70
|
+
try {
|
|
71
|
+
const resourceId = extractResourceIdFromBlockAri(payload.blockAri);
|
|
72
|
+
if (!resourceId) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const createdAt = convertTimestampToISOString(payload.createdAt);
|
|
76
|
+
return {
|
|
77
|
+
blockAri: payload.blockAri,
|
|
78
|
+
blockInstanceId: payload.blockInstanceId,
|
|
79
|
+
content: JSON.parse(payload.content),
|
|
80
|
+
contentUpdatedAt: convertContentUpdatedAt(payload.contentUpdatedAt),
|
|
81
|
+
createdAt,
|
|
82
|
+
createdBy: payload.createdBy,
|
|
83
|
+
product: payload.product,
|
|
84
|
+
resourceId,
|
|
85
|
+
sourceAri: payload.sourceAri,
|
|
86
|
+
status: payload.status
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
};
|
package/dist/es2019/index.js
CHANGED
|
@@ -25,6 +25,10 @@ export { SyncBlockInMemorySessionCache, syncBlockInMemorySessionCache } from './
|
|
|
25
25
|
export { SyncBlockStoreManager, useMemoizedSyncBlockStoreManager } from './store-manager/syncBlockStoreManager';
|
|
26
26
|
|
|
27
27
|
// utils
|
|
28
|
+
export { BLOCK_SERVICE_SUBSCRIPTION_QUERY } from './clients/block-service/sharedSubscriptionUtils';
|
|
29
|
+
export { parseSubscriptionPayload } from './clients/block-service/sharedSubscriptionUtils';
|
|
30
|
+
export { convertRelayResponseToSyncBlockInstance } from './utils/relayResponseConverter';
|
|
31
|
+
export { createRelayBlockSubscription, createRelaySubscriptionFunction } from './utils/relaySubscriptionUtils';
|
|
28
32
|
export { resolveSyncBlockInstance } from './utils/resolveSyncBlockInstance';
|
|
29
33
|
export { parseResourceId, createResourceIdForReference } from './utils/resourceId';
|
|
30
34
|
export { createSyncBlockNode, convertSyncBlockPMNodeToSyncBlockData, convertSyncBlockJSONNodeToSyncBlockNode, convertPMNodesToSyncBlockNodes, getContentIdAndProductFromResourceId } from './utils/utils';
|
|
@@ -6,6 +6,7 @@ import { batchRetrieveSyncedBlocks, BlockError, createSyncedBlock, deleteSyncedB
|
|
|
6
6
|
import { subscribeToBlockUpdates as subscribeToBlockUpdatesWS } from '../../clients/block-service/blockSubscription';
|
|
7
7
|
import { SyncBlockError } from '../../common/types';
|
|
8
8
|
import { stringifyError } from '../../utils/errorHandling';
|
|
9
|
+
import { createRelaySubscriptionFunction } from '../../utils/relaySubscriptionUtils';
|
|
9
10
|
import { createResourceIdForReference } from '../../utils/resourceId';
|
|
10
11
|
import { convertContentUpdatedAt } from '../../utils/utils';
|
|
11
12
|
const mapBlockError = error => {
|
|
@@ -313,10 +314,12 @@ export const batchFetchData = async (cloudId, parentAri, blockNodeIdentifiers) =
|
|
|
313
314
|
class BlockServiceADFFetchProvider {
|
|
314
315
|
constructor({
|
|
315
316
|
cloudId,
|
|
316
|
-
parentAri
|
|
317
|
+
parentAri,
|
|
318
|
+
relayEnvironment
|
|
317
319
|
}) {
|
|
318
320
|
this.cloudId = cloudId;
|
|
319
321
|
this.parentAri = parentAri;
|
|
322
|
+
this.relayEnvironment = relayEnvironment;
|
|
320
323
|
}
|
|
321
324
|
|
|
322
325
|
// resourceId of the reference synced block.
|
|
@@ -442,6 +445,7 @@ class BlockServiceADFFetchProvider {
|
|
|
442
445
|
|
|
443
446
|
/**
|
|
444
447
|
* Subscribes to real-time updates for a specific block using GraphQL WebSocket subscriptions.
|
|
448
|
+
* If a Relay environment is provided, uses Relay subscriptions; otherwise falls back to WebSocket.
|
|
445
449
|
* @param resourceId - The resource ID of the block to subscribe to
|
|
446
450
|
* @param onUpdate - Callback function invoked when the block is updated
|
|
447
451
|
* @param onError - Optional callback function invoked on subscription errors
|
|
@@ -452,6 +456,14 @@ class BlockServiceADFFetchProvider {
|
|
|
452
456
|
cloudId: this.cloudId,
|
|
453
457
|
resourceId
|
|
454
458
|
});
|
|
459
|
+
|
|
460
|
+
// If Relay environment is available, use Relay subscriptions
|
|
461
|
+
if (this.relayEnvironment && fg('platform_synced_block_patch_3')) {
|
|
462
|
+
const relaySubscribeToBlockUpdates = createRelaySubscriptionFunction(this.cloudId, this.relayEnvironment);
|
|
463
|
+
return relaySubscribeToBlockUpdates(resourceId, onUpdate, onError);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Fall back to WebSocket subscriptions
|
|
455
467
|
return subscribeToBlockUpdatesWS(blockAri, parsedData => {
|
|
456
468
|
// Convert ParsedBlockSubscriptionData to SyncBlockInstance
|
|
457
469
|
const syncBlockInstance = {
|
|
@@ -672,12 +684,14 @@ const createBlockServiceAPIProviders = ({
|
|
|
672
684
|
parentAri,
|
|
673
685
|
parentId,
|
|
674
686
|
product,
|
|
675
|
-
getVersion
|
|
687
|
+
getVersion,
|
|
688
|
+
relayEnvironment
|
|
676
689
|
}) => {
|
|
677
690
|
return {
|
|
678
691
|
fetchProvider: new BlockServiceADFFetchProvider({
|
|
679
692
|
cloudId,
|
|
680
|
-
parentAri
|
|
693
|
+
parentAri,
|
|
694
|
+
relayEnvironment
|
|
681
695
|
}),
|
|
682
696
|
writeProvider: new BlockServiceADFWriteProvider({
|
|
683
697
|
cloudId,
|
|
@@ -693,7 +707,8 @@ export const useMemoizedBlockServiceAPIProviders = ({
|
|
|
693
707
|
parentAri,
|
|
694
708
|
parentId,
|
|
695
709
|
product,
|
|
696
|
-
getVersion
|
|
710
|
+
getVersion,
|
|
711
|
+
relayEnvironment
|
|
697
712
|
}) => {
|
|
698
713
|
return useMemo(() => {
|
|
699
714
|
return createBlockServiceAPIProviders({
|
|
@@ -701,9 +716,10 @@ export const useMemoizedBlockServiceAPIProviders = ({
|
|
|
701
716
|
parentAri,
|
|
702
717
|
parentId,
|
|
703
718
|
product,
|
|
704
|
-
getVersion
|
|
719
|
+
getVersion,
|
|
720
|
+
relayEnvironment
|
|
705
721
|
});
|
|
706
|
-
}, [cloudId, parentAri, parentId, product, getVersion]);
|
|
722
|
+
}, [cloudId, parentAri, parentId, product, getVersion, relayEnvironment]);
|
|
707
723
|
};
|
|
708
724
|
const createBlockServiceFetchOnlyAPIProvider = ({
|
|
709
725
|
cloudId,
|
|
@@ -659,17 +659,12 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
659
659
|
this.providerFactories.delete(resourceId);
|
|
660
660
|
}
|
|
661
661
|
debouncedBatchedFetchSyncBlocks(resourceId) {
|
|
662
|
-
if
|
|
663
|
-
|
|
664
|
-
if (this.subscriptions.has(resourceId) && Object.keys(this.subscriptions.get(resourceId) || {}).length > 0) {
|
|
665
|
-
this.pendingFetchRequests.add(resourceId);
|
|
666
|
-
this.scheduledBatchFetch();
|
|
667
|
-
} else {
|
|
668
|
-
this.pendingFetchRequests.delete(resourceId);
|
|
669
|
-
}
|
|
670
|
-
} else {
|
|
662
|
+
// Only add to pending requests if there are active subscriptions for this resource
|
|
663
|
+
if (this.subscriptions.has(resourceId) && Object.keys(this.subscriptions.get(resourceId) || {}).length > 0) {
|
|
671
664
|
this.pendingFetchRequests.add(resourceId);
|
|
672
665
|
this.scheduledBatchFetch();
|
|
666
|
+
} else {
|
|
667
|
+
this.pendingFetchRequests.delete(resourceId);
|
|
673
668
|
}
|
|
674
669
|
}
|
|
675
670
|
setSSRDataInSessionCache(resourceIds) {
|
|
@@ -979,16 +974,14 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
979
974
|
}
|
|
980
975
|
|
|
981
976
|
// Prevent concurrent flushes to avoid race conditions with lastFlushedSyncedBlocks
|
|
982
|
-
if (
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
this.flushNeededAfterCurrent = true;
|
|
977
|
+
if (this.isFlushInProgress) {
|
|
978
|
+
// Mark that another flush is needed after the current one completes
|
|
979
|
+
this.flushNeededAfterCurrent = true;
|
|
986
980
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
981
|
+
// We return true here because we know the pending flush will handle the dirty cache
|
|
982
|
+
return true;
|
|
983
|
+
} else {
|
|
984
|
+
this.isFlushInProgress = true;
|
|
992
985
|
}
|
|
993
986
|
let success = true;
|
|
994
987
|
// a copy of the subscriptions STRUCTURE (without the callbacks)
|
|
@@ -1000,39 +993,28 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
1000
993
|
throw new Error('Data provider not set');
|
|
1001
994
|
}
|
|
1002
995
|
const blocks = [];
|
|
1003
|
-
if (fg('platform_synced_block_patch_2')) {
|
|
1004
|
-
// First, build the complete subscription structure
|
|
1005
|
-
for (const [resourceId, callbacks] of this.subscriptions.entries()) {
|
|
1006
|
-
syncedBlocksToFlush[resourceId] = {};
|
|
1007
|
-
Object.keys(callbacks).forEach(localId => {
|
|
1008
|
-
blocks.push({
|
|
1009
|
-
resourceId,
|
|
1010
|
-
localId
|
|
1011
|
-
});
|
|
1012
|
-
syncedBlocksToFlush[resourceId][localId] = true;
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
996
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}
|
|
1024
|
-
} else {
|
|
1025
|
-
// Collect all reference synced blocks on the current document
|
|
1026
|
-
Array.from(this.subscriptions.entries()).forEach(([resourceId, callbacks]) => {
|
|
1027
|
-
Object.keys(callbacks).forEach(localId => {
|
|
1028
|
-
blocks.push({
|
|
1029
|
-
resourceId,
|
|
1030
|
-
localId
|
|
1031
|
-
});
|
|
997
|
+
// First, build the complete subscription structure
|
|
998
|
+
for (const [resourceId, callbacks] of this.subscriptions.entries()) {
|
|
999
|
+
syncedBlocksToFlush[resourceId] = {};
|
|
1000
|
+
Object.keys(callbacks).forEach(localId => {
|
|
1001
|
+
blocks.push({
|
|
1002
|
+
resourceId,
|
|
1003
|
+
localId
|
|
1032
1004
|
});
|
|
1005
|
+
syncedBlocksToFlush[resourceId][localId] = true;
|
|
1033
1006
|
});
|
|
1034
1007
|
}
|
|
1035
1008
|
|
|
1009
|
+
// Then, compare with the last flushed structure to detect changes
|
|
1010
|
+
// We check against the last flushed structure to prevent unnecessary flushes
|
|
1011
|
+
// Note that we will always flush at least once when editor starts
|
|
1012
|
+
// This is useful for eventual consistency between the editor and the BE.
|
|
1013
|
+
if (isEqual(syncedBlocksToFlush, this.lastFlushedSyncedBlocks)) {
|
|
1014
|
+
this.isCacheDirty = false; // Reset since we're considering this a successful no-op flush
|
|
1015
|
+
return true;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1036
1018
|
// reset isCacheDirty early to prevent race condition
|
|
1037
1019
|
// There is a race condition where if a user makes changes (create/delete) to a reference sync block
|
|
1038
1020
|
// on a live page and the reference sync block is being saved while the user
|
|
@@ -1065,25 +1047,22 @@ export class ReferenceSyncBlockStoreManager {
|
|
|
1065
1047
|
this.isCacheDirty = true;
|
|
1066
1048
|
} else {
|
|
1067
1049
|
var _this$saveExperience4;
|
|
1068
|
-
|
|
1069
|
-
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1070
|
-
}
|
|
1050
|
+
this.lastFlushedSyncedBlocks = syncedBlocksToFlush;
|
|
1071
1051
|
(_this$saveExperience4 = this.saveExperience) === null || _this$saveExperience4 === void 0 ? void 0 : _this$saveExperience4.success();
|
|
1072
1052
|
}
|
|
1073
|
-
if (fg('platform_synced_block_patch_2')) {
|
|
1074
|
-
// Always reset isFlushInProgress regardless of feature flag
|
|
1075
|
-
this.isFlushInProgress = false;
|
|
1076
1053
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1054
|
+
// Always reset isFlushInProgress
|
|
1055
|
+
this.isFlushInProgress = false;
|
|
1056
|
+
|
|
1057
|
+
// If another flush was requested while this one was in progress, execute it now
|
|
1058
|
+
if (this.flushNeededAfterCurrent) {
|
|
1059
|
+
this.flushNeededAfterCurrent = false;
|
|
1060
|
+
// Use setTimeout to avoid deep recursion and run queued flush asynchronously
|
|
1061
|
+
// Note: flush() handles all exceptions internally and never rejects
|
|
1062
|
+
this.queuedFlushTimeout = setTimeout(() => {
|
|
1063
|
+
this.queuedFlushTimeout = undefined;
|
|
1064
|
+
void this.flush();
|
|
1065
|
+
}, 0);
|
|
1087
1066
|
}
|
|
1088
1067
|
}
|
|
1089
1068
|
return success;
|