@atlaskit/editor-synced-block-provider 12.1.4 → 12.2.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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-synced-block-provider
2
2
 
3
+ ## 12.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`6cf6b735bf90a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6cf6b735bf90a) -
8
+ Add migrateSyncBlockIds to the utils/resourceId entry point. Rewrites syncBlock and
9
+ bodiedSyncBlock identifiers so a document stays internally consistent after cloud-to-cloud
10
+ migration, preserving block UUIDs by default and supporting optional UUID regeneration.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
16
+ ## 12.1.5
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
3
22
  ## 12.1.4
4
23
 
5
24
  ### Patch Changes
@@ -9,10 +9,17 @@ Object.defineProperty(exports, "createResourceIdForReference", {
9
9
  return _resourceId.createResourceIdForReference;
10
10
  }
11
11
  });
12
+ Object.defineProperty(exports, "migrateSyncBlockIds", {
13
+ enumerable: true,
14
+ get: function get() {
15
+ return _migrateSyncBlockIds.migrateSyncBlockIds;
16
+ }
17
+ });
12
18
  Object.defineProperty(exports, "parseResourceId", {
13
19
  enumerable: true,
14
20
  get: function get() {
15
21
  return _resourceId.parseResourceId;
16
22
  }
17
23
  });
18
- var _resourceId = require("../utils/resourceId");
24
+ var _resourceId = require("../utils/resourceId");
25
+ var _migrateSyncBlockIds = require("../utils/migrateSyncBlockIds");
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.migrateSyncBlockIds = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _traverse = require("@atlaskit/adf-utils/traverse");
10
+ var _resourceId = require("./resourceId");
11
+ 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; }
12
+ 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; }
13
+ /*
14
+ * Rewrites `syncBlock`/`bodiedSyncBlock` identifiers so a document stays internally
15
+ * consistent after cloud-to-cloud migration or a copy to different host content.
16
+ *
17
+ * Source nodes (`bodiedSyncBlock`) carry a bare UUID as both `resourceId` and `localId` -
18
+ * the host content ID never appears in the ADF, only in the block ARI, which Block
19
+ * Service derives from the cloud ID and parent content ID at runtime. Reference nodes
20
+ * (`syncBlock`) carry `{product}/{contentId}/{sourceUuid}`. So preserving the UUID (the
21
+ * default) needs no edit on source nodes, and only remaps the `contentId` segment on
22
+ * references - which also keeps re-runs idempotent.
23
+ */
24
+
25
+ /** Bails to `undefined` on anything that isn't a syncBlock/bodiedSyncBlock attrs shape. */
26
+ var parseSyncBlockAttrs = function parseSyncBlockAttrs(attrs) {
27
+ var resourceId = attrs.resourceId,
28
+ localId = attrs.localId;
29
+ if (typeof resourceId !== 'string' || resourceId === '') {
30
+ return undefined;
31
+ }
32
+ return {
33
+ resourceId: resourceId,
34
+ localId: typeof localId === 'string' ? localId : undefined
35
+ };
36
+ };
37
+ var migrateAttrs = function migrateAttrs(attrs, resolvers, unresolvedContentIds) {
38
+ var _resolvers$resolveUui3;
39
+ var resourceId = attrs.resourceId,
40
+ localId = attrs.localId;
41
+ var parsed = (0, _resourceId.parseResourceId)(resourceId);
42
+ if (parsed) {
43
+ var _resolvers$resolveUui, _resolvers$resolveUui2;
44
+ var mappedContentId = resolvers.resolveContentId(parsed.contentId, parsed.product);
45
+ if (mappedContentId === undefined) {
46
+ unresolvedContentIds.add(parsed.contentId);
47
+ }
48
+ var nextContentId = mappedContentId !== null && mappedContentId !== void 0 ? mappedContentId : parsed.contentId;
49
+ var _nextUuid = (_resolvers$resolveUui = (_resolvers$resolveUui2 = resolvers.resolveUuid) === null || _resolvers$resolveUui2 === void 0 ? void 0 : _resolvers$resolveUui2.call(resolvers, parsed.uuid)) !== null && _resolvers$resolveUui !== void 0 ? _resolvers$resolveUui : parsed.uuid;
50
+ if (nextContentId === parsed.contentId && _nextUuid === parsed.uuid) {
51
+ return undefined;
52
+ }
53
+ return {
54
+ resourceId: (0, _resourceId.createResourceIdForReference)(parsed.product, nextContentId, _nextUuid),
55
+ // Only mirror the UUID when this node's localId tracked it in the first place.
56
+ localId: localId === parsed.uuid ? _nextUuid : localId
57
+ };
58
+ }
59
+
60
+ // bare-UUID source node: no content ID segment to remap
61
+ var nextUuid = (_resolvers$resolveUui3 = resolvers.resolveUuid) === null || _resolvers$resolveUui3 === void 0 ? void 0 : _resolvers$resolveUui3.call(resolvers, resourceId);
62
+ if (nextUuid === undefined || nextUuid === resourceId) {
63
+ return undefined;
64
+ }
65
+ return {
66
+ resourceId: nextUuid,
67
+ localId: localId === resourceId ? nextUuid : localId
68
+ };
69
+ };
70
+
71
+ /** `isTransformed` is `false` when no node changed, so callers can skip persisting the result. */
72
+ var migrateSyncBlockIds = exports.migrateSyncBlockIds = function migrateSyncBlockIds(adf, resolvers) {
73
+ var unresolvedContentIds = new Set();
74
+ var isTransformed = false;
75
+ var migrateNode = function migrateNode(node) {
76
+ var syncBlockAttrs = node.attrs && parseSyncBlockAttrs(node.attrs);
77
+ if (!syncBlockAttrs) {
78
+ return undefined;
79
+ }
80
+ var nextAttrs = migrateAttrs(syncBlockAttrs, resolvers, unresolvedContentIds);
81
+ if (!nextAttrs) {
82
+ return undefined;
83
+ }
84
+ isTransformed = true;
85
+ return _objectSpread(_objectSpread({}, node), {}, {
86
+ attrs: _objectSpread(_objectSpread({}, node.attrs), nextAttrs)
87
+ });
88
+ };
89
+ var transformedAdf = (0, _traverse.traverse)(adf, {
90
+ syncBlock: migrateNode,
91
+ bodiedSyncBlock: migrateNode
92
+ });
93
+
94
+ // traverse() only returns false when a visitor deletes the root node; migrateNode never does.
95
+ if (transformedAdf === false) {
96
+ throw new Error('migrateSyncBlockIds: traverse unexpectedly removed the root node');
97
+ }
98
+ return {
99
+ transformedAdf: transformedAdf,
100
+ isTransformed: isTransformed,
101
+ unresolvedContentIds: Array.from(unresolvedContentIds)
102
+ };
103
+ };
@@ -1,2 +1,3 @@
1
1
  /* eslint-disable @atlaskit/editor/no-re-export */
