@atlaskit/editor-synced-block-provider 0.2.0 → 0.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/common/rebase-transaction.js +31 -0
  3. package/dist/cjs/common/syncBlockProvider.js +24 -12
  4. package/dist/cjs/common/syncBlockStoreManager.js +24 -11
  5. package/dist/cjs/index.js +34 -1
  6. package/dist/cjs/providers/confluenceContentAPI.js +238 -0
  7. package/dist/cjs/utils/ari.js +42 -0
  8. package/dist/cjs/utils/contentProperty.js +192 -0
  9. package/dist/es2019/common/rebase-transaction.js +26 -0
  10. package/dist/es2019/common/syncBlockProvider.js +25 -13
  11. package/dist/es2019/common/syncBlockStoreManager.js +18 -7
  12. package/dist/es2019/index.js +5 -2
  13. package/dist/es2019/providers/confluenceContentAPI.js +150 -0
  14. package/dist/es2019/utils/ari.js +32 -0
  15. package/dist/es2019/utils/contentProperty.js +160 -0
  16. package/dist/esm/common/rebase-transaction.js +26 -0
  17. package/dist/esm/common/syncBlockProvider.js +24 -12
  18. package/dist/esm/common/syncBlockStoreManager.js +24 -11
  19. package/dist/esm/index.js +5 -2
  20. package/dist/esm/providers/confluenceContentAPI.js +232 -0
  21. package/dist/esm/utils/ari.js +36 -0
  22. package/dist/esm/utils/contentProperty.js +185 -0
  23. package/dist/types/common/rebase-transaction.d.ts +11 -0
  24. package/dist/types/common/syncBlockProvider.d.ts +3 -1
  25. package/dist/types/common/syncBlockStoreManager.d.ts +5 -2
  26. package/dist/types/common/types.d.ts +1 -0
  27. package/dist/types/index.d.ts +5 -2
  28. package/dist/types/providers/confluenceContentAPI.d.ts +41 -0
  29. package/dist/types/utils/ari.d.ts +10 -0
  30. package/dist/types/utils/contentProperty.d.ts +61 -0
  31. package/dist/types-ts4.5/common/rebase-transaction.d.ts +11 -0
  32. package/dist/types-ts4.5/common/syncBlockProvider.d.ts +3 -1
  33. package/dist/types-ts4.5/common/syncBlockStoreManager.d.ts +5 -2
  34. package/dist/types-ts4.5/common/types.d.ts +1 -0
  35. package/dist/types-ts4.5/index.d.ts +5 -2
  36. package/dist/types-ts4.5/providers/confluenceContentAPI.d.ts +41 -0
  37. package/dist/types-ts4.5/utils/ari.d.ts +10 -0
  38. package/dist/types-ts4.5/utils/contentProperty.d.ts +61 -0
  39. package/package.json +2 -2
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.updateContentProperty = exports.getContentProperty = exports.createContentProperty = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
+ var _ari = require("./ari");
12
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14
+ var COMMON_HEADERS = {
15
+ 'Content-Type': 'application/json',
16
+ Accept: 'application/json'
17
+ };
18
+ var AGG_HEADERS = {
19
+ 'X-ExperimentalApi': 'confluence-agg-beta'
20
+ };
21
+ var GRAPHQL_ENDPOINT = '/gateway/api/graphql';
22
+ var GET_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_GET';
23
+ var CREATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_CREATE';
24
+ var UPDATE_OPERATION_NAME = 'EDITOR_SYNCED_BLOCK_UPDATE';
25
+ /**
26
+ * Query to get the page property by key
27
+ * @param documentARI
28
+ * @param key
29
+ * @returns
30
+ */
31
+ var GET_QUERY = "query ".concat(GET_OPERATION_NAME, " ($id: ID!, $keys: [String]!) {\n\t\t\t\t\tconfluence {\n\t\t\t\t\t\tpage (id: $id) {\n\t\t\t\t\t\t\tproperties(keys: $keys) {\n\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}");
32
+
33
+ /**
34
+ * Query to create a page property with key and value
35
+ * @param documentARI
36
+ * @param key
37
+ * @param value
38
+ * @returns
39
+ */
40
+ var CREATE_QUERY = "mutation ".concat(CREATE_OPERATION_NAME, " ($input: ConfluenceCreatePagePropertyInput!){\n\t\t\t\t\t\tconfluence {\n\t\t\t\t\t\t\tcreatePageProperty(input: $input) {\n\t\t\t\t\t\t\t\tpageProperty {\n\t\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}");
41
+
42
+ /**
43
+ * Query to update a page property with key and value without bumping the version
44
+ * @param documentARI
45
+ * @param key
46
+ * @param value
47
+ * @returns
48
+ */
49
+ var UPDATE_QUERY = "mutation ".concat(UPDATE_OPERATION_NAME, " ($input: ConfluenceUpdateValuePagePropertyInput!) {\n\t\t\t\t\t\tconfluence {\n\t\t\t\t\t\t\tupdateValuePageProperty(input: $input) {\n\t\t\t\t\t\t\t\tpageProperty {\n\t\t\t\t\t\t\t\t\tkey,\n\t\t\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}");
50
+ var getContentProperty = exports.getContentProperty = /*#__PURE__*/function () {
51
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
52
+ var pageId, key, cloudId, documentARI, bodyData, response, contentProperty;
53
+ return _regenerator.default.wrap(function _callee$(_context) {
54
+ while (1) switch (_context.prev = _context.next) {
55
+ case 0:
56
+ pageId = _ref.pageId, key = _ref.key, cloudId = _ref.cloudId;
57
+ documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId);
58
+ bodyData = {
59
+ query: GET_QUERY,
60
+ operationName: GET_OPERATION_NAME,
61
+ variables: {
62
+ id: documentARI,
63
+ keys: [key]
64
+ }
65
+ };
66
+ _context.next = 5;
67
+ return fetch(GRAPHQL_ENDPOINT, {
68
+ method: 'POST',
69
+ headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
70
+ body: JSON.stringify(bodyData)
71
+ });
72
+ case 5:
73
+ response = _context.sent;
74
+ if (response.ok) {
75
+ _context.next = 8;
76
+ break;
77
+ }
78
+ throw new Error("Failed to get content property: ".concat(response.statusText));
79
+ case 8:
80
+ _context.next = 10;
81
+ return response.json();
82
+ case 10:
83
+ contentProperty = _context.sent;
84
+ return _context.abrupt("return", contentProperty);
85
+ case 12:
86
+ case "end":
87
+ return _context.stop();
88
+ }
89
+ }, _callee);
90
+ }));
91
+ return function getContentProperty(_x) {
92
+ return _ref2.apply(this, arguments);
93
+ };
94
+ }();
95
+ var updateContentProperty = exports.updateContentProperty = /*#__PURE__*/function () {
96
+ var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(_ref3) {
97
+ var pageId, key, value, cloudId, documentARI, bodyData, response, contentProperty;
98
+ return _regenerator.default.wrap(function _callee2$(_context2) {
99
+ while (1) switch (_context2.prev = _context2.next) {
100
+ case 0:
101
+ pageId = _ref3.pageId, key = _ref3.key, value = _ref3.value, cloudId = _ref3.cloudId;
102
+ documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId);
103
+ bodyData = {
104
+ query: UPDATE_QUERY,
105
+ operationName: UPDATE_OPERATION_NAME,
106
+ variables: {
107
+ input: {
108
+ pageId: documentARI,
109
+ key: key,
110
+ value: value,
111
+ useSameVersion: true
112
+ }
113
+ }
114
+ };
115
+ _context2.next = 5;
116
+ return fetch(GRAPHQL_ENDPOINT, {
117
+ method: 'POST',
118
+ headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
119
+ body: JSON.stringify(bodyData)
120
+ });
121
+ case 5:
122
+ response = _context2.sent;
123
+ if (response.ok) {
124
+ _context2.next = 8;
125
+ break;
126
+ }
127
+ throw new Error("Failed to update content property: ".concat(response.statusText));
128
+ case 8:
129
+ _context2.next = 10;
130
+ return response.json();
131
+ case 10:
132
+ contentProperty = _context2.sent;
133
+ return _context2.abrupt("return", contentProperty);
134
+ case 12:
135
+ case "end":
136
+ return _context2.stop();
137
+ }
138
+ }, _callee2);
139
+ }));
140
+ return function updateContentProperty(_x2) {
141
+ return _ref4.apply(this, arguments);
142
+ };
143
+ }();
144
+ var createContentProperty = exports.createContentProperty = /*#__PURE__*/function () {
145
+ var _ref6 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3(_ref5) {
146
+ var pageId, key, value, cloudId, documentARI, escapedValue, bodyData, response, contentProperty;
147
+ return _regenerator.default.wrap(function _callee3$(_context3) {
148
+ while (1) switch (_context3.prev = _context3.next) {
149
+ case 0:
150
+ pageId = _ref5.pageId, key = _ref5.key, value = _ref5.value, cloudId = _ref5.cloudId;
151
+ documentARI = (0, _ari.getConfluencePageAri)(pageId, cloudId); // eslint-disable-next-line require-unicode-regexp
152
+ escapedValue = value.replace(/"/g, '\\"');
153
+ bodyData = {
154
+ query: CREATE_QUERY,
155
+ operationName: CREATE_OPERATION_NAME,
156
+ variables: {
157
+ input: {
158
+ pageId: documentARI,
159
+ key: key,
160
+ value: escapedValue
161
+ }
162
+ }
163
+ };
164
+ _context3.next = 6;
165
+ return fetch(GRAPHQL_ENDPOINT, {
166
+ method: 'POST',
167
+ headers: _objectSpread(_objectSpread({}, COMMON_HEADERS), AGG_HEADERS),
168
+ body: JSON.stringify(bodyData)
169
+ });
170
+ case 6:
171
+ response = _context3.sent;
172
+ if (response.ok) {
173
+ _context3.next = 9;
174
+ break;
175
+ }
176
+ throw new Error("Failed to create content property: ".concat(response.statusText));
177
+ case 9:
178
+ _context3.next = 11;
179
+ return response.json();
180
+ case 11:
181
+ contentProperty = _context3.sent;
182
+ return _context3.abrupt("return", contentProperty);
183
+ case 13:
184
+ case "end":
185
+ return _context3.stop();
186
+ }
187
+ }, _callee3);
188
+ }));
189
+ return function createContentProperty(_x3) {
190
+ return _ref6.apply(this, arguments);
191
+ };
192
+ }();
@@ -0,0 +1,26 @@
1
+ import { Mapping } from '@atlaskit/editor-prosemirror/transform';
2
+
3
+ /**
4
+ * Rebase `currentTr` over `incomingTr` based on the provided `state`.
5
+ * This function adjusts the steps in `currentTr` to account for the changes made by `incomingTr`.
6
+ *
7
+ * @param currentTr - The transaction to be rebased.
8
+ * @param incomingTr - The transaction that has already been applied to the state.
9
+ * @param state - The editor state after applying `incomingTr`.
10
+ * @returns A new transaction that represents `currentTr` rebased over `incomingTr`.
11
+ */
12
+ export const rebaseTransaction = (currentTr, incomingTr, state) => {
13
+ if (!incomingTr.docChanged) {
14
+ return currentTr;
15
+ }
16
+ const currentMapping = new Mapping(incomingTr.mapping.maps);
17
+ const rebasedTransaction = state.tr;
18
+ currentTr.steps.forEach(step => {
19
+ const mappedStep = step.map(currentMapping);
20
+ if (mappedStep) {
21
+ rebasedTransaction.step(mappedStep);
22
+ currentMapping.appendMap(mappedStep.getMap());
23
+ }
24
+ });
25
+ return rebasedTransaction;
26
+ };
@@ -1,5 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import { useEffect, useState } from 'react';
2
+ import { useEffect, useState, useMemo } from 'react';
3
3
  import { convertSyncBlockPMNodeToSyncBlockData } from '../utils/utils';
