@jbctechsolutions/mcp-office365 2.5.1 → 4.0.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 (369) hide show
  1. package/README.md +66 -57
  2. package/dist/approval/hash.d.ts +8 -8
  3. package/dist/approval/token-manager.d.ts +37 -19
  4. package/dist/approval/token-manager.d.ts.map +1 -1
  5. package/dist/approval/token-manager.js +133 -37
  6. package/dist/approval/token-manager.js.map +1 -1
  7. package/dist/approval/types.d.ts +7 -3
  8. package/dist/approval/types.d.ts.map +1 -1
  9. package/dist/cli.d.ts +21 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +100 -2
  12. package/dist/cli.js.map +1 -1
  13. package/dist/database/repository.d.ts +48 -48
  14. package/dist/database/repository.d.ts.map +1 -1
  15. package/dist/database/repository.js.map +1 -1
  16. package/dist/delta/mirror.d.ts +67 -0
  17. package/dist/delta/mirror.d.ts.map +1 -0
  18. package/dist/delta/mirror.js +183 -0
  19. package/dist/delta/mirror.js.map +1 -0
  20. package/dist/graph/auth/account-id.d.ts +19 -0
  21. package/dist/graph/auth/account-id.d.ts.map +1 -0
  22. package/dist/graph/auth/account-id.js +55 -0
  23. package/dist/graph/auth/account-id.js.map +1 -0
  24. package/dist/graph/auth/config.d.ts +1 -1
  25. package/dist/graph/auth/config.d.ts.map +1 -1
  26. package/dist/graph/auth/config.js +13 -1
  27. package/dist/graph/auth/config.js.map +1 -1
  28. package/dist/graph/auth/device-code-flow.d.ts +3 -1
  29. package/dist/graph/auth/device-code-flow.d.ts.map +1 -1
  30. package/dist/graph/auth/device-code-flow.js +42 -14
  31. package/dist/graph/auth/device-code-flow.js.map +1 -1
  32. package/dist/graph/auth/index.d.ts +1 -0
  33. package/dist/graph/auth/index.d.ts.map +1 -1
  34. package/dist/graph/auth/index.js +1 -0
  35. package/dist/graph/auth/index.js.map +1 -1
  36. package/dist/graph/auth/token-cache.js +2 -2
  37. package/dist/graph/auth/token-cache.js.map +1 -1
  38. package/dist/graph/client/graph-client.d.ts +141 -8
  39. package/dist/graph/client/graph-client.d.ts.map +1 -1
  40. package/dist/graph/client/graph-client.js +536 -48
  41. package/dist/graph/client/graph-client.js.map +1 -1
  42. package/dist/graph/client/immutable-id-middleware.d.ts +50 -0
  43. package/dist/graph/client/immutable-id-middleware.d.ts.map +1 -0
  44. package/dist/graph/client/immutable-id-middleware.js +137 -0
  45. package/dist/graph/client/immutable-id-middleware.js.map +1 -0
  46. package/dist/graph/content-readers.d.ts +0 -12
  47. package/dist/graph/content-readers.d.ts.map +1 -1
  48. package/dist/graph/content-readers.js +0 -21
  49. package/dist/graph/content-readers.js.map +1 -1
  50. package/dist/graph/index.d.ts +5 -6
  51. package/dist/graph/index.d.ts.map +1 -1
  52. package/dist/graph/index.js +5 -6
  53. package/dist/graph/index.js.map +1 -1
  54. package/dist/graph/mailbox-adapter.d.ts +15 -15
  55. package/dist/graph/mappers/contact-mapper.d.ts +4 -1
  56. package/dist/graph/mappers/contact-mapper.d.ts.map +1 -1
  57. package/dist/graph/mappers/contact-mapper.js +7 -3
  58. package/dist/graph/mappers/contact-mapper.js.map +1 -1
  59. package/dist/graph/mappers/email-mapper.d.ts.map +1 -1
  60. package/dist/graph/mappers/email-mapper.js +7 -4
  61. package/dist/graph/mappers/email-mapper.js.map +1 -1
  62. package/dist/graph/mappers/event-mapper.d.ts.map +1 -1
  63. package/dist/graph/mappers/event-mapper.js +6 -3
  64. package/dist/graph/mappers/event-mapper.js.map +1 -1
  65. package/dist/graph/mappers/folder-mapper.d.ts +0 -4
  66. package/dist/graph/mappers/folder-mapper.d.ts.map +1 -1
  67. package/dist/graph/mappers/folder-mapper.js +9 -19
  68. package/dist/graph/mappers/folder-mapper.js.map +1 -1
  69. package/dist/graph/mappers/index.d.ts +2 -2
  70. package/dist/graph/mappers/index.d.ts.map +1 -1
  71. package/dist/graph/mappers/index.js +2 -2
  72. package/dist/graph/mappers/index.js.map +1 -1
  73. package/dist/graph/mappers/task-mapper.d.ts +4 -2
  74. package/dist/graph/mappers/task-mapper.d.ts.map +1 -1
  75. package/dist/graph/mappers/task-mapper.js +7 -5
  76. package/dist/graph/mappers/task-mapper.js.map +1 -1
  77. package/dist/graph/mappers/utils.d.ts +0 -10
  78. package/dist/graph/mappers/utils.d.ts.map +1 -1
  79. package/dist/graph/mappers/utils.js +0 -19
  80. package/dist/graph/mappers/utils.js.map +1 -1
  81. package/dist/graph/repository.d.ts +409 -276
  82. package/dist/graph/repository.d.ts.map +1 -1
  83. package/dist/graph/repository.js +772 -1082
  84. package/dist/graph/repository.js.map +1 -1
  85. package/dist/ids/mint.d.ts +31 -0
  86. package/dist/ids/mint.d.ts.map +1 -0
  87. package/dist/ids/mint.js +36 -0
  88. package/dist/ids/mint.js.map +1 -0
  89. package/dist/ids/next-action.d.ts +29 -0
  90. package/dist/ids/next-action.d.ts.map +1 -0
  91. package/dist/ids/next-action.js +58 -0
  92. package/dist/ids/next-action.js.map +1 -0
  93. package/dist/ids/resolver.d.ts +31 -0
  94. package/dist/ids/resolver.d.ts.map +1 -0
  95. package/dist/ids/resolver.js +52 -0
  96. package/dist/ids/resolver.js.map +1 -0
  97. package/dist/ids/schema.d.ts +91 -0
  98. package/dist/ids/schema.d.ts.map +1 -0
  99. package/dist/ids/schema.js +131 -0
  100. package/dist/ids/schema.js.map +1 -0
  101. package/dist/ids/token.d.ts +62 -0
  102. package/dist/ids/token.d.ts.map +1 -0
  103. package/dist/ids/token.js +225 -0
  104. package/dist/ids/token.js.map +1 -0
  105. package/dist/index.d.ts +10 -6
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +255 -5925
  108. package/dist/index.js.map +1 -1
  109. package/dist/parsers/index.d.ts +0 -1
  110. package/dist/parsers/index.d.ts.map +1 -1
  111. package/dist/parsers/index.js +0 -1
  112. package/dist/parsers/index.js.map +1 -1
  113. package/dist/registry/all-tools.d.ts +14 -0
  114. package/dist/registry/all-tools.d.ts.map +1 -0
  115. package/dist/registry/all-tools.js +68 -0
  116. package/dist/registry/all-tools.js.map +1 -0
  117. package/dist/registry/context.d.ts +19 -0
  118. package/dist/registry/context.d.ts.map +1 -0
  119. package/dist/registry/context.js +20 -0
  120. package/dist/registry/context.js.map +1 -0
  121. package/dist/registry/define-tool.d.ts +15 -0
  122. package/dist/registry/define-tool.d.ts.map +1 -0
  123. package/dist/registry/define-tool.js +8 -0
  124. package/dist/registry/define-tool.js.map +1 -0
  125. package/dist/registry/elicit-links.d.ts +35 -0
  126. package/dist/registry/elicit-links.d.ts.map +1 -0
  127. package/dist/registry/elicit-links.js +102 -0
  128. package/dist/registry/elicit-links.js.map +1 -0
  129. package/dist/registry/elicitor.d.ts +24 -0
  130. package/dist/registry/elicitor.d.ts.map +1 -0
  131. package/dist/registry/elicitor.js +51 -0
  132. package/dist/registry/elicitor.js.map +1 -0
  133. package/dist/registry/index.d.ts +13 -0
  134. package/dist/registry/index.d.ts.map +1 -0
  135. package/dist/registry/index.js +8 -0
  136. package/dist/registry/index.js.map +1 -0
  137. package/dist/registry/registry.d.ts +73 -0
  138. package/dist/registry/registry.d.ts.map +1 -0
  139. package/dist/registry/registry.js +221 -0
  140. package/dist/registry/registry.js.map +1 -0
  141. package/dist/registry/types.d.ts +146 -0
  142. package/dist/registry/types.d.ts.map +1 -0
  143. package/dist/registry/types.js +6 -0
  144. package/dist/registry/types.js.map +1 -0
  145. package/dist/search/compiler.d.ts +49 -0
  146. package/dist/search/compiler.d.ts.map +1 -0
  147. package/dist/search/compiler.js +168 -0
  148. package/dist/search/compiler.js.map +1 -0
  149. package/dist/signature.js +2 -2
  150. package/dist/signature.js.map +1 -1
  151. package/dist/state/delta-store.d.ts +85 -0
  152. package/dist/state/delta-store.d.ts.map +1 -0
  153. package/dist/state/delta-store.js +124 -0
  154. package/dist/state/delta-store.js.map +1 -0
  155. package/dist/state/migrate.d.ts +23 -0
  156. package/dist/state/migrate.d.ts.map +1 -0
  157. package/dist/state/migrate.js +81 -0
  158. package/dist/state/migrate.js.map +1 -0
  159. package/dist/state/schema.d.ts +26 -0
  160. package/dist/state/schema.d.ts.map +1 -0
  161. package/dist/state/schema.js +77 -0
  162. package/dist/state/schema.js.map +1 -0
  163. package/dist/state/store.d.ts +155 -0
  164. package/dist/state/store.d.ts.map +1 -0
  165. package/dist/state/store.js +321 -0
  166. package/dist/state/store.js.map +1 -0
  167. package/dist/tools/calendar-graph.d.ts +41 -0
  168. package/dist/tools/calendar-graph.d.ts.map +1 -0
  169. package/dist/tools/calendar-graph.js +299 -0
  170. package/dist/tools/calendar-graph.js.map +1 -0
  171. package/dist/tools/calendar-permissions.d.ts +17 -7
  172. package/dist/tools/calendar-permissions.d.ts.map +1 -1
  173. package/dist/tools/calendar-permissions.js +60 -4
  174. package/dist/tools/calendar-permissions.js.map +1 -1
  175. package/dist/tools/calendar.d.ts +133 -82
  176. package/dist/tools/calendar.d.ts.map +1 -1
  177. package/dist/tools/calendar.js +249 -136
  178. package/dist/tools/calendar.js.map +1 -1
  179. package/dist/tools/categories.d.ts +14 -4
  180. package/dist/tools/categories.d.ts.map +1 -1
  181. package/dist/tools/categories.js +59 -2
  182. package/dist/tools/categories.js.map +1 -1
  183. package/dist/tools/checklist-items.d.ts +19 -9
  184. package/dist/tools/checklist-items.d.ts.map +1 -1
  185. package/dist/tools/checklist-items.js +71 -5
  186. package/dist/tools/checklist-items.js.map +1 -1
  187. package/dist/tools/contact-folders.d.ts +50 -0
  188. package/dist/tools/contact-folders.d.ts.map +1 -0
  189. package/dist/tools/contact-folders.js +136 -0
  190. package/dist/tools/contact-folders.js.map +1 -0
  191. package/dist/tools/contacts-graph.d.ts +45 -0
  192. package/dist/tools/contacts-graph.d.ts.map +1 -0
  193. package/dist/tools/contacts-graph.js +174 -0
  194. package/dist/tools/contacts-graph.js.map +1 -0
  195. package/dist/tools/contacts.d.ts +61 -30
  196. package/dist/tools/contacts.d.ts.map +1 -1
  197. package/dist/tools/contacts.js +150 -93
  198. package/dist/tools/contacts.js.map +1 -1
  199. package/dist/tools/excel.d.ts +20 -10
  200. package/dist/tools/excel.d.ts.map +1 -1
  201. package/dist/tools/excel.js +82 -6
  202. package/dist/tools/excel.js.map +1 -1
  203. package/dist/tools/focused-overrides.d.ts +14 -4
  204. package/dist/tools/focused-overrides.d.ts.map +1 -1
  205. package/dist/tools/focused-overrides.js +59 -2
  206. package/dist/tools/focused-overrides.js.map +1 -1
  207. package/dist/tools/index.d.ts +4 -5
  208. package/dist/tools/index.d.ts.map +1 -1
  209. package/dist/tools/index.js +4 -6
  210. package/dist/tools/index.js.map +1 -1
  211. package/dist/tools/linked-resources.d.ts +17 -7
  212. package/dist/tools/linked-resources.d.ts.map +1 -1
  213. package/dist/tools/linked-resources.js +60 -4
  214. package/dist/tools/linked-resources.js.map +1 -1
  215. package/dist/tools/mail-graph.d.ts +38 -0
  216. package/dist/tools/mail-graph.d.ts.map +1 -0
  217. package/dist/tools/mail-graph.js +187 -0
  218. package/dist/tools/mail-graph.js.map +1 -0
  219. package/dist/tools/mail-rules.d.ts +20 -8
  220. package/dist/tools/mail-rules.d.ts.map +1 -1
  221. package/dist/tools/mail-rules.js +63 -7
  222. package/dist/tools/mail-rules.js.map +1 -1
  223. package/dist/tools/mail-send.d.ts +39 -27
  224. package/dist/tools/mail-send.d.ts.map +1 -1
  225. package/dist/tools/mail-send.js +249 -24
  226. package/dist/tools/mail-send.js.map +1 -1
  227. package/dist/tools/mail.d.ts +91 -68
  228. package/dist/tools/mail.d.ts.map +1 -1
  229. package/dist/tools/mail.js +232 -243
  230. package/dist/tools/mail.js.map +1 -1
  231. package/dist/tools/mailbox-organization.d.ts +51 -34
  232. package/dist/tools/mailbox-organization.d.ts.map +1 -1
  233. package/dist/tools/mailbox-organization.js +318 -35
  234. package/dist/tools/mailbox-organization.js.map +1 -1
  235. package/dist/tools/mailbox-settings.d.ts +59 -0
  236. package/dist/tools/mailbox-settings.d.ts.map +1 -0
  237. package/dist/tools/mailbox-settings.js +136 -0
  238. package/dist/tools/mailbox-settings.js.map +1 -0
  239. package/dist/tools/meetings.d.ts +24 -14
  240. package/dist/tools/meetings.d.ts.map +1 -1
  241. package/dist/tools/meetings.js +82 -6
  242. package/dist/tools/meetings.js.map +1 -1
  243. package/dist/tools/onedrive.d.ts +26 -16
  244. package/dist/tools/onedrive.d.ts.map +1 -1
  245. package/dist/tools/onedrive.js +140 -11
  246. package/dist/tools/onedrive.js.map +1 -1
  247. package/dist/tools/onenote.d.ts +63 -0
  248. package/dist/tools/onenote.d.ts.map +1 -0
  249. package/dist/tools/onenote.js +221 -0
  250. package/dist/tools/onenote.js.map +1 -0
  251. package/dist/tools/people.d.ts +10 -0
  252. package/dist/tools/people.d.ts.map +1 -1
  253. package/dist/tools/people.js +94 -0
  254. package/dist/tools/people.js.map +1 -1
  255. package/dist/tools/planner-visualization.d.ts +15 -5
  256. package/dist/tools/planner-visualization.d.ts.map +1 -1
  257. package/dist/tools/planner-visualization.js +58 -4
  258. package/dist/tools/planner-visualization.js.map +1 -1
  259. package/dist/tools/planner.d.ts +69 -39
  260. package/dist/tools/planner.d.ts.map +1 -1
  261. package/dist/tools/planner.js +231 -23
  262. package/dist/tools/planner.js.map +1 -1
  263. package/dist/tools/scheduling.d.ts +12 -0
  264. package/dist/tools/scheduling.d.ts.map +1 -1
  265. package/dist/tools/scheduling.js +38 -0
  266. package/dist/tools/scheduling.js.map +1 -1
  267. package/dist/tools/shared-mailbox.d.ts +164 -0
  268. package/dist/tools/shared-mailbox.d.ts.map +1 -0
  269. package/dist/tools/shared-mailbox.js +318 -0
  270. package/dist/tools/shared-mailbox.js.map +1 -0
  271. package/dist/tools/sharepoint-lists.d.ts +131 -0
  272. package/dist/tools/sharepoint-lists.d.ts.map +1 -0
  273. package/dist/tools/sharepoint-lists.js +246 -0
  274. package/dist/tools/sharepoint-lists.js.map +1 -0
  275. package/dist/tools/sharepoint.d.ts +24 -14
  276. package/dist/tools/sharepoint.d.ts.map +1 -1
  277. package/dist/tools/sharepoint.js +85 -9
  278. package/dist/tools/sharepoint.js.map +1 -1
  279. package/dist/tools/task-attachments.d.ts +17 -7
  280. package/dist/tools/task-attachments.d.ts.map +1 -1
  281. package/dist/tools/task-attachments.js +60 -4
  282. package/dist/tools/task-attachments.js.map +1 -1
  283. package/dist/tools/task-lists.d.ts +56 -0
  284. package/dist/tools/task-lists.d.ts.map +1 -0
  285. package/dist/tools/task-lists.js +155 -0
  286. package/dist/tools/task-lists.js.map +1 -0
  287. package/dist/tools/tasks-graph.d.ts +49 -0
  288. package/dist/tools/tasks-graph.d.ts.map +1 -0
  289. package/dist/tools/tasks-graph.js +193 -0
  290. package/dist/tools/tasks-graph.js.map +1 -0
  291. package/dist/tools/tasks.d.ts +121 -29
  292. package/dist/tools/tasks.d.ts.map +1 -1
  293. package/dist/tools/tasks.js +136 -84
  294. package/dist/tools/tasks.js.map +1 -1
  295. package/dist/tools/teams.d.ts +57 -46
  296. package/dist/tools/teams.d.ts.map +1 -1
  297. package/dist/tools/teams.js +288 -26
  298. package/dist/tools/teams.js.map +1 -1
  299. package/dist/tools/what-changed.d.ts +48 -0
  300. package/dist/tools/what-changed.d.ts.map +1 -0
  301. package/dist/tools/what-changed.js +149 -0
  302. package/dist/tools/what-changed.js.map +1 -0
  303. package/dist/types/calendar.d.ts +1 -1
  304. package/dist/types/calendar.d.ts.map +1 -1
  305. package/dist/types/calendar.js.map +1 -1
  306. package/dist/types/contacts.d.ts +1 -1
  307. package/dist/types/contacts.d.ts.map +1 -1
  308. package/dist/types/contacts.js.map +1 -1
  309. package/dist/types/mail.d.ts +1 -1
  310. package/dist/types/mail.d.ts.map +1 -1
  311. package/dist/types/mail.js.map +1 -1
  312. package/dist/utils/errors.d.ts +110 -38
  313. package/dist/utils/errors.d.ts.map +1 -1
  314. package/dist/utils/errors.js +239 -56
  315. package/dist/utils/errors.js.map +1 -1
  316. package/dist/visualization/types.d.ts +4 -4
  317. package/package.json +2 -3
  318. package/dist/applescript/account-repository.d.ts +0 -30
  319. package/dist/applescript/account-repository.d.ts.map +0 -1
  320. package/dist/applescript/account-repository.js +0 -38
  321. package/dist/applescript/account-repository.js.map +0 -1
  322. package/dist/applescript/account-scripts.d.ts +0 -21
  323. package/dist/applescript/account-scripts.d.ts.map +0 -1
  324. package/dist/applescript/account-scripts.js +0 -180
  325. package/dist/applescript/account-scripts.js.map +0 -1
  326. package/dist/applescript/calendar-manager.d.ts +0 -44
  327. package/dist/applescript/calendar-manager.d.ts.map +0 -1
  328. package/dist/applescript/calendar-manager.js +0 -92
  329. package/dist/applescript/calendar-manager.js.map +0 -1
  330. package/dist/applescript/calendar-writer.d.ts +0 -36
  331. package/dist/applescript/calendar-writer.d.ts.map +0 -1
  332. package/dist/applescript/calendar-writer.js +0 -94
  333. package/dist/applescript/calendar-writer.js.map +0 -1
  334. package/dist/applescript/content-readers.d.ts +0 -114
  335. package/dist/applescript/content-readers.d.ts.map +0 -1
  336. package/dist/applescript/content-readers.js +0 -328
  337. package/dist/applescript/content-readers.js.map +0 -1
  338. package/dist/applescript/executor.d.ts +0 -60
  339. package/dist/applescript/executor.d.ts.map +0 -1
  340. package/dist/applescript/executor.js +0 -173
  341. package/dist/applescript/executor.js.map +0 -1
  342. package/dist/applescript/index.d.ts +0 -20
  343. package/dist/applescript/index.d.ts.map +0 -1
  344. package/dist/applescript/index.js +0 -29
  345. package/dist/applescript/index.js.map +0 -1
  346. package/dist/applescript/mail-sender.d.ts +0 -38
  347. package/dist/applescript/mail-sender.d.ts.map +0 -1
  348. package/dist/applescript/mail-sender.js +0 -67
  349. package/dist/applescript/mail-sender.js.map +0 -1
  350. package/dist/applescript/parser.d.ts +0 -235
  351. package/dist/applescript/parser.d.ts.map +0 -1
  352. package/dist/applescript/parser.js +0 -496
  353. package/dist/applescript/parser.js.map +0 -1
  354. package/dist/applescript/repository.d.ts +0 -64
  355. package/dist/applescript/repository.d.ts.map +0 -1
  356. package/dist/applescript/repository.js +0 -444
  357. package/dist/applescript/repository.js.map +0 -1
  358. package/dist/applescript/scripts.d.ts +0 -265
  359. package/dist/applescript/scripts.d.ts.map +0 -1
  360. package/dist/applescript/scripts.js +0 -1483
  361. package/dist/applescript/scripts.js.map +0 -1
  362. package/dist/parsers/olk15.d.ts +0 -87
  363. package/dist/parsers/olk15.d.ts.map +0 -1
  364. package/dist/parsers/olk15.js +0 -368
  365. package/dist/parsers/olk15.js.map +0 -1
  366. package/dist/tools/notes.d.ts +0 -74
  367. package/dist/tools/notes.d.ts.map +0 -1
  368. package/dist/tools/notes.js +0 -136
  369. package/dist/tools/notes.js.map +0 -1
