@pipedream/microsoft_outlook_calendar 8.0.0 → 8.1.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/actions/create-calendar-event/create-calendar-event.mjs +1 -1
- package/actions/delete-calendar-event/delete-calendar-event.mjs +1 -1
- package/actions/delete-recurring-event-instance/delete-recurring-event-instance.mjs +1 -1
- package/actions/find-meeting-times/find-meeting-times.mjs +256 -0
- package/actions/get-current-user/get-current-user.mjs +33 -0
- package/actions/get-schedule/get-schedule.mjs +1 -1
- package/actions/list-events/list-events.mjs +1 -1
- package/actions/search-contacts/search-contacts.mjs +1 -1
- package/actions/search-people/search-people.mjs +1 -1
- package/actions/update-calendar-event/update-calendar-event.mjs +1 -1
- package/actions/update-recurring-event-instance/update-recurring-event-instance.mjs +1 -1
- package/common/utils.mjs +15 -0
- package/microsoft_outlook_calendar.app.mjs +14 -0
- package/package.json +1 -1
- package/sources/new-calendar-event/new-calendar-event.mjs +1 -1
- package/sources/new-upcoming-event/new-upcoming-event.mjs +1 -1
- package/sources/new-upcoming-event-polling/new-upcoming-event-polling.mjs +1 -1
- package/sources/updated-calendar-event/updated-calendar-event.mjs +1 -1
|
@@ -3,7 +3,7 @@ import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
type: "action",
|
|
5
5
|
key: "microsoft_outlook_calendar-delete-calendar-event",
|
|
6
|
-
version: "0.0.
|
|
6
|
+
version: "0.0.9",
|
|
7
7
|
annotations: {
|
|
8
8
|
destructiveHint: true,
|
|
9
9
|
openWorldHint: true,
|
|
@@ -4,7 +4,7 @@ import { ConfigurationError } from "@pipedream/platform";
|
|
|
4
4
|
export default {
|
|
5
5
|
type: "action",
|
|
6
6
|
key: "microsoft_outlook_calendar-delete-recurring-event-instance",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.6",
|
|
8
8
|
annotations: {
|
|
9
9
|
destructiveHint: true,
|
|
10
10
|
openWorldHint: true,
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { ConfigurationError } from "@pipedream/platform";
|
|
2
|
+
import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
|
|
3
|
+
import utils from "../../common/utils.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
key: "microsoft_outlook_calendar-find-meeting-times",
|
|
7
|
+
name: "Find Meeting Times",
|
|
8
|
+
description: "Suggest meeting times and locations based on organizer and attendee availability. [See the documentation](https://learn.microsoft.com/en-us/graph/api/user-findmeetingtimes?view=graph-rest-1.0)",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "action",
|
|
11
|
+
annotations: {
|
|
12
|
+
destructiveHint: false,
|
|
13
|
+
openWorldHint: true,
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
},
|
|
16
|
+
props: {
|
|
17
|
+
microsoftOutlook,
|
|
18
|
+
userId: {
|
|
19
|
+
type: "string",
|
|
20
|
+
label: "User ID or UPN",
|
|
21
|
+
description: "Optional. If set, runs the request for a specific user (`/users/{id|userPrincipalName}/findMeetingTimes`). If unset, uses the signed-in user (`/me/findMeetingTimes`).",
|
|
22
|
+
optional: true,
|
|
23
|
+
},
|
|
24
|
+
attendees: {
|
|
25
|
+
type: "string[]",
|
|
26
|
+
label: "Attendees",
|
|
27
|
+
description: "Optional. A list of attendee email addresses.",
|
|
28
|
+
optional: true,
|
|
29
|
+
},
|
|
30
|
+
resourceAttendees: {
|
|
31
|
+
type: "string[]",
|
|
32
|
+
label: "Resource Attendees",
|
|
33
|
+
description: "Optional. A list of resource email addresses (for example, rooms/equipment).",
|
|
34
|
+
optional: true,
|
|
35
|
+
},
|
|
36
|
+
start: {
|
|
37
|
+
propDefinition: [
|
|
38
|
+
microsoftOutlook,
|
|
39
|
+
"start",
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
end: {
|
|
43
|
+
propDefinition: [
|
|
44
|
+
microsoftOutlook,
|
|
45
|
+
"end",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
timeZone: {
|
|
49
|
+
propDefinition: [
|
|
50
|
+
microsoftOutlook,
|
|
51
|
+
"timeZone",
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
activityDomain: {
|
|
55
|
+
type: "string",
|
|
56
|
+
label: "Activity Domain",
|
|
57
|
+
description: "Optional. Restrict suggestions to work hours, personal hours, or unrestricted.",
|
|
58
|
+
options: [
|
|
59
|
+
"work",
|
|
60
|
+
"personal",
|
|
61
|
+
"unrestricted",
|
|
62
|
+
],
|
|
63
|
+
default: "work",
|
|
64
|
+
optional: true,
|
|
65
|
+
},
|
|
66
|
+
duration: {
|
|
67
|
+
type: "integer",
|
|
68
|
+
label: "Duration (Minutes)",
|
|
69
|
+
description: "Optional. Meeting duration in minutes. Defaults to 30.",
|
|
70
|
+
optional: true,
|
|
71
|
+
default: 30,
|
|
72
|
+
min: 1,
|
|
73
|
+
},
|
|
74
|
+
maxResults: {
|
|
75
|
+
type: "integer",
|
|
76
|
+
label: "Max Results",
|
|
77
|
+
description: "Optional. Maximum number of meeting time suggestions to return (default: 20, max: 50).",
|
|
78
|
+
optional: true,
|
|
79
|
+
min: 1,
|
|
80
|
+
max: 50,
|
|
81
|
+
default: 20,
|
|
82
|
+
},
|
|
83
|
+
minimumAttendeePercentage: {
|
|
84
|
+
type: "integer",
|
|
85
|
+
label: "Minimum Attendee Percentage",
|
|
86
|
+
description: "Optional. Minimum confidence (0-100) for a suggestion to be returned.",
|
|
87
|
+
optional: true,
|
|
88
|
+
min: 0,
|
|
89
|
+
max: 100,
|
|
90
|
+
},
|
|
91
|
+
isOrganizerOptional: {
|
|
92
|
+
type: "boolean",
|
|
93
|
+
label: "Is Organizer Optional",
|
|
94
|
+
description: "Optional. Set to true if the organizer doesn't necessarily have to attend.",
|
|
95
|
+
optional: true,
|
|
96
|
+
},
|
|
97
|
+
returnSuggestionReasons: {
|
|
98
|
+
type: "boolean",
|
|
99
|
+
label: "Return Suggestion Reasons",
|
|
100
|
+
description: "Optional. Set to true to include a reason for each suggestion in the response.",
|
|
101
|
+
optional: true,
|
|
102
|
+
default: true,
|
|
103
|
+
},
|
|
104
|
+
suggestLocation: {
|
|
105
|
+
type: "boolean",
|
|
106
|
+
label: "Suggest Location",
|
|
107
|
+
description: "Optional. Set to true to ask Microsoft Graph to suggest meeting locations.",
|
|
108
|
+
optional: true,
|
|
109
|
+
},
|
|
110
|
+
locations: {
|
|
111
|
+
type: "string[]",
|
|
112
|
+
label: "Locations",
|
|
113
|
+
description: "Optional. A list of location display names to constrain suggestions (for example, conference room names).",
|
|
114
|
+
optional: true,
|
|
115
|
+
},
|
|
116
|
+
isLocationRequired: {
|
|
117
|
+
type: "boolean",
|
|
118
|
+
label: "Location Required",
|
|
119
|
+
description: "Optional. Set to true if a location is required for the suggestion.",
|
|
120
|
+
optional: true,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
methods: {
|
|
124
|
+
_toAttendeeBase(address, type) {
|
|
125
|
+
return {
|
|
126
|
+
type,
|
|
127
|
+
emailAddress: {
|
|
128
|
+
address,
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
async run({ $ }) {
|
|
134
|
+
const userId = this.userId?.trim();
|
|
135
|
+
|
|
136
|
+
const cleanedAttendees = utils.normalizeStringArray(this.attendees);
|
|
137
|
+
const cleanedResourceAttendees = utils.normalizeStringArray(this.resourceAttendees);
|
|
138
|
+
const cleanedLocations = utils.normalizeStringArray(this.locations);
|
|
139
|
+
const startMs = Date.parse(this.start);
|
|
140
|
+
const endMs = Date.parse(this.end);
|
|
141
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || startMs >= endMs) {
|
|
142
|
+
throw new Error("`start` must be before `end` and both must be valid date-time strings.");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const attendees = [
|
|
146
|
+
...cleanedAttendees.map((address) => this._toAttendeeBase(address, "required")),
|
|
147
|
+
...cleanedResourceAttendees.map((address) => this._toAttendeeBase(address, "resource")),
|
|
148
|
+
];
|
|
149
|
+
if (!attendees.length) {
|
|
150
|
+
throw new ConfigurationError("Provide at least one attendee or resource attendee email address.");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const durationMinutes = utils.clampInt(this.duration, {
|
|
154
|
+
min: 1,
|
|
155
|
+
max: 24 * 60,
|
|
156
|
+
}) ?? 30;
|
|
157
|
+
const meetingDuration = `PT${durationMinutes}M`;
|
|
158
|
+
|
|
159
|
+
const maxCandidates = utils.clampInt(this.maxResults, {
|
|
160
|
+
min: 1,
|
|
161
|
+
max: 50,
|
|
162
|
+
}) ?? 20;
|
|
163
|
+
|
|
164
|
+
const body = {
|
|
165
|
+
...(attendees.length
|
|
166
|
+
? {
|
|
167
|
+
attendees,
|
|
168
|
+
}
|
|
169
|
+
: {}),
|
|
170
|
+
...(this.isOrganizerOptional !== undefined
|
|
171
|
+
? {
|
|
172
|
+
isOrganizerOptional: this.isOrganizerOptional,
|
|
173
|
+
}
|
|
174
|
+
: {}),
|
|
175
|
+
maxCandidates,
|
|
176
|
+
meetingDuration,
|
|
177
|
+
...(this.minimumAttendeePercentage !== undefined
|
|
178
|
+
? {
|
|
179
|
+
minimumAttendeePercentage: this.minimumAttendeePercentage,
|
|
180
|
+
}
|
|
181
|
+
: {}),
|
|
182
|
+
returnSuggestionReasons: this.returnSuggestionReasons ?? true,
|
|
183
|
+
timeConstraint: {
|
|
184
|
+
activityDomain: this.activityDomain,
|
|
185
|
+
timeSlots: [
|
|
186
|
+
{
|
|
187
|
+
start: {
|
|
188
|
+
dateTime: this.start,
|
|
189
|
+
timeZone: this.timeZone,
|
|
190
|
+
},
|
|
191
|
+
end: {
|
|
192
|
+
dateTime: this.end,
|
|
193
|
+
timeZone: this.timeZone,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
...(this.suggestLocation !== undefined
|
|
199
|
+
|| cleanedLocations.length
|
|
200
|
+
|| this.isLocationRequired !== undefined
|
|
201
|
+
? {
|
|
202
|
+
locationConstraint: {
|
|
203
|
+
...(this.isLocationRequired !== undefined
|
|
204
|
+
? {
|
|
205
|
+
isRequired: this.isLocationRequired,
|
|
206
|
+
}
|
|
207
|
+
: {}),
|
|
208
|
+
...(this.suggestLocation !== undefined
|
|
209
|
+
? {
|
|
210
|
+
suggestLocation: this.suggestLocation,
|
|
211
|
+
}
|
|
212
|
+
: {}),
|
|
213
|
+
...(cleanedLocations.length
|
|
214
|
+
? {
|
|
215
|
+
locations: cleanedLocations.map((displayName) => ({
|
|
216
|
+
resolveAvailability: false,
|
|
217
|
+
displayName,
|
|
218
|
+
})),
|
|
219
|
+
}
|
|
220
|
+
: {}),
|
|
221
|
+
},
|
|
222
|
+
}
|
|
223
|
+
: {}),
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const graphResponse = await this.microsoftOutlook.findMeetingTimes({
|
|
227
|
+
$,
|
|
228
|
+
userId,
|
|
229
|
+
timeZone: this.timeZone,
|
|
230
|
+
data: body,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const suggestionCount = graphResponse?.meetingTimeSuggestions?.length ?? 0;
|
|
234
|
+
$.export("$summary", suggestionCount
|
|
235
|
+
? `Successfully found ${suggestionCount} meeting time suggestion${suggestionCount === 1
|
|
236
|
+
? ""
|
|
237
|
+
: "s"}`
|
|
238
|
+
: `No meeting time suggestions found${graphResponse?.emptySuggestionsReason
|
|
239
|
+
? ` (${graphResponse.emptySuggestionsReason})`
|
|
240
|
+
: ""}`);
|
|
241
|
+
|
|
242
|
+
const meetingTimesData = (graphResponse?.meetingTimeSuggestions ?? []).map((time) => ({
|
|
243
|
+
confidence: time.confidence,
|
|
244
|
+
organizerAvailability: time.organizerAvailability,
|
|
245
|
+
attendeeAvailability: time.attendeeAvailability,
|
|
246
|
+
meetingTimeSlot: time.meetingTimeSlot,
|
|
247
|
+
suggestionReason: time.suggestionReason,
|
|
248
|
+
locations: time.locations,
|
|
249
|
+
}));
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
data: meetingTimesData,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import microsoftOutlookCalendar from "../../microsoft_outlook_calendar.app.mjs";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
key: "microsoft_outlook_calendar-get-current-user",
|
|
5
|
+
name: "Get Current User",
|
|
6
|
+
description: "Returns the authenticated Microsoft user's ID, display name, email, and principal name via Microsoft Graph. Call this first when the user says 'my calendar', 'my events', or needs to identify themselves as organizer/attendee. Use `id` or `mail` to filter results from **List Events** or set the organizer in **Create Calendar Event**. [See the documentation](https://learn.microsoft.com/en-us/graph/api/user-get).",
|
|
7
|
+
version: "0.0.2",
|
|
8
|
+
type: "action",
|
|
9
|
+
annotations: {
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
openWorldHint: true,
|
|
12
|
+
readOnlyHint: true,
|
|
13
|
+
},
|
|
14
|
+
props: {
|
|
15
|
+
microsoftOutlookCalendar,
|
|
16
|
+
},
|
|
17
|
+
async run({ $ }) {
|
|
18
|
+
const user = await this.microsoftOutlookCalendar.client()
|
|
19
|
+
.api("/me")
|
|
20
|
+
.select("id,displayName,mail,userPrincipalName")
|
|
21
|
+
.get();
|
|
22
|
+
|
|
23
|
+
const summaryName = user.displayName || user.mail || user.id;
|
|
24
|
+
$.export("$summary", `Retrieved user ${summaryName}`);
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
id: user.id,
|
|
28
|
+
displayName: user.displayName,
|
|
29
|
+
mail: user.mail,
|
|
30
|
+
userPrincipalName: user.userPrincipalName,
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
@@ -8,7 +8,7 @@ export default {
|
|
|
8
8
|
key: "microsoft_outlook_calendar-get-schedule",
|
|
9
9
|
name: "Get Free/Busy Schedule",
|
|
10
10
|
description: "Get the free/busy availability information for a collection of users, distributions lists, or resources (rooms or equipment) for a specified time period. [See the documentation](https://learn.microsoft.com/en-us/graph/api/calendar-getschedule)",
|
|
11
|
-
version: "0.0.
|
|
11
|
+
version: "0.0.11",
|
|
12
12
|
annotations: {
|
|
13
13
|
destructiveHint: false,
|
|
14
14
|
openWorldHint: true,
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
key: "microsoft_outlook_calendar-list-events",
|
|
6
6
|
name: "List Events",
|
|
7
7
|
description: "Get a list of event objects in the user's mailbox. [See the documentation](https://learn.microsoft.com/en-us/graph/api/user-list-events)",
|
|
8
|
-
version: "0.0.
|
|
8
|
+
version: "0.0.12",
|
|
9
9
|
annotations: {
|
|
10
10
|
destructiveHint: false,
|
|
11
11
|
openWorldHint: true,
|
|
@@ -4,7 +4,7 @@ export default {
|
|
|
4
4
|
key: "microsoft_outlook_calendar-search-contacts",
|
|
5
5
|
name: "Search Contacts",
|
|
6
6
|
description: "Search for contacts by name from your saved contacts list and retrieve their email addresses. [See the documentation](https://learn.microsoft.com/en-us/graph/api/user-list-contacts)",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.5",
|
|
8
8
|
type: "action",
|
|
9
9
|
annotations: {
|
|
10
10
|
destructiveHint: false,
|
|
@@ -4,7 +4,7 @@ export default {
|
|
|
4
4
|
key: "microsoft_outlook_calendar-search-people",
|
|
5
5
|
name: "Search People",
|
|
6
6
|
description: "Retrieve a collection of person objects ordered by their relevance to the user, based on communication and collaboration patterns and business relationships. [See the documentation](https://learn.microsoft.com/en-us/graph/api/user-list-people)",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.5",
|
|
8
8
|
type: "action",
|
|
9
9
|
annotations: {
|
|
10
10
|
destructiveHint: false,
|
|
@@ -3,7 +3,7 @@ import microsoftOutlook from "../../microsoft_outlook_calendar.app.mjs";
|
|
|
3
3
|
export default {
|
|
4
4
|
type: "action",
|
|
5
5
|
key: "microsoft_outlook_calendar-update-calendar-event",
|
|
6
|
-
version: "0.0.
|
|
6
|
+
version: "0.0.9",
|
|
7
7
|
annotations: {
|
|
8
8
|
destructiveHint: true,
|
|
9
9
|
openWorldHint: true,
|
|
@@ -4,7 +4,7 @@ import { ConfigurationError } from "@pipedream/platform";
|
|
|
4
4
|
export default {
|
|
5
5
|
type: "action",
|
|
6
6
|
key: "microsoft_outlook_calendar-update-recurring-event-instance",
|
|
7
|
-
version: "0.0.
|
|
7
|
+
version: "0.0.6",
|
|
8
8
|
annotations: {
|
|
9
9
|
destructiveHint: false,
|
|
10
10
|
openWorldHint: true,
|
package/common/utils.mjs
CHANGED
|
@@ -35,4 +35,19 @@ export default {
|
|
|
35
35
|
String(input),
|
|
36
36
|
];
|
|
37
37
|
},
|
|
38
|
+
normalizeStringArray(values) {
|
|
39
|
+
return (values ?? [])
|
|
40
|
+
.map((v) => v?.toString?.().trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
},
|
|
43
|
+
clampInt(value, {
|
|
44
|
+
min,
|
|
45
|
+
max,
|
|
46
|
+
}) {
|
|
47
|
+
if (value === null || value === undefined) return undefined;
|
|
48
|
+
const n = Number(value);
|
|
49
|
+
if (!Number.isFinite(n)) return undefined;
|
|
50
|
+
const rounded = Math.round(n);
|
|
51
|
+
return Math.max(min, Math.min(max, rounded));
|
|
52
|
+
},
|
|
38
53
|
};
|
|
@@ -281,5 +281,19 @@ export default {
|
|
|
281
281
|
.header("Prefer", `outlook.timezone="${timeZone}"`)
|
|
282
282
|
.post(data);
|
|
283
283
|
},
|
|
284
|
+
async findMeetingTimes({
|
|
285
|
+
userId,
|
|
286
|
+
timeZone,
|
|
287
|
+
data = {},
|
|
288
|
+
} = {}) {
|
|
289
|
+
const basePath = userId
|
|
290
|
+
? `/users/${encodeURIComponent(userId)}`
|
|
291
|
+
: "/me";
|
|
292
|
+
let request = this.client().api(`${basePath}/findMeetingTimes`);
|
|
293
|
+
if (timeZone) {
|
|
294
|
+
request = request.header("Prefer", `outlook.timezone="${timeZone}"`);
|
|
295
|
+
}
|
|
296
|
+
return await request.post(data);
|
|
297
|
+
},
|
|
284
298
|
},
|
|
285
299
|
};
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
key: "microsoft_outlook_calendar-new-calendar-event",
|
|
6
6
|
name: "New Calendar Event (Instant)",
|
|
7
7
|
description: "Emit new event when a new Calendar event is created",
|
|
8
|
-
version: "0.0.
|
|
8
|
+
version: "0.0.14",
|
|
9
9
|
type: "source",
|
|
10
10
|
hooks: {
|
|
11
11
|
...common.hooks,
|
|
@@ -6,7 +6,7 @@ export default {
|
|
|
6
6
|
key: "microsoft_outlook_calendar-new-upcoming-event",
|
|
7
7
|
name: "New Upcoming Calendar Event",
|
|
8
8
|
description: "Emit new event when a Calendar event is upcoming, this source is using `reminderMinutesBeforeStart` property of the event to determine the time it should emit.",
|
|
9
|
-
version: "0.0.
|
|
9
|
+
version: "0.0.10",
|
|
10
10
|
type: "source",
|
|
11
11
|
props: {
|
|
12
12
|
...common.props,
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
key: "microsoft_outlook_calendar-new-upcoming-event-polling",
|
|
6
6
|
name: "New Upcoming Calendar Event (Polling)",
|
|
7
7
|
description: "Emit new event based on a time interval before an upcoming calendar event. [See the documentation](https://docs.microsoft.com/en-us/graph/api/user-list-events)",
|
|
8
|
-
version: "0.0.
|
|
8
|
+
version: "0.0.6",
|
|
9
9
|
type: "source",
|
|
10
10
|
dedupe: "unique",
|
|
11
11
|
props: {
|
|
@@ -5,7 +5,7 @@ export default {
|
|
|
5
5
|
key: "microsoft_outlook_calendar-updated-calendar-event",
|
|
6
6
|
name: "New Calendar Event Update (Instant)",
|
|
7
7
|
description: "Emit new event when a Calendar event is updated",
|
|
8
|
-
version: "0.0.
|
|
8
|
+
version: "0.0.14",
|
|
9
9
|
type: "source",
|
|
10
10
|
hooks: {
|
|
11
11
|
...common.hooks,
|