4
4
  import { SyncBlockDataProvider } from './types';
5
5
  export class SyncBlockProvider extends SyncBlockDataProvider {
@@ -32,6 +32,9 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
32
32
  });
33
33
  return Promise.all(resourceIds);
34
34
  });
35
+ _defineProperty(this, "getSourceId", () => {
36
+ return this.sourceId;
37
+ });
35
38
  this.fetchProvider = fetchProvider;
36
39
  this.writeProvider = writeProvider;
37
40
  this.sourceId = sourceId;
@@ -39,26 +42,35 @@ export class SyncBlockProvider extends SyncBlockDataProvider {
39
42
  }
40
43
  export const useFetchDocNode = (editorView, node, defaultDocNode, provider) => {
41
44
  const [docNode, setDocNode] = useState(defaultDocNode);
45
+ const fetchNode = (editorView, node, provider) => {
46
+ const nodes = [convertSyncBlockPMNodeToSyncBlockData(node, false)];
47
+ provider === null || provider === void 0 ? void 0 : provider.fetchNodesData(nodes).then(data => {
48
+ var _data$;
49
+ if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
50
+ const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
51
+ setDocNode({
52
+ ...newNode.toJSON(),
53
+ version: 1
54
+ });
55
+ }
56
+ });
57
+ };
42
58
  useEffect(() => {
43
59
  if (!provider) {
44
60
  return;
45
61
  }
62
+ fetchNode(editorView, node, provider);
46
63
  const interval = window.setInterval(() => {
47
- const nodes = [convertSyncBlockPMNodeToSyncBlockData(node, false)];
48
- provider === null || provider === void 0 ? void 0 : provider.fetchNodesData(nodes).then(data => {
49
- var _data$;
50
- if (data && (_data$ = data[0]) !== null && _data$ !== void 0 && _data$.content) {
51
- const newNode = editorView.state.schema.nodeFromJSON(data[0].content);
52
- setDocNode({
53
- ...newNode.toJSON(),
54
- version: 1
55
- });
56
- }
57
- });
58
- }, 1000);
64
+ fetchNode(editorView, node, provider);
65
+ }, 3000);
59
66
  return () => {
60
67
  window.clearInterval(interval);
61
68
  };