@@ -12,10 +12,47 @@
12
12
  * - Error handling
13
13
  */
14
14
  import 'isomorphic-fetch';
15
- import { Client } from '@microsoft/microsoft-graph-client';
15
+ import { Client, ResponseType, AuthenticationHandler, RetryHandler, RetryHandlerOptions, RedirectHandler, HTTPMessageHandler, } from '@microsoft/microsoft-graph-client';
16
16
  import { getAccessToken } from '../auth/index.js';
17
17
  import { buildBatchPayload, splitIntoBatches, parseBatchResponse } from './batch.js';
18
18
  import { ResponseCache, CacheTTL, createCacheKey } from './cache.js';
19
+ import { ImmutableIdMiddleware } from './immutable-id-middleware.js';
20
+ // The SDK's RetryHandler only considers 429/503/504 retriable (its private
21
+ // static RETRY_STATUS_CODES), gating `this.isRetry(response)` BEFORE our
22
+ // shouldRetry callback runs — so a callback can only *narrow* that set, never
23
+ // widen it. D5 also wants 502 Bad Gateway retried, so we widen the SDK's set
24
+ // once at load. Our shouldRetry still narrows to idempotent GET/HEAD/OPTIONS,
25
+ // and this server builds only its own client, so the shared-static mutation
26
+ // has no cross-tenant effect.
27
+ const RETRY_STATUS_CODES = RetryHandler
28
+ .RETRY_STATUS_CODES;
29
+ if (Array.isArray(RETRY_STATUS_CODES) && !RETRY_STATUS_CODES.includes(502)) {
30
+ RETRY_STATUS_CODES.push(502);
31
+ }
32
+ /**
33
+ * D5 retry policy (exported for testing). Retries only idempotent reads on
34
+ * transient failures — 429 plus 502/503/504 — honoring Retry-After via the
35
+ * SDK-computed delay. NEVER retries a send/write once the body is on the wire:
36
+ * an ambiguous 429/5xx on a POST could double-send. Paired with the module-load
37
+ * widening above (the SDK gates 502 out by default), this makes 502 genuinely
38
+ * retriable for idempotent reads while leaving writes/sendMail untouched.
39
+ */
40
+ export function shouldRetryGraphRequest(method, url, status) {
41
+ const m = method.toUpperCase();
42
+ if (m !== 'GET' && m !== 'HEAD' && m !== 'OPTIONS') {
43
+ return false;
44
+ }
45
+ // Never retry an OData action (POST-shaped operation like /sendMail, /reply,
46
+ // /createReply of writes) even if the transport surfaces it as a GET. Anchor
47
+ // the action to the end of the path (or a query string) so a drive item
48
+ // literally named e.g. "reply.docx" read via GET is still retriable.
49
+ if (/\/(sendMail|reply|forward|createReply|createForward)(?:\?|$)/i.test(url)) {
50
+ return false;
51
+ }
52
+ return status === 429 || status === 502 || status === 503 || status === 504;
53
+ }
54
+ /** Fields selected for message search results (shared across search mechanisms). */
55
+ const MESSAGE_SEARCH_SELECT = 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId';
19
56
  /**
20
57
  * Graph client wrapper with caching and token management.
21
58
  */
