@atlaskit/rovo-triggers 7.2.0 → 7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @atlaskit/rovo-triggers
2
2
 
3
+ ## 7.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`d2eb72209de52`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d2eb72209de52) -
8
+ Thread ADF through the insert-prompt path so Rovo nudges can seed the chat input with full rich
9
+ content (skill pill, links, inline cards) instead of plain text. Adds optional dynamicPromptAdf on
10
+ ChatAction and promptAdf on InsertPromptPayload, gated behind rovo_insert_prompt_adf.
11
+
12
+ ## 7.3.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`8740b2dde71e7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8740b2dde71e7) -
17
+ Add opt-in consume-once pubsub delivery so mounted Rovo Chat listeners can deduplicate action
18
+ events from a single publish.
19
+ - [`925c8d1b15bba`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/925c8d1b15bba) -
20
+ Add optional parameter to exported API: Optional `projectContext` when `chat-open` is emitted in
21
+ the conversation assistant package.
22
+
3
23
  ## 7.2.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -70,6 +70,42 @@ const YourComponent = () => {
70
70
  }
71
71
  ```
72
72
 
73
+ #### Consume once
74
+
75
+ Publishers can mark an event as `consumeOnce: true` when only one mounted instance of a
76
+ consumer should process that event. Subscribers opt into this behavior with a stable
77
+ `consumeOnceKey`; subscribers sharing the same key will process a `consumeOnce` event once total.
78
+ Subscribers without a `consumeOnceKey` continue to observe the event normally.
79
+
80
+ The delivery id used to deduplicate a publish is generated internally by `rovo-triggers` and is not
81
+ exposed in the event payload. This deduplicates one publish fanout across multiple mounted
82
+ subscribers; separate publish calls are treated as separate events.
83
+
84
+ ```ts
85
+ const YourComponent = () => {
86
+ useSubscribe({ topic: 'ai-mate', consumeOnceKey: 'your-consumer' }, ({ type, data }) => {
87
+ if(type === 'message-send') {
88
+ sendMessage(data)
89
+ }
90
+ });
91
+ }
92
+ ```
93
+
94
+ ```ts
95
+ const AnotherComponent = () => {
96
+ const publish = usePublish('ai-mate');
97
+
98
+ const handleClick = () => {
99
+ publish({
100
+ type: 'message-send',
101
+ source: 'my-source',
102
+ consumeOnce: true,
103
+ data: { prompt: 'hello Rovo Chat' },
104
+ })
105
+ }
106
+ }
107
+ ```
108
+
73
109
  ### Subscribe All
74
110
 
75
111
  **Note** - Avoid using this. Right now it's only intended to be a proxy for Rovo Chat to trigger
package/dist/cjs/main.js CHANGED
@@ -5,12 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.useSubscribeAll = exports.useSubscribe = exports.usePublish = exports.Subscriber = void 0;
8
- var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _toArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toArray"));
10
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
11
  var _react = require("react");
11
12
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
13
  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
14
  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; }
15
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
16
+ var MAX_CONSUMED_EVENT_KEYS = 5000;
14
17
  var ignoredTriggerLatestEvents = new Set(['editor-context-payload', 'agent-changed',
15
18
  // Internal signals that must never overwrite the publish queue — they would
16
19
  // cause `triggerLatest` subscribers (e.g. PubSubListener) to replay them
@@ -27,39 +30,86 @@ var createPubSub = function createPubSub() {
27
30
  var subscribedEvents = {};
28
31
  var publishQueue = {};
29
32
  var wildcardEvents = [];
33
+ var consumedEventKeys = new Set();
34
+ var consumedEventKeyQueue = [];
30
35
  var subIdCounter = 0;
36
+ var eventIdCounter = 0;
31
37
  var generateSubId = function generateSubId() {
32
38
  subIdCounter += 1;
33
39
  return subIdCounter.toString();
34
40
  };
35
- var subscribe = function subscribe(_ref, callback) {
41
+ var createEvent = function createEvent(payload) {
42
+ eventIdCounter += 1;
43
+ return {
44
+ id: eventIdCounter.toString(),
45
+ payload: payload
46
+ };
47
+ };
48
+ var rememberConsumedEventKey = function rememberConsumedEventKey(claimKey) {
49
+ consumedEventKeys.add(claimKey);
50
+ consumedEventKeyQueue = [].concat((0, _toConsumableArray2.default)(consumedEventKeyQueue), [claimKey]);
51
+ if (consumedEventKeyQueue.length > MAX_CONSUMED_EVENT_KEYS) {
52
+ var _consumedEventKeyQueu = consumedEventKeyQueue,
53
+ _consumedEventKeyQueu2 = (0, _toArray2.default)(_consumedEventKeyQueu),
54
+ oldestClaimKey = _consumedEventKeyQueu2[0],
55
+ remainingClaimKeys = _arrayLikeToArray(_consumedEventKeyQueu2).slice(1);
56
+ consumedEventKeyQueue = remainingClaimKeys;
57
+ if (oldestClaimKey) {
58
+ consumedEventKeys.delete(oldestClaimKey);
59
+ }
60
+ }
61
+ };
62
+ var claimConsumeOnceEvent = function claimConsumeOnceEvent(_ref) {
63
+ var event = _ref.event,
64
+ consumeOnceKey = _ref.consumeOnceKey;
65
+ if (!event.payload.consumeOnce || !consumeOnceKey) {
66
+ return true;
67
+ }
68
+ var claimKey = "".concat(consumeOnceKey, ":").concat(event.id);
69
+ if (consumedEventKeys.has(claimKey)) {
70
+ return false;
71
+ }
72
+ rememberConsumedEventKey(claimKey);
73
+ return true;
74
+ };
75
+ var subscribe = function subscribe(_ref2, callback) {
36
76
  var _subscribedEvents$top;
37
- var topic = _ref.topic,
38
- triggerLatest = _ref.triggerLatest;
77
+ var topic = _ref2.topic,
78
+ triggerLatest = _ref2.triggerLatest,
79
+ consumeOnceKey = _ref2.consumeOnceKey;
39
80
  var events = (_subscribedEvents$top = subscribedEvents[topic]) !== null && _subscribedEvents$top !== void 0 ? _subscribedEvents$top : [];
40
81
  var subId = generateSubId();
41
- var subExists = events.some(function (_ref2) {
42
- var id = _ref2.id;
82
+ var subExists = events.some(function (_ref3) {
83
+ var id = _ref3.id;
43
84
  return id === subId;
44
85
  });
86
+ var callbackWithConsumption = function callbackWithConsumption(event) {
87
+ if (!claimConsumeOnceEvent({
88
+ event: event,
89
+ consumeOnceKey: consumeOnceKey
90
+ })) {
91
+ return;
92
+ }
93
+ callback(event.payload);
94
+ };
45
95
 
46
96
  // Push to Topic stack if not already there
47
97
  if (!subExists) {
48
98
  subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, (0, _defineProperty2.default)({}, topic, [].concat((0, _toConsumableArray2.default)(events), [{
49
- callback: callback,
99
+ callback: callbackWithConsumption,
50
100
  id: subId
51
101
  }])));
52
102
  // If this Topic already has a published event and `triggerLatest` is true, trigger the callback then clear the publishQueue for that Topic
53
103
  if (triggerLatest && !!publishQueue[topic]) {
54
- var _payload = publishQueue[topic];
55
- callback(_payload);
104
+ var _event = publishQueue[topic];
105
+ callbackWithConsumption(_event);
56
106
  delete publishQueue[topic];
57
107
  }
58
108
  }
59
109
  return function () {
60
110
  // Remove from Topic stack
61
- subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, (0, _defineProperty2.default)({}, topic, (subscribedEvents[topic] || []).filter(function (_ref3) {
62
- var id = _ref3.id;
111
+ subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, (0, _defineProperty2.default)({}, topic, (subscribedEvents[topic] || []).filter(function (_ref4) {
112
+ var id = _ref4.id;
63
113
  return id !== subId;
64
114
  })));
65
115
  };
@@ -71,26 +121,28 @@ var createPubSub = function createPubSub() {
71
121
  id: subId
72
122
  }]);
73
123
  return function () {
74
- wildcardEvents = wildcardEvents.filter(function (_ref4) {
75
- var id = _ref4.id;
124
+ wildcardEvents = wildcardEvents.filter(function (_ref5) {
125
+ var id = _ref5.id;
76
126
  return id !== subId;
77
127
  });
78
128
  };
79
129
  };
80
130
  var publish = function publish(topic, payload) {
131
+ var event = createEvent(payload);
132
+
81
133
  /**
82
134
  * Log that this Topic received a published event, regardless of whether it has subscribers or not.
83
135
  * This ensures new subscribers can trigger their callback if `triggerLatest` is true, and the event hasn't already been triggered.
84
136
  */
85
137
  // This `ignoredTriggerLatestEvents` is a quick fix to prevent triggering the latest event for certain events
86
138
  if (!isIgnoredForTriggerLatest(payload.type)) {
87
- publishQueue[topic] = payload;
139
+ publishQueue[topic] = event;
88
140
  }
89
141
 
90
142
  // Notify `subscribeAll` subscribers as they are Topic agnostic
91
- wildcardEvents.forEach(function (_ref5) {
92
- var callback = _ref5.callback;
93
- return callback(payload);
143
+ wildcardEvents.forEach(function (_ref6) {
144
+ var callback = _ref6.callback;
145
+ return callback(event.payload);
94
146
  });
95
147
  var topicSubs = subscribedEvents[topic] || [];
96
148
 
@@ -100,9 +152,9 @@ var createPubSub = function createPubSub() {
100
152
  }
101
153
 
102
154
  // Notify all Topic subscribers of this event
103
- topicSubs.forEach(function (_ref6) {
104
- var callback = _ref6.callback;
105
- return callback(payload);
155
+ topicSubs.forEach(function (_ref7) {
156
+ var callback = _ref7.callback;
157
+ return callback(event);
106
158
  });
107
159
  };
108
160
  var flushQueue = function flushQueue() {
@@ -119,9 +171,10 @@ var pubSub = createPubSub();
119
171
  var usePubSub = function usePubSub() {
120
172
  return pubSub;
121
173
  };
122
- var useSubscribe = exports.useSubscribe = function useSubscribe(_ref7, callback) {
123
- var topic = _ref7.topic,
124
- triggerLatest = _ref7.triggerLatest;
174
+ var useSubscribe = exports.useSubscribe = function useSubscribe(_ref8, callback) {
175
+ var topic = _ref8.topic,
176
+ triggerLatest = _ref8.triggerLatest,
177
+ consumeOnceKey = _ref8.consumeOnceKey;
125
178
  var _usePubSub = usePubSub(),
126
179
  subscribe = _usePubSub.subscribe;
127
180
  var callbackRef = (0, _react.useRef)(callback);
@@ -129,14 +182,15 @@ var useSubscribe = exports.useSubscribe = function useSubscribe(_ref7, callback)
129
182
  (0, _react.useEffect)(function () {
130
183
  var unsubscribe = subscribe({
131
184
  topic: topic,
132
- triggerLatest: triggerLatest
185
+ triggerLatest: triggerLatest,
186
+ consumeOnceKey: consumeOnceKey
133
187
  }, function () {
134
188
  return callbackRef.current.apply(callbackRef, arguments);
135
189
  });
136
190
  return unsubscribe;
137
191
  },
138
192
  // eslint-disable-next-line react-hooks/exhaustive-deps
139
- [topic]);
193
+ [topic, consumeOnceKey]);
140
194
  };
141
195
  var useSubscribeAll = exports.useSubscribeAll = function useSubscribeAll(callback) {
142
196
  var _usePubSub2 = usePubSub(),
@@ -172,14 +226,16 @@ var usePublish = exports.usePublish = function usePublish(topic) {
172
226
  }, [publish, topic]);
173
227
  return publishFn;
174
228
  };
175
- var Subscriber = exports.Subscriber = function Subscriber(_ref8) {
176
- var topic = _ref8.topic,
177
- triggerLatest = _ref8.triggerLatest,
178
- onEvent = _ref8.onEvent,
179
- flushQueueOnUnmount = _ref8.flushQueueOnUnmount;
229
+ var Subscriber = exports.Subscriber = function Subscriber(_ref9) {
230
+ var topic = _ref9.topic,
231
+ triggerLatest = _ref9.triggerLatest,
232
+ onEvent = _ref9.onEvent,
233
+ flushQueueOnUnmount = _ref9.flushQueueOnUnmount,
234
+ consumeOnceKey = _ref9.consumeOnceKey;
180
235
  useSubscribe({
181
236
  topic: topic,
182
- triggerLatest: triggerLatest
237
+ triggerLatest: triggerLatest,
238
+ consumeOnceKey: consumeOnceKey
183
239
  }, onEvent);
184
240
  useFlushOnUnmount(flushQueueOnUnmount);
185
241
  return null;
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
2
2
  import { fg } from '@atlaskit/platform-feature-flags';
3
+ const MAX_CONSUMED_EVENT_KEYS = 5000;
3
4
  const ignoredTriggerLatestEvents = new Set(['editor-context-payload', 'agent-changed',
4
5
  // Internal signals that must never overwrite the publish queue — they would
5
6
  // cause `triggerLatest` subscribers (e.g. PubSubListener) to replay them
@@ -16,14 +17,50 @@ const createPubSub = () => {
16
17
  let subscribedEvents = {};
17
18
  let publishQueue = {};
18
19
  let wildcardEvents = [];
20
+ let consumedEventKeys = new Set();
21
+ let consumedEventKeyQueue = [];
19
22
  let subIdCounter = 0;
23
+ let eventIdCounter = 0;
20
24
  const generateSubId = () => {
21
25
  subIdCounter += 1;
22
26
  return subIdCounter.toString();
23
27
  };
28
+ const createEvent = payload => {
29
+ eventIdCounter += 1;
30
+ return {
31
+ id: eventIdCounter.toString(),
32
+ payload
33
+ };
34
+ };
35
+ const rememberConsumedEventKey = claimKey => {
36
+ consumedEventKeys.add(claimKey);
37
+ consumedEventKeyQueue = [...consumedEventKeyQueue, claimKey];
38
+ if (consumedEventKeyQueue.length > MAX_CONSUMED_EVENT_KEYS) {
39
+ const [oldestClaimKey, ...remainingClaimKeys] = consumedEventKeyQueue;
40
+ consumedEventKeyQueue = remainingClaimKeys;
41
+ if (oldestClaimKey) {
42
+ consumedEventKeys.delete(oldestClaimKey);
43
+ }
44
+ }
45
+ };
46
+ const claimConsumeOnceEvent = ({
47
+ event,
48
+ consumeOnceKey
49
+ }) => {
50
+ if (!event.payload.consumeOnce || !consumeOnceKey) {
51
+ return true;
52
+ }
53
+ const claimKey = `${consumeOnceKey}:${event.id}`;
54
+ if (consumedEventKeys.has(claimKey)) {
55
+ return false;
56
+ }
57
+ rememberConsumedEventKey(claimKey);
58
+ return true;
59
+ };
24
60
  const subscribe = ({
25
61
  topic,
26
- triggerLatest
62
+ triggerLatest,
63
+ consumeOnceKey
27
64
  }, callback) => {
28
65
  var _subscribedEvents$top;
29
66
  const events = (_subscribedEvents$top = subscribedEvents[topic]) !== null && _subscribedEvents$top !== void 0 ? _subscribedEvents$top : [];
@@ -31,20 +68,29 @@ const createPubSub = () => {
31
68
  const subExists = events.some(({
32
69
  id
33
70
  }) => id === subId);
71
+ const callbackWithConsumption = event => {
72
+ if (!claimConsumeOnceEvent({
73
+ event,
74
+ consumeOnceKey
75
+ })) {
76
+ return;
77
+ }
78
+ callback(event.payload);
79
+ };
34
80
 
35
81
  // Push to Topic stack if not already there
36
82
  if (!subExists) {
37
83
  subscribedEvents = {
38
84
  ...subscribedEvents,
39
85
  [topic]: [...events, {
40
- callback,
86
+ callback: callbackWithConsumption,
41
87
  id: subId
42
88
  }]
43
89
  };
44
90
  // If this Topic already has a published event and `triggerLatest` is true, trigger the callback then clear the publishQueue for that Topic
45
91
  if (triggerLatest && !!publishQueue[topic]) {
46
- const payload = publishQueue[topic];
47
- callback(payload);
92
+ const event = publishQueue[topic];
93
+ callbackWithConsumption(event);
48
94
  delete publishQueue[topic];
49
95
  }
50
96
  }
@@ -71,19 +117,21 @@ const createPubSub = () => {
71
117
  };
72
118
  };
73
119
  const publish = (topic, payload) => {
120
+ const event = createEvent(payload);
121
+
74
122
  /**
75
123
  * Log that this Topic received a published event, regardless of whether it has subscribers or not.
76
124
  * This ensures new subscribers can trigger their callback if `triggerLatest` is true, and the event hasn't already been triggered.
77
125
  */
78
126
  // This `ignoredTriggerLatestEvents` is a quick fix to prevent triggering the latest event for certain events
79
127
  if (!isIgnoredForTriggerLatest(payload.type)) {
80
- publishQueue[topic] = payload;
128
+ publishQueue[topic] = event;
81
129
  }
82
130
 
83
131
  // Notify `subscribeAll` subscribers as they are Topic agnostic
84
132
  wildcardEvents.forEach(({
85
133
  callback
86
- }) => callback(payload));
134
+ }) => callback(event.payload));
87
135
  const topicSubs = subscribedEvents[topic] || [];
88
136
 
89
137
  // If there are no subscribers for this Topic, nothing to do.
@@ -94,7 +142,7 @@ const createPubSub = () => {
94
142
  // Notify all Topic subscribers of this event
95
143
  topicSubs.forEach(({
96
144
  callback
97
- }) => callback(payload));
145
+ }) => callback(event));
98
146
  };
99
147
  const flushQueue = () => {
100
148
  publishQueue = {};
@@ -112,7 +160,8 @@ const usePubSub = () => {
112
160
  };
113
161
  export const useSubscribe = ({
114
162
  topic,
115
- triggerLatest
163
+ triggerLatest,
164
+ consumeOnceKey
116
165
  }, callback) => {
117
166
  const {
118
167
  subscribe
@@ -122,12 +171,13 @@ export const useSubscribe = ({
122
171
  useEffect(() => {
123
172
  const unsubscribe = subscribe({
124
173
  topic,
125
- triggerLatest
174
+ triggerLatest,
175
+ consumeOnceKey
126
176
  }, (...args) => callbackRef.current(...args));
127
177
  return unsubscribe;
128
178
  },
129
179
  // eslint-disable-next-line react-hooks/exhaustive-deps
130
- [topic]);
180
+ [topic, consumeOnceKey]);
131
181
  };
132
182
  export const useSubscribeAll = callback => {
133
183
  const {
@@ -165,11 +215,13 @@ export const Subscriber = ({
165
215
  topic,
166
216
  triggerLatest,
167
217
  onEvent,
168
- flushQueueOnUnmount
218
+ flushQueueOnUnmount,
219
+ consumeOnceKey
169
220
  }) => {
170
221
  useSubscribe({
171
222
  topic,
172
- triggerLatest
223
+ triggerLatest,
224
+ consumeOnceKey
173
225
  }, onEvent);
174
226
  useFlushOnUnmount(flushQueueOnUnmount);
175
227
  return null;
package/dist/esm/main.js CHANGED
@@ -1,9 +1,12 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toArray from "@babel/runtime/helpers/toArray";
3
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
4
  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; }
4
5
  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; }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
7
  import { useCallback, useEffect, useLayoutEffect, useRef } from 'react';
6
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
+ var MAX_CONSUMED_EVENT_KEYS = 5000;
7
10
  var ignoredTriggerLatestEvents = new Set(['editor-context-payload', 'agent-changed',
8
11
  // Internal signals that must never overwrite the publish queue — they would
9
12
  // cause `triggerLatest` subscribers (e.g. PubSubListener) to replay them
@@ -20,39 +23,86 @@ var createPubSub = function createPubSub() {
20
23
  var subscribedEvents = {};
21
24
  var publishQueue = {};
22
25
  var wildcardEvents = [];
26
+ var consumedEventKeys = new Set();
27
+ var consumedEventKeyQueue = [];
23
28
  var subIdCounter = 0;
29
+ var eventIdCounter = 0;
24
30
  var generateSubId = function generateSubId() {
25
31
  subIdCounter += 1;
26
32
  return subIdCounter.toString();
27
33
  };
28
- var subscribe = function subscribe(_ref, callback) {
34
+ var createEvent = function createEvent(payload) {
35
+ eventIdCounter += 1;
36
+ return {
37
+ id: eventIdCounter.toString(),
38
+ payload: payload
39
+ };
40
+ };
41
+ var rememberConsumedEventKey = function rememberConsumedEventKey(claimKey) {
42
+ consumedEventKeys.add(claimKey);
43
+ consumedEventKeyQueue = [].concat(_toConsumableArray(consumedEventKeyQueue), [claimKey]);
44
+ if (consumedEventKeyQueue.length > MAX_CONSUMED_EVENT_KEYS) {
45
+ var _consumedEventKeyQueu = consumedEventKeyQueue,
46
+ _consumedEventKeyQueu2 = _toArray(_consumedEventKeyQueu),
47
+ oldestClaimKey = _consumedEventKeyQueu2[0],
48
+ remainingClaimKeys = _arrayLikeToArray(_consumedEventKeyQueu2).slice(1);
49
+ consumedEventKeyQueue = remainingClaimKeys;
50
+ if (oldestClaimKey) {
51
+ consumedEventKeys.delete(oldestClaimKey);
52
+ }
53
+ }
54
+ };
55
+ var claimConsumeOnceEvent = function claimConsumeOnceEvent(_ref) {
56
+ var event = _ref.event,
57
+ consumeOnceKey = _ref.consumeOnceKey;
58
+ if (!event.payload.consumeOnce || !consumeOnceKey) {
59
+ return true;
60
+ }
61
+ var claimKey = "".concat(consumeOnceKey, ":").concat(event.id);
62
+ if (consumedEventKeys.has(claimKey)) {
63
+ return false;
64
+ }
65
+ rememberConsumedEventKey(claimKey);
66
+ return true;
67
+ };
68
+ var subscribe = function subscribe(_ref2, callback) {
29
69
  var _subscribedEvents$top;
30
- var topic = _ref.topic,
31
- triggerLatest = _ref.triggerLatest;
70
+ var topic = _ref2.topic,
71
+ triggerLatest = _ref2.triggerLatest,
72
+ consumeOnceKey = _ref2.consumeOnceKey;
32
73
  var events = (_subscribedEvents$top = subscribedEvents[topic]) !== null && _subscribedEvents$top !== void 0 ? _subscribedEvents$top : [];
33
74
  var subId = generateSubId();
34
- var subExists = events.some(function (_ref2) {
35
- var id = _ref2.id;
75
+ var subExists = events.some(function (_ref3) {
76
+ var id = _ref3.id;
36
77
  return id === subId;
37
78
  });
79
+ var callbackWithConsumption = function callbackWithConsumption(event) {
80
+ if (!claimConsumeOnceEvent({
81
+ event: event,
82
+ consumeOnceKey: consumeOnceKey
83
+ })) {
84
+ return;
85
+ }
86
+ callback(event.payload);
87
+ };
38
88
 
39
89
  // Push to Topic stack if not already there
40
90
  if (!subExists) {
41
91
  subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, _defineProperty({}, topic, [].concat(_toConsumableArray(events), [{
42
- callback: callback,
92
+ callback: callbackWithConsumption,
43
93
  id: subId
44
94
  }])));
45
95
  // If this Topic already has a published event and `triggerLatest` is true, trigger the callback then clear the publishQueue for that Topic
46
96
  if (triggerLatest && !!publishQueue[topic]) {
47
- var _payload = publishQueue[topic];
48
- callback(_payload);
97
+ var _event = publishQueue[topic];
98
+ callbackWithConsumption(_event);
49
99
  delete publishQueue[topic];
50
100
  }
51
101
  }
52
102
  return function () {
53
103
  // Remove from Topic stack
54
- subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, _defineProperty({}, topic, (subscribedEvents[topic] || []).filter(function (_ref3) {
55
- var id = _ref3.id;
104
+ subscribedEvents = _objectSpread(_objectSpread({}, subscribedEvents), {}, _defineProperty({}, topic, (subscribedEvents[topic] || []).filter(function (_ref4) {
105
+ var id = _ref4.id;
56
106
  return id !== subId;
57
107
  })));
58
108
  };
@@ -64,26 +114,28 @@ var createPubSub = function createPubSub() {
64
114
  id: subId
65
115
  }]);
66
116
  return function () {
67
- wildcardEvents = wildcardEvents.filter(function (_ref4) {
68
- var id = _ref4.id;
117
+ wildcardEvents = wildcardEvents.filter(function (_ref5) {
118
+ var id = _ref5.id;
69
119
  return id !== subId;
70
120
  });
71
121
  };
72
122
  };
73
123
  var publish = function publish(topic, payload) {
124
+ var event = createEvent(payload);
125
+
74
126
  /**
75
127
  * Log that this Topic received a published event, regardless of whether it has subscribers or not.
76
128
  * This ensures new subscribers can trigger their callback if `triggerLatest` is true, and the event hasn't already been triggered.
77
129
  */
78
130
  // This `ignoredTriggerLatestEvents` is a quick fix to prevent triggering the latest event for certain events
79
131
  if (!isIgnoredForTriggerLatest(payload.type)) {
80
- publishQueue[topic] = payload;
132
+ publishQueue[topic] = event;
81
133
  }
82
134
 
83
135
  // Notify `subscribeAll` subscribers as they are Topic agnostic
84
- wildcardEvents.forEach(function (_ref5) {
85
- var callback = _ref5.callback;
86
- return callback(payload);
136
+ wildcardEvents.forEach(function (_ref6) {
137
+ var callback = _ref6.callback;
138
+ return callback(event.payload);
87
139
  });
88
140
  var topicSubs = subscribedEvents[topic] || [];
89
141
 
@@ -93,9 +145,9 @@ var createPubSub = function createPubSub() {
93
145
  }
94
146
 
95
147
  // Notify all Topic subscribers of this event
96
- topicSubs.forEach(function (_ref6) {
97
- var callback = _ref6.callback;
98
- return callback(payload);
148
+ topicSubs.forEach(function (_ref7) {
149
+ var callback = _ref7.callback;
150
+ return callback(event);
99
151
  });
100
152
  };
101
153
  var flushQueue = function flushQueue() {
@@ -112,9 +164,10 @@ var pubSub = createPubSub();
112
164
  var usePubSub = function usePubSub() {
113
165
  return pubSub;
114
166
  };
115
- export var useSubscribe = function useSubscribe(_ref7, callback) {
116
- var topic = _ref7.topic,
117
- triggerLatest = _ref7.triggerLatest;
167
+ export var useSubscribe = function useSubscribe(_ref8, callback) {
168
+ var topic = _ref8.topic,
169
+ triggerLatest = _ref8.triggerLatest,
170
+ consumeOnceKey = _ref8.consumeOnceKey;
118
171
  var _usePubSub = usePubSub(),
119
172
  subscribe = _usePubSub.subscribe;
120
173
  var callbackRef = useRef(callback);
@@ -122,14 +175,15 @@ export var useSubscribe = function useSubscribe(_ref7, callback) {
122
175
  useEffect(function () {
123
176
  var unsubscribe = subscribe({
124
177
  topic: topic,
125
- triggerLatest: triggerLatest
178
+ triggerLatest: triggerLatest,
179
+ consumeOnceKey: consumeOnceKey
126
180
  }, function () {
127
181
  return callbackRef.current.apply(callbackRef, arguments);
128
182
  });
129
183
  return unsubscribe;
130
184
  },
131
185
  // eslint-disable-next-line react-hooks/exhaustive-deps
132
- [topic]);
186
+ [topic, consumeOnceKey]);
133
187
  };
134
188
  export var useSubscribeAll = function useSubscribeAll(callback) {
135
189
  var _usePubSub2 = usePubSub(),
@@ -165,14 +219,16 @@ export var usePublish = function usePublish(topic) {
165
219
  }, [publish, topic]);
166
220
  return publishFn;
167
221
  };
168
- export var Subscriber = function Subscriber(_ref8) {
169
- var topic = _ref8.topic,
170
- triggerLatest = _ref8.triggerLatest,
171
- onEvent = _ref8.onEvent,
172
- flushQueueOnUnmount = _ref8.flushQueueOnUnmount;
222
+ export var Subscriber = function Subscriber(_ref9) {
223
+ var topic = _ref9.topic,
224
+ triggerLatest = _ref9.triggerLatest,
225
+ onEvent = _ref9.onEvent,
226
+ flushQueueOnUnmount = _ref9.flushQueueOnUnmount,
227
+ consumeOnceKey = _ref9.consumeOnceKey;
173
228
  useSubscribe({
174
229
  topic: topic,
175
- triggerLatest: triggerLatest
230
+ triggerLatest: triggerLatest,
231
+ consumeOnceKey: consumeOnceKey
176
232
  }, onEvent);
177
233
  useFlushOnUnmount(flushQueueOnUnmount);
178
234
  return null;
@@ -2,14 +2,16 @@ import type { Callback, Payload, Topic } from './types';
2
2
  interface SubscribeOptions {
3
3
  topic: Topic;
4
4
  triggerLatest?: boolean;
5
+ consumeOnceKey?: string;
5
6
  }
6
- export declare const useSubscribe: ({ topic, triggerLatest }: SubscribeOptions, callback: Callback) => void;
7
+ export declare const useSubscribe: ({ topic, triggerLatest, consumeOnceKey }: SubscribeOptions, callback: Callback) => void;
7
8
  export declare const useSubscribeAll: (callback: Callback) => void;
8
9
  export declare const usePublish: (topic: Topic) => ((payload: Payload) => void);
9
- export declare const Subscriber: ({ topic, triggerLatest, onEvent, flushQueueOnUnmount, }: {
10
+ export declare const Subscriber: ({ topic, triggerLatest, onEvent, flushQueueOnUnmount, consumeOnceKey, }: {
10
11
  topic: Topic;
11
12
  triggerLatest?: boolean;
12
13
  onEvent: Callback;
13
14
  flushQueueOnUnmount?: boolean;
15
+ consumeOnceKey?: string;
14
16
  }) => null;
15
17
  export {};
@@ -18,6 +18,12 @@ export type PayloadCore<TKey extends string, TData = void> = {
18
18
  openChatMode?: 'sidebar' | 'mini-modal';
19
19
  product?: string;
20
20
  interactionSource?: string;
21
+ /**
22
+ * When true, subscribers that opt into the same consumeOnceKey should process this
23
+ * logical event once total. The delivery id used for deduplication is internal to
24
+ * rovo-triggers and is generated for each publish call.
25
+ */
26
+ consumeOnce?: boolean;
21
27
  } & (TData extends void ? {} : {
22
28
  data: TData;
23
29
  });
@@ -208,6 +214,7 @@ export type ChatOpenPayload = PayloadCore<'chat-open', {
208
214
  avatarUrl?: string;
209
215
  resetActiveMenu?: boolean;
210
216
  openAgentSelector?: boolean;
217
+ aiFeatureContext?: Record<string, unknown>;
211
218
  }>;
212
219
  export type ForgeAppAuthSuccess = PayloadCore<'forge-auth-success', {
213
220
  is3pActionAuth?: boolean;
@@ -224,6 +231,12 @@ export type ForgeAppAuthFailure = PayloadCore<'forge-auth-failure', {
224
231
  */
225
232
  export type InsertPromptPayload = PayloadCore<'insert-prompt', {
226
233
  prompt: string;
234
+ /**
235
+ * Optional complete ADF document to seed the chat input with instead of the plain-string
236
+ * `prompt`. When provided, rich nodes (skill pills, links, inline cards, mentions) are
237
+ * preserved rather than being flattened to text. `prompt` is still used as a fallback.
238
+ */
239
+ promptAdf?: DocNode;
227
240
  /**
228
241
  * Overrides the default auto-send behavior for prompts.
229
242
  * By default, prompts with backticks (`) are inserted as placeholders into the chat input
