@atlaskit/editor-synced-block-provider 4.1.1 → 4.1.3
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/AGENTS.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Synced Block Provider — Developer Agent Guide
|
|
2
|
+
|
|
3
|
+
> **Package**: `@atlaskit/editor-synced-block-provider`
|
|
4
|
+
> **Purpose**: Data layer for synced blocks — store managers, block service API client, ARI generation, permissions, media tokens.
|
|
5
|
+
> **Full Knowledge Base**: [Synced Blocks — Comprehensive Knowledge Base](https://hello.atlassian.net/wiki/spaces/egcuc/pages/6679548384)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Context
|
|
10
|
+
|
|
11
|
+
This package manages the lifecycle and state of synced blocks for both source and reference nodes.
|
|
12
|
+
It provides the data fetching, caching, subscription, and persistence layer used by the editor plugin
|
|
13
|
+
and the renderer across Confluence and Jira.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Source Structure
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/
|
|
21
|
+
├── index.ts ← Barrel export
|
|
22
|
+
├── store-manager/
|
|
23
|
+
│ ├── syncBlockStoreManager.ts ← Parent coordinator for source + reference managers
|
|
24
|
+
│ ├── referenceSyncBlockStoreManager.ts ← Reference block lifecycle, cache, subscriptions, flush
|
|
25
|
+
│ └── sourceSyncBlockStoreManager.ts ← Source block create, update, delete, flush
|
|
26
|
+
├── clients/
|
|
27
|
+
│ ├── block-service/
|
|
28
|
+
│ │ ├── blockService.ts ← Block service API client (fetch, batch, CRUD)
|
|
29
|
+
│ │ └── ari.ts ← Block ARI generation/parsing
|
|
30
|
+
│ ├── confluence/
|
|
31
|
+
│ │ ├── ari.ts ← Confluence page ARI generation/parsing
|
|
32
|
+
│ │ └── fetchMediaToken.ts ← Media token fetching via GraphQL (MediaUploadTokenQuery)
|
|
33
|
+
│ └── jira/
|
|
34
|
+
│ └── ari.ts ← Jira work item ARI generation/parsing
|
|
35
|
+
├── providers/
|
|
36
|
+
│ └── block-service/
|
|
37
|
+
│ └── blockServiceAPI.ts ← Provider factory and API helpers
|
|
38
|
+
└── types/ ← Shared types
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Key Concepts
|
|
44
|
+
|
|
45
|
+
### Store Manager Hierarchy
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
SyncBlockStoreManager (parent coordinator)
|
|
49
|
+
├── SourceSyncBlockStoreManager
|
|
50
|
+
│ ├── create(content) → Block Service API → returns resourceId
|
|
51
|
+
│ ├── update(resourceId, content) → debounced 3s write
|
|
52
|
+
│ ├── delete(resourceId) → soft delete
|
|
53
|
+
│ └── flush() → persist all pending changes on page save
|
|
54
|
+
└── ReferenceSyncBlockStoreManager
|
|
55
|
+
├── fetchSyncBlocksData(nodes) → batch fetch with deduplication
|
|
56
|
+
├── subscribeToSyncBlock(resourceId, localId, callback) → AGG WebSocket
|
|
57
|
+
├── fetchSyncBlockSourceInfo(resourceId) → title, URL metadata
|
|
58
|
+
├── getFromCache(resourceId) → retrieve cached data
|
|
59
|
+
├── flush() → save reference changes to backend
|
|
60
|
+
└── destroy() → cleanup subscriptions and batchers
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### ARI Formats & Utilities
|
|
64
|
+
|
|
65
|
+
| Function | ARI Pattern | Example |
|
|
66
|
+
|----------|-------------|---------|
|
|
67
|
+
| `generateBlockAri({cloudId, parentId, product, resourceId})` | Source block ARI | `ari:cloud:block::{cloudId}/confluence-page:{pageId}/{localId}` |
|
|
68
|
+
| `generateBlockAriFromReference({cloudId, resourceId})` | Reference block ARI | — |
|
|
69
|
+
| `getConfluencePageAri({pageId, cloudId, pageType})` | Confluence page | `ari:cloud:confluence::{cloudId}:page/{pageId}` |
|
|
70
|
+
| `getJiraWorkItemAri({cloudId, workItemId})` | Jira issue | `ari:cloud:jira::{cloudId}:work-item/{issueId}` |
|
|
71
|
+
| `getJiraWorkItemIdFromAri(ari)` | Extract issue ID from ARI | — |
|
|
72
|
+
|
|
73
|
+
### Block Service API
|
|
74
|
+
|
|
75
|
+
The client in `clients/block-service/blockService.ts` communicates via GraphQL at `/gateway/api/graphql`:
|
|
76
|
+
- **Fetch**: Single or batch block content retrieval
|
|
77
|
+
- **Create**: Register new source block with content
|
|
78
|
+
- **Update**: Push content changes (debounced 3s)
|
|
79
|
+
- **Delete**: Soft delete a source block
|
|
80
|
+
- **Source Info**: Fetch metadata (source page title, URL)
|
|
81
|
+
- **References Info**: Fetch list of locations referencing a block
|
|
82
|
+
|
|
83
|
+
### Media Token Fetching
|
|
84
|
+
|
|
85
|
+
`fetchMediaToken(contentId)` → GraphQL `MediaUploadTokenQuery` → returns `{token, config: {clientId, fileStoreUrl}, collectionId}`
|
|
86
|
+
|
|
87
|
+
Used when synced blocks contain media (images, files) — the reference needs a valid token to render media from the source page.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Common Tasks
|
|
92
|
+
|
|
93
|
+
### Adding a new API method
|
|
94
|
+
1. Add the GraphQL query/mutation in `clients/block-service/blockService.ts`
|
|
95
|
+
2. Expose it through the appropriate store manager
|
|
96
|
+
3. Export from `src/index.ts` if needed by product integrations
|
|
97
|
+
4. Add tests in `editor-synced-block-provider-tests`
|
|
98
|
+
|
|
99
|
+
### Debugging data issues
|
|
100
|
+
1. Check `ReferenceSyncBlockStoreManager` cache state
|
|
101
|
+
2. Verify ARI format matches expected pattern for the product
|
|
102
|
+
3. Check Block Service API responses in network tab (look for `/gateway/api/graphql`)
|
|
103
|
+
4. Use analytics: [HOW-TO: Debug errors](https://hello.atlassian.net/wiki/spaces/egcuc/pages/6342760320)
|
|
104
|
+
|
|
105
|
+
### Adding support for a new product
|
|
106
|
+
1. Create ARI utilities in `clients/{product}/ari.ts`
|
|
107
|
+
2. Ensure `generateBlockAri` supports the new product type
|
|
108
|
+
3. Add media token fetching if the product has media content
|
|
109
|
+
4. Update store managers if the product has unique lifecycle requirements
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Related Packages
|
|
114
|
+
- **Plugin**: `platform/packages/editor/editor-plugin-synced-block/` — uses store managers
|
|
115
|
+
- **Renderer**: `platform/packages/editor/editor-synced-block-renderer/` — uses fetch provider
|
|
116
|
+
- **Confluence**: `confluence/next/packages/fabric-providers/src/SyncedBlockProvider.ts` — wraps this provider
|
|
117
|
+
- **Jira**: `jira/src/packages/issue/issue-view-synced-block-provider/` — wraps this provider with Relay
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/editor-synced-block-provider
|
|
2
2
|
|
|
3
|
+
## 4.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`3895f6d32cc49`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3895f6d32cc49) -
|
|
8
|
+
Set hasReceivedContentChange on successful sync block creation to ensure unsaved changes are
|
|
9
|
+
flushed
|
|
10
|
+
- Updated dependencies
|
|
11
|
+
|
|
12
|
+
## 4.1.2
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
|
|
3
18
|
## 4.1.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -12,6 +12,7 @@ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/creat
|
|
|
12
12
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
13
|
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
14
14
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
15
|
+
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
15
16
|
var _types = require("../common/types");
|
|
16
17
|
var _errorHandling = require("../utils/errorHandling");
|
|
17
18
|
var _experienceTracking = require("../utils/experienceTracking");
|
|
@@ -227,6 +228,13 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
|
|
|
227
228
|
if (onCompletion) {
|
|
228
229
|
this.creationCompletionCallbacks.delete(resourceId);
|
|
229
230
|
onCompletion(success);
|
|
231
|
+
if (success && (0, _experiments.editorExperiment)('platform_synced_block_patch_6', true, {
|
|
232
|
+
exposure: true
|
|
233
|
+
})) {
|
|
234
|
+
// If creation is successful, set hasReceivedContentChange to true
|
|
235
|
+
// to indicate that there are unsaved changes in the cache
|
|
236
|
+
this.hasReceivedContentChange = true;
|
|
237
|
+
}
|
|
230
238
|
} else {
|
|
231
239
|
var _this$fireAnalyticsEv3;
|
|
232
240
|
(_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 || _this$fireAnalyticsEv3.call(this, (0, _errorHandling.createErrorPayload)('creation complete callback missing', resourceId));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import isEqual from 'lodash/isEqual';
|
|
3
3
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
4
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
4
5
|
import { SyncBlockError } from '../common/types';
|
|
5
6
|
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload } from '../utils/errorHandling';
|
|
6
7
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
@@ -168,6 +169,13 @@ export class SourceSyncBlockStoreManager {
|
|
|
168
169
|
if (onCompletion) {
|
|
169
170
|
this.creationCompletionCallbacks.delete(resourceId);
|
|
170
171
|
onCompletion(success);
|
|
172
|
+
if (success && editorExperiment('platform_synced_block_patch_6', true, {
|
|
173
|
+
exposure: true
|
|
174
|
+
})) {
|
|
175
|
+
// If creation is successful, set hasReceivedContentChange to true
|
|
176
|
+
// to indicate that there are unsaved changes in the cache
|
|
177
|
+
this.hasReceivedContentChange = true;
|
|
178
|
+
}
|
|
171
179
|
} else {
|
|
172
180
|
var _this$fireAnalyticsEv5;
|
|
173
181
|
(_this$fireAnalyticsEv5 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv5 === void 0 ? void 0 : _this$fireAnalyticsEv5.call(this, createErrorPayload('creation complete callback missing', resourceId));
|
|
@@ -7,6 +7,7 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
|
|
|
7
7
|
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) { _defineProperty(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; }
|
|
8
8
|
import isEqual from 'lodash/isEqual';
|
|
9
9
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
10
|
+
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
10
11
|
import { SyncBlockError } from '../common/types';
|
|
11
12
|
import { updateErrorPayload, createErrorPayload, deleteErrorPayload, updateCacheErrorPayload, getSourceInfoErrorPayload, updateSuccessPayload, createSuccessPayload, deleteSuccessPayload, fetchReferencesErrorPayload } from '../utils/errorHandling';
|
|
12
13
|
import { getCreateSourceExperience, getDeleteSourceExperience, getSaveSourceExperience, getFetchSourceInfoExperience } from '../utils/experienceTracking';
|
|
@@ -220,6 +221,13 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
|
|
|
220
221
|
if (onCompletion) {
|
|
221
222
|
this.creationCompletionCallbacks.delete(resourceId);
|
|
222
223
|
onCompletion(success);
|
|
224
|
+
if (success && editorExperiment('platform_synced_block_patch_6', true, {
|
|
225
|
+
exposure: true
|
|
226
|
+
})) {
|
|
227
|
+
// If creation is successful, set hasReceivedContentChange to true
|
|
228
|
+
// to indicate that there are unsaved changes in the cache
|
|
229
|
+
this.hasReceivedContentChange = true;
|
|
230
|
+
}
|
|
223
231
|
} else {
|
|
224
232
|
var _this$fireAnalyticsEv3;
|
|
225
233
|
(_this$fireAnalyticsEv3 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv3 === void 0 || _this$fireAnalyticsEv3.call(this, createErrorPayload('creation complete callback missing', resourceId));
|
package/package.json
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
30
30
|
"@atlaskit/node-data-provider": "^9.0.0",
|
|
31
31
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
32
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
32
|
+
"@atlaskit/tmp-editor-statsig": "^42.0.0",
|
|
33
33
|
"@babel/runtime": "^7.0.0",
|
|
34
34
|
"@compiled/react": "^0.20.0",
|
|
35
35
|
"graphql-ws": "^5.14.2",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"uuid": "^3.1.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@atlaskit/editor-common": "^112.
|
|
41
|
+
"@atlaskit/editor-common": "^112.7.0",
|
|
42
42
|
"react": "^18.2.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"name": "@atlaskit/editor-synced-block-provider",
|
|
84
|
-
"version": "4.1.
|
|
84
|
+
"version": "4.1.3",
|
|
85
85
|
"description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
|
|
86
86
|
"author": "Atlassian Pty Ltd",
|
|
87
87
|
"license": "Apache-2.0",
|