@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
package/README.md ADDED
@@ -0,0 +1,600 @@
1
+ # Basecamp TypeScript SDK
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@37signals/basecamp.svg)](https://www.npmjs.com/package/@37signals/basecamp)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
5
+ [![Test](https://github.com/basecamp/basecamp-sdk/actions/workflows/test.yml/badge.svg)](https://github.com/basecamp/basecamp-sdk/actions/workflows/test.yml)
6
+
7
+ Official TypeScript SDK for the [Basecamp 3 API](https://github.com/basecamp/bc3-api).
8
+
9
+ ## Features
10
+
11
+ - Full type safety with TypeScript generics
12
+ - 30+ services covering the complete Basecamp API
13
+ - OAuth 2.0 with PKCE support
14
+ - ETag-based HTTP caching
15
+ - Automatic retry with exponential backoff
16
+ - Pagination helpers for large result sets
17
+ - Observability hooks for logging, metrics, and tracing
18
+ - OpenTelemetry integration
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @37signals/basecamp
24
+ ```
25
+
26
+ Requires Node.js 18+ and TypeScript 5.0+.
27
+
28
+ ## Quick Start
29
+
30
+ ```ts
31
+ import { createBasecampClient } from "@37signals/basecamp";
32
+
33
+ const client = createBasecampClient({
34
+ accountId: process.env.BASECAMP_ACCOUNT_ID!,
35
+ accessToken: process.env.BASECAMP_TOKEN!,
36
+ });
37
+
38
+ // List all projects
39
+ const projects = await client.projects.list();
40
+ for (const project of projects) {
41
+ console.log(`${project.id}: ${project.name}`);
42
+ }
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ ### Client Options
48
+
49
+ ```ts
50
+ import { createBasecampClient } from "@37signals/basecamp";
51
+
52
+ const client = createBasecampClient({
53
+ // Required
54
+ accountId: "12345",
55
+ accessToken: "your-token", // or async token provider
56
+
57
+ // Optional
58
+ baseUrl: "https://3.basecampapi.com/12345", // default
59
+ userAgent: "my-app/1.0",
60
+ enableCache: true, // ETag caching (default: true)
61
+ enableRetry: true, // Auto retry 429 and 503 (default: true)
62
+ hooks: myHooks, // Observability hooks
63
+ });
64
+ ```
65
+
66
+ ### Token Providers
67
+
68
+ For simple use cases, pass a static token string:
69
+
70
+ ```ts
71
+ const client = createBasecampClient({
72
+ accountId: "12345",
73
+ accessToken: "your-access-token",
74
+ });
75
+ ```
76
+
77
+ For token refresh scenarios, pass an async function:
78
+
79
+ ```ts
80
+ const client = createBasecampClient({
81
+ accountId: "12345",
82
+ accessToken: async () => {
83
+ // Fetch or refresh your token
84
+ const token = await myTokenStore.getValidToken();
85
+ return token.accessToken;
86
+ },
87
+ });
88
+ ```
89
+
90
+ ## OAuth 2.0
91
+
92
+ The SDK includes utilities for implementing OAuth 2.0 with PKCE support.
93
+
94
+ ### Authorization Flow
95
+
96
+ ```ts
97
+ import {
98
+ discoverLaunchpad,
99
+ generatePKCE,
100
+ generateState,
101
+ exchangeCode,
102
+ refreshToken,
103
+ isTokenExpired,
104
+ } from "@37signals/basecamp";
105
+
106
+ // 1. Discover OAuth endpoints
107
+ const config = await discoverLaunchpad();
108
+
109
+ // 2. Generate PKCE challenge and state
110
+ const pkce = await generatePKCE();
111
+ const state = generateState();
112
+
113
+ // Store pkce.verifier and state in session for later
114
+
115
+ // 3. Build authorization URL with PKCE challenge
116
+ const authUrl = new URL(config.authorizationEndpoint);
117
+ authUrl.searchParams.set("type", "web_server");
118
+ authUrl.searchParams.set("client_id", CLIENT_ID);
119
+ authUrl.searchParams.set("redirect_uri", REDIRECT_URI);
120
+ authUrl.searchParams.set("state", state);
121
+ authUrl.searchParams.set("code_challenge", pkce.challenge);
122
+ authUrl.searchParams.set("code_challenge_method", "S256");
123
+ // Redirect user to authUrl.toString()
124
+
125
+ // 4. Exchange code for tokens (in callback handler)
126
+ const token = await exchangeCode({
127
+ tokenEndpoint: config.tokenEndpoint,
128
+ code: callbackParams.code,
129
+ redirectUri: REDIRECT_URI,
130
+ clientId: CLIENT_ID,
131
+ clientSecret: CLIENT_SECRET,
132
+ codeVerifier: pkce.verifier, // PKCE verifier from step 2
133
+ useLegacyFormat: true, // Required for Basecamp Launchpad
134
+ });
135
+
136
+ // 5. Refresh when expired
137
+ if (isTokenExpired(token)) {
138
+ const newToken = await refreshToken({
139
+ tokenEndpoint: config.tokenEndpoint,
140
+ refreshToken: token.refreshToken!,
141
+ useLegacyFormat: true,
142
+ });
143
+ }
144
+ ```
145
+
146
+ ## Services
147
+
148
+ The SDK provides typed services for the complete Basecamp API:
149
+
150
+ ### Projects & Organization
151
+
152
+ | Service | Methods |
153
+ |---------|---------|
154
+ | `projects` | list, get, create, update, trash |
155
+ | `templates` | list, get, createProject |
156
+ | `tools` | list, get, update |
157
+ | `people` | list, get, me, listPingable |
158
+
159
+ ### To-dos
160
+
161
+ | Service | Methods |
162
+ |---------|---------|
163
+ | `todos` | list, get, create, update, trash, complete, uncomplete, reposition |
164
+ | `todolists` | list, get, create, update, trash |
165
+ | `todosets` | get |
166
+ | `todolistGroups` | list, get, create, reposition |
167
+
168
+ ### Messages & Communication
169
+
170
+ | Service | Methods |
171
+ |---------|---------|
172
+ | `messages` | list, get, create, update, pin, unpin |
173
+ | `messageBoards` | get |
174
+ | `messageTypes` | list, get, create, update, delete |
175
+ | `comments` | list, get, create, update |
176
+ | `campfires` | list, get, listLines, getLine, createLine, deleteLine |
177
+
178
+ ### Card Tables (Kanban)
179
+
180
+ | Service | Methods |
181
+ |---------|---------|
182
+ | `cardTables` | get, listColumns |
183
+ | `cards` | list, get, create, update, move |
184
+ | `cardColumns` | get, create, update, move |
185
+ | `cardSteps` | list, get, create, update, complete, uncomplete |
186
+
187
+ ### Scheduling
188
+
189
+ | Service | Methods |
190
+ |---------|---------|
191
+ | `schedules` | get, listEntries, getEntry, createEntry, updateEntry, trashEntry |
192
+ | `lineup` | create, update, delete |
193
+ | `checkins` | get, listQuestions, getQuestion, listAnswers, getAnswer |
194
+
195
+ ### Files & Documents
196
+
197
+ | Service | Methods |
198
+ |---------|---------|
199
+ | `vaults` | list, get, create, update |
200
+ | `documents` | list, get, create, update, trash |
201
+ | `uploads` | list, get, create, update, trash |
202
+ | `attachments` | createUploadUrl, create |
203
+
204
+ ### Integrations & Events
205
+
206
+ | Service | Methods |
207
+ |---------|---------|
208
+ | `webhooks` | list, get, create, update, delete |
209
+ | `subscriptions` | get, subscribe, unsubscribe, update |
210
+ | `events` | list, listForRecording |
211
+ | `recordings` | archive, unarchive, trash |
212
+
213
+ ### Search & Reports
214
+
215
+ | Service | Methods |
216
+ |---------|---------|
217
+ | `search` | search |
218
+ | `reports` | progress, upcoming, assigned, overdue, personProgress |
219
+ | `timesheets` | forRecording, forProject, report |
220
+ | `timeline` | get |
221
+
222
+ ### Client Portal
223
+
224
+ | Service | Methods |
225
+ |---------|---------|
226
+ | `clientApprovals` | list, get |
227
+ | `clientCorrespondences` | list, get |
228
+ | `clientReplies` | list, get |
229
+ | `clientVisibility` | get, update |
230
+
231
+ ### Email
232
+
233
+ | Service | Methods |
234
+ |---------|---------|
235
+ | `forwards` | list, get, createReply |
236
+
237
+ ## Pagination
238
+
239
+ List methods return a single page of results by default. Use the pagination helpers with low-level API calls to fetch all pages:
240
+
241
+ ```ts
242
+ import { fetchAllPages, paginateAll } from "@37signals/basecamp";
243
+
244
+ // First, fetch the initial page using the low-level client
245
+ const initialResponse = await client.GET("/projects.json");
246
+
247
+ // Option 1: fetchAllPages - returns all results as an array
248
+ const allProjects = await fetchAllPages(
249
+ initialResponse.response,
250
+ (response) => response.json()
251
+ );
252
+
253
+ // Option 2: paginateAll - async generator for streaming large result sets
254
+ for await (const page of paginateAll(
255
+ initialResponse.response,
256
+ (response) => response.json()
257
+ )) {
258
+ for (const project of page) {
259
+ console.log(project.name);
260
+ }
261
+ }
262
+ ```
263
+
264
+ Paginated endpoints include an `X-Total-Count` HTTP header when available. You can access this header via the `response.headers` field on low-level `client.GET`/`client.POST` calls.
265
+
266
+ ## Low-Level API Access
267
+
268
+ For endpoints not covered by services or advanced use cases, use the raw typed client:
269
+
270
+ ```ts
271
+ // Direct API calls with full type inference
272
+ const { data, error, response } = await client.GET("/projects.json");
273
+
274
+ if (error) {
275
+ console.error("Failed:", error);
276
+ } else {
277
+ console.log(data.map((p) => p.name));
278
+ }
279
+
280
+ // With path parameters
281
+ const { data: project } = await client.GET("/projects/{projectId}", {
282
+ params: { path: { projectId: 12345 } },
283
+ });
284
+
285
+ // POST with body
286
+ const { data: newProject } = await client.POST("/projects.json", {
287
+ body: { name: "My Project", description: "A new project" },
288
+ });
289
+ ```
290
+
291
+ ## Error Handling
292
+
293
+ The SDK provides structured errors with codes, hints, and exit codes for CLI applications:
294
+
295
+ ```ts
296
+ import { BasecampError, isBasecampError, isErrorCode } from "@37signals/basecamp";
297
+
298
+ try {
299
+ await client.todos.get(todoId);
300
+ } catch (err) {
301
+ if (isBasecampError(err)) {
302
+ console.error(`Error [${err.code}]: ${err.message}`);
303
+
304
+ if (err.hint) {
305
+ console.error(`Hint: ${err.hint}`);
306
+ }
307
+
308
+ if (err.retryable && err.retryAfter) {
309
+ console.log(`Retry after ${err.retryAfter} seconds`);
310
+ }
311
+
312
+ // Use exit codes for CLI applications
313
+ process.exit(err.exitCode);
314
+ }
315
+ throw err;
316
+ }
317
+ ```
318
+
319
+ ### Error Codes
320
+
321
+ | Code | HTTP Status | Exit Code | Description |
322
+ |------|-------------|-----------|-------------|
323
+ | `auth` | 401 | 3 | Authentication required |
324
+ | `forbidden` | 403 | 4 | Access denied |
325
+ | `not_found` | 404 | 2 | Resource not found |
326
+ | `rate_limit` | 429 | 5 | Rate limit exceeded (retryable) |
327
+ | `validation` | 400, 422 | 1 | Invalid request data |
328
+ | `network` | - | 6 | Network error (retryable) |
329
+ | `api_error` | 5xx | 7 | Server error |
330
+ | `usage` | - | 1 | Configuration or argument error |
331
+
332
+ ## Retry Behavior
333
+
334
+ The SDK automatically retries requests on transient failures:
335
+
336
+ - **Retryable errors**: 429 (rate limit) and 503 (service unavailable)
337
+ - **Backoff**: Exponential with jitter
338
+ - **Rate limits**: Respects `Retry-After` header
339
+ - **Max retries**: 3 attempts by default
340
+
341
+ Disable retry for specific use cases:
342
+
343
+ ```ts
344
+ const client = createBasecampClient({
345
+ accountId: "12345",
346
+ accessToken: "token",
347
+ enableRetry: false,
348
+ });
349
+ ```
350
+
351
+ ## Caching
352
+
353
+ The SDK uses ETag-based HTTP caching to reduce API calls and respect Basecamp's rate limits:
354
+
355
+ ```ts
356
+ // First request fetches from API
357
+ const projects = await client.projects.list();
358
+
359
+ // Second request returns cached data if unchanged (304 Not Modified)
360
+ const projects2 = await client.projects.list();
361
+ ```
362
+
363
+ Disable caching if needed:
364
+
365
+ ```ts
366
+ const client = createBasecampClient({
367
+ accountId: "12345",
368
+ accessToken: "token",
369
+ enableCache: false,
370
+ });
371
+ ```
372
+
373
+ ## Observability
374
+
375
+ ### Console Logging
376
+
377
+ For debugging or verbose CLI modes:
378
+
379
+ ```ts
380
+ import { createBasecampClient, consoleHooks } from "@37signals/basecamp";
381
+
382
+ const client = createBasecampClient({
383
+ accountId: "12345",
384
+ accessToken: "token",
385
+ hooks: consoleHooks({
386
+ logOperations: true,
387
+ logRequests: true, // More verbose
388
+ logRetries: true,
389
+ minDurationMs: 100, // Only log slow requests
390
+ }),
391
+ });
392
+ ```
393
+
394
+ Output:
395
+ ```
396
+ [Basecamp] Projects.List
397
+ [Basecamp] -> GET https://3.basecampapi.com/12345/projects.json
398
+ [Basecamp] <- GET https://3.basecampapi.com/12345/projects.json 200 (145ms)
399
+ [Basecamp] Projects.List completed (147ms)
400
+ ```
401
+
402
+ ### Custom Hooks
403
+
404
+ Implement the `BasecampHooks` interface for custom observability:
405
+
406
+ ```ts
407
+ import type { BasecampHooks } from "@37signals/basecamp";
408
+
409
+ const metricsHooks: BasecampHooks = {
410
+ onOperationStart(info) {
411
+ metrics.startTimer(`${info.service}.${info.operation}`);
412
+ },
413
+
414
+ onOperationEnd(info, result) {
415
+ metrics.recordDuration(`${info.service}.${info.operation}`, result.durationMs);
416
+ if (result.error) {
417
+ metrics.incrementError(`${info.service}.${info.operation}`);
418
+ }
419
+ },
420
+
421
+ onRetry(info, attempt, error, delayMs) {
422
+ logger.warn(`Retrying ${info.method} ${info.url} (attempt ${attempt})`);
423
+ },
424
+ };
425
+
426
+ const client = createBasecampClient({
427
+ accountId: "12345",
428
+ accessToken: "token",
429
+ hooks: metricsHooks,
430
+ });
431
+ ```
432
+
433
+ ### OpenTelemetry Integration
434
+
435
+ For distributed tracing and metrics:
436
+
437
+ ```ts
438
+ import { createBasecampClient, otelHooks } from "@37signals/basecamp";
439
+ import { trace, metrics } from "@opentelemetry/api";
440
+
441
+ const tracer = trace.getTracer("my-app");
442
+ const meter = metrics.getMeter("my-app");
443
+
444
+ const client = createBasecampClient({
445
+ accountId: "12345",
446
+ accessToken: "token",
447
+ hooks: otelHooks({
448
+ tracer,
449
+ meter,
450
+ recordRequestSpans: true, // Include HTTP-level spans
451
+ }),
452
+ });
453
+ ```
454
+
455
+ Creates spans and metrics:
456
+ - `basecamp.operation.duration` - Histogram of operation durations
457
+ - `basecamp.operations.total` - Counter of operations
458
+ - `basecamp.errors.total` - Counter of errors
459
+ - `basecamp.retries.total` - Counter of retry attempts
460
+
461
+ ### Combining Multiple Hooks
462
+
463
+ ```ts
464
+ import { chainHooks, consoleHooks, otelHooks } from "@37signals/basecamp";
465
+
466
+ const client = createBasecampClient({
467
+ accountId: "12345",
468
+ accessToken: "token",
469
+ hooks: chainHooks(
470
+ consoleHooks(),
471
+ otelHooks({ tracer, meter }),
472
+ myCustomHooks,
473
+ ),
474
+ });
475
+ ```
476
+
477
+ ## Examples
478
+
479
+ ### Working with Todos
480
+
481
+ ```ts
482
+ // List todos in a todolist
483
+ const todos = await client.todos.list(todolistId);
484
+
485
+ // Create a todo with assignees
486
+ const todo = await client.todos.create(todolistId, {
487
+ content: "Review pull request",
488
+ description: "<p>Check the new auth flow</p>",
489
+ dueOn: "2026-02-01",
490
+ assigneeIds: [12345, 67890],
491
+ });
492
+
493
+ // Complete a todo
494
+ await client.todos.complete(todo.id);
495
+
496
+ // Reposition a todo to the top
497
+ await client.todos.reposition(todo.id, { position: 1 });
498
+ ```
499
+
500
+ ### Working with Messages
501
+
502
+ ```ts
503
+ // Get a message board
504
+ const board = await client.messageBoards.get(boardId);
505
+
506
+ // List messages
507
+ const messages = await client.messages.list(board.id);
508
+
509
+ // Create a message
510
+ const msg = await client.messages.create(board.id, {
511
+ subject: "Weekly Update",
512
+ content: "<p>Here's what we accomplished...</p>",
513
+ });
514
+
515
+ // Pin a message
516
+ await client.messages.pin(msg.id);
517
+ ```
518
+
519
+ ### Working with Campfire
520
+
521
+ ```ts
522
+ // List campfires
523
+ const campfires = await client.campfires.list();
524
+
525
+ // Send a message
526
+ await client.campfires.createLine(campfireId, {
527
+ content: "Hello, team!",
528
+ });
529
+
530
+ // List recent messages
531
+ const lines = await client.campfires.listLines(campfireId);
532
+ ```
533
+
534
+ ### Working with Webhooks
535
+
536
+ ```ts
537
+ const bucketId = 12345; // project/bucket ID
538
+
539
+ // Create a webhook
540
+ const webhook = await client.webhooks.create(bucketId, {
541
+ payloadUrl: "https://example.com/webhook",
542
+ types: ["Todo", "Comment"],
543
+ });
544
+
545
+ // List webhooks
546
+ const webhooks = await client.webhooks.list(bucketId);
547
+
548
+ // Delete a webhook
549
+ await client.webhooks.delete(webhook.id);
550
+ ```
551
+
552
+ ## TypeScript Types
553
+
554
+ All types are exported for use in your code:
555
+
556
+ ```ts
557
+ import type {
558
+ Project,
559
+ Todo,
560
+ Message,
561
+ Person,
562
+ CreateTodoRequest,
563
+ BasecampError,
564
+ ErrorCode,
565
+ } from "@37signals/basecamp";
566
+
567
+ function processTodo(todo: Todo): void {
568
+ console.log(todo.content);
569
+ }
570
+
571
+ function createTodo(data: CreateTodoRequest): Promise<Todo> {
572
+ return client.todos.create(todolistId, data);
573
+ }
574
+ ```
575
+
576
+ ## Development
577
+
578
+ ```bash
579
+ # Install dependencies
580
+ npm install
581
+
582
+ # Generate types from OpenAPI spec
583
+ npm run generate
584
+
585
+ # Build
586
+ npm run build
587
+
588
+ # Run tests
589
+ npm test
590
+
591
+ # Type check
592
+ npm run typecheck
593
+
594
+ # Lint
595
+ npm run lint
596
+ ```
597
+
598
+ ## License
599
+
600
+ MIT
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Authentication strategy for the Basecamp SDK.
3
+ *
4
+ * AuthStrategy controls how authentication is applied to HTTP requests.
5
+ * The default strategy is bearerAuth, which uses a TokenProvider to set
6
+ * the Authorization header with a Bearer token.
7
+ *
8
+ * Custom strategies can implement alternative auth schemes such as
9
+ * cookie-based auth, API keys, or mutual TLS.
10
+ */
11
+ import type { TokenProvider } from "./client.js";
12
+ /**
13
+ * AuthStrategy controls how authentication is applied to HTTP requests.
14
+ * Called before every HTTP request to apply credentials to headers.
15
+ */
16
+ export interface AuthStrategy {
17
+ /**
18
+ * Apply authentication to the given request headers.
19
+ * Called before every HTTP request.
20
+ */
21
+ authenticate(headers: Headers): Promise<void>;
22
+ }
23
+ /**
24
+ * Bearer token authentication strategy (default).
25
+ * Sets the Authorization header with "Bearer {token}".
26
+ */
27
+ export declare class BearerAuth implements AuthStrategy {
28
+ private tokenProvider;
29
+ constructor(tokenProvider: TokenProvider);
30
+ authenticate(headers: Headers): Promise<void>;
31
+ }
32
+ /**
33
+ * Creates a BearerAuth strategy from a TokenProvider.
34
+ */
35
+ export declare function bearerAuth(tokenProvider: TokenProvider): AuthStrategy;
36
+ //# sourceMappingURL=auth-strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-strategy.d.ts","sourceRoot":"","sources":["../src/auth-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED;;;GAGG;AACH,qBAAa,UAAW,YAAW,YAAY;IAC7C,OAAO,CAAC,aAAa,CAAgB;gBAEzB,aAAa,EAAE,aAAa;IAIlC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAOpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,aAAa,EAAE,aAAa,GAAG,YAAY,CAErE"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Authentication strategy for the Basecamp SDK.
3
+ *
4
+ * AuthStrategy controls how authentication is applied to HTTP requests.
5
+ * The default strategy is bearerAuth, which uses a TokenProvider to set
6
+ * the Authorization header with a Bearer token.
7
+ *
8
+ * Custom strategies can implement alternative auth schemes such as
9
+ * cookie-based auth, API keys, or mutual TLS.
10
+ */
11
+ /**
12
+ * Bearer token authentication strategy (default).
13
+ * Sets the Authorization header with "Bearer {token}".
14
+ */
15
+ export class BearerAuth {
16
+ tokenProvider;
17
+ constructor(tokenProvider) {
18
+ this.tokenProvider = tokenProvider;
19
+ }
20
+ async authenticate(headers) {
21
+ const token = typeof this.tokenProvider === "function"
22
+ ? await this.tokenProvider()
23
+ : this.tokenProvider;
24
+ headers.set("Authorization", `Bearer ${token}`);
25
+ }
26
+ }
27
+ /**
28
+ * Creates a BearerAuth strategy from a TokenProvider.
29
+ */
30
+ export function bearerAuth(tokenProvider) {
31
+ return new BearerAuth(tokenProvider);
32
+ }
33
+ //# sourceMappingURL=auth-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-strategy.js","sourceRoot":"","sources":["../src/auth-strategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAgBH;;;GAGG;AACH,MAAM,OAAO,UAAU;IACb,aAAa,CAAgB;IAErC,YAAY,aAA4B;QACtC,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAAgB;QACjC,MAAM,KAAK,GACT,OAAO,IAAI,CAAC,aAAa,KAAK,UAAU;YACtC,CAAC,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE;YAC5B,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;IAClD,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,aAA4B;IACrD,OAAO,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC"}