@@ -2,14 +2,16 @@ import type { Callback, Payload, Topic } from './types';
2
2
  interface SubscribeOptions {
3
3
  topic: Topic;
4
4
  triggerLatest?: boolean;
5
+ consumeOnceKey?: string;
5
6
  }
6
- export declare const useSubscribe: ({ topic, triggerLatest }: SubscribeOptions, callback: Callback) => void;
7
+ export declare const useSubscribe: ({ topic, triggerLatest, consumeOnceKey }: SubscribeOptions, callback: Callback) => void;
7
8
  export declare const useSubscribeAll: (callback: Callback) => void;
8
9
  export declare const usePublish: (topic: Topic) => ((payload: Payload) => void);
9
- export declare const Subscriber: ({ topic, triggerLatest, onEvent, flushQueueOnUnmount, }: {
10
+ export declare const Subscriber: ({ topic, triggerLatest, onEvent, flushQueueOnUnmount, consumeOnceKey, }: {
10
11
  topic: Topic;
11
12
  triggerLatest?: boolean;
12
13
  onEvent: Callback;
13
14
  flushQueueOnUnmount?: boolean;
15
+ consumeOnceKey?: string;
14
16
  }) => null;
15
17
  export {};
@@ -18,6 +18,12 @@ export type PayloadCore<TKey extends string, TData = void> = {
18
18
  openChatMode?: 'sidebar' | 'mini-modal';
19
19
  product?: string;
20
20
  interactionSource?: string;
21
+ /**
22
+ * When true, subscribers that opt into the same consumeOnceKey should process this
23
+ * logical event once total. The delivery id used for deduplication is internal to
24
+ * rovo-triggers and is generated for each publish call.
25
+ */
26
+ consumeOnce?: boolean;
21
27
  } & (TData extends void ? {} : {
22
28
  data: TData;
23
29
  });
@@ -208,6 +214,7 @@ export type ChatOpenPayload = PayloadCore<'chat-open', {
208
214
  avatarUrl?: string;
209
215
  resetActiveMenu?: boolean;
210
216
  openAgentSelector?: boolean;
217
+ aiFeatureContext?: Record<string, unknown>;
211
218
  }>;
212
219
  export type ForgeAppAuthSuccess = PayloadCore<'forge-auth-success', {
213
220
  is3pActionAuth?: boolean;
@@ -224,6 +231,12 @@ export type ForgeAppAuthFailure = PayloadCore<'forge-auth-failure', {
224
231
  */
225
232
  export type InsertPromptPayload = PayloadCore<'insert-prompt', {
226
233
  prompt: string;
234
+ /**
235
+ * Optional complete ADF document to seed the chat input with instead of the plain-string
236
+ * `prompt`. When provided, rich nodes (skill pills, links, inline cards, mentions) are
237
+ * preserved rather than being flattened to text. `prompt` is still used as a fallback.
238
+ */
239
+ promptAdf?: DocNode;
227
240
  /**
228
241
  * Overrides the default auto-send behavior for prompts.
229
242
  * By default, prompts with backticks (`) are inserted as placeholders into the chat input
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/rovo-triggers",
3
- "version": "7.2.0",
3
+ "version": "7.4.0",
4
4
  "description": "Provides various trigger events to drive Rovo Chat functionality, such as a publish-subscribe and URL parameter hooks",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "atlaskit:src": "src/index.ts",
38
38
  "dependencies": {
39
- "@atlaskit/adf-schema": "^52.15.0",
39
+ "@atlaskit/adf-schema": "^52.16.0",
40
40
  "@atlaskit/platform-feature-flags": "^1.1.0",
41
41
  "@babel/runtime": "^7.0.0",
42
42
  "bind-event-listener": "^3.0.0"