botframework-webchat-core 4.13.0 → 4.15.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 (138) hide show
  1. package/.eslintrc.yml +4 -198
  2. package/.prettierrc.yml +1 -1
  3. package/lib/actions/setNotification.js +1 -1
  4. package/lib/createPromiseQueue.js +1 -1
  5. package/lib/createStore.d.ts +3 -2
  6. package/lib/createStore.d.ts.map +1 -0
  7. package/lib/createStore.js +5 -5
  8. package/lib/index.d.ts +50 -0
  9. package/lib/index.d.ts.map +1 -0
  10. package/lib/index.js +34 -6
  11. package/lib/reducer.d.ts +1 -0
  12. package/lib/reducer.d.ts.map +1 -0
  13. package/lib/reducers/activities.js +1 -1
  14. package/lib/reducers/clockSkewAdjustment.js +1 -1
  15. package/lib/reducers/lastTypingAt.js +1 -1
  16. package/lib/reducers/notifications.js +21 -14
  17. package/lib/reducers/typing.js +1 -1
  18. package/lib/sagas/connectSaga.js +14 -8
  19. package/lib/sagas/connectionStatusToNotificationSaga.js +1 -1
  20. package/lib/sagas/effects/forkPut.js +1 -1
  21. package/lib/sagas/markAllAsSpokenOnStopSpeakActivitySaga.js +1 -1
  22. package/lib/sagas/observeActivitySaga.js +1 -1
  23. package/lib/sagas/postActivitySaga.js +39 -32
  24. package/lib/sagas/queueIncomingActivitySaga.js +7 -3
  25. package/lib/sagas/sendMessageBackToPostActivitySaga.js +4 -4
  26. package/lib/sagas/sendPostBackToPostActivitySaga.js +4 -4
  27. package/lib/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
  28. package/lib/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
  29. package/lib/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
  30. package/lib/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
  31. package/lib/sagas/submitSendBoxSaga.js +1 -1
  32. package/lib/selectors/combineSelectors.js +8 -3
  33. package/lib/types/OneOrMany.d.ts +3 -0
  34. package/lib/types/OneOrMany.d.ts.map +1 -0
  35. package/lib/types/OneOrMany.js +2 -0
  36. package/lib/types/external/DirectLineActivity.d.ts +3 -0
  37. package/lib/types/external/DirectLineActivity.d.ts.map +1 -0
  38. package/lib/types/external/DirectLineActivity.js +2 -0
  39. package/lib/types/external/DirectLineAnimationCard.d.ts +3 -0
  40. package/lib/types/external/DirectLineAnimationCard.d.ts.map +1 -0
  41. package/lib/types/external/DirectLineAnimationCard.js +2 -0
  42. package/lib/types/external/DirectLineAttachment.d.ts +3 -0
  43. package/lib/types/external/DirectLineAttachment.d.ts.map +1 -0
  44. package/lib/types/external/DirectLineAttachment.js +2 -0
  45. package/lib/types/external/DirectLineAudioCard.d.ts +3 -0
  46. package/lib/types/external/DirectLineAudioCard.d.ts.map +1 -0
  47. package/lib/types/external/DirectLineAudioCard.js +2 -0
  48. package/lib/types/external/DirectLineCardAction.d.ts +110 -0
  49. package/lib/types/external/DirectLineCardAction.d.ts.map +1 -0
  50. package/lib/types/external/DirectLineCardAction.js +2 -0
  51. package/lib/types/external/DirectLineHeroCard.d.ts +3 -0
  52. package/lib/types/external/DirectLineHeroCard.d.ts.map +1 -0
  53. package/lib/types/external/DirectLineHeroCard.js +2 -0
  54. package/lib/types/external/DirectLineJSBotConnection.d.ts +3 -0
  55. package/lib/types/external/DirectLineJSBotConnection.d.ts.map +1 -0
  56. package/lib/types/external/DirectLineJSBotConnection.js +2 -0
  57. package/lib/types/external/DirectLineOAuthCard.d.ts +3 -0
  58. package/lib/types/external/DirectLineOAuthCard.d.ts.map +1 -0
  59. package/lib/types/external/DirectLineOAuthCard.js +2 -0
  60. package/lib/types/external/DirectLineReceiptCard.d.ts +3 -0
  61. package/lib/types/external/DirectLineReceiptCard.d.ts.map +1 -0
  62. package/lib/types/external/DirectLineReceiptCard.js +2 -0
  63. package/lib/types/external/DirectLineSignInCard.d.ts +3 -0
  64. package/lib/types/external/DirectLineSignInCard.d.ts.map +1 -0
  65. package/lib/types/external/DirectLineSignInCard.js +2 -0
  66. package/lib/types/external/DirectLineSuggestedAction.d.ts +3 -0
  67. package/lib/types/external/DirectLineSuggestedAction.d.ts.map +1 -0
  68. package/lib/types/external/DirectLineSuggestedAction.js +2 -0
  69. package/lib/types/external/DirectLineThumbnailCard.d.ts +3 -0
  70. package/lib/types/external/DirectLineThumbnailCard.d.ts.map +1 -0
  71. package/lib/types/external/DirectLineThumbnailCard.js +2 -0
  72. package/lib/types/external/DirectLineVideoCard.d.ts +3 -0
  73. package/lib/types/external/DirectLineVideoCard.d.ts.map +1 -0
  74. package/lib/types/external/DirectLineVideoCard.js +2 -0
  75. package/lib/utils/dateToLocaleISOString.js +37 -0
  76. package/lib/utils/deleteKey.js +2 -3
  77. package/lib/utils/isForbiddenPropertyName.d.ts +2 -0
  78. package/lib/utils/isForbiddenPropertyName.d.ts.map +1 -0
  79. package/lib/utils/isForbiddenPropertyName.js +21 -0
  80. package/lib/utils/mime-wrapper.js +2 -4
  81. package/lib/utils/once.d.ts +2 -0
  82. package/lib/utils/once.d.ts.map +1 -0
  83. package/lib/utils/once.js +17 -0
  84. package/lib/utils/singleToArray.d.ts +2 -0
  85. package/lib/utils/singleToArray.d.ts.map +1 -0
  86. package/lib/utils/singleToArray.js +11 -0
  87. package/lib/utils/uniqueID.js +1 -1
  88. package/lib/utils/warnOnce.d.ts +2 -0
  89. package/lib/utils/warnOnce.d.ts.map +1 -0
  90. package/lib/utils/warnOnce.js +17 -0
  91. package/package.json +19 -22
  92. package/src/__tests__/detectSlowConnectionSaga.spec.js +1 -1
  93. package/src/__tests__/observeOnce.spec.js +3 -3
  94. package/src/actions/setNotification.js +8 -2
  95. package/src/createStore.ts +6 -6
  96. package/src/{index.js → index.ts} +40 -4
  97. package/src/reducers/notifications.js +22 -16
  98. package/src/sagas/connectSaga.js +9 -2
  99. package/src/sagas/connectionStatusToNotificationSaga.js +1 -1
  100. package/src/sagas/postActivitySaga.js +11 -4
  101. package/src/sagas/queueIncomingActivitySaga.js +40 -39
  102. package/src/sagas/sendMessageBackToPostActivitySaga.js +2 -3
  103. package/src/sagas/sendPostBackToPostActivitySaga.js +2 -3
  104. package/src/sagas/sendTypingIndicatorOnSetSendBoxSaga.js +1 -1
  105. package/src/sagas/speakActivityAndStartDictateOnIncomingActivityFromOthersSaga.js +1 -1
  106. package/src/sagas/startSpeakActivityOnPostActivitySaga.js +1 -1
  107. package/src/sagas/stopSpeakingActivityOnInputSaga.js +1 -1
  108. package/src/sagas/submitSendBoxSaga.js +1 -1
  109. package/src/selectors/combineSelectors.js +9 -1
  110. package/src/tsconfig.json +7 -3
  111. package/src/types/OneOrMany.ts +3 -0
  112. package/src/types/external/DirectLineActivity.ts +7 -0
  113. package/src/types/external/DirectLineAnimationCard.ts +5 -0
  114. package/src/types/external/DirectLineAttachment.ts +5 -0
  115. package/src/types/external/DirectLineAudioCard.ts +5 -0
  116. package/src/types/external/DirectLineCardAction.ts +128 -0
  117. package/src/types/external/DirectLineHeroCard.ts +5 -0
  118. package/src/types/external/DirectLineJSBotConnection.ts +5 -0
  119. package/src/types/external/DirectLineOAuthCard.ts +5 -0
  120. package/src/types/external/DirectLineReceiptCard.ts +5 -0
  121. package/src/types/external/DirectLineSignInCard.ts +5 -0
  122. package/src/types/external/DirectLineSuggestedAction.ts +5 -0
  123. package/src/types/external/DirectLineThumbnailCard.ts +5 -0
  124. package/src/types/external/DirectLineVideoCard.ts +5 -0
  125. package/src/utils/dateToLocaleISOString.chatham.spec.js +14 -0
  126. package/src/utils/dateToLocaleISOString.japan.spec.js +14 -0
  127. package/src/utils/dateToLocaleISOString.js +34 -0
  128. package/src/utils/dateToLocaleISOString.pacific.spec.js +14 -0
  129. package/src/utils/dateToLocaleISOString.utc.spec.js +22 -0
  130. package/src/utils/deleteKey.js +1 -3
  131. package/src/utils/isForbiddenPropertyName.spec.js +6 -0
  132. package/src/utils/isForbiddenPropertyName.ts +33 -0
  133. package/src/utils/mime-wrapper.js +1 -2
  134. package/src/utils/once.ts +10 -0
  135. package/src/utils/singleToArray.ts +3 -0
  136. package/src/utils/uniqueID.js +1 -6
  137. package/src/utils/warnOnce.ts +5 -0
  138. package/.eslintignore +0 -9
