@atlaskit/editor-synced-block-provider 4.3.2 → 4.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # Synced Block Provider — Developer Agent Guide
2
2
 
3
- > **Package**: `@atlaskit/editor-synced-block-provider` **Purpose**: Data layer for synced blocks —
4
- > store managers, block service API client, ARI generation, permissions, media tokens. **Full
5
- > Knowledge Base**:
6
- > [Synced Blocks — Comprehensive Knowledge Base](https://hello.atlassian.net/wiki/spaces/egcuc/pages/6679548384)
3
+ > **Package**: `@atlaskit/editor-synced-block-provider`
4
+ >
5
+ > **For workflow guidance, debugging, and cross-package task guides, load the `synced-blocks` skill:**
6
+ > `get_skill(skill_name_or_path="platform/packages/editor/.rovodev/skills/synced-blocks/SKILL.md")`
7
7
 
8
8
  ---
9
9
 
@@ -19,29 +19,29 @@ plugin and the renderer across Confluence and Jira.
19
19
 
20
20
  ```
21
21
  src/
22
- ├── index.ts Barrel export
22
+ ├── index.ts # Barrel export
23
23
  ├── store-manager/
24
- │ ├── syncBlockStoreManager.ts Parent coordinator for source + reference managers
25
- │ ├── referenceSyncBlockStoreManager.ts Reference block lifecycle, cache, subscriptions, flush
26
- │ └── sourceSyncBlockStoreManager.ts Source block create, update, delete, flush
24
+ │ ├── syncBlockStoreManager.ts # Parent coordinator for source + reference managers
25
+ │ ├── referenceSyncBlockStoreManager.ts # Reference block lifecycle, cache, subscriptions, flush
26
+ │ └── sourceSyncBlockStoreManager.ts # Source block create, update, delete, flush
27
27
  ├── clients/
28
28
  │ ├── block-service/
29
- │ │ ├── blockService.ts Block service API client (fetch, batch, CRUD)
30
- │ │ └── ari.ts Block ARI generation/parsing
29
+ │ │ ├── blockService.ts # Block service API client (fetch, batch, CRUD)
30
+ │ │ └── ari.ts # Block ARI generation/parsing
31
31
  │ ├── confluence/
32
- │ │ ├── ari.ts Confluence page ARI generation/parsing
33
- │ │ └── fetchMediaToken.ts Media token fetching via GraphQL (MediaUploadTokenQuery)
32
+ │ │ ├── ari.ts # Confluence page ARI generation/parsing
33
+ │ │ └── fetchMediaToken.ts # Media token fetching via GraphQL (MediaUploadTokenQuery)
34
34
  │ └── jira/
35
- │ └── ari.ts Jira work item ARI generation/parsing
35
+ │ └── ari.ts # Jira work item ARI generation/parsing
36
36
  ├── providers/
37
37
  │ └── block-service/
38
- │ └── blockServiceAPI.ts Provider factory and API helpers
39
- └── types/ Shared types
38
+ │ └── blockServiceAPI.ts # Provider factory and API helpers
39
+ └── types/ # Shared types
40
40
  ```
41
41
 
42
42
  ---
43
43
 
44
- ## Key Concepts
44
+ ## Key Exports and Types
45
45
 
46
46
  ### Store Manager Hierarchy
47
47
 
@@ -49,81 +49,41 @@ src/
49
49
  SyncBlockStoreManager (parent coordinator)
50
50
  ├── SourceSyncBlockStoreManager
51
51
  │ ├── create(content) → Block Service API → returns resourceId
52
- │ ├── update(resourceId, content) → debounced 3s write
53
- │ ├── delete(resourceId) → soft delete
54
- └── flush() → persist all pending changes on page save
52
+ │ ├── updateSyncBlockData(node) → marks isDirty, caches content
53
+ │ ├── flush() → persist all dirty changes to backend
54
+ ├── hasUnsavedChanges() → checks isDirty + hasReceivedContentChange
55
+ │ └── delete(resourceId) → soft delete with confirmation
55
56
  └── ReferenceSyncBlockStoreManager
56
57
  ├── fetchSyncBlocksData(nodes) → batch fetch with deduplication
57
- ├── subscribeToSyncBlock(resourceId, localId, callback) → AGG WebSocket
58
+ ├── subscribeToSyncBlock(resourceId, callback) → AGG WebSocket
58
59
  ├── fetchSyncBlockSourceInfo(resourceId) → title, URL metadata
59
- ├── getFromCache(resourceId) → retrieve cached data
60
- ├── flush() → save reference changes to backend
61
60
  └── destroy() → cleanup subscriptions and batchers
62
61
  ```
63
62
 
64
- ### ARI Formats & Utilities
63
+ ### ARI Utilities
65
64
 
66
- | Function | ARI Pattern | Example |
67
- | ------------------------------------------------------------ | ------------------------- | --------------------------------------------------------------- |
68
- | `generateBlockAri({cloudId, parentId, product, resourceId})` | Source block ARI | `ari:cloud:block::{cloudId}/confluence-page:{pageId}/{localId}` |
69
- | `generateBlockAriFromReference({cloudId, resourceId})` | Reference block ARI | — |
70
- | `getConfluencePageAri({pageId, cloudId, pageType})` | Confluence page | `ari:cloud:confluence::{cloudId}:page/{pageId}` |
71
- | `getJiraWorkItemAri({cloudId, workItemId})` | Jira issue | `ari:cloud:jira::{cloudId}:work-item/{issueId}` |
72
- | `getJiraWorkItemIdFromAri(ari)` | Extract issue ID from ARI | — |
65
+ | Function | Purpose |
66
+ | ------------------------------------------------------------ | -------------------------- |
67
+ | `generateBlockAri({cloudId, parentId, product, resourceId})` | Generate source block ARI |
68
+ | `generateBlockAriFromReference({cloudId, resourceId})` | Generate reference ARI |
69
+ | `getConfluencePageAri({pageId, cloudId, pageType})` | Confluence page ARI |
70
+ | `getJiraWorkItemAri({cloudId, workItemId})` | Jira issue ARI |
73
71
 
74
72
  ### Block Service API
75
73
 
76
74
  The client in `clients/block-service/blockService.ts` communicates via GraphQL at
77
- `/gateway/api/graphql`:
78
-
79
- - **Fetch**: Single or batch block content retrieval
80
- - **Create**: Register new source block with content
81
- - **Update**: Push content changes (debounced 3s)
82
- - **Delete**: Soft delete a source block
83
- - **Source Info**: Fetch metadata (source page title, URL)
84
- - **References Info**: Fetch list of locations referencing a block
75
+ `/gateway/api/graphql`: Fetch, Create, Update (debounced 3s), Delete, Source Info, References Info.
85
76
 
86
77
  ### Media Token Fetching
87
78
 
88
79
  `fetchMediaToken(contentId)` → GraphQL `MediaUploadTokenQuery` → returns
89
80
  `{token, config: {clientId, fileStoreUrl}, collectionId}`
90
81
 
91
- Used when synced blocks contain media (images, files) — the reference needs a valid token to render
92
- media from the source page.
93
-
94
- ---
95
-
96
- ## Common Tasks
97
-
98
- ### Adding a new API method
99
-
100
- 1. Add the GraphQL query/mutation in `clients/block-service/blockService.ts`
101
- 2. Expose it through the appropriate store manager
102
- 3. Export from `src/index.ts` if needed by product integrations
103
- 4. Add tests in `editor-synced-block-provider-tests`
104
-
105
- ### Debugging data issues
106
-
107
- 1. Check `ReferenceSyncBlockStoreManager` cache state
108
- 2. Verify ARI format matches expected pattern for the product
109
- 3. Check Block Service API responses in network tab (look for `/gateway/api/graphql`)
110
- 4. Use analytics:
111
- [HOW-TO: Debug errors](https://hello.atlassian.net/wiki/spaces/egcuc/pages/6342760320)
112
-
113
- ### Adding support for a new product
114
-
115
- 1. Create ARI utilities in `clients/{product}/ari.ts`
116
- 2. Ensure `generateBlockAri` supports the new product type
117
- 3. Add media token fetching if the product has media content
118
- 4. Update store managers if the product has unique lifecycle requirements
119
-
120
82
  ---
121
83
 
122
84
  ## Related Packages
123
85
 
124
- - **Plugin**: `platform/packages/editor/editor-plugin-synced-block/` — uses store managers
125
- - **Renderer**: `platform/packages/editor/editor-synced-block-renderer/` — uses fetch provider
126
- - **Confluence**: `confluence/next/packages/fabric-providers/src/SyncedBlockProvider.ts` — wraps
127
- this provider
128
- - **Jira**: `jira/src/packages/issue/issue-view-synced-block-provider/` — wraps this provider with
129
- Relay
86
+ - **Plugin**: `platform/packages/editor/editor-plugin-synced-block/`
87
+ - **Renderer**: `platform/packages/editor/editor-synced-block-renderer/`
88
+ - **Confluence**: `confluence/next/packages/fabric-providers/src/SyncedBlockProvider.ts`
89
+ - **Jira**: `jira/src/packages/issue/issue-view-synced-block-provider/`
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 4.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ef5f5792a6aaf`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ef5f5792a6aaf) -
8
+ Clean up platform_synced_block_patch_7 feature gate
9
+
10
+ ## 4.3.3
11
+
12
+ ### Patch Changes
13
+
14
+ - [`15deee785151b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/15deee785151b) -
15
+ EDITOR-6174 Pass node to createBodiedSyncBlockNode to cache content on creation, preventing false
16
+ unsaved changes on page refresh
17
+ - Updated dependencies
18
+
3
19
  ## 4.3.2
4
20
 
5
21
  ### Patch Changes
@@ -237,9 +237,7 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
237
237
  return _context.abrupt("return", false);
238
238
  case 31:
239
239
  _context.prev = 31;
240
- if ((0, _platformFeatureFlags.fg)('platform_synced_block_patch_7')) {
241
- (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 || _this$flushCompletion.call(this);
242
- }
240
+ (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 || _this$flushCompletion.call(this);
243
241
  return _context.finish(31);
244
242
  case 34:
245
243
  case "end":
@@ -339,10 +337,12 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
339
337
  /**
340
338
  * Create a bodiedSyncBlock node with empty content to backend
341
339
  * @param attrs attributes Ids of the node
340
+ * @param node the ProseMirror node to cache
341
+ * @param onCompletion callback invoked when creation completes
342
342
  */
343
343
  }, {
344
344
  key: "createBodiedSyncBlockNode",
345
- value: function createBodiedSyncBlockNode(attrs, onCompletion) {
345
+ value: function createBodiedSyncBlockNode(attrs, node, onCompletion) {
346
346
  var _this4 = this;
347
347
  if (this.viewMode === 'view' && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_8')) {
348
348
  return;
@@ -354,6 +354,10 @@ var SourceSyncBlockStoreManager = exports.SourceSyncBlockStoreManager = /*#__PUR
354
354
  if (!this.dataProvider) {
355
355
  throw new Error('Data provider not set');
356
356
  }
357
+ if ((0, _platformFeatureFlags.fg)('platform_synced_block_update_refactor')) {
358
+ // add the node to the cache
359
+ this.updateSyncBlockData(node);
360
+ }
357
361
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
358
362
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
359
363
  this.dataProvider.createNodeData({
@@ -190,10 +190,8 @@ export class SourceSyncBlockStoreManager {
190
190
  (_this$fireAnalyticsEv4 = this.fireAnalyticsEvent) === null || _this$fireAnalyticsEv4 === void 0 ? void 0 : _this$fireAnalyticsEv4.call(this, updateErrorPayload(error.message));
191
191
  return false;
192
192
  } finally {
193
- if (fg('platform_synced_block_patch_7')) {
194
- var _this$flushCompletion;
195
- (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 ? void 0 : _this$flushCompletion.call(this);
196
- }
193
+ var _this$flushCompletion;
194
+ (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 ? void 0 : _this$flushCompletion.call(this);
197
195
  }
198
196
  }
199
197
  hasUnsavedChanges() {
@@ -269,8 +267,10 @@ export class SourceSyncBlockStoreManager {
269
267
  /**
270
268
  * Create a bodiedSyncBlock node with empty content to backend
271
269
  * @param attrs attributes Ids of the node
270
+ * @param node the ProseMirror node to cache
271
+ * @param onCompletion callback invoked when creation completes
272
272
  */
273
- createBodiedSyncBlockNode(attrs, onCompletion) {
273
+ createBodiedSyncBlockNode(attrs, node, onCompletion) {
274
274
  if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
275
275
  return;
276
276
  }
@@ -283,6 +283,10 @@ export class SourceSyncBlockStoreManager {
283
283
  if (!this.dataProvider) {
284
284
  throw new Error('Data provider not set');
285
285
  }
286
+ if (fg('platform_synced_block_update_refactor')) {
287
+ // add the node to the cache
288
+ this.updateSyncBlockData(node);
289
+ }
286
290
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
287
291
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 ? void 0 : _this$createExperienc.start({});
288
292
  this.dataProvider.createNodeData({
@@ -230,9 +230,7 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
230
230
  return _context.abrupt("return", false);
231
231
  case 31:
232
232
  _context.prev = 31;
233
- if (fg('platform_synced_block_patch_7')) {
234
- (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 || _this$flushCompletion.call(this);
235
- }
233
+ (_this$flushCompletion = this.flushCompletionCallback) === null || _this$flushCompletion === void 0 || _this$flushCompletion.call(this);
236
234
  return _context.finish(31);
237
235
  case 34:
238
236
  case "end":
@@ -332,10 +330,12 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
332
330
  /**
333
331
  * Create a bodiedSyncBlock node with empty content to backend
334
332
  * @param attrs attributes Ids of the node
333
+ * @param node the ProseMirror node to cache
334
+ * @param onCompletion callback invoked when creation completes
335
335
  */
336
336
  }, {
337
337
  key: "createBodiedSyncBlockNode",
338
- value: function createBodiedSyncBlockNode(attrs, onCompletion) {
338
+ value: function createBodiedSyncBlockNode(attrs, node, onCompletion) {
339
339
  var _this4 = this;
340
340
  if (this.viewMode === 'view' && fg('platform_synced_block_patch_8')) {
341
341
  return;
@@ -347,6 +347,10 @@ export var SourceSyncBlockStoreManager = /*#__PURE__*/function () {
347
347
  if (!this.dataProvider) {
348
348
  throw new Error('Data provider not set');
349
349
  }
350
+ if (fg('platform_synced_block_update_refactor')) {
351
+ // add the node to the cache
352
+ this.updateSyncBlockData(node);
353
+ }
350
354
  this.creationCompletionCallbacks.set(resourceId, onCompletion);
351
355
  (_this$createExperienc = this.createExperience) === null || _this$createExperienc === void 0 || _this$createExperienc.start({});
352
356
  this.dataProvider.createNodeData({
@@ -57,8 +57,10 @@ export declare class SourceSyncBlockStoreManager {
57
57
  /**
58
58
  * Create a bodiedSyncBlock node with empty content to backend
59
59
  * @param attrs attributes Ids of the node
60
+ * @param node the ProseMirror node to cache
61
+ * @param onCompletion callback invoked when creation completes
60
62
  */
61
- createBodiedSyncBlockNode(attrs: SyncBlockAttrs, onCompletion: OnCompletion): void;
63
+ createBodiedSyncBlockNode(attrs: SyncBlockAttrs, node: PMNode, onCompletion: OnCompletion): void;
62
64
  private setPendingDeletion;
63
65
  private delete;
64
66
  isRetryingDeletion(): boolean;
@@ -57,8 +57,10 @@ export declare class SourceSyncBlockStoreManager {
57
57
  /**
58
58
  * Create a bodiedSyncBlock node with empty content to backend
59
59
  * @param attrs attributes Ids of the node
60
+ * @param node the ProseMirror node to cache
61
+ * @param onCompletion callback invoked when creation completes
60
62
  */
61
- createBodiedSyncBlockNode(attrs: SyncBlockAttrs, onCompletion: OnCompletion): void;
63
+ createBodiedSyncBlockNode(attrs: SyncBlockAttrs, node: PMNode, onCompletion: OnCompletion): void;
62
64
  private setPendingDeletion;
63
65
  private delete;
64
66
  isRetryingDeletion(): boolean;
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": "^54.1.0",
32
+ "@atlaskit/tmp-editor-statsig": "^54.5.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.16.0",
41
+ "@atlaskit/editor-common": "^112.18.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.3.2",
84
+ "version": "4.3.4",
85
85
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
86
86
  "author": "Atlassian Pty Ltd",
87
87
  "license": "Apache-2.0",
@@ -92,9 +92,6 @@
92
92
  "block_service_source_repair": {
93
93
  "type": "boolean"
94
94
  },
95
- "platform_synced_block_patch_7": {
96
- "type": "boolean"
97
- },
98
95
  "platform_synced_block_update_refactor": {
99
96
  "type": "boolean"
100
97
  },