62
69
  }, [editorView, node, provider]);
63
70
  return docNode;
71
+ };
72
+ export const useMemoizedSyncedBlockProvider = (fetchProvider, writeProvider, sourceId) => {
73
+ return useMemo(() => {
74
+ return new SyncBlockProvider(fetchProvider, writeProvider, sourceId);
75
+ }, [fetchProvider, writeProvider, sourceId]);
64
76
  };
@@ -1,4 +1,5 @@
1
1
  import uuid from 'uuid';
2
+ import { rebaseTransaction } from './rebase-transaction';
2
3
 
3
4
  // Do this typedef to make it clear that
4
5
  // this is a local identifier for a resource for local use
@@ -9,14 +10,14 @@ import uuid from 'uuid';
9
10
  // Handles caching, debouncing updates, and publish/subscribe for local changes.
10
11
  // Ensures consistency between local and remote state, and can be used in both editor and renderer contexts.
11
12
  export class SyncBlockStoreManager {
12
- constructor(_dataProvider) {
13
+ constructor(dataProvider) {
13
14
  this.syncBlocks = new Map();
15
+ this.dataProvider = dataProvider;
14
16
  }
15
17
  setEditorView(editorView) {
16
18
  this.editorView = editorView;
17
19
  }
18
20
  isSourceBlock(node) {
19
- var _this$syncBlocks$get;
20
21
  if (node.type.name !== 'syncBlock') {
21
22
  return false;
22
23
  }
@@ -24,7 +25,7 @@ export class SyncBlockStoreManager {
24
25
  resourceId,
25
26
  localId
26
27
  } = node.attrs;
27
- return this.syncBlocks.has(resourceId) && ((_this$syncBlocks$get = this.syncBlocks.get(resourceId)) === null || _this$syncBlocks$get === void 0 ? void 0 : _this$syncBlocks$get.sourceLocalId) === localId;
28
+ return resourceId.includes(localId);
28
29
  }
29
30
  registerConfirmationCallback(callback) {
30
31
  this.confirmationCallback = callback;
@@ -36,15 +37,18 @@ export class SyncBlockStoreManager {
36
37
  return !!this.confirmationCallback;
37
38
  }
38
39
  createSyncBlockNode() {
40
+ var _this$dataProvider;
39
41
  // TODO: EDITOR-1644 - properly implement creation of the synced block
40
42
  // below is a temporary implementation for the creation of the synced block
41
43
  // the resource id needs to have pageId and content property key in it
44
+ // Note: If the data provider is not set, the resource id will be the local id
42
45
 
43
- const blockInstanceId = uuid();
44
46
  const localId = uuid();
47
+ const sourceId = (_this$dataProvider = this.dataProvider) === null || _this$dataProvider === void 0 ? void 0 : _this$dataProvider.getSourceId();
48
+ const resourceId = sourceId ? `${sourceId}/${localId}` : localId;
45
49
  const syncBlockNode = {
46
50
  attrs: {
47
- resourceId: `ari:cloud:confluence:fake_cloud_id:page/fake_page_id/${blockInstanceId}`,
51
+ resourceId,
48
52
  localId
49
53
  },
50
54
  type: 'syncBlock'
@@ -57,16 +61,23 @@ export class SyncBlockStoreManager {
57
61
  }
58
62
  async deleteSyncBlocksWithConfirmation(tr, syncBlockIds) {
59
63
  if (this.confirmationCallback) {
64
+ this.confirmationTransaction = tr;
60
65
  const confirmed = await this.confirmationCallback();
61
66
  if (confirmed) {
62
67
  var _this$editorView;
63
- // TODO: EDITOR-1779 - "rebase" the transaction to reflect the latest document state
64
- (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.dispatch(tr.setMeta('isConfirmedSyncBlockDeletion', true));
68
+ (_this$editorView = this.editorView) === null || _this$editorView === void 0 ? void 0 : _this$editorView.dispatch(this.confirmationTransaction.setMeta('isConfirmedSyncBlockDeletion', true));
65
69
  // Need to update the BE on deletion
66
70
  syncBlockIds.forEach(({
67
71
  resourceId
68
72
  }) => this.syncBlocks.delete(resourceId));
69
73
  }
74
+ this.confirmationTransaction = undefined;
70
75
  }
71
76
  }
77
+ rebaseTransaction(incomingTr, state) {
78
+ if (!this.confirmationTransaction) {
79
+ return;
80
+ }
81
+ this.confirmationTransaction = rebaseTransaction(this.confirmationTransaction, incomingTr, state);
82
+ }
72
83
  }
@@ -1,7 +1,10 @@
1
1
  /* eslint-disable @atlaskit/editor/no-re-export */
2
2
 
3
- export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode } from './common/syncBlockProvider';
3
+ export { SyncBlockProvider as SyncedBlockProvider, useFetchDocNode, useMemoizedSyncedBlockProvider } from './common/syncBlockProvider';
4
4
  export { SyncBlockStoreManager } from './common/syncBlockStoreManager';
5
5
  export { inMemoryFetchProvider, inMemoryWriteProvider } from './providers/inMemory';
6
+ export { getDefaultSyncBlockSchema } from './common/schema';
7
+ export { createContentAPIProvidersWithDefaultKey, useMemoizedContentAPIProviders } from './providers/confluenceContentAPI';
8
+ export { getConfluencePageAri } from './utils/ari';
6
9
  export { convertSyncBlockPMNodeToSyncBlockData, generateSyncBlockSourceUrl } from './utils/utils';
7
- export { getDefaultSyncBlockSchema } from './common/schema';
10
+ export { rebaseTransaction } from './common/rebase-transaction';
@@ -0,0 +1,150 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { useMemo } from 'react';
3
+ import { getLocalIdFromAri, getPageIdFromAri } from '../utils/ari';
4
+ import { getContentProperty, createContentProperty, updateContentProperty } from '../utils/contentProperty';
5
+
6
+ /**
7
+ * Configuration for Content API providers
8
+ */
9
+
10
+ const getContentPropertyKey = (contentPropertyKey, localId) => {
11
+ return contentPropertyKey + '-' + localId;
12
+ };
13
+ const parseSyncedBlockContentPropertyValue = value => {
14
+ try {
15
+ if (typeof value === 'string') {
16
+ return JSON.parse(value);
17
+ }
18
+ return value;
19
+ } catch (error) {
20
+ // eslint-disable-next-line no-console
21
+ console.error('Failed to parse synced block content:', error);
22
+ return {
23
+ content: undefined
24
+ };
25
+ }
26
+ };
27
+
28
+ /**
29
+ * ADFFetchProvider implementation that fetches synced block data from Confluence Content API
30
+ */
31
+ class ConfluenceADFFetchProvider {
32
+ constructor(config) {
33
+ this.config = config;
34
+ }
35
+ async fetchData(resourceId) {
36
+ try {
37
+ var _contentProperty$data, _contentProperty$data2;
38
+ const pageId = getPageIdFromAri(resourceId);
39
+ const localId = getLocalIdFromAri(resourceId);
40
+ const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
41
+ const options = {
42
+ pageId,
43
+ key,
44
+ cloudId: this.config.cloudId
45
+ };
46
+ const contentProperty = await getContentProperty(options);
47
+ const value = (_contentProperty$data = contentProperty.data.confluence.page.properties) === null || _contentProperty$data === void 0 ? void 0 : (_contentProperty$data2 = _contentProperty$data[0]) === null || _contentProperty$data2 === void 0 ? void 0 : _contentProperty$data2.value;
48
+ if (!value) {
49
+ throw new Error('Content property value does not exist');
50
+ }
51
+
52
+ // Parse the synced block content from the property value
53
+ const syncedBlockData = parseSyncedBlockContentPropertyValue(value);
54
+ return {
55
+ content: syncedBlockData.content
56
+ };
57
+ } catch (error) {
58
+ // eslint-disable-next-line no-console
59
+ console.error('Failed to fetch synced block data:', error);
60
+ return {
61
+ content: undefined
62
+ };
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * ADFWriteProvider implementation that writes synced block data to Confluence Content API
69
+ */
70
+ class ConfluenceADFWriteProvider {
71
+ constructor(config) {
72
+ _defineProperty(this, "createNewContentProperty", async (pageId, key, value) => {
73
+ var _contentProperty$data3;
74
+ const contentProperty = await createContentProperty({
75
+ pageId,
76
+ key,
77
+ value,
78
+ cloudId: this.config.cloudId
79
+ });
80
+ if (((_contentProperty$data3 = contentProperty.data.confluence.createPageProperty.pageProperty) === null || _contentProperty$data3 === void 0 ? void 0 : _contentProperty$data3.key) === key) {
81
+ return key;
82
+ } else {
83
+ throw new Error('Failed to create content property');
84
+ }
85
+ });
86
+ this.config = config;
87
+ }
88
+ async writeData(sourceId, localId, data, resourceId) {
89
+ try {
90
+ const pageId = getPageIdFromAri(sourceId);
91
+ const syncedBlockValue = JSON.stringify({
92
+ content: data
93
+ });
94
+ if (resourceId) {
95
+ var _contentProperty$data4;
96
+ // Update existing content property
97
+ const localId = getLocalIdFromAri(resourceId);
98
+ const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
99
+ const contentProperty = await updateContentProperty({
100
+ pageId,
101
+ key,
102
+ value: syncedBlockValue,
103
+ cloudId: this.config.cloudId
104
+ });
105
+ if (((_contentProperty$data4 = contentProperty.data.confluence.updateValuePageProperty.pageProperty) === null || _contentProperty$data4 === void 0 ? void 0 : _contentProperty$data4.key) === key) {
106
+ return key;
107
+ } else if (contentProperty.data.confluence.updateValuePageProperty.pageProperty === null) {
108
+ return this.createNewContentProperty(pageId, key, syncedBlockValue);
109
+ } else {
110
+ throw new Error('Failed to update content property');
111
+ }
112
+ } else {
113
+ // Create new content property
114
+ const key = getContentPropertyKey(this.config.contentPropertyKey, localId);
115
+ return this.createNewContentProperty(pageId, key, syncedBlockValue);
116
+ }
117
+ } catch (error) {
118
+ // eslint-disable-next-line no-console
119
+ console.error('Failed to write synced block data:', error);
120
+ return Promise.reject(error);
121
+ }
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Factory function to create both providers with shared configuration
127
+ */
128
+ const createContentAPIProviders = config => {
129
+ const fetchProvider = new ConfluenceADFFetchProvider(config);
130
+ const writeProvider = new ConfluenceADFWriteProvider(config);
131
+ return {
132
+ fetchProvider,
133
+ writeProvider
134
+ };
135
+ };
136
+
137
+ /**
138
+ * Convenience function to create providers with default content property key
139
+ */
140
+ export const createContentAPIProvidersWithDefaultKey = cloudId => {
141
+ return createContentAPIProviders({
142
+ cloudId,
143
+ contentPropertyKey: 'editor-synced-block'
144
+ });
145
+ };
146
+ export const useMemoizedContentAPIProviders = cloudId => {
147
+ return useMemo(() => {
148
+ return createContentAPIProvidersWithDefaultKey(cloudId);
149
+ }, [cloudId]);
150
+ };
@@ -0,0 +1,32 @@
1
+ export const getConfluencePageAri = (pageId, cloudId) => `ari:cloud:confluence:${cloudId}:page/${pageId}`;
2
+ export const getPageIdFromAri = ari => {
3
+ // eslint-disable-next-line require-unicode-regexp
4
+ const match = ari.match(/ari:cloud:confluence:[^:]+:page\/(\d+)/);
5
+ if (match !== null && match !== void 0 && match[1]) {
6
+ return match[1];
7
+ }
8
+ throw new Error(`Invalid page ARI: ${ari}`);
9
+ };
10
+
11
+ /**
12
+ *
13
+ * @param ari ari:cloud:confluence:<cloudId>:page/<pageId>/<localId>
14
+ * @returns
15
+ */
16
+ export const getLocalIdFromAri = ari => {
17
+ // eslint-disable-next-line require-unicode-regexp
18
+ const match = ari.match(/ari:cloud:confluence:[^:]+:page\/\d+\/([a-zA-Z0-9-]+)/);
19
+ if (match !== null && match !== void 0 && match[1]) {
20
+ return match[1];
21
+ }
22
+ throw new Error(`Invalid page ARI: ${ari}`);
23
+ };
24
+ export const getContentPropertyAri = (contentPropertyId, cloudId) => `ari:cloud:confluence:${cloudId}:content/${contentPropertyId}`;
25
+ export const getContentPropertyIdFromAri = ari => {
26
+ // eslint-disable-next-line require-unicode-regexp
27
+ const match = ari.match(/ari:cloud:confluence:[^:]+:content\/([^/]+)/);
28
+ if (match) {
29
+ return match[1];
30
+ }
31
+ throw new Error(`Invalid content property ARI: ${ari}`);
32
+ };