@37signals/basecamp 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/README.md +600 -0
  2. package/dist/auth-strategy.d.ts +36 -0
  3. package/dist/auth-strategy.d.ts.map +1 -0
  4. package/dist/auth-strategy.js +33 -0
  5. package/dist/auth-strategy.js.map +1 -0
  6. package/dist/client.d.ts +224 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +715 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/errors.d.ts +137 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +269 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/generated/metadata.json +2281 -0
  15. package/dist/generated/openapi-stripped.json +21268 -0
  16. package/dist/generated/path-mapping.d.ts +8 -0
  17. package/dist/generated/path-mapping.d.ts.map +1 -0
  18. package/dist/generated/path-mapping.js +192 -0
  19. package/dist/generated/path-mapping.js.map +1 -0
  20. package/dist/generated/path-mapping.ts +199 -0
  21. package/dist/generated/schema.d.ts +14883 -0
  22. package/dist/generated/services/attachments.d.ts +27 -0
  23. package/dist/generated/services/attachments.d.ts.map +1 -0
  24. package/dist/generated/services/attachments.js +49 -0
  25. package/dist/generated/services/attachments.js.map +1 -0
  26. package/dist/generated/services/attachments.ts +59 -0
  27. package/dist/generated/services/boosts.d.ts +114 -0
  28. package/dist/generated/services/boosts.d.ts.map +1 -0
  29. package/dist/generated/services/boosts.js +179 -0
  30. package/dist/generated/services/boosts.js.map +1 -0
  31. package/dist/generated/services/boosts.ts +250 -0
  32. package/dist/generated/services/campfires.d.ts +202 -0
  33. package/dist/generated/services/campfires.d.ts.map +1 -0
  34. package/dist/generated/services/campfires.js +309 -0
  35. package/dist/generated/services/campfires.js.map +1 -0
  36. package/dist/generated/services/campfires.ts +433 -0
  37. package/dist/generated/services/card-columns.d.ts +163 -0
  38. package/dist/generated/services/card-columns.d.ts.map +1 -0
  39. package/dist/generated/services/card-columns.js +264 -0
  40. package/dist/generated/services/card-columns.js.map +1 -0
  41. package/dist/generated/services/card-columns.ts +360 -0
  42. package/dist/generated/services/card-steps.d.ts +105 -0
  43. package/dist/generated/services/card-steps.d.ts.map +1 -0
  44. package/dist/generated/services/card-steps.js +148 -0
  45. package/dist/generated/services/card-steps.js.map +1 -0
  46. package/dist/generated/services/card-steps.ts +221 -0
  47. package/dist/generated/services/card-tables.d.ts +27 -0
  48. package/dist/generated/services/card-tables.d.ts.map +1 -0
  49. package/dist/generated/services/card-tables.js +40 -0
  50. package/dist/generated/services/card-tables.js.map +1 -0
  51. package/dist/generated/services/card-tables.ts +55 -0
  52. package/dist/generated/services/cards.d.ts +118 -0
  53. package/dist/generated/services/cards.d.ts.map +1 -0
  54. package/dist/generated/services/cards.js +166 -0
  55. package/dist/generated/services/cards.js.map +1 -0
  56. package/dist/generated/services/cards.ts +247 -0
  57. package/dist/generated/services/checkins.d.ts +278 -0
  58. package/dist/generated/services/checkins.d.ts.map +1 -0
  59. package/dist/generated/services/checkins.js +420 -0
  60. package/dist/generated/services/checkins.js.map +1 -0
  61. package/dist/generated/services/checkins.ts +600 -0
  62. package/dist/generated/services/client-approvals.d.ts +45 -0
  63. package/dist/generated/services/client-approvals.d.ts.map +1 -0
  64. package/dist/generated/services/client-approvals.js +58 -0
  65. package/dist/generated/services/client-approvals.js.map +1 -0
  66. package/dist/generated/services/client-approvals.ts +89 -0
  67. package/dist/generated/services/client-correspondences.d.ts +45 -0
  68. package/dist/generated/services/client-correspondences.d.ts.map +1 -0
  69. package/dist/generated/services/client-correspondences.js +58 -0
  70. package/dist/generated/services/client-correspondences.js.map +1 -0
  71. package/dist/generated/services/client-correspondences.ts +89 -0
  72. package/dist/generated/services/client-replies.d.ts +47 -0
  73. package/dist/generated/services/client-replies.d.ts.map +1 -0
  74. package/dist/generated/services/client-replies.js +65 -0
  75. package/dist/generated/services/client-replies.js.map +1 -0
  76. package/dist/generated/services/client-replies.ts +95 -0
  77. package/dist/generated/services/client-visibility.d.ts +35 -0
  78. package/dist/generated/services/client-visibility.d.ts.map +1 -0
  79. package/dist/generated/services/client-visibility.js +44 -0
  80. package/dist/generated/services/client-visibility.js.map +1 -0
  81. package/dist/generated/services/client-visibility.ts +68 -0
  82. package/dist/generated/services/comments.d.ts +86 -0
  83. package/dist/generated/services/comments.d.ts.map +1 -0
  84. package/dist/generated/services/comments.js +129 -0
  85. package/dist/generated/services/comments.js.map +1 -0
  86. package/dist/generated/services/comments.ts +185 -0
  87. package/dist/generated/services/documents.d.ts +92 -0
  88. package/dist/generated/services/documents.d.ts.map +1 -0
  89. package/dist/generated/services/documents.js +129 -0
  90. package/dist/generated/services/documents.js.map +1 -0
  91. package/dist/generated/services/documents.ts +191 -0
  92. package/dist/generated/services/events.d.ts +34 -0
  93. package/dist/generated/services/events.d.ts.map +1 -0
  94. package/dist/generated/services/events.js +39 -0
  95. package/dist/generated/services/events.js.map +1 -0
  96. package/dist/generated/services/events.ts +64 -0
  97. package/dist/generated/services/forwards.d.ts +112 -0
  98. package/dist/generated/services/forwards.d.ts.map +1 -0
  99. package/dist/generated/services/forwards.js +172 -0
  100. package/dist/generated/services/forwards.js.map +1 -0
  101. package/dist/generated/services/forwards.ts +241 -0
  102. package/dist/generated/services/index.d.ts +39 -0
  103. package/dist/generated/services/index.d.ts.map +1 -0
  104. package/dist/generated/services/index.js +39 -0
  105. package/dist/generated/services/index.js.map +1 -0
  106. package/dist/generated/services/index.ts +38 -0
  107. package/dist/generated/services/lineup.d.ts +67 -0
  108. package/dist/generated/services/lineup.d.ts.map +1 -0
  109. package/dist/generated/services/lineup.js +99 -0
  110. package/dist/generated/services/lineup.js.map +1 -0
  111. package/dist/generated/services/lineup.ts +143 -0
  112. package/dist/generated/services/message-boards.d.ts +27 -0
  113. package/dist/generated/services/message-boards.d.ts.map +1 -0
  114. package/dist/generated/services/message-boards.js +40 -0
  115. package/dist/generated/services/message-boards.js.map +1 -0
  116. package/dist/generated/services/message-boards.ts +55 -0
  117. package/dist/generated/services/message-types.d.ts +100 -0
  118. package/dist/generated/services/message-types.d.ts.map +1 -0
  119. package/dist/generated/services/message-types.js +144 -0
  120. package/dist/generated/services/message-types.js.map +1 -0
  121. package/dist/generated/services/message-types.ts +210 -0
  122. package/dist/generated/services/messages.d.ts +122 -0
  123. package/dist/generated/services/messages.d.ts.map +1 -0
  124. package/dist/generated/services/messages.js +180 -0
  125. package/dist/generated/services/messages.js.map +1 -0
  126. package/dist/generated/services/messages.ts +258 -0
  127. package/dist/generated/services/people.d.ts +122 -0
  128. package/dist/generated/services/people.d.ts.map +1 -0
  129. package/dist/generated/services/people.js +167 -0
  130. package/dist/generated/services/people.js.map +1 -0
  131. package/dist/generated/services/people.ts +252 -0
  132. package/dist/generated/services/projects.d.ts +109 -0
  133. package/dist/generated/services/projects.d.ts.map +1 -0
  134. package/dist/generated/services/projects.js +153 -0
  135. package/dist/generated/services/projects.js.map +1 -0
  136. package/dist/generated/services/projects.ts +224 -0
  137. package/dist/generated/services/recordings.d.ts +93 -0
  138. package/dist/generated/services/recordings.d.ts.map +1 -0
  139. package/dist/generated/services/recordings.js +138 -0
  140. package/dist/generated/services/recordings.js.map +1 -0
  141. package/dist/generated/services/recordings.ts +191 -0
  142. package/dist/generated/services/reports.d.ts +90 -0
  143. package/dist/generated/services/reports.d.ts.map +1 -0
  144. package/dist/generated/services/reports.js +124 -0
  145. package/dist/generated/services/reports.js.map +1 -0
  146. package/dist/generated/services/reports.ts +187 -0
  147. package/dist/generated/services/schedules.d.ts +162 -0
  148. package/dist/generated/services/schedules.d.ts.map +1 -0
  149. package/dist/generated/services/schedules.js +228 -0
  150. package/dist/generated/services/schedules.js.map +1 -0
  151. package/dist/generated/services/schedules.ts +335 -0
  152. package/dist/generated/services/search.d.ts +45 -0
  153. package/dist/generated/services/search.d.ts.map +1 -0
  154. package/dist/generated/services/search.js +56 -0
  155. package/dist/generated/services/search.js.map +1 -0
  156. package/dist/generated/services/search.ts +89 -0
  157. package/dist/generated/services/subscriptions.d.ts +73 -0
  158. package/dist/generated/services/subscriptions.d.ts.map +1 -0
  159. package/dist/generated/services/subscriptions.js +119 -0
  160. package/dist/generated/services/subscriptions.js.map +1 -0
  161. package/dist/generated/services/subscriptions.ts +160 -0
  162. package/dist/generated/services/templates.d.ts +140 -0
  163. package/dist/generated/services/templates.d.ts.map +1 -0
  164. package/dist/generated/services/templates.js +207 -0
  165. package/dist/generated/services/templates.js.map +1 -0
  166. package/dist/generated/services/templates.ts +294 -0
  167. package/dist/generated/services/timeline.d.ts +31 -0
  168. package/dist/generated/services/timeline.d.ts.map +1 -0
  169. package/dist/generated/services/timeline.js +39 -0
  170. package/dist/generated/services/timeline.js.map +1 -0
  171. package/dist/generated/services/timeline.ts +62 -0
  172. package/dist/generated/services/timesheets.d.ts +145 -0
  173. package/dist/generated/services/timesheets.d.ts.map +1 -0
  174. package/dist/generated/services/timesheets.js +186 -0
  175. package/dist/generated/services/timesheets.js.map +1 -0
  176. package/dist/generated/services/timesheets.ts +289 -0
  177. package/dist/generated/services/todolist-groups.d.ts +74 -0
  178. package/dist/generated/services/todolist-groups.d.ts.map +1 -0
  179. package/dist/generated/services/todolist-groups.js +100 -0
  180. package/dist/generated/services/todolist-groups.js.map +1 -0
  181. package/dist/generated/services/todolist-groups.ts +151 -0
  182. package/dist/generated/services/todolists.d.ts +95 -0
  183. package/dist/generated/services/todolists.d.ts.map +1 -0
  184. package/dist/generated/services/todolists.js +130 -0
  185. package/dist/generated/services/todolists.js.map +1 -0
  186. package/dist/generated/services/todolists.ts +192 -0
  187. package/dist/generated/services/todos.d.ts +175 -0
  188. package/dist/generated/services/todos.d.ts.map +1 -0
  189. package/dist/generated/services/todos.js +255 -0
  190. package/dist/generated/services/todos.js.map +1 -0
  191. package/dist/generated/services/todos.ts +369 -0
  192. package/dist/generated/services/todosets.d.ts +27 -0
  193. package/dist/generated/services/todosets.d.ts.map +1 -0
  194. package/dist/generated/services/todosets.js +40 -0
  195. package/dist/generated/services/todosets.js.map +1 -0
  196. package/dist/generated/services/todosets.ts +55 -0
  197. package/dist/generated/services/tools.d.ts +122 -0
  198. package/dist/generated/services/tools.d.ts.map +1 -0
  199. package/dist/generated/services/tools.js +197 -0
  200. package/dist/generated/services/tools.js.map +1 -0
  201. package/dist/generated/services/tools.ts +267 -0
  202. package/dist/generated/services/uploads.d.ts +109 -0
  203. package/dist/generated/services/uploads.d.ts.map +1 -0
  204. package/dist/generated/services/uploads.js +153 -0
  205. package/dist/generated/services/uploads.js.map +1 -0
  206. package/dist/generated/services/uploads.ts +227 -0
  207. package/dist/generated/services/vaults.d.ts +86 -0
  208. package/dist/generated/services/vaults.d.ts.map +1 -0
  209. package/dist/generated/services/vaults.js +126 -0
  210. package/dist/generated/services/vaults.js.map +1 -0
  211. package/dist/generated/services/vaults.ts +182 -0
  212. package/dist/generated/services/webhooks.d.ts +106 -0
  213. package/dist/generated/services/webhooks.d.ts.map +1 -0
  214. package/dist/generated/services/webhooks.js +157 -0
  215. package/dist/generated/services/webhooks.js.map +1 -0
  216. package/dist/generated/services/webhooks.ts +226 -0
  217. package/dist/hooks/otel.d.ts +136 -0
  218. package/dist/hooks/otel.d.ts.map +1 -0
  219. package/dist/hooks/otel.js +240 -0
  220. package/dist/hooks/otel.js.map +1 -0
  221. package/dist/hooks.d.ts +171 -0
  222. package/dist/hooks.d.ts.map +1 -0
  223. package/dist/hooks.js +196 -0
  224. package/dist/hooks.js.map +1 -0
  225. package/dist/index.d.ts +90 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +122 -0
  228. package/dist/index.js.map +1 -0
  229. package/dist/oauth/authorize.d.ts +48 -0
  230. package/dist/oauth/authorize.d.ts.map +1 -0
  231. package/dist/oauth/authorize.js +56 -0
  232. package/dist/oauth/authorize.js.map +1 -0
  233. package/dist/oauth/callback-server.d.ts +56 -0
  234. package/dist/oauth/callback-server.d.ts.map +1 -0
  235. package/dist/oauth/callback-server.js +150 -0
  236. package/dist/oauth/callback-server.js.map +1 -0
  237. package/dist/oauth/discovery.d.ts +54 -0
  238. package/dist/oauth/discovery.d.ts.map +1 -0
  239. package/dist/oauth/discovery.js +116 -0
  240. package/dist/oauth/discovery.js.map +1 -0
  241. package/dist/oauth/exchange.d.ts +96 -0
  242. package/dist/oauth/exchange.d.ts.map +1 -0
  243. package/dist/oauth/exchange.js +324 -0
  244. package/dist/oauth/exchange.js.map +1 -0
  245. package/dist/oauth/identity.d.ts +30 -0
  246. package/dist/oauth/identity.d.ts.map +1 -0
  247. package/dist/oauth/identity.js +82 -0
  248. package/dist/oauth/identity.js.map +1 -0
  249. package/dist/oauth/index.d.ts +50 -0
  250. package/dist/oauth/index.d.ts.map +1 -0
  251. package/dist/oauth/index.js +58 -0
  252. package/dist/oauth/index.js.map +1 -0
  253. package/dist/oauth/interactive-login.d.ts +62 -0
  254. package/dist/oauth/interactive-login.d.ts.map +1 -0
  255. package/dist/oauth/interactive-login.js +106 -0
  256. package/dist/oauth/interactive-login.js.map +1 -0
  257. package/dist/oauth/pkce.d.ts +68 -0
  258. package/dist/oauth/pkce.d.ts.map +1 -0
  259. package/dist/oauth/pkce.js +80 -0
  260. package/dist/oauth/pkce.js.map +1 -0
  261. package/dist/oauth/token-manager.d.ts +92 -0
  262. package/dist/oauth/token-manager.d.ts.map +1 -0
  263. package/dist/oauth/token-manager.js +126 -0
  264. package/dist/oauth/token-manager.js.map +1 -0
  265. package/dist/oauth/token-store.d.ts +45 -0
  266. package/dist/oauth/token-store.d.ts.map +1 -0
  267. package/dist/oauth/token-store.js +105 -0
  268. package/dist/oauth/token-store.js.map +1 -0
  269. package/dist/oauth/types.d.ts +98 -0
  270. package/dist/oauth/types.d.ts.map +1 -0
  271. package/dist/oauth/types.js +8 -0
  272. package/dist/oauth/types.js.map +1 -0
  273. package/dist/pagination-utils.d.ts +27 -0
  274. package/dist/pagination-utils.d.ts.map +1 -0
  275. package/dist/pagination-utils.js +54 -0
  276. package/dist/pagination-utils.js.map +1 -0
  277. package/dist/pagination.d.ts +56 -0
  278. package/dist/pagination.d.ts.map +1 -0
  279. package/dist/pagination.js +57 -0
  280. package/dist/pagination.js.map +1 -0
  281. package/dist/security.d.ts +68 -0
  282. package/dist/security.d.ts.map +1 -0
  283. package/dist/security.js +107 -0
  284. package/dist/security.js.map +1 -0
  285. package/dist/services/authorization.d.ts +124 -0
  286. package/dist/services/authorization.d.ts.map +1 -0
  287. package/dist/services/authorization.js +117 -0
  288. package/dist/services/authorization.js.map +1 -0
  289. package/dist/services/base.d.ts +102 -0
  290. package/dist/services/base.d.ts.map +1 -0
  291. package/dist/services/base.js +230 -0
  292. package/dist/services/base.js.map +1 -0
  293. package/dist/webhooks/adapters/node-http.d.ts +14 -0
  294. package/dist/webhooks/adapters/node-http.d.ts.map +1 -0
  295. package/dist/webhooks/adapters/node-http.js +60 -0
  296. package/dist/webhooks/adapters/node-http.js.map +1 -0
  297. package/dist/webhooks/events.d.ts +52 -0
  298. package/dist/webhooks/events.d.ts.map +1 -0
  299. package/dist/webhooks/events.js +52 -0
  300. package/dist/webhooks/events.js.map +1 -0
  301. package/dist/webhooks/handler.d.ts +62 -0
  302. package/dist/webhooks/handler.d.ts.map +1 -0
  303. package/dist/webhooks/handler.js +226 -0
  304. package/dist/webhooks/handler.js.map +1 -0
  305. package/dist/webhooks/index.d.ts +5 -0
  306. package/dist/webhooks/index.d.ts.map +1 -0
  307. package/dist/webhooks/index.js +5 -0
  308. package/dist/webhooks/index.js.map +1 -0
  309. package/dist/webhooks/verify.d.ts +11 -0
  310. package/dist/webhooks/verify.d.ts.map +1 -0
  311. package/dist/webhooks/verify.js +25 -0
  312. package/dist/webhooks/verify.js.map +1 -0
  313. package/package.json +70 -0
  314. package/src/generated/metadata.json +2281 -0
  315. package/src/generated/openapi-stripped.json +21268 -0
  316. package/src/generated/path-mapping.ts +199 -0
  317. package/src/generated/schema.d.ts +14883 -0
  318. package/src/generated/services/attachments.ts +59 -0
  319. package/src/generated/services/boosts.ts +250 -0
  320. package/src/generated/services/campfires.ts +433 -0
  321. package/src/generated/services/card-columns.ts +360 -0
  322. package/src/generated/services/card-steps.ts +221 -0
  323. package/src/generated/services/card-tables.ts +55 -0
  324. package/src/generated/services/cards.ts +247 -0
  325. package/src/generated/services/checkins.ts +600 -0
  326. package/src/generated/services/client-approvals.ts +89 -0
  327. package/src/generated/services/client-correspondences.ts +89 -0
  328. package/src/generated/services/client-replies.ts +95 -0
  329. package/src/generated/services/client-visibility.ts +68 -0
  330. package/src/generated/services/comments.ts +185 -0
  331. package/src/generated/services/documents.ts +191 -0
  332. package/src/generated/services/events.ts +64 -0
  333. package/src/generated/services/forwards.ts +241 -0
  334. package/src/generated/services/index.ts +38 -0
  335. package/src/generated/services/lineup.ts +143 -0
  336. package/src/generated/services/message-boards.ts +55 -0
  337. package/src/generated/services/message-types.ts +210 -0
  338. package/src/generated/services/messages.ts +258 -0
  339. package/src/generated/services/people.ts +252 -0
  340. package/src/generated/services/projects.ts +224 -0
  341. package/src/generated/services/recordings.ts +191 -0
  342. package/src/generated/services/reports.ts +187 -0
  343. package/src/generated/services/schedules.ts +335 -0
  344. package/src/generated/services/search.ts +89 -0
  345. package/src/generated/services/subscriptions.ts +160 -0
  346. package/src/generated/services/templates.ts +294 -0
  347. package/src/generated/services/timeline.ts +62 -0
  348. package/src/generated/services/timesheets.ts +289 -0
  349. package/src/generated/services/todolist-groups.ts +151 -0
  350. package/src/generated/services/todolists.ts +192 -0
  351. package/src/generated/services/todos.ts +369 -0
  352. package/src/generated/services/todosets.ts +55 -0
  353. package/src/generated/services/tools.ts +267 -0
  354. package/src/generated/services/uploads.ts +227 -0
  355. package/src/generated/services/vaults.ts +182 -0
  356. package/src/generated/services/webhooks.ts +226 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Local HTTP callback server for OAuth 2.0 authorization code flow.