@@ -32,18 +69,33 @@ export class GraphClient {
32
69
  // eslint-disable-next-line @typescript-eslint/require-await
33
70
  async getClient() {
34
71
  if (this.client == null) {
35
- this.client = Client.init({
36
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
37
- authProvider: async (done) => {
38
- try {
39
- const token = await getAccessToken(this.deviceCodeCallback);
40
- done(null, token);
41
- }
42
- catch (error) {
43
- done(error, null);
44
- }
45
- },
46
- });
72
+ const authProvider = {
73
+ getAccessToken: () => getAccessToken(this.deviceCodeCallback),
74
+ };
75
+ const shouldRetry = (_delay, _attempt, request, options, response) => {
76
+ const method = (options?.method ?? 'GET').toString();
77
+ // The SDK types `request` as RequestInfo, which resolves to `any` under
78
+ // this repo's lib config; cast to the shape we read to stay lint-clean.
79
+ const url = typeof request === 'string' ? request : String(request.url ?? '');
80
+ return shouldRetryGraphRequest(method, url, response?.status ?? 0);
81
+ };
82
+ // Chain mirrors the SDK default order (auth → retry → redirect → http),
83
+ // minus the cosmetic TelemetryHandler, plus the immutable-ID preference
84
+ // (U5b-3) sitting right after auth so its Prefer header rides every retry
85
+ // attempt without being re-appended. RedirectHandler matters: the binary
86
+ // download endpoints (/content, /photo/$value, meeting recordings) 302 to
87
+ // a pre-authenticated CDN URL, so the redirect must be followed explicitly
88
+ // rather than relying on the underlying fetch default.
89
+ const auth = new AuthenticationHandler(authProvider);
90
+ const immutableId = new ImmutableIdMiddleware();
91
+ const retry = new RetryHandler(new RetryHandlerOptions(undefined, undefined, shouldRetry));
92
+ const redirect = new RedirectHandler();
93
+ const http = new HTTPMessageHandler();
94
+ auth.setNext(immutableId);
95
+ immutableId.setNext(retry);
96
+ retry.setNext(redirect);
97
+ redirect.setNext(http);
98
+ this.client = Client.initWithMiddleware({ middleware: auth });
47
99
  }
48
100
  return this.client;
49
101
  }
@@ -160,7 +212,9 @@ export class GraphClient {
160
212
  .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
161
213
  .top(limit)
162
214
  .get();
163
- return response.value;
215
+ const messages = response.value;
216
+ await this.upgradeMessageIdsToImmutable(messages);
217
+ return messages;
164
218
  }
165
219
  /**
166
220
  * Searches messages in a specific folder.
@@ -173,35 +227,142 @@ export class GraphClient {
173
227
  .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId')
174
228
  .top(limit)
175
229
  .get();
176
- return response.value;
230
+ const messages = response.value;
231
+ await this.upgradeMessageIdsToImmutable(messages);
232
+ return messages;
177
233
  }
178
234
  /**
179
- * Searches messages using raw KQL (Keyword Query Language).
180
- * Unlike searchMessages, the query is passed directly without quote-wrapping,
181
- * enabling KQL operators like from:, subject:, hasAttachments:, received>=, AND, OR.
235
+ * Property-only structured search (U7 / D9): `$filter` on messages. The filter
236
+ * string is built server-side by the search compiler (validated OData syntax).
182
237
  */
183
- async searchMessagesKql(query, limit = 50) {
238
+ async searchMessagesFilter(filter, limit = 50) {
184
239
  const client = await this.getClient();
240
+ // No $orderby: Graph returns InefficientFilter when a sender/flag $filter is
241
+ // combined with $orderby (D9 spike), and /me/messages already defaults to
242
+ // receivedDateTime desc, so results stay recent-first without it.
185
243
  const response = await client
186
244
  .api('/me/messages')
187
- .search(query)
188
- .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
245
+ .filter(filter)
246
+ .select(MESSAGE_SEARCH_SELECT)
189
247
  .top(limit)
190
248
  .get();
191
249
  return response.value;
192
250
  }
193
251
  /**
194
- * Searches messages in a specific folder using raw KQL.
252
+ * Free-text structured search (U7 / D9): quoted `$search` on messages. The
253
+ * search value is built by the compiler already correctly quoted (e.g.
254
+ * `"subject:report"`), which the D9 spike confirmed property-scopes correctly.
195
255
  */
196
- async searchMessagesKqlInFolder(folderId, query, limit = 50) {
256
+ async searchMessagesSearchValue(searchValue, limit = 50) {
197
257
  const client = await this.getClient();
198
258
  const response = await client
199
- .api(`/me/mailFolders/${folderId}/messages`)
200
- .search(query)
201
- .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
259
+ .api('/me/messages')
260
+ .search(searchValue)
261
+ .select(MESSAGE_SEARCH_SELECT)
202
262
  .top(limit)
203
263
  .get();
204
- return response.value;
264
+ const messages = response.value;
265
+ await this.upgradeMessageIdsToImmutable(messages);
266
+ return messages;
267
+ }
268
+ /**
269
+ * Mixed property + free-text structured search (U7 / D9): `POST /search/query`
270
+ * with a server-built KQL string — the only single-request path when both are
271
+ * present. Normalizes the hitsContainers response to a Message[].
272
+ *
273
+ * Limitation (verified in the D9 spike): /search/query populates from/subject/
274
+ * receivedDateTime/isRead/hasAttachments/parentFolderId, but NOT toRecipients
275
+ * or flag, regardless of the requested fields — so mixed-mode results carry no
276
+ * recipient list or flag. Acceptable for a result list (sender/subject/date
277
+ * are present); the property-only and free-text-only paths are unaffected.
278
+ */
279
+ async searchMessagesQuery(kql, limit = 50) {
280
+ const client = await this.getClient();
281
+ const body = {
282
+ requests: [
283
+ {
284
+ entityTypes: ['message'],
285
+ query: { queryString: kql },
286
+ from: 0,
287
+ size: limit,
288
+ fields: MESSAGE_SEARCH_SELECT.split(','),
289
+ },
290
+ ],
291
+ };
292
+ const response = (await client.api('/search/query').post(body));
293
+ const hits = response.value?.[0]?.hitsContainers?.[0]?.hits ?? [];
294
+ // Guard against a hit without a resource so no undefined enters the mapper.
295
+ const messages = hits.map((h) => h.resource).filter((r) => r != null);
296
+ await this.upgradeMessageIdsToImmutable(messages);
297
+ return messages;
298
+ }
299
+ /**
300
+ * Translates Exchange item IDs between formats (U5b-3 / D2). Used to upgrade
301
+ * the mutable REST IDs that `$search` returns into immutable REST entry IDs, so
302
+ * search-minted self-encoding tokens (`em_`) survive a later move. Returns a map
303
+ * of source ID → immutable target ID; IDs that Graph could not translate are
304
+ * simply absent (partial success is normal and handled by the caller).
305
+ */
306
+ async translateExchangeIds(inputIds, sourceIdType = 'restId', targetIdType = 'restImmutableEntryId') {
307
+ const out = new Map();
308
+ if (inputIds.length === 0) {
309
+ return out;
310
+ }
311
+ const client = await this.getClient();
312
+ // Graph caps translateExchangeIds at 1000 input IDs per call; chunk to stay
313
+ // under it even though search result sets are far smaller.
314
+ for (let i = 0; i < inputIds.length; i += 1000) {
315
+ const chunk = inputIds.slice(i, i + 1000);
316
+ const response = (await client.api('/me/translateExchangeIds').post({
317
+ inputIds: chunk,
318
+ sourceIdType,
319
+ targetIdType,
320
+ }));
321
+ for (const pair of response.value ?? []) {
322
+ if (pair.sourceId != null && pair.sourceId.length > 0 && pair.targetId != null && pair.targetId.length > 0) {
323
+ out.set(pair.sourceId, pair.targetId);
324
+ }
325
+ }
326
+ }
327
+ return out;
328
+ }
329
+ /**
330
+ * Rewrites the mutable `$search`-minted IDs on a message list to their
331
+ * immutable equivalents in place (U5b-3 / D2), so the mapper mints durable
332
+ * `em_` tokens. Graceful degradation: if translation fails wholesale (throw) or
333
+ * for individual IDs (absent from the map), those messages keep their mutable
334
+ * ID — the `em_` token still resolves this session, it just isn't move-durable.
335
+ * Returns the number of IDs left un-upgraded (the caller's `degraded_ids` count).
336
+ */
337
+ async upgradeMessageIdsToImmutable(messages) {
338
+ const mutableIds = messages
339
+ .map((m) => m.id)
340
+ .filter((id) => typeof id === 'string' && id.length > 0);
341
+ if (mutableIds.length === 0) {
342
+ return 0;
343
+ }
344
+ let translated;
345
+ try {
346
+ translated = await this.translateExchangeIds(mutableIds);
347
+ }
348
+ catch {
349
+ // Whole-batch failure (throttled/unavailable): leave every ID mutable.
350
+ return mutableIds.length;
351
+ }
352
+ let degraded = 0;
353
+ for (const message of messages) {
354
+ if (typeof message.id !== 'string' || message.id.length === 0) {
355
+ continue;
356
+ }
357
+ const immutable = translated.get(message.id);
358
+ if (immutable != null) {
359
+ message.id = immutable;
360
+ }
361
+ else {
362
+ degraded += 1;
363
+ }
364
+ }
365
+ return degraded;
205
366
  }
206
367
  /**
207
368
  * Lists messages in a conversation by conversationId.
@@ -237,27 +398,62 @@ export class GraphClient {
237
398
  */
238
399
  async getMessagesDelta(folderId, deltaLink) {
239
400
  const client = await this.getClient();
240
- let response;
401
+ let page;
241
402
  if (deltaLink != null) {
242
- response = await client.api(deltaLink).get();
403
+ page = await client.api(deltaLink).get();
243
404
  }
244
405
  else {
245
- response = await client
406
+ page = await client
246
407
  .api(`/me/mailFolders/${folderId}/messages/delta`)
247
408
  .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,internetMessageId,parentFolderId')
248
409
  .top(50)
249
410
  .get();
250
411
  }
251
- const messages = [...(response.value ?? [])];
252
- let nextLink = response['@odata.nextLink'];
412
+ const messages = [...(page.value ?? [])];
413
+ // The deltaLink lands on the LAST page; paging through nextLinks and reading
414
+ // it from the first response would yield '' whenever the delta spans >1 page.
415
+ let nextLink = page['@odata.nextLink'];
253
416
  while (nextLink != null) {
254
- const nextPage = await client.api(nextLink).get();
255
- messages.push(...(nextPage.value ?? []));
256
- nextLink = nextPage['@odata.nextLink'];
417
+ page = await client.api(nextLink).get();
418
+ messages.push(...(page.value ?? []));
419
+ nextLink = page['@odata.nextLink'];
257
420
  }
258
- const newDeltaLink = response['@odata.deltaLink'] ?? '';
421
+ const newDeltaLink = page['@odata.deltaLink'] ?? '';
259
422
  return { messages, deltaLink: newDeltaLink };
260
423
  }
424
+ /**
425
+ * Gets calendar-view delta for incremental event sync (U12).
426
+ *
427
+ * The v1.0 event delta is served by `/me/calendarView/delta`, bounded by a
428
+ * start/end window that is baked into the returned deltaLink — subsequent
429
+ * rounds just follow that link. `$select` is unsupported here, so full event
430
+ * objects come back; deletes arrive as `@removed` entries. The deltaLink lands
431
+ * on the *last* page, so it is read after paging (not from the first response).
432
+ */
433
+ async getCalendarViewDelta(startDateTime, endDateTime, deltaLink) {
434
+ const client = await this.getClient();
435
+ let page;
436
+ if (deltaLink != null) {
437
+ page = await client.api(deltaLink).get();
438
+ }
439
+ else {
440
+ const start = encodeURIComponent(startDateTime);
441
+ const end = encodeURIComponent(endDateTime);
442
+ page = await client
443
+ .api(`/me/calendarView/delta?startDateTime=${start}&endDateTime=${end}`)
444
+ .header('Prefer', 'odata.maxpagesize=50')
445
+ .get();
446
+ }
447
+ const events = [...(page.value ?? [])];
448
+ let nextLink = page['@odata.nextLink'];
449
+ while (nextLink != null) {
450
+ page = await client.api(nextLink).get();
451
+ events.push(...(page.value ?? []));
452
+ nextLink = page['@odata.nextLink'];
453
+ }
454
+ const newDeltaLink = page['@odata.deltaLink'] ?? '';
455
+ return { events, deltaLink: newDeltaLink };
456
+ }
261
457
  // ===========================================================================
262
458
  // Mail Rules
263
459
  // ===========================================================================
@@ -560,7 +756,7 @@ export class GraphClient {
560
756
  const client = await this.getClient();
561
757
  return await client
562
758
  .api(`/me/contacts/${contactId}/photo/$value`)
563
- .get();
759
+ .responseType(ResponseType.ARRAYBUFFER).get();
564
760
  }
565
761
  /**
566
762
  * Sets or updates the photo for a contact.
@@ -628,7 +824,7 @@ export class GraphClient {
628
824
  const client = await this.getClient();
629
825
  const response = await client
630
826
  .api('/me/todo/lists')
631
- .select('id,displayName,isOwner,isShared,wellknownListName')
827
+ .select('id,displayName,wellknownListName')
632
828
  .get();
633
829
  const result = response.value;
634
830
  this.cache.set(cacheKey, result, CacheTTL.FOLDERS);
@@ -894,22 +1090,32 @@ export class GraphClient {
894
1090
  /**
895
1091
  * Creates a reply draft for a message.
896
1092
  */
897
- async createReplyDraft(messageId) {
1093
+ async createReplyDraft(messageId, comment, body) {
898
1094
  const client = await this.getClient();
1095
+ const postBody = {};
1096
+ if (comment != null)
1097
+ postBody.comment = comment;
1098
+ if (body != null)
1099
+ postBody.message = { body };
899
1100
  const result = await client
900
1101
  .api(`/me/messages/${messageId}/createReply`)
901
- .post(null);
1102
+ .post(Object.keys(postBody).length > 0 ? postBody : null);
902
1103
  this.cache.clear();
903
1104
  return result;
904
1105
  }
905
1106
  /**
906
1107
  * Creates a reply-all draft for a message.
907
1108
  */
908
- async createReplyAllDraft(messageId) {
1109
+ async createReplyAllDraft(messageId, comment, body) {
909
1110
  const client = await this.getClient();
1111
+ const postBody = {};
1112
+ if (comment != null)
1113
+ postBody.comment = comment;
1114
+ if (body != null)
1115
+ postBody.message = { body };
910
1116
  const result = await client
911
1117
  .api(`/me/messages/${messageId}/createReplyAll`)
912
- .post(null);
1118
+ .post(Object.keys(postBody).length > 0 ? postBody : null);
913
1119
  this.cache.clear();
914
1120
  return result;
915
1121
  }
@@ -1493,6 +1699,10 @@ export class GraphClient {
1493
1699
  const client = await this.getClient();
1494
1700
  return await client.api('/planner/buckets').post({ planId, name });
1495
1701
  }
1702
+ async getBucket(bucketId) {
1703
+ const client = await this.getClient();
1704
+ return await client.api(`/planner/buckets/${bucketId}`).get();
1705
+ }
1496
1706
  async updateBucket(bucketId, updates, etag) {
1497
1707
  const client = await this.getClient();
1498
1708
  return await client.api(`/planner/buckets/${bucketId}`).header('If-Match', etag).patch(updates);
@@ -1509,6 +1719,12 @@ export class GraphClient {
1509
1719
  const response = await client.api(`/planner/plans/${planId}/tasks`).get();
1510
1720
  return response.value;
1511
1721
  }
1722
+ /** All Planner tasks assigned to the signed-in user, across every plan. */
1723
+ async listMyPlannerTasks() {
1724
+ const client = await this.getClient();
1725
+ const response = await client.api('/me/planner/tasks').get();
1726
+ return response.value;
1727
+ }
1512
1728
  async getPlannerTask(taskId) {
1513
1729
  const client = await this.getClient();
1514
1730
  return await client.api(`/planner/tasks/${taskId}`).get();
@@ -1561,7 +1777,7 @@ export class GraphClient {
1561
1777
  }
1562
1778
  async getUserPhoto(identifier) {
1563
1779
  const client = await this.getClient();
1564
- return await client.api(`/users/${identifier}/photo/$value`).get();
1780
+ return await client.api(`/users/${identifier}/photo/$value`).responseType(ResponseType.ARRAYBUFFER).get();
1565
1781
  }
1566
1782
  async getUserPresence(identifier) {
1567
1783
  const client = await this.getClient();
@@ -1572,6 +1788,116 @@ export class GraphClient {
1572
1788
  const response = await client.api('/communications/getPresencesByUserId').post({ ids: userIds });
1573
1789
  return response.value;
1574
1790
  }
1791
+ // ===========================================================================
1792
+ // Shared Mailbox / Delegate Access (/users/{upn}/...) — read-only (#40)
1793
+ // ===========================================================================
1794
+ //
1795
+ // These mirror the `/me/...` read paths but target another user's mailbox,
1796
+ // calendar, or drive via delegate/shared access. Unlike the `/me` variants,
1797
+ // they deliberately DO NOT swallow errors: a 403 must surface (mapped to
1798
+ // GRAPH_PERMISSION_DENIED at the dispatch chokepoint) so the caller learns
1799
+ // they lack shared access rather than getting a silent empty/`null` result.
1800
+ // Results are returned with raw Graph ids (durable tokens are `/me`- and
1801
+ // account-scoped, so they cannot address another mailbox's items).
1802
+ async listSharedMailFolders(mailbox) {
1803
+ const client = await this.getClient();
1804
+ const response = await client
1805
+ .api(`/users/${encodeURIComponent(mailbox)}/mailFolders`)
1806
+ .select('id,displayName,parentFolderId,totalItemCount,unreadItemCount')
1807
+ .top(100)
1808
+ .get();
1809
+ return response.value;
1810
+ }
1811
+ async listSharedMessages(mailbox, folderId, limit = 25, unreadOnly = false) {
1812
+ const client = await this.getClient();
1813
+ const apiPath = folderId != null
1814
+ ? `/users/${encodeURIComponent(mailbox)}/mailFolders/${encodeURIComponent(folderId)}/messages`
1815
+ : `/users/${encodeURIComponent(mailbox)}/messages`;
1816
+ let request = client
1817
+ .api(apiPath)
1818
+ .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId')
1819
+ .top(limit);
1820
+ if (unreadOnly) {
1821
+ // Graph rejects `$orderby` on a property not led by `$filter` (isRead),
1822
+ // so drop the sort when filtering unread and rely on the default order.
1823
+ request = request.filter('isRead eq false');
1824
+ }
1825
+ else {
1826
+ request = request.orderby('receivedDateTime desc');
1827
+ }
1828
+ const response = await request.get();
1829
+ return response.value;
1830
+ }
1831
+ async getSharedMessage(mailbox, messageId, includeBody = false) {
1832
+ const client = await this.getClient();
1833
+ const select = includeBody
1834
+ ? 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,body,bodyPreview,conversationId,parentFolderId'
1835
+ : 'id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId,parentFolderId';
1836
+ return await client
1837
+ .api(`/users/${encodeURIComponent(mailbox)}/messages/${encodeURIComponent(messageId)}`)
1838
+ .select(select)
1839
+ .get();
1840
+ }
1841
+ async searchSharedMessages(mailbox, query, limit = 25) {
1842
+ const client = await this.getClient();
1843
+ // Mail $search cannot be combined with $orderby, so ordering is omitted.
1844
+ // Strip embedded double quotes so a stray `"` can't break out of the
1845
+ // quoted KQL `$search="..."` term.
1846
+ const safeQuery = query.replace(/"/g, ' ');
1847
+ const response = await client
1848
+ .api(`/users/${encodeURIComponent(mailbox)}/messages`)
1849
+ .query({ $search: `"${safeQuery}"` })
1850
+ .select('id,subject,from,toRecipients,ccRecipients,receivedDateTime,sentDateTime,isRead,hasAttachments,importance,flag,bodyPreview,conversationId')
1851
+ .top(limit)
1852
+ .get();
1853
+ return response.value;
1854
+ }
1855
+ async listSharedEvents(mailbox, limit = 25, startDate, endDate) {
1856
+ const client = await this.getClient();
1857
+ if (startDate != null && endDate != null) {
1858
+ const response = await client
1859
+ .api(`/users/${encodeURIComponent(mailbox)}/calendarView`)
1860
+ .query({ startDateTime: startDate.toISOString(), endDateTime: endDate.toISOString() })
1861
+ .select('id,subject,start,end,location,isAllDay,organizer,attendees,bodyPreview')
1862
+ .orderby('start/dateTime')
1863
+ .top(limit)
1864
+ .get();
1865
+ return response.value;
1866
+ }
1867
+ const response = await client
1868
+ .api(`/users/${encodeURIComponent(mailbox)}/events`)
1869
+ .select('id,subject,start,end,location,isAllDay,organizer,attendees,bodyPreview')
1870
+ .orderby('start/dateTime')
1871
+ .top(limit)
1872
+ .get();
1873
+ return response.value;
1874
+ }
1875
+ async getSharedEvent(mailbox, eventId) {
1876
+ const client = await this.getClient();
1877
+ return await client
1878
+ .api(`/users/${encodeURIComponent(mailbox)}/events/${encodeURIComponent(eventId)}`)
1879
+ .select('id,subject,start,end,location,isAllDay,organizer,attendees,body,recurrence,bodyPreview')
1880
+ .get();
1881
+ }
1882
+ async listSharedDriveItems(mailbox, itemId) {
1883
+ const client = await this.getClient();
1884
+ const apiPath = itemId != null
1885
+ ? `/users/${encodeURIComponent(mailbox)}/drive/items/${encodeURIComponent(itemId)}/children`
1886
+ : `/users/${encodeURIComponent(mailbox)}/drive/root/children`;
1887
+ const response = await client.api(apiPath).get();
1888
+ return response.value;
1889
+ }
1890
+ async searchSharedDriveItems(mailbox, query, limit = 25) {
1891
+ const client = await this.getClient();
1892
+ // Escape for the OData string literal first (a lone `'` closes the literal;
1893
+ // OData escapes it by doubling), then URL-encode the whole term.
1894
+ const safeQuery = encodeURIComponent(query.replace(/'/g, "''"));
1895
+ const response = await client
1896
+ .api(`/users/${encodeURIComponent(mailbox)}/drive/root/search(q='${safeQuery}')`)
1897
+ .top(limit)
1898
+ .get();
1899
+ return response.value;
1900
+ }
1575
1901
  /**
1576
1902
  * Sends multiple requests in a single $batch call to the Graph API.
1577
1903
  * Automatically splits into multiple batches if there are more than 20 requests.
@@ -1595,11 +1921,13 @@ export class GraphClient {
1595
1921
  // ===========================================================================
1596
1922
  async listOnlineMeetings(limit = 20) {
1597
1923
  const client = await this.getClient();
1924
+ // Graph rejects $top on /me/onlineMeetings, so limit client-side. Guard a
1925
+ // negative limit (slice(0, -n) would count from the end).
1926
+ const cap = Math.max(0, limit);
1598
1927
  const response = await client.api('/me/onlineMeetings')
1599
- .top(limit)
1600
1928
  .orderby('startDateTime desc')
1601
1929
  .get();
1602
- return response.value;
1930
+ return response.value.slice(0, cap);
1603
1931
  }
1604
1932
  async getOnlineMeeting(meetingId) {
1605
1933
  const client = await this.getClient();
@@ -1612,7 +1940,7 @@ export class GraphClient {
1612
1940
  }
1613
1941
  async getMeetingRecordingContent(meetingId, recordingId) {
1614
1942
  const client = await this.getClient();
1615
- return await client.api(`/me/onlineMeetings/${meetingId}/recordings/${recordingId}/content`).get();
1943
+ return await client.api(`/me/onlineMeetings/${meetingId}/recordings/${recordingId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
1616
1944
  }
1617
1945
  async listMeetingTranscripts(meetingId) {
1618
1946
  const client = await this.getClient();
@@ -1685,7 +2013,7 @@ export class GraphClient {
1685
2013
  }
1686
2014
  async downloadDriveItem(itemId) {
1687
2015
  const client = await this.getClient();
1688
- return await client.api(`/me/drive/items/${itemId}/content`).get();
2016
+ return await client.api(`/me/drive/items/${itemId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
1689
2017
  }
1690
2018
  async uploadDriveItem(parentPath, fileName, content) {
1691
2019
  const client = await this.getClient();
@@ -1759,7 +2087,167 @@ export class GraphClient {
1759
2087
  */
1760
2088
  async downloadLibraryFile(driveId, itemId) {
1761
2089
  const client = await this.getClient();
1762
- return await client.api(`/drives/${driveId}/items/${itemId}/content`).get();
2090
+ return await client.api(`/drives/${driveId}/items/${itemId}/content`).responseType(ResponseType.ARRAYBUFFER).get();
2091
+ }
2092
+ // ===========================================================================
2093
+ // SharePoint Lists
2094
+ // ===========================================================================
2095
+ /**
2096
+ * Lists the SharePoint lists in a site.
2097
+ */
2098
+ async listSharePointLists(siteId) {
2099
+ const client = await this.getClient();
2100
+ const response = await client
2101
+ .api(`/sites/${siteId}/lists`)
2102
+ .select('id,name,displayName,description,webUrl,createdDateTime,lastModifiedDateTime')
2103
+ .get();
2104
+ return response.value;
2105
+ }
2106
+ /**
2107
+ * Gets a specific SharePoint list.
2108
+ */
2109
+ async getSharePointList(siteId, listId) {
2110
+ const client = await this.getClient();
2111
+ return await client.api(`/sites/${siteId}/lists/${listId}`).get();
2112
+ }
2113
+ /**
2114
+ * Creates a SharePoint list in a site.
2115
+ */
2116
+ async createSharePointList(siteId, body) {
2117
+ const client = await this.getClient();
2118
+ return await client.api(`/sites/${siteId}/lists`).post(body);
2119
+ }
2120
+ /**
2121
+ * Lists the column definitions for a SharePoint list.
2122
+ */
2123
+ async listSharePointListColumns(siteId, listId) {
2124
+ const client = await this.getClient();
2125
+ const response = await client
2126
+ .api(`/sites/${siteId}/lists/${listId}/columns`)
2127
+ .get();
2128
+ return response.value;
2129
+ }
2130
+ /**
2131
+ * Lists the items in a SharePoint list, expanding their field values.
2132
+ */
2133
+ async listSharePointListItems(siteId, listId, limit = 50) {
2134
+ const client = await this.getClient();
2135
+ const response = await client
2136
+ .api(`/sites/${siteId}/lists/${listId}/items`)
2137
+ .expand('fields')
2138
+ .top(limit)
2139
+ .get();
2140
+ return response.value;
2141
+ }
2142
+ /**
2143
+ * Gets a specific SharePoint list item, expanding its field values.
2144
+ */
2145
+ async getSharePointListItem(siteId, listId, itemId) {
2146
+ const client = await this.getClient();
2147
+ return await client
2148
+ .api(`/sites/${siteId}/lists/${listId}/items/${itemId}`)
2149
+ .expand('fields')
2150
+ .get();
2151
+ }
2152
+ /**
2153
+ * Creates an item in a SharePoint list from a map of column → value.
2154
+ */
2155
+ async createSharePointListItem(siteId, listId, fields) {
2156
+ const client = await this.getClient();
2157
+ return await client.api(`/sites/${siteId}/lists/${listId}/items`).post({ fields });
2158
+ }
2159
+ /**
2160
+ * Updates the field values of a SharePoint list item.
2161
+ */
2162
+ async updateSharePointListItem(siteId, listId, itemId, fields) {
2163
+ const client = await this.getClient();
2164
+ return await client.api(`/sites/${siteId}/lists/${listId}/items/${itemId}/fields`).patch(fields);
2165
+ }
2166
+ /**
2167
+ * Deletes an item from a SharePoint list.
2168
+ */
2169
+ async deleteSharePointListItem(siteId, listId, itemId) {
2170
+ const client = await this.getClient();
2171
+ await client.api(`/sites/${siteId}/lists/${listId}/items/${itemId}`).delete();
2172
+ }
2173
+ // ===========================================================================
2174
+ // OneNote
2175
+ // ===========================================================================
2176
+ /**
2177
+ * Lists all OneNote notebooks for the current user.
2178
+ */
2179
+ async listNotebooks() {
2180
+ const client = await this.getClient();
2181
+ const response = await client.api('/me/onenote/notebooks').get();
2182
+ return response.value;
2183
+ }
2184
+ /**
2185
+ * Lists OneNote sections, optionally scoped to a notebook.
2186
+ */
2187
+ async listNoteSections(notebookGraphId) {
2188
+ const client = await this.getClient();
2189
+ const apiPath = notebookGraphId != null
2190
+ ? `/me/onenote/notebooks/${notebookGraphId}/sections`
2191
+ : '/me/onenote/sections';
2192
+ const response = await client.api(apiPath).get();
2193
+ return response.value;
2194
+ }
2195
+ /**
2196
+ * Lists OneNote pages, optionally scoped to a section.
2197
+ */
2198
+ async listNotePages(sectionGraphId) {
2199
+ const client = await this.getClient();
2200
+ if (sectionGraphId != null) {
2201
+ const response = await client.api(`/me/onenote/sections/${sectionGraphId}/pages`).get();
2202
+ return response.value;
2203
+ }
2204
+ const response = await client
2205
+ .api('/me/onenote/pages')
2206
+ .top(50)
2207
+ .orderby('lastModifiedDateTime desc')
2208
+ .get();
2209
+ return response.value;
2210
+ }
2211
+ /**
2212
+ * Gets a OneNote page's metadata.
2213
+ */
2214
+ async getNotePage(pageGraphId) {
2215
+ const client = await this.getClient();
2216
+ return await client.api(`/me/onenote/pages/${pageGraphId}`).get();
2217
+ }
2218
+ /**
2219
+ * Gets a OneNote page's HTML content.
2220
+ */
2221
+ async getNotePageContent(pageGraphId) {
2222
+ const client = await this.getClient();
2223
+ return await client
2224
+ .api(`/me/onenote/pages/${pageGraphId}/content`)
2225
+ .responseType(ResponseType.TEXT)
2226
+ .get();
2227
+ }
2228
+ /**
2229
+ * Searches OneNote pages by keyword.
2230
+ */
2231
+ async searchNotePages(query) {
2232
+ const client = await this.getClient();
2233
+ // Strip embedded double-quotes so they can't unbalance the quoted $search
2234
+ // expression (`$search="..."`) and 400 the request.
2235
+ const safeQuery = query.replace(/"/g, '');
2236
+ const response = await client
2237
+ .api('/me/onenote/pages')
2238
+ .search(`"${safeQuery}"`)
2239
+ .get();
2240
+ return response.value;
2241
+ }
2242
+ /**
2243
+ * Creates a new OneNote page in a section from raw HTML.
2244
+ */
2245
+ async createNotePage(sectionGraphId, html) {
2246
+ const client = await this.getClient();
2247
+ return await client
2248
+ .api(`/me/onenote/sections/${sectionGraphId}/pages`)
2249
+ .header('Content-Type', 'text/html')
2250
+ .post(html);
1763
2251
  }
1764
2252
  }
1765
2253
  /**