2
- export { parseResourceId, createResourceIdForReference } from '../utils/resourceId';
2
+ export { parseResourceId, createResourceIdForReference } from '../utils/resourceId';
3
+ export { migrateSyncBlockIds } from '../utils/migrateSyncBlockIds';
@@ -0,0 +1,102 @@
1
+ import { traverse } from '@atlaskit/adf-utils/traverse';
2
+ import { createResourceIdForReference, parseResourceId } from './resourceId';
3
+
4
+ /*
5
+ * Rewrites `syncBlock`/`bodiedSyncBlock` identifiers so a document stays internally
6
+ * consistent after cloud-to-cloud migration or a copy to different host content.
7
+ *
8
+ * Source nodes (`bodiedSyncBlock`) carry a bare UUID as both `resourceId` and `localId` -
9
+ * the host content ID never appears in the ADF, only in the block ARI, which Block
10
+ * Service derives from the cloud ID and parent content ID at runtime. Reference nodes
11
+ * (`syncBlock`) carry `{product}/{contentId}/{sourceUuid}`. So preserving the UUID (the
12
+ * default) needs no edit on source nodes, and only remaps the `contentId` segment on
13
+ * references - which also keeps re-runs idempotent.
14
+ */
15
+
16
+ /** Bails to `undefined` on anything that isn't a syncBlock/bodiedSyncBlock attrs shape. */
17
+ const parseSyncBlockAttrs = attrs => {
18
+ const {
19
+ resourceId,
20
+ localId
21
+ } = attrs;
22
+ if (typeof resourceId !== 'string' || resourceId === '') {
23
+ return undefined;
24
+ }
25
+ return {
26
+ resourceId,
27
+ localId: typeof localId === 'string' ? localId : undefined
28
+ };
29
+ };
30
+ const migrateAttrs = (attrs, resolvers, unresolvedContentIds) => {
31
+ var _resolvers$resolveUui3;
32
+ const {
33
+ resourceId,
34
+ localId
35
+ } = attrs;
36
+ const parsed = parseResourceId(resourceId);
37
+ if (parsed) {
38
+ var _resolvers$resolveUui, _resolvers$resolveUui2;
39
+ const mappedContentId = resolvers.resolveContentId(parsed.contentId, parsed.product);
40
+ if (mappedContentId === undefined) {
41
+ unresolvedContentIds.add(parsed.contentId);
42
+ }
43
+ const nextContentId = mappedContentId !== null && mappedContentId !== void 0 ? mappedContentId : parsed.contentId;
44
+ const nextUuid = (_resolvers$resolveUui = (_resolvers$resolveUui2 = resolvers.resolveUuid) === null || _resolvers$resolveUui2 === void 0 ? void 0 : _resolvers$resolveUui2.call(resolvers, parsed.uuid)) !== null && _resolvers$resolveUui !== void 0 ? _resolvers$resolveUui : parsed.uuid;
45
+ if (nextContentId === parsed.contentId && nextUuid === parsed.uuid) {
46
+ return undefined;
47
+ }
48
+ return {
49
+ resourceId: createResourceIdForReference(parsed.product, nextContentId, nextUuid),
50
+ // Only mirror the UUID when this node's localId tracked it in the first place.
51
+ localId: localId === parsed.uuid ? nextUuid : localId
52
+ };
53
+ }
54
+
55
+ // bare-UUID source node: no content ID segment to remap
56
+ const nextUuid = (_resolvers$resolveUui3 = resolvers.resolveUuid) === null || _resolvers$resolveUui3 === void 0 ? void 0 : _resolvers$resolveUui3.call(resolvers, resourceId);
57
+ if (nextUuid === undefined || nextUuid === resourceId) {
58
+ return undefined;
59
+ }
60
+ return {
61
+ resourceId: nextUuid,
62
+ localId: localId === resourceId ? nextUuid : localId
63
+ };
64
+ };
65
+
66
+ /** `isTransformed` is `false` when no node changed, so callers can skip persisting the result. */
67
+ export const migrateSyncBlockIds = (adf, resolvers) => {
68
+ const unresolvedContentIds = new Set();
69
+ let isTransformed = false;
70
+ const migrateNode = node => {
71
+ const syncBlockAttrs = node.attrs && parseSyncBlockAttrs(node.attrs);
72
+ if (!syncBlockAttrs) {
73
+ return undefined;
74
+ }
75
+ const nextAttrs = migrateAttrs(syncBlockAttrs, resolvers, unresolvedContentIds);
76
+ if (!nextAttrs) {
77
+ return undefined;
78
+ }
79
+ isTransformed = true;
80
+ return {
81
+ ...node,
82
+ attrs: {
83
+ ...node.attrs,
84
+ ...nextAttrs
85
+ }
86
+ };
87
+ };
88
+ const transformedAdf = traverse(adf, {
89
+ syncBlock: migrateNode,
90
+ bodiedSyncBlock: migrateNode
91
+ });
92
+
93
+ // traverse() only returns false when a visitor deletes the root node; migrateNode never does.
94
+ if (transformedAdf === false) {
95
+ throw new Error('migrateSyncBlockIds: traverse unexpectedly removed the root node');
96
+ }
97
+ return {
98
+ transformedAdf,
99
+ isTransformed,
100
+ unresolvedContentIds: Array.from(unresolvedContentIds)
101
+ };
102
+ };
@@ -1,2 +1,3 @@
1
1
  /* eslint-disable @atlaskit/editor/no-re-export */
