parasut-node-sdk-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/.github/workflows/deploy.yaml +51 -0
  2. package/.github/workflows/npm-publish.yml +71 -0
  3. package/.travis.yml +15 -0
  4. package/.yo-rc.json +11 -0
  5. package/LICENSE +21 -0
  6. package/README.md +187 -0
  7. package/gulpfile.js +50 -0
  8. package/package.json +42 -0
  9. package/packages/parasut-mcp-server/README.md +312 -0
  10. package/packages/parasut-mcp-server/dist/client.d.ts +24 -0
  11. package/packages/parasut-mcp-server/dist/client.d.ts.map +1 -0
  12. package/packages/parasut-mcp-server/dist/client.js +46 -0
  13. package/packages/parasut-mcp-server/dist/client.js.map +1 -0
  14. package/packages/parasut-mcp-server/dist/config.d.ts +39 -0
  15. package/packages/parasut-mcp-server/dist/config.d.ts.map +1 -0
  16. package/packages/parasut-mcp-server/dist/config.js +74 -0
  17. package/packages/parasut-mcp-server/dist/config.js.map +1 -0
  18. package/packages/parasut-mcp-server/dist/index.d.ts +21 -0
  19. package/packages/parasut-mcp-server/dist/index.d.ts.map +1 -0
  20. package/packages/parasut-mcp-server/dist/index.js +26 -0
  21. package/packages/parasut-mcp-server/dist/index.js.map +1 -0
  22. package/packages/parasut-mcp-server/dist/server.d.ts +17 -0
  23. package/packages/parasut-mcp-server/dist/server.d.ts.map +1 -0
  24. package/packages/parasut-mcp-server/dist/server.js +61 -0
  25. package/packages/parasut-mcp-server/dist/server.js.map +1 -0
  26. package/packages/parasut-mcp-server/dist/tools/bills.d.ts +13 -0
  27. package/packages/parasut-mcp-server/dist/tools/bills.d.ts.map +1 -0
  28. package/packages/parasut-mcp-server/dist/tools/bills.js +387 -0
  29. package/packages/parasut-mcp-server/dist/tools/bills.js.map +1 -0
  30. package/packages/parasut-mcp-server/dist/tools/contacts.d.ts +13 -0
  31. package/packages/parasut-mcp-server/dist/tools/contacts.d.ts.map +1 -0
  32. package/packages/parasut-mcp-server/dist/tools/contacts.js +522 -0
  33. package/packages/parasut-mcp-server/dist/tools/contacts.js.map +1 -0
  34. package/packages/parasut-mcp-server/dist/tools/edocuments.d.ts +13 -0
  35. package/packages/parasut-mcp-server/dist/tools/edocuments.d.ts.map +1 -0
  36. package/packages/parasut-mcp-server/dist/tools/edocuments.js +514 -0
  37. package/packages/parasut-mcp-server/dist/tools/edocuments.js.map +1 -0
  38. package/packages/parasut-mcp-server/dist/tools/financial.d.ts +13 -0
  39. package/packages/parasut-mcp-server/dist/tools/financial.d.ts.map +1 -0
  40. package/packages/parasut-mcp-server/dist/tools/financial.js +321 -0
  41. package/packages/parasut-mcp-server/dist/tools/financial.js.map +1 -0
  42. package/packages/parasut-mcp-server/dist/tools/index.d.ts +19 -0
  43. package/packages/parasut-mcp-server/dist/tools/index.d.ts.map +1 -0
  44. package/packages/parasut-mcp-server/dist/tools/index.js +132 -0
  45. package/packages/parasut-mcp-server/dist/tools/index.js.map +1 -0
  46. package/packages/parasut-mcp-server/dist/tools/inventory.d.ts +11 -0
  47. package/packages/parasut-mcp-server/dist/tools/inventory.d.ts.map +1 -0
  48. package/packages/parasut-mcp-server/dist/tools/inventory.js +143 -0
  49. package/packages/parasut-mcp-server/dist/tools/inventory.js.map +1 -0
  50. package/packages/parasut-mcp-server/dist/tools/invoices.d.ts +16 -0
  51. package/packages/parasut-mcp-server/dist/tools/invoices.d.ts.map +1 -0
  52. package/packages/parasut-mcp-server/dist/tools/invoices.js +850 -0
  53. package/packages/parasut-mcp-server/dist/tools/invoices.js.map +1 -0
  54. package/packages/parasut-mcp-server/dist/tools/organization.d.ts +14 -0
  55. package/packages/parasut-mcp-server/dist/tools/organization.d.ts.map +1 -0
  56. package/packages/parasut-mcp-server/dist/tools/organization.js +377 -0
  57. package/packages/parasut-mcp-server/dist/tools/organization.js.map +1 -0
  58. package/packages/parasut-mcp-server/dist/tools/products.d.ts +13 -0
  59. package/packages/parasut-mcp-server/dist/tools/products.d.ts.map +1 -0
  60. package/packages/parasut-mcp-server/dist/tools/products.js +299 -0
  61. package/packages/parasut-mcp-server/dist/tools/products.js.map +1 -0
  62. package/packages/parasut-mcp-server/dist/utils/errors.d.ts +34 -0
  63. package/packages/parasut-mcp-server/dist/utils/errors.d.ts.map +1 -0
  64. package/packages/parasut-mcp-server/dist/utils/errors.js +217 -0
  65. package/packages/parasut-mcp-server/dist/utils/errors.js.map +1 -0
  66. package/packages/parasut-mcp-server/dist/utils/response.d.ts +94 -0
  67. package/packages/parasut-mcp-server/dist/utils/response.d.ts.map +1 -0
  68. package/packages/parasut-mcp-server/dist/utils/response.js +194 -0
  69. package/packages/parasut-mcp-server/dist/utils/response.js.map +1 -0
  70. package/packages/parasut-mcp-server/package-lock.json +2831 -0
  71. package/packages/parasut-mcp-server/package.json +70 -0
  72. package/packages/parasut-mcp-server/src/client.ts +55 -0
  73. package/packages/parasut-mcp-server/src/config.ts +99 -0
  74. package/packages/parasut-mcp-server/src/index.ts +27 -0
  75. package/packages/parasut-mcp-server/src/server.ts +79 -0
  76. package/packages/parasut-mcp-server/src/tools/bills.ts +416 -0
  77. package/packages/parasut-mcp-server/src/tools/contacts.ts +549 -0
  78. package/packages/parasut-mcp-server/src/tools/edocuments.ts +543 -0
  79. package/packages/parasut-mcp-server/src/tools/financial.ts +345 -0
  80. package/packages/parasut-mcp-server/src/tools/index.ts +158 -0
  81. package/packages/parasut-mcp-server/src/tools/inventory.ts +154 -0
  82. package/packages/parasut-mcp-server/src/tools/invoices.ts +905 -0
  83. package/packages/parasut-mcp-server/src/tools/organization.ts +405 -0
  84. package/packages/parasut-mcp-server/src/tools/products.ts +320 -0
  85. package/packages/parasut-mcp-server/src/utils/errors.ts +296 -0
  86. package/packages/parasut-mcp-server/src/utils/response.ts +280 -0
  87. package/packages/parasut-mcp-server/tsconfig.json +48 -0
  88. package/packages/parasut-node-sdk/README.md +393 -0
  89. package/packages/parasut-node-sdk/dist/client/HttpTransport.d.ts +92 -0
  90. package/packages/parasut-node-sdk/dist/client/HttpTransport.d.ts.map +1 -0
  91. package/packages/parasut-node-sdk/dist/client/HttpTransport.js +237 -0
  92. package/packages/parasut-node-sdk/dist/client/HttpTransport.js.map +1 -0
  93. package/packages/parasut-node-sdk/dist/client/JsonApi.d.ts +122 -0
  94. package/packages/parasut-node-sdk/dist/client/JsonApi.d.ts.map +1 -0
  95. package/packages/parasut-node-sdk/dist/client/JsonApi.js +192 -0
  96. package/packages/parasut-node-sdk/dist/client/JsonApi.js.map +1 -0
  97. package/packages/parasut-node-sdk/dist/client/OAuth.d.ts +117 -0
  98. package/packages/parasut-node-sdk/dist/client/OAuth.d.ts.map +1 -0
  99. package/packages/parasut-node-sdk/dist/client/OAuth.js +322 -0
  100. package/packages/parasut-node-sdk/dist/client/OAuth.js.map +1 -0
  101. package/packages/parasut-node-sdk/dist/client/ParasutClient.d.ts +198 -0
  102. package/packages/parasut-node-sdk/dist/client/ParasutClient.d.ts.map +1 -0
  103. package/packages/parasut-node-sdk/dist/client/ParasutClient.js +334 -0
  104. package/packages/parasut-node-sdk/dist/client/ParasutClient.js.map +1 -0
  105. package/packages/parasut-node-sdk/dist/client/QueryBuilder.d.ts +115 -0
  106. package/packages/parasut-node-sdk/dist/client/QueryBuilder.d.ts.map +1 -0
  107. package/packages/parasut-node-sdk/dist/client/QueryBuilder.js +206 -0
  108. package/packages/parasut-node-sdk/dist/client/QueryBuilder.js.map +1 -0
  109. package/packages/parasut-node-sdk/dist/client/RateLimiter.d.ts +77 -0
  110. package/packages/parasut-node-sdk/dist/client/RateLimiter.d.ts.map +1 -0
  111. package/packages/parasut-node-sdk/dist/client/RateLimiter.js +142 -0
  112. package/packages/parasut-node-sdk/dist/client/RateLimiter.js.map +1 -0
  113. package/packages/parasut-node-sdk/dist/client/RetryHandler.d.ts +88 -0
  114. package/packages/parasut-node-sdk/dist/client/RetryHandler.d.ts.map +1 -0
  115. package/packages/parasut-node-sdk/dist/client/RetryHandler.js +125 -0
  116. package/packages/parasut-node-sdk/dist/client/RetryHandler.js.map +1 -0
  117. package/packages/parasut-node-sdk/dist/client/errors.d.ts +87 -0
  118. package/packages/parasut-node-sdk/dist/client/errors.d.ts.map +1 -0
  119. package/packages/parasut-node-sdk/dist/client/errors.js +153 -0
  120. package/packages/parasut-node-sdk/dist/client/errors.js.map +1 -0
  121. package/packages/parasut-node-sdk/dist/generated/operations.d.ts +157 -0
  122. package/packages/parasut-node-sdk/dist/generated/operations.d.ts.map +1 -0
  123. package/packages/parasut-node-sdk/dist/generated/operations.js +1285 -0
  124. package/packages/parasut-node-sdk/dist/generated/operations.js.map +1 -0
  125. package/packages/parasut-node-sdk/dist/generated/types.d.ts +1743 -0
  126. package/packages/parasut-node-sdk/dist/generated/types.d.ts.map +1 -0
  127. package/packages/parasut-node-sdk/dist/generated/types.js +6 -0
  128. package/packages/parasut-node-sdk/dist/generated/types.js.map +1 -0
  129. package/packages/parasut-node-sdk/dist/index.d.ts +49 -0
  130. package/packages/parasut-node-sdk/dist/index.d.ts.map +1 -0
  131. package/packages/parasut-node-sdk/dist/index.js +91 -0
  132. package/packages/parasut-node-sdk/dist/index.js.map +1 -0
  133. package/packages/parasut-node-sdk/dist/resources/BaseResource.d.ts +143 -0
  134. package/packages/parasut-node-sdk/dist/resources/BaseResource.d.ts.map +1 -0
  135. package/packages/parasut-node-sdk/dist/resources/BaseResource.js +148 -0
  136. package/packages/parasut-node-sdk/dist/resources/BaseResource.js.map +1 -0
  137. package/packages/parasut-node-sdk/dist/resources/accounts.d.ts +71 -0
  138. package/packages/parasut-node-sdk/dist/resources/accounts.d.ts.map +1 -0
  139. package/packages/parasut-node-sdk/dist/resources/accounts.js +43 -0
  140. package/packages/parasut-node-sdk/dist/resources/accounts.js.map +1 -0
  141. package/packages/parasut-node-sdk/dist/resources/bankFees.d.ts +52 -0
  142. package/packages/parasut-node-sdk/dist/resources/bankFees.d.ts.map +1 -0
  143. package/packages/parasut-node-sdk/dist/resources/bankFees.js +25 -0
  144. package/packages/parasut-node-sdk/dist/resources/bankFees.js.map +1 -0
  145. package/packages/parasut-node-sdk/dist/resources/contacts.d.ts +62 -0
  146. package/packages/parasut-node-sdk/dist/resources/contacts.d.ts.map +1 -0
  147. package/packages/parasut-node-sdk/dist/resources/contacts.js +43 -0
  148. package/packages/parasut-node-sdk/dist/resources/contacts.js.map +1 -0
  149. package/packages/parasut-node-sdk/dist/resources/eArchives.d.ts +125 -0
  150. package/packages/parasut-node-sdk/dist/resources/eArchives.d.ts.map +1 -0
  151. package/packages/parasut-node-sdk/dist/resources/eArchives.js +100 -0
  152. package/packages/parasut-node-sdk/dist/resources/eArchives.js.map +1 -0
  153. package/packages/parasut-node-sdk/dist/resources/eInvoiceInboxes.d.ts +48 -0
  154. package/packages/parasut-node-sdk/dist/resources/eInvoiceInboxes.d.ts.map +1 -0
  155. package/packages/parasut-node-sdk/dist/resources/eInvoiceInboxes.js +46 -0
  156. package/packages/parasut-node-sdk/dist/resources/eInvoiceInboxes.js.map +1 -0
  157. package/packages/parasut-node-sdk/dist/resources/eInvoices.d.ts +104 -0
  158. package/packages/parasut-node-sdk/dist/resources/eInvoices.d.ts.map +1 -0
  159. package/packages/parasut-node-sdk/dist/resources/eInvoices.js +72 -0
  160. package/packages/parasut-node-sdk/dist/resources/eInvoices.js.map +1 -0
  161. package/packages/parasut-node-sdk/dist/resources/eSmms.d.ts +73 -0
  162. package/packages/parasut-node-sdk/dist/resources/eSmms.d.ts.map +1 -0
  163. package/packages/parasut-node-sdk/dist/resources/eSmms.js +59 -0
  164. package/packages/parasut-node-sdk/dist/resources/eSmms.js.map +1 -0
  165. package/packages/parasut-node-sdk/dist/resources/employees.d.ts +37 -0
  166. package/packages/parasut-node-sdk/dist/resources/employees.d.ts.map +1 -0
  167. package/packages/parasut-node-sdk/dist/resources/employees.js +31 -0
  168. package/packages/parasut-node-sdk/dist/resources/employees.js.map +1 -0
  169. package/packages/parasut-node-sdk/dist/resources/index.d.ts +26 -0
  170. package/packages/parasut-node-sdk/dist/resources/index.d.ts.map +1 -0
  171. package/packages/parasut-node-sdk/dist/resources/index.js +35 -0
  172. package/packages/parasut-node-sdk/dist/resources/index.js.map +1 -0
  173. package/packages/parasut-node-sdk/dist/resources/inventoryLevels.d.ts +25 -0
  174. package/packages/parasut-node-sdk/dist/resources/inventoryLevels.d.ts.map +1 -0
  175. package/packages/parasut-node-sdk/dist/resources/inventoryLevels.js +22 -0
  176. package/packages/parasut-node-sdk/dist/resources/inventoryLevels.js.map +1 -0
  177. package/packages/parasut-node-sdk/dist/resources/itemCategories.d.ts +31 -0
  178. package/packages/parasut-node-sdk/dist/resources/itemCategories.d.ts.map +1 -0
  179. package/packages/parasut-node-sdk/dist/resources/itemCategories.js +22 -0
  180. package/packages/parasut-node-sdk/dist/resources/itemCategories.js.map +1 -0
  181. package/packages/parasut-node-sdk/dist/resources/products.d.ts +50 -0
  182. package/packages/parasut-node-sdk/dist/resources/products.d.ts.map +1 -0
  183. package/packages/parasut-node-sdk/dist/resources/products.js +31 -0
  184. package/packages/parasut-node-sdk/dist/resources/products.js.map +1 -0
  185. package/packages/parasut-node-sdk/dist/resources/purchaseBills.d.ts +105 -0
  186. package/packages/parasut-node-sdk/dist/resources/purchaseBills.d.ts.map +1 -0
  187. package/packages/parasut-node-sdk/dist/resources/purchaseBills.js +67 -0
  188. package/packages/parasut-node-sdk/dist/resources/purchaseBills.js.map +1 -0
  189. package/packages/parasut-node-sdk/dist/resources/salaries.d.ts +50 -0
  190. package/packages/parasut-node-sdk/dist/resources/salaries.d.ts.map +1 -0
  191. package/packages/parasut-node-sdk/dist/resources/salaries.js +25 -0
  192. package/packages/parasut-node-sdk/dist/resources/salaries.js.map +1 -0
  193. package/packages/parasut-node-sdk/dist/resources/salesInvoices.d.ts +137 -0
  194. package/packages/parasut-node-sdk/dist/resources/salesInvoices.d.ts.map +1 -0
  195. package/packages/parasut-node-sdk/dist/resources/salesInvoices.js +84 -0
  196. package/packages/parasut-node-sdk/dist/resources/salesInvoices.js.map +1 -0
  197. package/packages/parasut-node-sdk/dist/resources/salesOffers.d.ts +72 -0
  198. package/packages/parasut-node-sdk/dist/resources/salesOffers.d.ts.map +1 -0
  199. package/packages/parasut-node-sdk/dist/resources/salesOffers.js +66 -0
  200. package/packages/parasut-node-sdk/dist/resources/salesOffers.js.map +1 -0
  201. package/packages/parasut-node-sdk/dist/resources/shipmentDocuments.d.ts +26 -0
  202. package/packages/parasut-node-sdk/dist/resources/shipmentDocuments.d.ts.map +1 -0
  203. package/packages/parasut-node-sdk/dist/resources/shipmentDocuments.js +16 -0
  204. package/packages/parasut-node-sdk/dist/resources/shipmentDocuments.js.map +1 -0
  205. package/packages/parasut-node-sdk/dist/resources/stockMovements.d.ts +27 -0
  206. package/packages/parasut-node-sdk/dist/resources/stockMovements.d.ts.map +1 -0
  207. package/packages/parasut-node-sdk/dist/resources/stockMovements.js +16 -0
  208. package/packages/parasut-node-sdk/dist/resources/stockMovements.js.map +1 -0
  209. package/packages/parasut-node-sdk/dist/resources/tags.d.ts +22 -0
  210. package/packages/parasut-node-sdk/dist/resources/tags.d.ts.map +1 -0
  211. package/packages/parasut-node-sdk/dist/resources/tags.js +16 -0
  212. package/packages/parasut-node-sdk/dist/resources/tags.js.map +1 -0
  213. package/packages/parasut-node-sdk/dist/resources/taxes.d.ts +50 -0
  214. package/packages/parasut-node-sdk/dist/resources/taxes.d.ts.map +1 -0
  215. package/packages/parasut-node-sdk/dist/resources/taxes.js +25 -0
  216. package/packages/parasut-node-sdk/dist/resources/taxes.js.map +1 -0
  217. package/packages/parasut-node-sdk/dist/resources/trackableJobs.d.ts +72 -0
  218. package/packages/parasut-node-sdk/dist/resources/trackableJobs.d.ts.map +1 -0
  219. package/packages/parasut-node-sdk/dist/resources/trackableJobs.js +108 -0
  220. package/packages/parasut-node-sdk/dist/resources/trackableJobs.js.map +1 -0
  221. package/packages/parasut-node-sdk/dist/resources/transactions.d.ts +26 -0
  222. package/packages/parasut-node-sdk/dist/resources/transactions.d.ts.map +1 -0
  223. package/packages/parasut-node-sdk/dist/resources/transactions.js +16 -0
  224. package/packages/parasut-node-sdk/dist/resources/transactions.js.map +1 -0
  225. package/packages/parasut-node-sdk/package-lock.json +4030 -0
  226. package/packages/parasut-node-sdk/package.json +71 -0
  227. package/packages/parasut-node-sdk/scripts/generate-types.ts +495 -0
  228. package/packages/parasut-node-sdk/src/client/HttpTransport.ts +334 -0
  229. package/packages/parasut-node-sdk/src/client/JsonApi.ts +314 -0
  230. package/packages/parasut-node-sdk/src/client/OAuth.ts +446 -0
  231. package/packages/parasut-node-sdk/src/client/ParasutClient.ts +423 -0
  232. package/packages/parasut-node-sdk/src/client/QueryBuilder.ts +292 -0
  233. package/packages/parasut-node-sdk/src/client/RateLimiter.ts +190 -0
  234. package/packages/parasut-node-sdk/src/client/RetryHandler.ts +216 -0
  235. package/packages/parasut-node-sdk/src/client/errors.ts +209 -0
  236. package/packages/parasut-node-sdk/src/generated/operations.ts +1454 -0
  237. package/packages/parasut-node-sdk/src/generated/types.ts +1856 -0
  238. package/packages/parasut-node-sdk/src/index.ts +235 -0
  239. package/packages/parasut-node-sdk/src/resources/BaseResource.ts +275 -0
  240. package/packages/parasut-node-sdk/src/resources/accounts.ts +108 -0
  241. package/packages/parasut-node-sdk/src/resources/bankFees.ts +74 -0
  242. package/packages/parasut-node-sdk/src/resources/contacts.ts +96 -0
  243. package/packages/parasut-node-sdk/src/resources/eArchives.ts +202 -0
  244. package/packages/parasut-node-sdk/src/resources/eInvoiceInboxes.ts +78 -0
  245. package/packages/parasut-node-sdk/src/resources/eInvoices.ts +166 -0
  246. package/packages/parasut-node-sdk/src/resources/eSmms.ts +132 -0
  247. package/packages/parasut-node-sdk/src/resources/employees.ts +69 -0
  248. package/packages/parasut-node-sdk/src/resources/index.ts +44 -0
  249. package/packages/parasut-node-sdk/src/resources/inventoryLevels.ts +45 -0
  250. package/packages/parasut-node-sdk/src/resources/itemCategories.ts +49 -0
  251. package/packages/parasut-node-sdk/src/resources/products.ts +78 -0
  252. package/packages/parasut-node-sdk/src/resources/purchaseBills.ts +162 -0
  253. package/packages/parasut-node-sdk/src/resources/salaries.ts +72 -0
  254. package/packages/parasut-node-sdk/src/resources/salesInvoices.ts +194 -0
  255. package/packages/parasut-node-sdk/src/resources/salesOffers.ts +149 -0
  256. package/packages/parasut-node-sdk/src/resources/shipmentDocuments.ts +40 -0
  257. package/packages/parasut-node-sdk/src/resources/stockMovements.ts +41 -0
  258. package/packages/parasut-node-sdk/src/resources/tags.ts +32 -0
  259. package/packages/parasut-node-sdk/src/resources/taxes.ts +68 -0
  260. package/packages/parasut-node-sdk/src/resources/trackableJobs.ts +167 -0
  261. package/packages/parasut-node-sdk/src/resources/transactions.ts +40 -0
  262. package/packages/parasut-node-sdk/tsconfig.json +47 -0
  263. package/scripts/build.js +14 -0
  264. package/scripts/deploy-branch.js +16 -0
  265. package/spec/README.md +24 -0
  266. package/spec/code_samples/README.md +9 -0
  267. package/spec/swagger.yaml +19616 -0
  268. package/web/CNAME +1 -0
  269. package/web/custom.css +34 -0
  270. package/web/favicon.ico +0 -0
  271. package/web/favicon.png +0 -0
  272. package/web/index.html +26 -0
  273. package/web/logo.png +0 -0
  274. package/web/v4.html +3 -0