@@ -50,51 +50,52 @@ function* waitForActivityId(replyToId, initialActivities) {
50
50
  }
51
51
 
52
52
  function* queueIncomingActivity({ userID }) {
53
- yield takeEveryAndSelect(QUEUE_INCOMING_ACTIVITY, activitiesSelector, function* queueIncomingActivity(
54
- { payload: { activity } },
55
- initialActivities
56
- ) {
57
- // This is for resolving an accessibility issue.
58
- // If the incoming activity has "replyToId" field, hold on it until the activity replied to is in the transcript, then release this one.
59
- const { replyToId } = activity;
60
- const initialBotActivities = initialActivities.filter(({ from: { role } }) => role === 'bot');
61
-
62
- // To speed up the first activity render time, we do not delay the first activity from the bot.
63
- // Even if it is the first activity from the bot, the bot might be "replying" to the "conversationUpdate" event.
64
- // Thus, the "replyToId" will always be there even it is the first activity in the conversation.
65
- if (replyToId && initialBotActivities.length) {
66
- // Either the activity replied to is in the transcript or after timeout.
67
- const result = yield race({
68
- _: waitForActivityId(replyToId, initialActivities),
69
- timeout: call(sleep, REPLY_TIMEOUT)
70
- });
71
-
72
- if ('timeout' in result) {
73
- console.warn(
74
- `botframework-webchat: Timed out while waiting for activity "${replyToId}" which activity "${activity.id}" is replying to.`,
75
- {
76
- activity,
77
- replyToId
78
- }
79
- );
53
+ yield takeEveryAndSelect(
54
+ QUEUE_INCOMING_ACTIVITY,
55
+ activitiesSelector,
56
+ function* queueIncomingActivity({ payload: { activity } }, initialActivities) {
57
+ // This is for resolving an accessibility issue.
58
+ // If the incoming activity has "replyToId" field, hold on it until the activity replied to is in the transcript, then release this one.
59
+ const { replyToId } = activity;
60
+ const initialBotActivities = initialActivities.filter(({ from: { role } }) => role === 'bot');
61
+
62
+ // To speed up the first activity render time, we do not delay the first activity from the bot.
63
+ // Even if it is the first activity from the bot, the bot might be "replying" to the "conversationUpdate" event.
64
+ // Thus, the "replyToId" will always be there even it is the first activity in the conversation.
65
+ if (replyToId && initialBotActivities.length) {
66
+ // Either the activity replied to is in the transcript or after timeout.
67
+ const result = yield race({
68
+ _: waitForActivityId(replyToId, initialActivities),
69
+ timeout: call(sleep, REPLY_TIMEOUT)
70
+ });
71
+
72
+ if ('timeout' in result) {
73
+ console.warn(
74
+ `botframework-webchat: Timed out while waiting for activity "${replyToId}" which activity "${activity.id}" is replying to.`,
75
+ {
76
+ activity,
77
+ replyToId
78
+ }
79
+ );
80
+ }
80
81
  }
81
- }
82
82
 
83
- yield put(incomingActivity(activity));
83
+ yield put(incomingActivity(activity));
84
84
 
85
- // Update suggested actions
86
- // TODO: [P3] We could put this logic inside reducer to minimize number of actions dispatched.
87
- const messageActivities = yield select(activitiesOfType('message'));
88
- const lastMessageActivity = messageActivities[messageActivities.length - 1];
85
+ // Update suggested actions
86
+ // TODO: [P3] We could put this logic inside reducer to minimize number of actions dispatched.
87
+ const messageActivities = yield select(activitiesOfType('message'));
88
+ const lastMessageActivity = messageActivities[messageActivities.length - 1];
89
89
 
90
- if (activityFromBot(lastMessageActivity)) {
91
- const { suggestedActions: { actions, to } = {} } = lastMessageActivity;
90
+ if (activityFromBot(lastMessageActivity)) {
91
+ const { suggestedActions: { actions, to } = {} } = lastMessageActivity;
92
92
 
93
- // If suggested actions is not destined to anyone, or is destined to the user, show it.
94
- // In other words, if suggested actions is destined to someone else, don't show it.
95
- yield put(setSuggestedActions(to && to.length && !to.includes(userID) ? null : actions));
93
+ // If suggested actions is not destined to anyone, or is destined to the user, show it.
94
+ // In other words, if suggested actions is destined to someone else, don't show it.
95
+ yield put(setSuggestedActions(to && to.length && !to.includes(userID) ? null : actions));
96
+ }
96
97
  }
97
- });
98
+ );
98
99
  }
99
100
 
100
101
  export default function* queueIncomingActivitySaga() {
@@ -1,10 +1,9 @@
1
1
  import { put, takeEvery } from 'redux-saga/effects';
2
-
3
- import { SEND_MESSAGE_BACK } from '../actions/sendMessageBack';
4
2
  import postActivity from '../actions/postActivity';
3
+ import { SEND_MESSAGE_BACK } from '../actions/sendMessageBack';
5
4
  import whileConnected from './effects/whileConnected';
6
5
 
7
- // https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md#message-back
6
+ // https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-back
8
7
  function* postActivityWithMessageBack({ payload: { displayText, text, value } }) {
9
8
  yield put(
10
9
  postActivity({
@@ -2,12 +2,11 @@
2
2
  /* eslint no-undefined: "off" */
3
3
 
4
4
  import { put, takeEvery } from 'redux-saga/effects';
5
-
6
- import { SEND_POST_BACK } from '../actions/sendPostBack';
7
5
  import postActivity from '../actions/postActivity';
6
+ import { SEND_POST_BACK } from '../actions/sendPostBack';
8
7
  import whileConnected from './effects/whileConnected';
9
8
 
10
- // https://github.com/microsoft/botframework-sdk/blob/master/specs/botframework-activity/botframework-activity.md#post-back
9
+ // https://github.com/microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#post-back
11
10
  function* postActivityWithPostBack({ payload: { value } }) {
12
11
  yield put(
13
12
  postActivity({
@@ -34,7 +34,7 @@ function* sendTypingIndicatorOnSetSendBox() {
34
34
  // When the user type, and then post the activity at t = 1500, we still have a pending typing indicator at t = 3000.
35
35
  // This code is to cancel the typing indicator at t = 3000.
36
36
  (type === POST_ACTIVITY && payload.activity.type !== 'typing'),
37
- function*({ payload, type }) {
37
+ function* ({ payload, type }) {
38
38
  if (type === SET_SEND_BOX) {
39
39
  const interval = SEND_INTERVAL - Date.now() + lastSend;
40
40
 
@@ -16,7 +16,7 @@ function* speakActivityAndStartDictateOnIncomingActivityFromOthers({ userID }) {
16
16
  // In Direct Line Speech, we do not know the user ID, but "role" is filled with "bot" or "user".
17
17
  // Here, we do two checks: the speakable activity must not have user ID, and must not have role === 'user'
18
18
  type === INCOMING_ACTIVITY && payload.activity.from.id !== userID && payload.activity.from.role !== 'user',
19
- function*({ payload: { activity } }) {
19
+ function* ({ payload: { activity } }) {
20
20
  const shouldSpeakIncomingActivity = yield select(shouldSpeakIncomingActivitySelector);
21
21
  const shouldSpeak = speakableActivity(activity) && shouldSpeakIncomingActivity;
22
22
 
@@ -8,7 +8,7 @@ function* startSpeakActivityOnPostActivity() {
8
8
  yield takeEvery(
9
9
  ({ meta, payload, type }) =>
10
10
  type === POST_ACTIVITY_PENDING && meta.method === 'speech' && payload.activity.type === 'message',
11
- function*() {
11
+ function* () {
12
12
  yield put(startSpeakingActivity());
13
13
  }
14
14
  );
@@ -14,7 +14,7 @@ function* stopSpeakingActivityOnInput() {
14
14
  // So, right now, we are using best-effort by listening to POST_ACTIVITY_PENDING with a "message" event
15
15
  // We filter out speech because we will call startSpeakingActivity() for POST_ACTIVITY_PENDING dispatched by speech
16
16
  (type === POST_ACTIVITY_PENDING && meta.method !== 'speech' && payload.activity.type === 'message'),
17
- function*() {
17
+ function* () {
18
18
  yield put(stopSpeakingActivity());
19
19
  }
20
20
  );
@@ -7,7 +7,7 @@ import setSendBox from '../actions/setSendBox';
7
7
  import whileConnected from './effects/whileConnected';
8
8
 
9
9
  function* submitSendBox() {
10
- yield takeEvery(SUBMIT_SEND_BOX, function*({ payload: { channelData, method } }) {
10
+ yield takeEvery(SUBMIT_SEND_BOX, function* ({ payload: { channelData, method } }) {
11
11
  const sendBoxValue = yield select(sendBoxValueSelector);
12
12
 
13
13
  if (sendBoxValue) {
@@ -1,8 +1,16 @@
1
+ import isForbiddenPropertyName from '../utils/isForbiddenPropertyName';
2
+
1
3
  export default function combineSelectors(selectors) {
2
4
  if (Array.isArray(selectors)) {
3
5
  return state => selectors.reduce((combinedState, selector) => [...combinedState, selector(state)], []);
4
6
  }
5
7
 
6
8
  return state =>
7
- Object.keys(selectors).reduce((combinedState, key) => ({ ...combinedState, [key]: selectors[key](state) }), {});
9
+ Object.keys(selectors).reduce(
10
+ (combinedState, key) =>
11
+ // Mitigated through denylisting.
12
+ // eslint-disable-next-line security/detect-object-injection
13
+ isForbiddenPropertyName(key) ? combinedState : { ...combinedState, [key]: selectors[key](state) },
14
+ {}
15
+ );
8
16
  }
package/src/tsconfig.json CHANGED
@@ -3,11 +3,15 @@
3
3
  "allowSyntheticDefaultImports": true,
4
4
  "declaration": true,
5
5
  "declarationDir": "../lib",
6
+ "declarationMap": true,
7
+ "downlevelIteration": true,
6
8
  "emitDeclarationOnly": true,
7
- "isolatedModules": true,
8
9
  "jsx": "react",
9
10
  "preserveWatchOutput": true,
10
11
  "pretty": true,
11
- "skipLibCheck": true
12
- }
12
+ "resolveJsonModule": true, // Required for localization files
13
+ "skipLibCheck": true,
14
+ "sourceMap": true
15
+ },
16
+ "files": ["index.ts"]
13
17
  }
@@ -0,0 +1,3 @@
1
+ type OneOrMany<T> = T | T[];
2
+
3
+ export default OneOrMany;
@@ -0,0 +1,7 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ // Until we fully typed out DirectLineActivity, we need to use "any" here.
4
+ // We only know the DirectLineActivity must be a map, and not other primitive types.
5
+ type DirectLineActivity = Exclude<any, [] | boolean | Function | number | string>;
6
+
7
+ export default DirectLineActivity;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineAnimationCard = any;
4
+
5
+ export default DirectLineAnimationCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineAttachment = any;
4
+
5
+ export default DirectLineAttachment;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineAudioCard = any;
4
+
5
+ export default DirectLineAudioCard;
@@ -0,0 +1,128 @@
1
+ type CardActionWithImageAndTitle =
2
+ | { image: string }
3
+ | { title: string }
4
+ | {
5
+ image: string;
6
+ title: string;
7
+ };
8
+
9
+ /**
10
+ * A `call` action represents a telephone number that may be called.
11
+ *
12
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#call
13
+ */
14
+ type CallCardAction = CardActionWithImageAndTitle & {
15
+ type: 'call';
16
+ value: string;
17
+ };
18
+
19
+ /**
20
+ * A `downloadFile` action represents a hyperlink to be downloaded.
21
+ *
22
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#download-file-actions
23
+ */
24
+ type DownloadFileCardAction = CardActionWithImageAndTitle & {
25
+ type: 'downloadFile';
26
+ value: string;
27
+ };
28
+
29
+ /**
30
+ * An `imBack` action represents a text response that is added to the chat feed.
31
+ *
32
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#im-back
33
+ */
34
+ type IMBackCardAction = CardActionWithImageAndTitle & {
35
+ type: 'imBack';
36
+ value: string;
37
+ };
38
+
39
+ /**
40
+ * A `messageBack` action represents a text response to be sent via the chat system.
41
+ *
42
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#message-back
43
+ */
44
+ type MessageBackCardAction = CardActionWithImageAndTitle & {
45
+ displayText?: string;
46
+ text?: string;
47
+ type: 'messageBack';
48
+ value?: { [key: string]: any };
49
+ };
50
+
51
+ /**
52
+ * An `openUrl` action represents a hyperlink to be handled by the client.
53
+ *
54
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#open-url-actions
55
+ */
56
+ type OpenURLCardAction = CardActionWithImageAndTitle & {
57
+ type: 'openUrl';
58
+ value: string;
59
+ };
60
+
61
+ /**
62
+ * A `playAudio` action represents audio media that may be played.
63
+ *
64
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#play-audio
65
+ */
66
+ type PlayAudioCardAction = CardActionWithImageAndTitle & {
67
+ type: 'playAudio';
68
+ value: string;
69
+ };
70
+
71
+ /**
72
+ * A `playVideo` action represents video media that may be played.
73
+ *
74
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#play-video
75
+ */
76
+ type PlayVideoCardAction = CardActionWithImageAndTitle & {
77
+ type: 'playVideo';
78
+ value: string;
79
+ };
80
+
81
+ /**
82
+ * A `postBack` action represents a text response that is not added to the chat feed.
83
+ *
84
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#post-back
85
+ */
86
+ type PostBackCardAction = CardActionWithImageAndTitle & {
87
+ type: 'postBack';
88
+ value: any; // For legacy reason, postBack support any.
89
+ };
90
+
91
+ /**
92
+ * A `showImage` action represents an image that may be displayed.
93
+ *
94
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#show-image-file-actions
95
+ */
96
+ type ShowImageCardAction = CardActionWithImageAndTitle & {
97
+ type: 'showImage';
98
+ value: string;
99
+ };
100
+
101
+ /**
102
+ * A `signin` action represents a hyperlink to be handled by the client's signin system.
103
+ *
104
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#signin
105
+ */
106
+ type SignInCardAction = CardActionWithImageAndTitle & {
107
+ type: 'signin';
108
+ value: string;
109
+ };
110
+
111
+ /**
112
+ * A card action represents a clickable or interactive button for use within cards or as suggested actions. They are used to solicit input from users. Despite their name, card actions are not limited to use solely on cards.
113
+ *
114
+ * https://github.com/Microsoft/botframework-sdk/blob/main/specs/botframework-activity/botframework-activity.md#card-action
115
+ */
116
+ type DirectLineCardAction =
117
+ | CallCardAction
118
+ | DownloadFileCardAction
119
+ | IMBackCardAction
120
+ | MessageBackCardAction
121
+ | OpenURLCardAction
122
+ | PlayAudioCardAction
123
+ | PlayVideoCardAction
124
+ | PostBackCardAction
125
+ | ShowImageCardAction
126
+ | SignInCardAction;
127
+
128
+ export default DirectLineCardAction;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineHeroCard = any;
4
+
5
+ export default DirectLineHeroCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineJSBotConnection = any;
4
+
5
+ export default DirectLineJSBotConnection;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineOAuthCard = any;
4
+
5
+ export default DirectLineOAuthCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineReceiptCard = any;
4
+
5
+ export default DirectLineReceiptCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineSignInCard = any;
4
+
5
+ export default DirectLineSignInCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineSuggestedAction = any;
4
+
5
+ export default DirectLineSuggestedAction;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineThumbnailCard = any;
4
+
5
+ export default DirectLineThumbnailCard;
@@ -0,0 +1,5 @@
1
+ // TODO: [P1] #3953 We should fully type it out.
2
+
3
+ type DirectLineVideoCard = any;
4
+
5
+ export default DirectLineVideoCard;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
3
+ * @timezone Pacific/Chatham
4
+ */
5
+
6
+ import dateToLocaleISOString from './dateToLocaleISOString';
7
+
8
+ test('formatting a time in Chatham Islands timezone', () => {
9
+ // eslint-disable-next-line no-magic-numbers
10
+ const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
11
+ const actual = dateToLocaleISOString(date);
12
+
13
+ expect(actual).toMatchInlineSnapshot(`"2000-01-01T13:57:34.567+13:45"`);
14
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
3
+ * @timezone Asia/Tokyo
4
+ */
5
+
6
+ import dateToLocaleISOString from './dateToLocaleISOString';
7
+
8
+ test('formatting a time in Japan timezone', () => {
9
+ // eslint-disable-next-line no-magic-numbers
10
+ const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
11
+ const actual = dateToLocaleISOString(date);
12
+
13
+ expect(actual).toMatchInlineSnapshot(`"2000-01-01T09:12:34.567+09:00"`);
14
+ });
@@ -0,0 +1,34 @@
1
+ /* eslint no-magic-numbers: ["off", { "ignore": [1, 2, 3, 60] }] */
2
+
3
+ function pad(value, count = 2) {
4
+ if (typeof value !== 'number') {
5
+ throw new Error('First argument must be a number');
6
+ }
7
+
8
+ value += '';
9
+
10
+ while (value.length < count) {
11
+ value = '0' + value;
12
+ }
13
+
14
+ return value;
15
+ }
16
+
17
+ // Adopted from https://stackoverflow.com/questions/17415579/how-to-iso-8601-format-a-date-with-timezone-offset-in-javascript.
18
+ export default function dateToLocaleISOString(date) {
19
+ if (!(date instanceof Date)) {
20
+ throw new Error('First argument must be a Date object');
21
+ }
22
+
23
+ const timezoneOffset = -date.getTimezoneOffset();
24
+ const timezoneSign = timezoneOffset < 0 ? '-' : '+';
25
+
26
+ // "yyyy-MM-DDTHH:mm:ss.fff+08:00" for GMT+08
27
+ // "yyyy-MM-DDTHH:mm:ss.fffZ" for UTC
28
+
29
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(
30
+ date.getMinutes()
31
+ )}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}${
32
+ timezoneOffset ? `${timezoneSign}${pad(~~(Math.abs(timezoneOffset) / 60))}:${pad(timezoneOffset % 60)}` : 'Z'
33
+ }`;
34
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
3
+ * @timezone America/Los_Angeles
4
+ */
5
+
6
+ import dateToLocaleISOString from './dateToLocaleISOString';
7
+
8
+ test('formatting a time in Pacific Standard Time timezone', () => {
9
+ // eslint-disable-next-line no-magic-numbers
10
+ const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
11
+ const actual = dateToLocaleISOString(date);
12
+
13
+ expect(actual).toMatchInlineSnapshot(`"1999-12-31T16:12:34.567-08:00"`);
14
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @jest-environment ../../../__tests__/setup/jestNodeEnvironmentWithTimezone.js
3
+ * @timezone Etc/UTC
4
+ */
5
+
6
+ import dateToLocaleISOString from './dateToLocaleISOString';
7
+
8
+ test('formatting a time in UTC timezone', () => {
9
+ // eslint-disable-next-line no-magic-numbers
10
+ const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 567));
11
+ const actual = dateToLocaleISOString(date);
12
+
13
+ expect(actual).toMatchInlineSnapshot(`"2000-01-01T00:12:34.567Z"`);
14
+ });
15
+
16
+ test('formatting a time in UTC timezone with zero milliseconds', () => {
17
+ // eslint-disable-next-line no-magic-numbers
18
+ const date = new Date(Date.UTC(2000, 0, 1, 0, 12, 34, 0));
19
+ const actual = dateToLocaleISOString(date);
20
+
21
+ expect(actual).toMatchInlineSnapshot(`"2000-01-01T00:12:34.000Z"`);
22
+ });
@@ -1,11 +1,9 @@
1
- /* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^deleted$" }] */
2
-
3
1
  export default function deleteKey(map, key) {
4
2
  if (!map) {
5
3
  return map;
6
4
  }
7
5
 
8
- const { [key]: deleted, ...nextMap } = map;
6
+ const { [key]: _deleted, ...nextMap } = map;
9
7
 
10
8
  return nextMap;
11
9
  }
@@ -0,0 +1,6 @@
1
+ import isForbiddenPropertyName from './isForbiddenPropertyName';
2
+
3
+ test('should forbid "__proto__"', () => expect(isForbiddenPropertyName('__proto__')).toBeTruthy());
4
+ test('should forbid "constructor"', () => expect(isForbiddenPropertyName('constructor')).toBeTruthy());
5
+ test('should forbid "prototype"', () => expect(isForbiddenPropertyName('prototype')).toBeTruthy());
6
+ test('should not forbid "abc"', () => expect(isForbiddenPropertyName('abc')).toBeFalsy());
@@ -0,0 +1,33 @@
1
+ let FORBIDDEN_PROPERTY_NAMES;
2
+
3
+ function getForbiddenPropertyNames(): string[] {
4
+ return (
5
+ FORBIDDEN_PROPERTY_NAMES ||
6
+ (FORBIDDEN_PROPERTY_NAMES = Object.freeze(
7
+ Array.from(
8
+ new Set([
9
+ // As-of writing, `Object.prototype` includes:
10
+ // __defineGetter__
11
+ // __defineSetter__
12
+ // __lookupGetter__
13
+ // __lookupSetter
14
+ // __proto__
15
+ // constructor
16
+ // hasOwnProperty
17
+ // isPrototypeOf
18
+ // propertyIsEnumerable
19
+ // toLocaleString
20
+ // toString
21
+ // valueOf
22
+ ...Object.getOwnPropertyNames(Object.prototype),
23
+
24
+ 'prototype'
25
+ ])
26
+ )
27
+ ))
28
+ );
29
+ }
30
+
31
+ export default function isForbiddenPropertyName(propertyName: string): boolean {
32
+ return getForbiddenPropertyNames().includes(propertyName);
33
+ }
@@ -1,5 +1,4 @@
1
- /* eslint no-undef: "off"*/
2
- /* eslint node/global-require: "off"*/
1
+ /* eslint no-undef: "off" */
3
2
 
4
3
  // We adopted the work from mime-wrapper, at https://github.com/marlon360/mime-wrapper.
5
4
 
@@ -0,0 +1,10 @@
1
+ export default function once(fn: () => void): () => void {
2
+ let done;
3
+
4
+ return () => {
5
+ if (!done) {
6
+ fn();
7
+ done = 1;
8
+ }
9
+ };
10
+ }
@@ -0,0 +1,3 @@
1
+ export default function singleToArray<T>(singleOrArray: T | T[]): T[] {
2
+ return singleOrArray ? (Array.isArray(singleOrArray) ? singleOrArray : [singleOrArray]) : [];
3
+ }
@@ -3,10 +3,5 @@
3
3
  import random from 'math-random';
4
4
 
5
5
  export default function uniqueID() {
6
- return (
7
- Date.now() +
8
- random()
9
- .toString(36)
10
- .substr(2)
11
- );
6
+ return Date.now() + random().toString(36).substr(2);
12
7
  }
@@ -0,0 +1,5 @@
1
+ import once from './once';
2
+
3
+ export default function warnOnce(message: string): () => void {
4
+ return once(() => console.warn(`botframework-webchat: ${message}.`));
5
+ }
package/.eslintignore DELETED
@@ -1,9 +0,0 @@
1
- /src/__tests__/**/*
2
- /src/**/*.spec.js
3
- /src/**/*.spec.jsx
4
- /src/**/*.spec.ts
5
- /src/**/*.spec.tsx
6
- /src/**/*.test.js
7
- /src/**/*.test.jsx
8
- /src/**/*.test.ts
9
- /src/**/*.test.tsx