3
+ *
4
+ * Starts a temporary server to receive the authorization callback,
5
+ * validates the state parameter, and extracts the authorization code.
6
+ */
7
+ import { createServer } from "node:http";
8
+ import { BasecampError } from "../errors.js";
9
+ const SUCCESS_HTML = `<!DOCTYPE html>
10
+ <html><head><title>Authorization Complete</title></head>
11
+ <body style="font-family:system-ui;text-align:center;padding:40px">
12
+ <h1>Authorization complete</h1>
13
+ <p>You can close this window.</p>
14
+ </body></html>`;
15
+ const ERROR_HTML = `<!DOCTYPE html>
16
+ <html><head><title>Authorization Failed</title></head>
17
+ <body style="font-family:system-ui;text-align:center;padding:40px">
18
+ <h1>Authorization failed</h1>
19
+ <p>Please try again.</p>
20
+ </body></html>`;
21
+ /**
22
+ * Starts a local HTTP server to receive the OAuth callback.
23
+ *
24
+ * The server listens for a single GET /callback request, validates
25
+ * the state parameter, and extracts the authorization code.
26
+ * It auto-closes after receiving the callback or on timeout.
27
+ *
28
+ * @param options - Server configuration
29
+ * @returns The callback URL, a promise for the result, and a close function
30
+ * @throws BasecampError if the port is already in use
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const { url, waitForCallback, close } = await startCallbackServer({
35
+ * expectedState: myState,
36
+ * });
37
+ *
38
+ * // Use `url` as the redirect_uri in the authorization request
39
+ * // Then wait for the user to complete the flow:
40
+ * const { code } = await waitForCallback();
41
+ * ```
42
+ */
43
+ export async function startCallbackServer(options) {
44
+ const { port = 14923, host = "localhost", timeoutMs = 120_000, expectedState, } = options;
45
+ let resolve;
46
+ let reject;
47
+ const promise = new Promise((res, rej) => {
48
+ resolve = res;
49
+ reject = rej;
50
+ });
51
+ // Prevent unhandled rejection warnings when reject() fires before
52
+ // the consumer has called waitForCallback(). The consumer's call
53
+ // still receives the rejection.
54
+ promise.catch(() => { });
55
+ let settled = false;
56
+ let timeoutId;
57
+ const httpServer = createServer((req, res) => {
58
+ if (settled) {
59
+ res.writeHead(404);
60
+ res.end();
61
+ return;
62
+ }
63
+ let reqUrl;
64
+ try {
65
+ const reqHost = req.headers.host ?? `${host}:${port}`;
66
+ reqUrl = new URL(req.url ?? "/", `http://${reqHost}`);
67
+ }
68
+ catch {
69
+ res.writeHead(400, { "Content-Type": "text/html" });
70
+ res.end(ERROR_HTML);
71
+ return;
72
+ }
73
+ if (reqUrl.pathname !== "/callback") {
74
+ res.writeHead(404);
75
+ res.end();
76
+ return;
77
+ }
78
+ const code = reqUrl.searchParams.get("code");
79
+ const state = reqUrl.searchParams.get("state");
80
+ const error = reqUrl.searchParams.get("error");
81
+ if (error) {
82
+ settled = true;
83
+ const description = reqUrl.searchParams.get("error_description") ?? error;
84
+ res.writeHead(400, { "Content-Type": "text/html" });
85
+ res.end(ERROR_HTML);
86
+ reject(new BasecampError("auth", `OAuth callback error: ${description}`));
87
+ scheduleClose();
88
+ return;
89
+ }
90
+ if (!code || !state) {
91
+ res.writeHead(400, { "Content-Type": "text/html" });
92
+ res.end(ERROR_HTML);
93
+ return;
94
+ }
95
+ if (state !== expectedState) {
96
+ settled = true;
97
+ res.writeHead(400, { "Content-Type": "text/html" });
98
+ res.end(ERROR_HTML);
99
+ reject(new BasecampError("auth", "OAuth state mismatch — possible CSRF attack"));
100
+ scheduleClose();
101
+ return;
102
+ }
103
+ settled = true;
104
+ res.writeHead(200, { "Content-Type": "text/html" });
105
+ res.end(SUCCESS_HTML);
106
+ resolve({ code, state });
107
+ scheduleClose();
108
+ });
109
+ function scheduleClose() {
110
+ if (timeoutId)
111
+ clearTimeout(timeoutId);
112
+ // Close on next tick to allow the response to flush
113
+ setImmediate(() => httpServer.close());
114
+ }
115
+ function close() {
116
+ if (timeoutId)
117
+ clearTimeout(timeoutId);
118
+ if (!settled) {
119
+ settled = true;
120
+ reject(new BasecampError("auth", "Callback server closed before receiving callback"));
121
+ }
122
+ httpServer.close();
123
+ }
124
+ // Start listening
125
+ await new Promise((listenResolve, listenReject) => {
126
+ httpServer.on("error", (err) => {
127
+ if (err.code === "EADDRINUSE") {
128
+ listenReject(new BasecampError("network", `Port ${port} is already in use — cannot start OAuth callback server`));
129
+ }
130
+ else {
131
+ listenReject(err);
132
+ }
133
+ });
134
+ httpServer.listen(port, host, listenResolve);
135
+ });
136
+ // Resolve actual bound port (important when port=0 for OS-assigned ports)
137
+ const addr = httpServer.address();
138
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
139
+ // Start timeout
140
+ timeoutId = setTimeout(() => {
141
+ if (!settled) {
142
+ settled = true;
143
+ reject(new BasecampError("auth", `OAuth callback timed out after ${timeoutMs}ms`));
144
+ httpServer.close();
145
+ }
146
+ }, timeoutMs);
147
+ const url = `http://${host}:${actualPort}/callback`;
148
+ return { url, waitForCallback: () => promise, close };
149
+ }
150
+ //# sourceMappingURL=callback-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback-server.js","sourceRoot":"","sources":["../../src/oauth/callback-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * OAuth 2.0 discovery for Basecamp SDK.
3
+ *
4
+ * Fetches OAuth server configuration from the well-known discovery endpoint.
5
+ */
6
+ import type { OAuthConfig } from "./types.js";
7
+ /**
8
+ * Options for OAuth discovery.
9
+ */
10
+ export interface DiscoverOptions {
11
+ /** Custom fetch function for testing or custom HTTP handling */
12
+ fetch?: typeof globalThis.fetch;
13
+ /** Request timeout in milliseconds (default: 10000) */
14
+ timeoutMs?: number;
15
+ }
16
+ /**
17
+ * Discovers OAuth server configuration from the well-known endpoint.
18
+ *
19
+ * Fetches the OAuth 2.0 Authorization Server Metadata from:
20
+ * `{baseUrl}/.well-known/oauth-authorization-server`
21
+ *
22
+ * @param baseUrl - The OAuth server's base URL (e.g., "https://launchpad.37signals.com")
23
+ * @param options - Optional configuration
24
+ * @returns The OAuth server configuration
25
+ * @throws BasecampError on network or parsing errors
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const config = await discover("https://launchpad.37signals.com");
30
+ * console.log(config.tokenEndpoint);
31
+ * // => "https://launchpad.37signals.com/authorization/token"
32
+ * ```
33
+ */
34
+ export declare function discover(baseUrl: string, options?: DiscoverOptions): Promise<OAuthConfig>;
35
+ /**
36
+ * Default Basecamp/Launchpad OAuth server URL.
37
+ */
38
+ export declare const LAUNCHPAD_BASE_URL = "https://launchpad.37signals.com";
39
+ /**
40
+ * Discovers OAuth configuration from Basecamp's Launchpad server.
41
+ *
42
+ * Convenience function that calls discover() with the Launchpad base URL.
43
+ *
44
+ * @param options - Optional configuration
45
+ * @returns The OAuth server configuration
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * const config = await discoverLaunchpad();
50
+ * // Use config.authorizationEndpoint to start OAuth flow
51
+ * ```
52
+ */
53
+ export declare function discoverLaunchpad(options?: DiscoverOptions): Promise<OAuthConfig>;
54
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/oauth/discovery.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAa9C;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAkFtB;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,oCAAoC,CAAC;AAEpE;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC,CAEtB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * OAuth 2.0 discovery for Basecamp SDK.
3
+ *
4
+ * Fetches OAuth server configuration from the well-known discovery endpoint.
5
+ */
6
+ import { BasecampError } from "../errors.js";
7
+ import { isLocalhost } from "../security.js";
8
+ /**
9
+ * Discovers OAuth server configuration from the well-known endpoint.
10
+ *
11
+ * Fetches the OAuth 2.0 Authorization Server Metadata from:
12
+ * `{baseUrl}/.well-known/oauth-authorization-server`
13
+ *
14
+ * @param baseUrl - The OAuth server's base URL (e.g., "https://launchpad.37signals.com")
15
+ * @param options - Optional configuration
16
+ * @returns The OAuth server configuration
17
+ * @throws BasecampError on network or parsing errors
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const config = await discover("https://launchpad.37signals.com");
22
+ * console.log(config.tokenEndpoint);
23
+ * // => "https://launchpad.37signals.com/authorization/token"
24
+ * ```
25
+ */
26
+ export async function discover(baseUrl, options = {}) {
27
+ const { fetch: customFetch = globalThis.fetch, timeoutMs = 10000 } = options;
28
+ // Validate HTTPS before making any network request (allow localhost for testing)
29
+ try {
30
+ const parsed = new URL(baseUrl);
31
+ if (parsed.protocol !== "https:" && !isLocalhost(parsed.hostname)) {
32
+ throw new BasecampError("validation", `OAuth discovery base URL must use HTTPS: ${baseUrl}`);
33
+ }
34
+ }
35
+ catch (err) {
36
+ if (err instanceof BasecampError)
37
+ throw err;
38
+ throw new BasecampError("validation", `Invalid OAuth discovery base URL: ${baseUrl}`);
39
+ }
40
+ // Normalize base URL (remove trailing slash)
41
+ const normalizedBase = baseUrl.replace(/\/$/, "");
42
+ const discoveryUrl = `${normalizedBase}/.well-known/oauth-authorization-server`;
43
+ // Create abort controller for timeout
44
+ const controller = new AbortController();
45
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
46
+ try {
47
+ const response = await customFetch(discoveryUrl, {
48
+ method: "GET",
49
+ headers: {
50
+ Accept: "application/json",
51
+ },
52
+ signal: controller.signal,
53
+ });
54
+ if (!response.ok) {
55
+ const body = await response.text().catch(() => "");
56
+ throw new BasecampError("network", `OAuth discovery failed with status ${response.status}: ${body}`, { httpStatus: response.status });
57
+ }
58
+ const data = (await response.json());
59
+ // Validate required fields
60
+ if (!data.issuer || !data.authorization_endpoint || !data.token_endpoint) {
61
+ throw new BasecampError("api_error", "Invalid OAuth discovery response: missing required fields");
62
+ }
63
+ return {
64
+ issuer: data.issuer,
65
+ authorizationEndpoint: data.authorization_endpoint,
66
+ tokenEndpoint: data.token_endpoint,
67
+ registrationEndpoint: data.registration_endpoint,
68
+ scopesSupported: data.scopes_supported,
69
+ };
70
+ }
71
+ catch (err) {
72
+ if (err instanceof BasecampError) {
73
+ throw err;
74
+ }
75
+ if (err instanceof Error) {
76
+ if (err.name === "AbortError") {
77
+ throw new BasecampError("network", "OAuth discovery request timed out", {
78
+ cause: err,
79
+ retryable: true,
80
+ });
81
+ }
82
+ throw new BasecampError("network", `OAuth discovery failed: ${err.message}`, {
83
+ cause: err,
84
+ retryable: true,
85
+ });
86
+ }
87
+ throw new BasecampError("network", "OAuth discovery failed with unknown error", {
88
+ retryable: true,
89
+ });
90
+ }
91
+ finally {
92
+ clearTimeout(timeoutId);
93
+ }
94
+ }
95
+ /**
96
+ * Default Basecamp/Launchpad OAuth server URL.
97
+ */
98
+ export const LAUNCHPAD_BASE_URL = "https://launchpad.37signals.com";
99
+ /**
100
+ * Discovers OAuth configuration from Basecamp's Launchpad server.
101
+ *
102
+ * Convenience function that calls discover() with the Launchpad base URL.
103
+ *
104
+ * @param options - Optional configuration
105
+ * @returns The OAuth server configuration
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const config = await discoverLaunchpad();
110
+ * // Use config.authorizationEndpoint to start OAuth flow
111
+ * ```
112
+ */
113
+ export async function discoverLaunchpad(options = {}) {
114
+ return discover(LAUNCHPAD_BASE_URL, options);
115
+ }
116
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/oauth/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * OAuth 2.0 token exchange and refresh for Basecamp SDK.
3
+ *
4
+ * Handles authorization code exchange and token refresh operations.
5
+ * Supports both standard OAuth 2.0 and Basecamp's Launchpad legacy format.
6
+ */
7
+ import type { ExchangeRequest, RefreshRequest, OAuthToken } from "./types.js";
8
+ /**
9
+ * Options for token exchange/refresh operations.
10
+ */
11
+ export interface TokenOptions {
12
+ /** Custom fetch function for testing or custom HTTP handling */
13
+ fetch?: typeof globalThis.fetch;
14
+ /** Request timeout in milliseconds (default: 30000) */
15
+ timeoutMs?: number;
16
+ }
17
+ /**
18
+ * Exchanges an authorization code for access and refresh tokens.
19
+ *
20
+ * Supports both standard OAuth 2.0 and Basecamp's Launchpad legacy format.
21
+ * Use `useLegacyFormat: true` for Launchpad compatibility.
22
+ *
23
+ * @param request - Exchange request parameters
24
+ * @param options - Optional configuration
25
+ * @returns The token response
26
+ * @throws BasecampError on validation, network, or authentication errors
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * // Standard OAuth 2.0
31
+ * const token = await exchangeCode({
32
+ * tokenEndpoint: config.tokenEndpoint,
33
+ * code: "auth_code_from_callback",
34
+ * redirectUri: "https://myapp.com/callback",
35
+ * clientId: "my_client_id",
36
+ * clientSecret: "my_client_secret",
37
+ * });
38
+ *
39
+ * // Launchpad legacy format
40
+ * const token = await exchangeCode({
41
+ * tokenEndpoint: "https://launchpad.37signals.com/authorization/token",
42
+ * code: "auth_code",
43
+ * redirectUri: "https://myapp.com/callback",
44
+ * clientId: "my_client_id",
45
+ * clientSecret: "my_client_secret",
46
+ * useLegacyFormat: true,
47
+ * });
48
+ * ```
49
+ */
50
+ export declare function exchangeCode(request: ExchangeRequest, options?: TokenOptions): Promise<OAuthToken>;
51
+ /**
52
+ * Refreshes an access token using a refresh token.
53
+ *
54
+ * Supports both standard OAuth 2.0 and Basecamp's Launchpad legacy format.
55
+ * Use `useLegacyFormat: true` for Launchpad compatibility.
56
+ *
57
+ * @param request - Refresh request parameters
58
+ * @param options - Optional configuration
59
+ * @returns The new token response
60
+ * @throws BasecampError on validation, network, or authentication errors
61
+ *
62
+ * @example
63
+ * ```ts
64
+ * // Standard OAuth 2.0
65
+ * const newToken = await refreshToken({
66
+ * tokenEndpoint: config.tokenEndpoint,
67
+ * refreshToken: oldToken.refreshToken,
68
+ * clientId: "my_client_id",
69
+ * clientSecret: "my_client_secret",
70
+ * });
71
+ *
72
+ * // Launchpad legacy format
73
+ * const newToken = await refreshToken({
74
+ * tokenEndpoint: "https://launchpad.37signals.com/authorization/token",
75
+ * refreshToken: oldToken.refreshToken,
76
+ * useLegacyFormat: true,
77
+ * });
78
+ * ```
79
+ */
80
+ export declare function refreshToken(request: RefreshRequest, options?: TokenOptions): Promise<OAuthToken>;
81
+ /**
82
+ * Checks if a token is expired or about to expire.
83
+ *
84
+ * @param token - The token to check
85
+ * @param bufferSeconds - Buffer time before actual expiration (default: 60)
86
+ * @returns true if the token is expired or will expire within the buffer time
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * if (isTokenExpired(token)) {
91
+ * token = await refreshToken({ ... });
92
+ * }
93
+ * ```
94
+ */
95
+ export declare function isTokenExpired(token: OAuthToken, bufferSeconds?: number): boolean;
96
+ //# sourceMappingURL=exchange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../../src/oauth/exchange.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,UAAU,EAGX,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,UAAU,CAAC,CAsCrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,UAAU,CAAC,CA8BrB;AA0ND;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,aAAa,SAAK,GAAG,OAAO,CAQ7E"}