@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,136 @@
1
+ /**
2
+ * OpenTelemetry hooks for the Basecamp SDK.
3
+ *
4
+ * Provides distributed tracing and metrics for API operations.
5
+ * Requires @opentelemetry/api as an optional peer dependency.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createBasecampClient, otelHooks } from "@37signals/basecamp";
10
+ * import { trace, metrics } from "@opentelemetry/api";
11
+ *
12
+ * const tracer = trace.getTracer("my-app");
13
+ * const meter = metrics.getMeter("my-app");
14
+ *
15
+ * const client = createBasecampClient({
16
+ * accountId: "12345",
17
+ * accessToken: "token",
18
+ * hooks: otelHooks({ tracer, meter }),
19
+ * });
20
+ *
21
+ * // All SDK operations will now create spans and record metrics
22
+ * const projects = await client.projects.list();
23
+ * ```
24
+ */
25
+ import type { BasecampHooks } from "../hooks.js";
26
+ interface OtelSpan {
27
+ setAttribute(key: string, value: string | number | boolean): this;
28
+ setStatus(status: {
29
+ code: number;
30
+ message?: string;
31
+ }): this;
32
+ recordException(exception: Error): this;
33
+ end(): void;
34
+ }
35
+ interface OtelTracer {
36
+ startSpan(name: string, options?: {
37
+ attributes?: Record<string, string | number | boolean>;
38
+ }): OtelSpan;
39
+ }
40
+ interface OtelMeter {
41
+ createHistogram(name: string, options?: {
42
+ description?: string;
43
+ unit?: string;
44
+ }): OtelHistogram;
45
+ createCounter(name: string, options?: {
46
+ description?: string;
47
+ }): OtelCounter;
48
+ }
49
+ interface OtelHistogram {
50
+ record(value: number, attributes?: Record<string, string | number | boolean>): void;
51
+ }
52
+ interface OtelCounter {
53
+ add(value: number, attributes?: Record<string, string | number | boolean>): void;
54
+ }
55
+ /**
56
+ * Options for OpenTelemetry hooks.
57
+ */
58
+ export interface OtelHooksOptions {
59
+ /**
60
+ * OpenTelemetry tracer instance.
61
+ * Create with: `trace.getTracer("your-app-name")`
62
+ */
63
+ tracer?: OtelTracer;
64
+ /**
65
+ * OpenTelemetry meter instance for metrics.
66
+ * Create with: `metrics.getMeter("your-app-name")`
67
+ */
68
+ meter?: OtelMeter;
69
+ /**
70
+ * Whether to record request-level spans (in addition to operation spans).
71
+ * Defaults to false for reduced noise.
72
+ */
73
+ recordRequestSpans?: boolean;
74
+ /**
75
+ * Prefix for span names.
76
+ * Defaults to "basecamp"
77
+ */
78
+ spanPrefix?: string;
79
+ /**
80
+ * Prefix for metric names.
81
+ * Defaults to "basecamp"
82
+ */
83
+ metricPrefix?: string;
84
+ }
85
+ /**
86
+ * Creates OpenTelemetry hooks for distributed tracing and metrics.
87
+ *
88
+ * This function creates hooks that integrate with OpenTelemetry for:
89
+ * - **Tracing**: Creates spans for SDK operations with relevant attributes
90
+ * - **Metrics**: Records histograms for latency and counters for operations/errors
91
+ *
92
+ * Span attributes include:
93
+ * - `basecamp.service`: Service name (e.g., "Todos", "Projects")
94
+ * - `basecamp.operation`: Operation name (e.g., "List", "Create")
95
+ * - `basecamp.resource_type`: Type of resource being accessed
96
+ * - `basecamp.is_mutation`: Whether the operation modifies data
97
+ * - `basecamp.project_id`: Project ID if applicable
98
+ * - `basecamp.resource_id`: Resource ID if applicable
99
+ *
100
+ * Metrics recorded:
101
+ * - `basecamp.operation.duration`: Histogram of operation durations (ms)
102
+ * - `basecamp.request.duration`: Histogram of HTTP request durations (ms)
103
+ * - `basecamp.operations.total`: Counter of total operations
104
+ * - `basecamp.errors.total`: Counter of errors
105
+ * - `basecamp.retries.total`: Counter of retry attempts
106
+ *
107
+ * @param options - Configuration options
108
+ * @returns Hooks object for use with createBasecampClient
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * import { createBasecampClient, otelHooks } from "@37signals/basecamp";
113
+ * import { trace, metrics } from "@opentelemetry/api";
114
+ *
115
+ * // Set up OpenTelemetry first (SDK configuration not shown)
116
+ * const tracer = trace.getTracer("my-app", "1.0.0");
117
+ * const meter = metrics.getMeter("my-app", "1.0.0");
118
+ *
119
+ * const client = createBasecampClient({
120
+ * accountId: "12345",
121
+ * accessToken: "token",
122
+ * hooks: otelHooks({
123
+ * tracer,
124
+ * meter,
125
+ * spanPrefix: "basecamp",
126
+ * recordRequestSpans: true,
127
+ * }),
128
+ * });
129
+ *
130
+ * // Operations will create spans and record metrics
131
+ * const todos = await client.todos.list(projectId, todolistId);
132
+ * ```
133
+ */
134
+ export declare function otelHooks(options?: OtelHooksOptions): BasecampHooks;
135
+ export {};
136
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../../src/hooks/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA8D,MAAM,aAAa,CAAC;AAI7G,UAAU,QAAQ;IAChB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAClE,SAAS,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5D,eAAe,CAAC,SAAS,EAAE,KAAK,GAAG,IAAI,CAAC;IACxC,GAAG,IAAI,IAAI,CAAC;CACb;AAED,UAAU,UAAU;IAClB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;KAAE,GAAG,QAAQ,CAAC;CACzG;AAED,UAAU,SAAS;IACjB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,CAAC;IAChG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC;CAC9E;AAED,UAAU,aAAa;IACrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;CACrF;AAED,UAAU,WAAW;IACnB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC;CAClF;AASD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,aAAa,CAyKvE"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * OpenTelemetry hooks for the Basecamp SDK.
3
+ *
4
+ * Provides distributed tracing and metrics for API operations.
5
+ * Requires @opentelemetry/api as an optional peer dependency.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { createBasecampClient, otelHooks } from "@37signals/basecamp";
10
+ * import { trace, metrics } from "@opentelemetry/api";
11
+ *
12
+ * const tracer = trace.getTracer("my-app");
13
+ * const meter = metrics.getMeter("my-app");
14
+ *
15
+ * const client = createBasecampClient({
16
+ * accountId: "12345",
17
+ * accessToken: "token",
18
+ * hooks: otelHooks({ tracer, meter }),
19
+ * });
20
+ *
21
+ * // All SDK operations will now create spans and record metrics
22
+ * const projects = await client.projects.list();
23
+ * ```
24
+ */
25
+ // Span status codes from OpenTelemetry
26
+ const SpanStatusCode = {
27
+ UNSET: 0,
28
+ OK: 1,
29
+ ERROR: 2,
30
+ };
31
+ /** Counter for generating unique keys */
32
+ let keyCounter = 0;
33
+ /**
34
+ * Creates a unique key for an operation.
35
+ */
36
+ function operationKey(info) {
37
+ return `${info.service}.${info.operation}:${info.resourceId ?? ""}:${++keyCounter}`;
38
+ }
39
+ /**
40
+ * Creates a unique key for a request.
41
+ */
42
+ function requestKey(info) {
43
+ return `${info.method}:${info.url}:${info.attempt}:${++keyCounter}`;
44
+ }
45
+ /**
46
+ * Creates OpenTelemetry hooks for distributed tracing and metrics.
47
+ *
48
+ * This function creates hooks that integrate with OpenTelemetry for:
49
+ * - **Tracing**: Creates spans for SDK operations with relevant attributes
50
+ * - **Metrics**: Records histograms for latency and counters for operations/errors
51
+ *
52
+ * Span attributes include:
53
+ * - `basecamp.service`: Service name (e.g., "Todos", "Projects")
54
+ * - `basecamp.operation`: Operation name (e.g., "List", "Create")
55
+ * - `basecamp.resource_type`: Type of resource being accessed
56
+ * - `basecamp.is_mutation`: Whether the operation modifies data
57
+ * - `basecamp.project_id`: Project ID if applicable
58
+ * - `basecamp.resource_id`: Resource ID if applicable
59
+ *
60
+ * Metrics recorded:
61
+ * - `basecamp.operation.duration`: Histogram of operation durations (ms)
62
+ * - `basecamp.request.duration`: Histogram of HTTP request durations (ms)
63
+ * - `basecamp.operations.total`: Counter of total operations
64
+ * - `basecamp.errors.total`: Counter of errors
65
+ * - `basecamp.retries.total`: Counter of retry attempts
66
+ *
67
+ * @param options - Configuration options
68
+ * @returns Hooks object for use with createBasecampClient
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * import { createBasecampClient, otelHooks } from "@37signals/basecamp";
73
+ * import { trace, metrics } from "@opentelemetry/api";
74
+ *
75
+ * // Set up OpenTelemetry first (SDK configuration not shown)
76
+ * const tracer = trace.getTracer("my-app", "1.0.0");
77
+ * const meter = metrics.getMeter("my-app", "1.0.0");
78
+ *
79
+ * const client = createBasecampClient({
80
+ * accountId: "12345",
81
+ * accessToken: "token",
82
+ * hooks: otelHooks({
83
+ * tracer,
84
+ * meter,
85
+ * spanPrefix: "basecamp",
86
+ * recordRequestSpans: true,
87
+ * }),
88
+ * });
89
+ *
90
+ * // Operations will create spans and record metrics
91
+ * const todos = await client.todos.list(projectId, todolistId);
92
+ * ```
93
+ */
94
+ export function otelHooks(options = {}) {
95
+ const { tracer, meter, recordRequestSpans = false, spanPrefix = "basecamp", metricPrefix = "basecamp", } = options;
96
+ // Initialize state
97
+ const state = {
98
+ operationSpans: new Map(),
99
+ requestSpans: new Map(),
100
+ };
101
+ // Initialize metrics if meter is provided
102
+ if (meter) {
103
+ state.operationDuration = meter.createHistogram(`${metricPrefix}.operation.duration`, {
104
+ description: "Duration of Basecamp SDK operations",
105
+ unit: "ms",
106
+ });
107
+ state.requestDuration = meter.createHistogram(`${metricPrefix}.request.duration`, {
108
+ description: "Duration of HTTP requests",
109
+ unit: "ms",
110
+ });
111
+ state.operationCounter = meter.createCounter(`${metricPrefix}.operations.total`, {
112
+ description: "Total number of Basecamp SDK operations",
113
+ });
114
+ state.errorCounter = meter.createCounter(`${metricPrefix}.errors.total`, {
115
+ description: "Total number of errors",
116
+ });
117
+ state.retryCounter = meter.createCounter(`${metricPrefix}.retries.total`, {
118
+ description: "Total number of retry attempts",
119
+ });
120
+ }
121
+ return {
122
+ onOperationStart(info) {
123
+ // Record operation count (even without tracer)
124
+ state.operationCounter?.add(1, {
125
+ service: info.service,
126
+ operation: info.operation,
127
+ is_mutation: info.isMutation,
128
+ });
129
+ if (!tracer)
130
+ return;
131
+ const spanName = `${spanPrefix}.${info.service}.${info.operation}`;
132
+ const attributes = {
133
+ [`${spanPrefix}.service`]: info.service,
134
+ [`${spanPrefix}.operation`]: info.operation,
135
+ [`${spanPrefix}.resource_type`]: info.resourceType,
136
+ [`${spanPrefix}.is_mutation`]: info.isMutation,
137
+ };
138
+ if (info.projectId) {
139
+ attributes[`${spanPrefix}.project_id`] = info.projectId;
140
+ }
141
+ if (info.resourceId) {
142
+ attributes[`${spanPrefix}.resource_id`] = info.resourceId;
143
+ }
144
+ const span = tracer.startSpan(spanName, { attributes });
145
+ const key = operationKey(info);
146
+ state.operationSpans.set(key, span);
147
+ },
148
+ onOperationEnd(info, result) {
149
+ // Find and end the span
150
+ if (tracer) {
151
+ // Find the most recent span for this operation
152
+ for (const [key, span] of state.operationSpans) {
153
+ if (key.startsWith(`${info.service}.${info.operation}:`)) {
154
+ if (result.error) {
155
+ span.setStatus({ code: SpanStatusCode.ERROR, message: result.error.message });
156
+ span.recordException(result.error);
157
+ span.setAttribute("error", true);
158
+ span.setAttribute("error.message", result.error.message);
159
+ }
160
+ else {
161
+ span.setStatus({ code: SpanStatusCode.OK });
162
+ }
163
+ span.setAttribute("duration_ms", result.durationMs);
164
+ span.end();
165
+ state.operationSpans.delete(key);
166
+ break;
167
+ }
168
+ }
169
+ }
170
+ // Record metrics
171
+ const labels = {
172
+ service: info.service,
173
+ operation: info.operation,
174
+ is_mutation: info.isMutation,
175
+ success: !result.error,
176
+ };
177
+ state.operationDuration?.record(result.durationMs, labels);
178
+ if (result.error) {
179
+ state.errorCounter?.add(1, {
180
+ service: info.service,
181
+ operation: info.operation,
182
+ error_type: result.error.name,
183
+ });
184
+ }
185
+ },
186
+ onRequestStart(info) {
187
+ if (!tracer || !recordRequestSpans)
188
+ return;
189
+ const spanName = `${spanPrefix}.http.${info.method}`;
190
+ const span = tracer.startSpan(spanName, {
191
+ attributes: {
192
+ "http.method": info.method,
193
+ "http.url": info.url,
194
+ "http.attempt": info.attempt,
195
+ },
196
+ });
197
+ const key = requestKey(info);
198
+ state.requestSpans.set(key, span);
199
+ },
200
+ onRequestEnd(info, result) {
201
+ // Find and end the span
202
+ if (tracer && recordRequestSpans) {
203
+ for (const [key, span] of state.requestSpans) {
204
+ if (key.startsWith(`${info.method}:${info.url}:${info.attempt}:`)) {
205
+ span.setAttribute("http.status_code", result.statusCode);
206
+ span.setAttribute("http.from_cache", result.fromCache);
207
+ span.setAttribute("duration_ms", result.durationMs);
208
+ if (result.error) {
209
+ span.setStatus({ code: SpanStatusCode.ERROR, message: result.error.message });
210
+ span.recordException(result.error);
211
+ }
212
+ else if (result.statusCode >= 400) {
213
+ span.setStatus({ code: SpanStatusCode.ERROR, message: `HTTP ${result.statusCode}` });
214
+ }
215
+ else {
216
+ span.setStatus({ code: SpanStatusCode.OK });
217
+ }
218
+ span.end();
219
+ state.requestSpans.delete(key);
220
+ break;
221
+ }
222
+ }
223
+ }
224
+ // Record request metrics
225
+ state.requestDuration?.record(result.durationMs, {
226
+ method: info.method,
227
+ status_code: result.statusCode,
228
+ from_cache: result.fromCache,
229
+ });
230
+ },
231
+ onRetry(info, attempt, error, _delayMs) {
232
+ state.retryCounter?.add(1, {
233
+ method: info.method,
234
+ attempt,
235
+ error_type: error.name,
236
+ });
237
+ },
238
+ };
239
+ }
240
+ //# sourceMappingURL=otel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.js","sourceRoot":"","sources":["../../src/hooks/otel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Observability hooks system for the Basecamp SDK.
3
+ *
4
+ * Provides a way to observe API operations and HTTP requests
5
+ * without modifying the core client logic. Hooks compile away
6
+ * when unused (no overhead).
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createBasecampClient, consoleHooks } from "@37signals/basecamp";
11
+ *
12
+ * // Simple console logging
13
+ * const client = createBasecampClient({
14
+ * accountId: "12345",
15
+ * accessToken: "token",
16
+ * hooks: consoleHooks(),
17
+ * });
18
+ *
19
+ * // Custom metrics
20
+ * const client = createBasecampClient({
21
+ * accountId: "12345",
22
+ * accessToken: "token",
23
+ * hooks: {
24
+ * onOperationEnd: (info, result) => {
25
+ * metrics.recordLatency(info.service, info.operation, result.durationMs);
26
+ * if (result.error) {
27
+ * metrics.recordError(info.service, info.operation);
28
+ * }
29
+ * },
30
+ * },
31
+ * });
32
+ * ```
33
+ */
34
+ /**
35
+ * Information about a high-level service operation.
36
+ */
37
+ export interface OperationInfo {
38
+ /** Service name (e.g., "Todos", "Projects") */
39
+ service: string;
40
+ /** Operation name (e.g., "List", "Get", "Create") */
41
+ operation: string;
42
+ /** Type of resource being accessed */
43
+ resourceType: string;
44
+ /** Whether this operation modifies data */
45
+ isMutation: boolean;
46
+ /** Project ID if the operation is scoped to a project */
47
+ projectId?: number;
48
+ /** Resource ID if the operation targets a specific resource */
49
+ resourceId?: number;
50
+ }
51
+ /**
52
+ * Information about an HTTP request.
53
+ */
54
+ export interface RequestInfo {
55
+ /** HTTP method */
56
+ method: string;
57
+ /** Full request URL */
58
+ url: string;
59
+ /** Current attempt number (1-based) */
60
+ attempt: number;
61
+ }
62
+ /**
63
+ * Result of an HTTP request.
64
+ */
65
+ export interface RequestResult {
66
+ /** HTTP status code */
67
+ statusCode: number;
68
+ /** Request duration in milliseconds */
69
+ durationMs: number;
70
+ /** Whether the response was served from cache */
71
+ fromCache: boolean;
72
+ /** Error if the request failed */
73
+ error?: Error;
74
+ }
75
+ /**
76
+ * Result of an operation.
77
+ */
78
+ export interface OperationResult {
79
+ /** Error if the operation failed */
80
+ error?: Error;
81
+ /** Operation duration in milliseconds */
82
+ durationMs: number;
83
+ }
84
+ /**
85
+ * Hooks interface for observing SDK operations.
86
+ * All hooks are optional - implement only what you need.
87
+ */
88
+ export interface BasecampHooks {
89
+ /**
90
+ * Called when a service operation starts.
91
+ * Use for logging, tracing, or metrics.
92
+ */
93
+ onOperationStart?(info: OperationInfo): void;
94
+ /**
95
+ * Called when a service operation completes (success or failure).
96
+ * Always called, even if onOperationStart threw.
97
+ */
98
+ onOperationEnd?(info: OperationInfo, result: OperationResult): void;
99
+ /**
100
+ * Called when an HTTP request starts.
101
+ * Called for each attempt (including retries).
102
+ */
103
+ onRequestStart?(info: RequestInfo): void;
104
+ /**
105
+ * Called when an HTTP request completes.
106
+ * Called for each attempt (including retries).
107
+ */
108
+ onRequestEnd?(info: RequestInfo, result: RequestResult): void;
109
+ /**
110
+ * Called before a retry attempt.
111
+ * Use for logging retry behavior or implementing custom backoff.
112
+ */
113
+ onRetry?(info: RequestInfo, attempt: number, error: Error, delayMs: number): void;
114
+ }
115
+ /**
116
+ * Combines multiple hooks into a single hooks object.
117
+ * All hooks are called in order. Errors in hooks are caught and logged.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * const hooks = chainHooks(
122
+ * consoleHooks(),
123
+ * metricsHooks(),
124
+ * sentryHooks(),
125
+ * );
126
+ * ```
127
+ */
128
+ export declare function chainHooks(...hooks: BasecampHooks[]): BasecampHooks;
129
+ /**
130
+ * Options for console logging hooks.
131
+ */
132
+ export interface ConsoleHooksOptions {
133
+ /** Whether to log operation events (default: true) */
134
+ logOperations?: boolean;
135
+ /** Whether to log request events (default: false) */
136
+ logRequests?: boolean;
137
+ /** Whether to log retry events (default: true) */
138
+ logRetries?: boolean;
139
+ /** Minimum duration in ms to log (default: 0) */
140
+ minDurationMs?: number;
141
+ /** Custom logger (default: console) */
142
+ logger?: Pick<Console, "log" | "warn" | "error">;
143
+ }
144
+ /**
145
+ * Creates hooks that log to the console.
146
+ * Useful for debugging and development.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * // Basic usage
151
+ * const hooks = consoleHooks();
152
+ *
153
+ * // With custom options
154
+ * const hooks = consoleHooks({
155
+ * logRequests: true,
156
+ * minDurationMs: 100,
157
+ * });
158
+ * ```
159
+ */
160
+ export declare function consoleHooks(options?: ConsoleHooksOptions): BasecampHooks;
161
+ /**
162
+ * Creates a no-op hooks object.
163
+ * Useful as a default when hooks are optional.
164
+ */
165
+ export declare function noopHooks(): BasecampHooks;
166
+ /**
167
+ * Internal helper to safely invoke a hook.
168
+ * Returns silently if the hook throws.
169
+ */
170
+ export declare function safeInvoke<K extends keyof BasecampHooks>(hooks: BasecampHooks | undefined, hookName: K, ...args: Parameters<NonNullable<BasecampHooks[K]>>): void;
171
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,UAAU,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IAE7C;;;OAGG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAEpE;;;OAGG;IACH,cAAc,CAAC,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IAEzC;;;OAGG;IACH,YAAY,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAE9D;;;OAGG;IACH,OAAO,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,GAAG,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,CA+DnE;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qDAAqD;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CAClD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,aAAa,CA4D7E;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,aAAa,CAEzC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,aAAa,EACtD,KAAK,EAAE,aAAa,GAAG,SAAS,EAChC,QAAQ,EAAE,CAAC,EACX,GAAG,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GACjD,IAAI,CAYN"}