2
- export { parseResourceId, createResourceIdForReference } from '../utils/resourceId';
2
+ export { parseResourceId, createResourceIdForReference } from '../utils/resourceId';
3
+ export { migrateSyncBlockIds } from '../utils/migrateSyncBlockIds';
@@ -0,0 +1,97 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ 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; }
3
+ 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; }
4
+ import { traverse } from '@atlaskit/adf-utils/traverse';
5
+ import { createResourceIdForReference, parseResourceId } from './resourceId';
6
+
7
+ /*
8
+ * Rewrites `syncBlock`/`bodiedSyncBlock` identifiers so a document stays internally
9
+ * consistent after cloud-to-cloud migration or a copy to different host content.
10
+ *
11
+ * Source nodes (`bodiedSyncBlock`) carry a bare UUID as both `resourceId` and `localId` -
12
+ * the host content ID never appears in the ADF, only in the block ARI, which Block
13
+ * Service derives from the cloud ID and parent content ID at runtime. Reference nodes
14
+ * (`syncBlock`) carry `{product}/{contentId}/{sourceUuid}`. So preserving the UUID (the
15
+ * default) needs no edit on source nodes, and only remaps the `contentId` segment on
16
+ * references - which also keeps re-runs idempotent.
17
+ */
18
+
19
+ /** Bails to `undefined` on anything that isn't a syncBlock/bodiedSyncBlock attrs shape. */
20
+ var parseSyncBlockAttrs = function parseSyncBlockAttrs(attrs) {
21
+ var resourceId = attrs.resourceId,
22
+ localId = attrs.localId;
23
+ if (typeof resourceId !== 'string' || resourceId === '') {
24
+ return undefined;
25
+ }
26
+ return {
27
+ resourceId: resourceId,
28
+ localId: typeof localId === 'string' ? localId : undefined
29
+ };
30
+ };
31
+ var migrateAttrs = function migrateAttrs(attrs, resolvers, unresolvedContentIds) {
32
+ var _resolvers$resolveUui3;
33
+ var resourceId = attrs.resourceId,
34
+ localId = attrs.localId;
35
+ var parsed = parseResourceId(resourceId);
36
+ if (parsed) {
37
+ var _resolvers$resolveUui, _resolvers$resolveUui2;
38
+ var mappedContentId = resolvers.resolveContentId(parsed.contentId, parsed.product);
39
+ if (mappedContentId === undefined) {
40
+ unresolvedContentIds.add(parsed.contentId);
41
+ }
42
+ var nextContentId = mappedContentId !== null && mappedContentId !== void 0 ? mappedContentId : parsed.contentId;
43
+ var _nextUuid = (_resolvers$resolveUui = (_resolvers$resolveUui2 = resolvers.resolveUuid) === null || _resolvers$resolveUui2 === void 0 ? void 0 : _resolvers$resolveUui2.call(resolvers, parsed.uuid)) !== null && _resolvers$resolveUui !== void 0 ? _resolvers$resolveUui : parsed.uuid;
44
+ if (nextContentId === parsed.contentId && _nextUuid === parsed.uuid) {
45
+ return undefined;
46
+ }
47
+ return {
48
+ resourceId: createResourceIdForReference(parsed.product, nextContentId, _nextUuid),
49
+ // Only mirror the UUID when this node's localId tracked it in the first place.
50
+ localId: localId === parsed.uuid ? _nextUuid : localId
51
+ };
52
+ }
53
+
54
+ // bare-UUID source node: no content ID segment to remap
55
+ var nextUuid = (_resolvers$resolveUui3 = resolvers.resolveUuid) === null || _resolvers$resolveUui3 === void 0 ? void 0 : _resolvers$resolveUui3.call(resolvers, resourceId);
56
+ if (nextUuid === undefined || nextUuid === resourceId) {
57
+ return undefined;
58
+ }
59
+ return {
60
+ resourceId: nextUuid,
61
+ localId: localId === resourceId ? nextUuid : localId
62
+ };
63
+ };
64
+
65
+ /** `isTransformed` is `false` when no node changed, so callers can skip persisting the result. */
66
+ export var migrateSyncBlockIds = function migrateSyncBlockIds(adf, resolvers) {
67
+ var unresolvedContentIds = new Set();
68
+ var isTransformed = false;
69
+ var migrateNode = function migrateNode(node) {
70
+ var syncBlockAttrs = node.attrs && parseSyncBlockAttrs(node.attrs);
71
+ if (!syncBlockAttrs) {
72
+ return undefined;
73
+ }
74
+ var nextAttrs = migrateAttrs(syncBlockAttrs, resolvers, unresolvedContentIds);
75
+ if (!nextAttrs) {
76
+ return undefined;
77
+ }
78
+ isTransformed = true;
79
+ return _objectSpread(_objectSpread({}, node), {}, {
80
+ attrs: _objectSpread(_objectSpread({}, node.attrs), nextAttrs)
81
+ });
82
+ };
83
+ var transformedAdf = traverse(adf, {
84
+ syncBlock: migrateNode,
85
+ bodiedSyncBlock: migrateNode
86
+ });
87
+
88
+ // traverse() only returns false when a visitor deletes the root node; migrateNode never does.
89
+ if (transformedAdf === false) {
90
+ throw new Error('migrateSyncBlockIds: traverse unexpectedly removed the root node');
91
+ }
92
+ return {
93
+ transformedAdf: transformedAdf,
94
+ isTransformed: isTransformed,
95
+ unresolvedContentIds: Array.from(unresolvedContentIds)
96
+ };
97
+ };
@@ -1 +1,3 @@
1
1
  export { parseResourceId, createResourceIdForReference } from '../utils/resourceId';