@@ -0,0 +1,446 @@
1
+ /**
2
+ * OAuth Token Manager
3
+ *
4
+ * Handles OAuth2 authentication with password grant and token refresh.
5
+ * Supports pluggable token storage for persistence.
6
+ */
7
+
8
+ import { ParasutAuthError, ParasutConfigError, ParasutNetworkError } from './errors.js';
9
+
10
+ // ============================================================================
11
+ // Types
12
+ // ============================================================================
13
+
14
+ export interface OAuthCredentials {
15
+ clientId: string;
16
+ clientSecret: string;
17
+ username: string;
18
+ password: string;
19
+ }
20
+
21
+ export interface OAuthToken {
22
+ accessToken: string;
23
+ refreshToken: string;
24
+ expiresAt: number; // Unix timestamp in milliseconds
25
+ tokenType: string;
26
+ }
27
+
28
+ /**
29
+ * Interface for pluggable token storage.
30
+ * Implement this to persist tokens (e.g., to Redis, file, etc.)
31
+ */
32
+ export interface TokenStorage {
33
+ get(): Promise<OAuthToken | null>;
34
+ set(token: OAuthToken): Promise<void>;
35
+ clear(): Promise<void>;
36
+ }
37
+
38
+ interface TokenResponse {
39
+ access_token: string;
40
+ refresh_token: string;
41
+ expires_in: number;
42
+ token_type: string;
43
+ }
44
+
45
+ // ============================================================================
46
+ // In-Memory Token Storage
47
+ // ============================================================================
48
+
49
+ export class MemoryTokenStorage implements TokenStorage {
50
+ private token: OAuthToken | null = null;
51
+
52
+ async get(): Promise<OAuthToken | null> {
53
+ return this.token;
54
+ }
55
+
56
+ async set(token: OAuthToken): Promise<void> {
57
+ this.token = token;
58
+ }
59
+
60
+ async clear(): Promise<void> {
61
+ this.token = null;
62
+ }
63
+ }
64
+
65
+ // ============================================================================
66
+ // OAuth Manager
67
+ // ============================================================================
68
+
69
+ export class OAuthManager {
70
+ private readonly tokenUrl: string;
71
+ private readonly credentials: OAuthCredentials;
72
+ private readonly storage: TokenStorage;
73
+ private refreshPromise: Promise<OAuthToken> | null = null;
74
+
75
+ /**
76
+ * Buffer time before token expiry to trigger refresh (60 seconds)
77
+ */
78
+ private readonly expiryBuffer = 60_000;
79
+
80
+ constructor(
81
+ credentials: OAuthCredentials,
82
+ options?: {
83
+ tokenUrl?: string;
84
+ storage?: TokenStorage;
85
+ }
86
+ ) {
87
+ this.credentials = credentials;
88
+ this.tokenUrl = options?.tokenUrl ?? 'https://api.parasut.com/oauth/token';
89
+ this.storage = options?.storage ?? new MemoryTokenStorage();
90
+
91
+ // Validate credentials
92
+ if (!credentials.clientId || !credentials.clientSecret) {
93
+ throw new ParasutConfigError('clientId and clientSecret are required');
94
+ }
95
+ if (!credentials.username || !credentials.password) {
96
+ throw new ParasutConfigError('username and password are required');
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Returns a valid access token, refreshing if necessary.
102
+ * Handles concurrent refresh requests to prevent multiple refreshes.
103
+ */
104
+ async getValidToken(): Promise<string> {
105
+ // Check if we have a cached token
106
+ const token = await this.storage.get();
107
+
108
+ if (token && this.isTokenValid(token)) {
109
+ return token.accessToken;
110
+ }
111
+
112
+ // If refresh is already in progress, wait for it
113
+ if (this.refreshPromise) {
114
+ const refreshedToken = await this.refreshPromise;
115
+ return refreshedToken.accessToken;
116
+ }
117
+
118
+ // If we have a refresh token, try to refresh
119
+ if (token?.refreshToken) {
120
+ try {
121
+ const newToken = await this.refreshToken(token.refreshToken);
122
+ return newToken.accessToken;
123
+ } catch (error) {
124
+ // If refresh fails, fall through to password grant
125
+ console.warn('Token refresh failed, attempting password grant');
126
+ }
127
+ }
128
+
129
+ // No valid token, perform password grant
130
+ const newToken = await this.authenticate();
131
+ return newToken.accessToken;
132
+ }
133
+
134
+ /**
135
+ * Performs password grant authentication.
136
+ */
137
+ async authenticate(): Promise<OAuthToken> {
138
+ // Prevent concurrent authentication attempts
139
+ if (this.refreshPromise) {
140
+ return this.refreshPromise;
141
+ }
142
+
143
+ this.refreshPromise = this.performPasswordGrant();
144
+
145
+ try {
146
+ const token = await this.refreshPromise;
147
+ return token;
148
+ } finally {
149
+ this.refreshPromise = null;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Refreshes the token using the refresh token.
155
+ */
156
+ async refreshToken(refreshToken: string): Promise<OAuthToken> {
157
+ // Prevent concurrent refresh attempts
158
+ if (this.refreshPromise) {
159
+ return this.refreshPromise;
160
+ }
161
+
162
+ this.refreshPromise = this.performRefresh(refreshToken);
163
+
164
+ try {
165
+ const token = await this.refreshPromise;
166
+ return token;
167
+ } finally {
168
+ this.refreshPromise = null;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Clears the stored token.
174
+ */
175
+ async clearToken(): Promise<void> {
176
+ await this.storage.clear();
177
+ }
178
+
179
+ /**
180
+ * Checks if a token is still valid.
181
+ */
182
+ private isTokenValid(token: OAuthToken): boolean {
183
+ return Date.now() < token.expiresAt - this.expiryBuffer;
184
+ }
185
+
186
+ /**
187
+ * Performs the password grant flow.
188
+ */
189
+ private async performPasswordGrant(): Promise<OAuthToken> {
190
+ const body = new URLSearchParams({
191
+ grant_type: 'password',
192
+ client_id: this.credentials.clientId,
193
+ client_secret: this.credentials.clientSecret,
194
+ username: this.credentials.username,
195
+ password: this.credentials.password,
196
+ redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
197
+ });
198
+
199
+ return this.requestToken(body);
200
+ }
201
+
202
+ /**
203
+ * Performs the refresh token flow.
204
+ */
205
+ private async performRefresh(refreshToken: string): Promise<OAuthToken> {
206
+ const body = new URLSearchParams({
207
+ grant_type: 'refresh_token',
208
+ client_id: this.credentials.clientId,
209
+ client_secret: this.credentials.clientSecret,
210
+ refresh_token: refreshToken,
211
+ });
212
+
213
+ return this.requestToken(body);
214
+ }
215
+
216
+ /**
217
+ * Makes the token request to the OAuth server.
218
+ */
219
+ private async requestToken(body: URLSearchParams): Promise<OAuthToken> {
220
+ try {
221
+ const response = await fetch(this.tokenUrl, {
222
+ method: 'POST',
223
+ headers: {
224
+ 'Content-Type': 'application/x-www-form-urlencoded',
225
+ },
226
+ body: body.toString(),
227
+ });
228
+
229
+ if (!response.ok) {
230
+ const errorBody = await response.text();
231
+ let errorDetail: string;
232
+ try {
233
+ const parsed = JSON.parse(errorBody);
234
+ errorDetail = parsed.error_description ?? parsed.error ?? errorBody;
235
+ } catch {
236
+ errorDetail = errorBody;
237
+ }
238
+
239
+ throw new ParasutAuthError(
240
+ [{ title: 'Authentication Failed', detail: errorDetail }],
241
+ undefined,
242
+ errorBody
243
+ );
244
+ }
245
+
246
+ const data = (await response.json()) as TokenResponse;
247
+
248
+ const token: OAuthToken = {
249
+ accessToken: data.access_token,
250
+ refreshToken: data.refresh_token,
251
+ expiresAt: Date.now() + data.expires_in * 1000,
252
+ tokenType: data.token_type,
253
+ };
254
+
255
+ // Store the new token
256
+ await this.storage.set(token);
257
+
258
+ return token;
259
+ } catch (error) {
260
+ if (error instanceof ParasutAuthError) {
261
+ throw error;
262
+ }
263
+
264
+ if (error instanceof Error) {
265
+ throw new ParasutNetworkError(
266
+ `OAuth request failed: ${error.message}`,
267
+ 'OAUTH_ERROR',
268
+ error
269
+ );
270
+ }
271
+
272
+ throw error;
273
+ }
274
+ }
275
+ }
276
+
277
+ // ============================================================================
278
+ // Authorization Code Flow (for web applications)
279
+ // ============================================================================
280
+
281
+ export interface AuthCodeConfig {
282
+ clientId: string;
283
+ clientSecret: string;
284
+ redirectUri: string;
285
+ tokenUrl?: string;
286
+ authorizeUrl?: string;
287
+ storage?: TokenStorage;
288
+ }
289
+
290
+ export class AuthCodeManager {
291
+ private readonly config: AuthCodeConfig;
292
+ private readonly tokenUrl: string;
293
+ private readonly authorizeUrl: string;
294
+ private readonly storage: TokenStorage;
295
+ private refreshPromise: Promise<OAuthToken> | null = null;
296
+ private readonly expiryBuffer = 60_000;
297
+
298
+ constructor(config: AuthCodeConfig) {
299
+ this.config = config;
300
+ this.tokenUrl = config.tokenUrl ?? 'https://api.parasut.com/oauth/token';
301
+ this.authorizeUrl = config.authorizeUrl ?? 'https://api.parasut.com/oauth/authorize';
302
+ this.storage = config.storage ?? new MemoryTokenStorage();
303
+ }
304
+
305
+ /**
306
+ * Generates the authorization URL for the user to visit.
307
+ */
308
+ getAuthorizationUrl(state?: string): string {
309
+ const params = new URLSearchParams({
310
+ client_id: this.config.clientId,
311
+ redirect_uri: this.config.redirectUri,
312
+ response_type: 'code',
313
+ });
314
+
315
+ if (state) {
316
+ params.set('state', state);
317
+ }
318
+
319
+ return `${this.authorizeUrl}?${params.toString()}`;
320
+ }
321
+
322
+ /**
323
+ * Exchanges an authorization code for tokens.
324
+ */
325
+ async exchangeCode(code: string): Promise<OAuthToken> {
326
+ const body = new URLSearchParams({
327
+ grant_type: 'authorization_code',
328
+ client_id: this.config.clientId,
329
+ client_secret: this.config.clientSecret,
330
+ code,
331
+ redirect_uri: this.config.redirectUri,
332
+ });
333
+
334
+ return this.requestToken(body);
335
+ }
336
+
337
+ /**
338
+ * Returns a valid access token, refreshing if necessary.
339
+ */
340
+ async getValidToken(): Promise<string> {
341
+ const token = await this.storage.get();
342
+
343
+ if (token && this.isTokenValid(token)) {
344
+ return token.accessToken;
345
+ }
346
+
347
+ if (this.refreshPromise) {
348
+ const refreshedToken = await this.refreshPromise;
349
+ return refreshedToken.accessToken;
350
+ }
351
+
352
+ if (token?.refreshToken) {
353
+ const newToken = await this.refreshToken(token.refreshToken);
354
+ return newToken.accessToken;
355
+ }
356
+
357
+ throw new ParasutAuthError(
358
+ [{ title: 'No Valid Token', detail: 'No token available. Please authenticate first.' }]
359
+ );
360
+ }
361
+
362
+ /**
363
+ * Refreshes the token using the refresh token.
364
+ */
365
+ async refreshToken(refreshToken: string): Promise<OAuthToken> {
366
+ if (this.refreshPromise) {
367
+ return this.refreshPromise;
368
+ }
369
+
370
+ const body = new URLSearchParams({
371
+ grant_type: 'refresh_token',
372
+ client_id: this.config.clientId,
373
+ client_secret: this.config.clientSecret,
374
+ refresh_token: refreshToken,
375
+ });
376
+
377
+ this.refreshPromise = this.requestToken(body);
378
+
379
+ try {
380
+ return await this.refreshPromise;
381
+ } finally {
382
+ this.refreshPromise = null;
383
+ }
384
+ }
385
+
386
+ async clearToken(): Promise<void> {
387
+ await this.storage.clear();
388
+ }
389
+
390
+ private isTokenValid(token: OAuthToken): boolean {
391
+ return Date.now() < token.expiresAt - this.expiryBuffer;
392
+ }
393
+
394
+ private async requestToken(body: URLSearchParams): Promise<OAuthToken> {
395
+ try {
396
+ const response = await fetch(this.tokenUrl, {
397
+ method: 'POST',
398
+ headers: {
399
+ 'Content-Type': 'application/x-www-form-urlencoded',
400
+ },
401
+ body: body.toString(),
402
+ });
403
+
404
+ if (!response.ok) {
405
+ const errorBody = await response.text();
406
+ let errorDetail: string;
407
+ try {
408
+ const parsed = JSON.parse(errorBody);
409
+ errorDetail = parsed.error_description ?? parsed.error ?? errorBody;
410
+ } catch {
411
+ errorDetail = errorBody;
412
+ }
413
+
414
+ throw new ParasutAuthError(
415
+ [{ title: 'Authentication Failed', detail: errorDetail }],
416
+ undefined,
417
+ errorBody
418
+ );
419
+ }
420
+
421
+ const data = (await response.json()) as TokenResponse;
422
+
423
+ const token: OAuthToken = {
424
+ accessToken: data.access_token,
425
+ refreshToken: data.refresh_token,
426
+ expiresAt: Date.now() + data.expires_in * 1000,
427
+ tokenType: data.token_type,
428
+ };
429
+
430
+ await this.storage.set(token);
431
+ return token;
432
+ } catch (error) {
433
+ if (error instanceof ParasutAuthError) {
434
+ throw error;
435
+ }
436
+ if (error instanceof Error) {
437
+ throw new ParasutNetworkError(
438
+ `OAuth request failed: ${error.message}`,
439
+ 'OAUTH_ERROR',
440
+ error
441
+ );
442
+ }
443
+ throw error;
444
+ }
445
+ }
446
+ }