2
+ export { migrateSyncBlockIds } from '../utils/migrateSyncBlockIds';
3
+ export type { SyncBlockIdResolvers, SyncBlockIdMigrationResult, } from '../utils/migrateSyncBlockIds';
@@ -0,0 +1,16 @@
1
+ import type { ADFEntity } from '@atlaskit/adf-utils/types';
2
+ import type { SyncBlockProduct } from '../common/types';
3
+ export type SyncBlockIdResolvers = {
4
+ /** Return `undefined` for an unknown content ID - it's left untouched and reported via `unresolvedContentIds`. */
5
+ resolveContentId: (contentId: string, product: SyncBlockProduct) => string | undefined;
6
+ /** Omit to preserve UUIDs (recommended). Return `undefined` to preserve one UUID while remapping others. */
7
+ resolveUuid?: (uuid: string) => string | undefined;
8
+ };
9
+ export type SyncBlockIdMigrationResult = {
10
+ isTransformed: boolean;
11
+ transformedAdf: ADFEntity;
12
+ /** Reference content IDs with no mapping - a signal to investigate, not necessarily a failure. */
13
+ unresolvedContentIds: string[];
14
+ };
15
+ /** `isTransformed` is `false` when no node changed, so callers can skip persisting the result. */
16
+ export declare const migrateSyncBlockIds: (adf: ADFEntity, resolvers: SyncBlockIdResolvers) => SyncBlockIdMigrationResult;
package/package.json CHANGED
@@ -23,7 +23,7 @@
23
23
  "@atlaskit/node-data-provider": "^17.0.0",
24
24
  "@atlaskit/platform-feature-experiments": "^0.3.0",
25
25
  "@atlaskit/platform-feature-flags": "^2.2.0",
26
- "@atlaskit/tmp-editor-statsig": "^172.0.0",
26
+ "@atlaskit/tmp-editor-statsig": "^174.0.0",
27
27
  "@babel/runtime": "^7.0.0",
28
28
  "@compiled/react": "^1.0.2",
29
29
  "bind-event-listener": "^3.0.0",
@@ -33,7 +33,7 @@
33
33
  "uuid": "^3.1.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@atlaskit/editor-common": "^120.11.0",
36
+ "@atlaskit/editor-common": "^120.12.0",
37
37
  "react": "^18.2.0 || ^19.2.0"
38
38
  },
39
39
  "devDependencies": {
@@ -42,7 +42,7 @@
42
42
  "react-dom": "^19.2.0"
43
43
  },
44
44
  "name": "@atlaskit/editor-synced-block-provider",
45
- "version": "12.1.4",
45
+ "version": "12.2.0",
46
46
  "description": "Synced Block Provider for @atlaskit/editor-plugin-synced-block",
47
47
  "author": "Atlassian Pty Ltd",
48
48
  "license": "Apache-2.0",