@polymorfa/sdk 0.1.0-dev.20260922093854

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 (347) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1881 -0
  3. package/dist/bridge.d.ts +31 -0
  4. package/dist/bridge.d.ts.map +1 -0
  5. package/dist/bridge.js +39 -0
  6. package/dist/bridge.js.map +1 -0
  7. package/dist/calls/api.d.ts +109 -0
  8. package/dist/calls/api.d.ts.map +1 -0
  9. package/dist/calls/api.js +179 -0
  10. package/dist/calls/api.js.map +1 -0
  11. package/dist/calls/call.d.ts +286 -0
  12. package/dist/calls/call.d.ts.map +1 -0
  13. package/dist/calls/call.js +860 -0
  14. package/dist/calls/call.js.map +1 -0
  15. package/dist/calls/client.d.ts +124 -0
  16. package/dist/calls/client.d.ts.map +1 -0
  17. package/dist/calls/client.js +447 -0
  18. package/dist/calls/client.js.map +1 -0
  19. package/dist/calls/diagnostics.d.ts +74 -0
  20. package/dist/calls/diagnostics.d.ts.map +1 -0
  21. package/dist/calls/diagnostics.js +133 -0
  22. package/dist/calls/diagnostics.js.map +1 -0
  23. package/dist/calls/errors.d.ts +37 -0
  24. package/dist/calls/errors.d.ts.map +1 -0
  25. package/dist/calls/errors.js +52 -0
  26. package/dist/calls/errors.js.map +1 -0
  27. package/dist/calls/events.d.ts +15 -0
  28. package/dist/calls/events.d.ts.map +1 -0
  29. package/dist/calls/events.js +34 -0
  30. package/dist/calls/events.js.map +1 -0
  31. package/dist/calls/index.d.ts +6 -0
  32. package/dist/calls/index.d.ts.map +1 -0
  33. package/dist/calls/index.js +7 -0
  34. package/dist/calls/index.js.map +1 -0
  35. package/dist/calls/internal.d.ts +18 -0
  36. package/dist/calls/internal.d.ts.map +1 -0
  37. package/dist/calls/internal.js +18 -0
  38. package/dist/calls/internal.js.map +1 -0
  39. package/dist/calls/lifecycle.d.ts +111 -0
  40. package/dist/calls/lifecycle.d.ts.map +1 -0
  41. package/dist/calls/lifecycle.js +490 -0
  42. package/dist/calls/lifecycle.js.map +1 -0
  43. package/dist/calls/media.d.ts +102 -0
  44. package/dist/calls/media.d.ts.map +1 -0
  45. package/dist/calls/media.js +414 -0
  46. package/dist/calls/media.js.map +1 -0
  47. package/dist/calls/protocol.d.ts +216 -0
  48. package/dist/calls/protocol.d.ts.map +1 -0
  49. package/dist/calls/protocol.js +268 -0
  50. package/dist/calls/protocol.js.map +1 -0
  51. package/dist/calls/token.d.ts +43 -0
  52. package/dist/calls/token.d.ts.map +1 -0
  53. package/dist/calls/token.js +146 -0
  54. package/dist/calls/token.js.map +1 -0
  55. package/dist/client.d.ts +65 -0
  56. package/dist/client.d.ts.map +1 -0
  57. package/dist/client.js +119 -0
  58. package/dist/client.js.map +1 -0
  59. package/dist/credentials.d.ts +50 -0
  60. package/dist/credentials.d.ts.map +1 -0
  61. package/dist/credentials.js +73 -0
  62. package/dist/credentials.js.map +1 -0
  63. package/dist/errors.d.ts +71 -0
  64. package/dist/errors.d.ts.map +1 -0
  65. package/dist/errors.js +129 -0
  66. package/dist/errors.js.map +1 -0
  67. package/dist/index.d.ts +70 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +61 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/media/whatsapp.d.ts +105 -0
  72. package/dist/media/whatsapp.d.ts.map +1 -0
  73. package/dist/media/whatsapp.js +624 -0
  74. package/dist/media/whatsapp.js.map +1 -0
  75. package/dist/messaging/bansafe.d.ts +30 -0
  76. package/dist/messaging/bansafe.d.ts.map +1 -0
  77. package/dist/messaging/bansafe.js +86 -0
  78. package/dist/messaging/bansafe.js.map +1 -0
  79. package/dist/messaging/business.d.ts +35 -0
  80. package/dist/messaging/business.d.ts.map +1 -0
  81. package/dist/messaging/business.js +145 -0
  82. package/dist/messaging/business.js.map +1 -0
  83. package/dist/messaging/calls.d.ts +9 -0
  84. package/dist/messaging/calls.d.ts.map +1 -0
  85. package/dist/messaging/calls.js +16 -0
  86. package/dist/messaging/calls.js.map +1 -0
  87. package/dist/messaging/campaigns.d.ts +19 -0
  88. package/dist/messaging/campaigns.d.ts.map +1 -0
  89. package/dist/messaging/campaigns.js +77 -0
  90. package/dist/messaging/campaigns.js.map +1 -0
  91. package/dist/messaging/channels.d.ts +23 -0
  92. package/dist/messaging/channels.d.ts.map +1 -0
  93. package/dist/messaging/channels.js +104 -0
  94. package/dist/messaging/channels.js.map +1 -0
  95. package/dist/messaging/chats.d.ts +14 -0
  96. package/dist/messaging/chats.d.ts.map +1 -0
  97. package/dist/messaging/chats.js +51 -0
  98. package/dist/messaging/chats.js.map +1 -0
  99. package/dist/messaging/client-tokens.d.ts +15 -0
  100. package/dist/messaging/client-tokens.d.ts.map +1 -0
  101. package/dist/messaging/client-tokens.js +64 -0
  102. package/dist/messaging/client-tokens.js.map +1 -0
  103. package/dist/messaging/client.d.ts +58 -0
  104. package/dist/messaging/client.d.ts.map +1 -0
  105. package/dist/messaging/client.js +100 -0
  106. package/dist/messaging/client.js.map +1 -0
  107. package/dist/messaging/contacts.d.ts +20 -0
  108. package/dist/messaging/contacts.d.ts.map +1 -0
  109. package/dist/messaging/contacts.js +75 -0
  110. package/dist/messaging/contacts.js.map +1 -0
  111. package/dist/messaging/groups.d.ts +34 -0
  112. package/dist/messaging/groups.d.ts.map +1 -0
  113. package/dist/messaging/groups.js +97 -0
  114. package/dist/messaging/groups.js.map +1 -0
  115. package/dist/messaging/identities.d.ts +9 -0
  116. package/dist/messaging/identities.d.ts.map +1 -0
  117. package/dist/messaging/identities.js +21 -0
  118. package/dist/messaging/identities.js.map +1 -0
  119. package/dist/messaging/labels.d.ts +16 -0
  120. package/dist/messaging/labels.d.ts.map +1 -0
  121. package/dist/messaging/labels.js +65 -0
  122. package/dist/messaging/labels.js.map +1 -0
  123. package/dist/messaging/media.d.ts +72 -0
  124. package/dist/messaging/media.d.ts.map +1 -0
  125. package/dist/messaging/media.js +142 -0
  126. package/dist/messaging/media.js.map +1 -0
  127. package/dist/messaging/messages.d.ts +18 -0
  128. package/dist/messaging/messages.d.ts.map +1 -0
  129. package/dist/messaging/messages.js +32 -0
  130. package/dist/messaging/messages.js.map +1 -0
  131. package/dist/messaging/observation-policies.d.ts +10 -0
  132. package/dist/messaging/observation-policies.d.ts.map +1 -0
  133. package/dist/messaging/observation-policies.js +28 -0
  134. package/dist/messaging/observation-policies.js.map +1 -0
  135. package/dist/messaging/onboarding.d.ts +53 -0
  136. package/dist/messaging/onboarding.d.ts.map +1 -0
  137. package/dist/messaging/onboarding.js +69 -0
  138. package/dist/messaging/onboarding.js.map +1 -0
  139. package/dist/messaging/presence.d.ts +12 -0
  140. package/dist/messaging/presence.d.ts.map +1 -0
  141. package/dist/messaging/presence.js +43 -0
  142. package/dist/messaging/presence.js.map +1 -0
  143. package/dist/messaging/privacy.d.ts +11 -0
  144. package/dist/messaging/privacy.d.ts.map +1 -0
  145. package/dist/messaging/privacy.js +34 -0
  146. package/dist/messaging/privacy.js.map +1 -0
  147. package/dist/messaging/profile.d.ts +13 -0
  148. package/dist/messaging/profile.d.ts.map +1 -0
  149. package/dist/messaging/profile.js +49 -0
  150. package/dist/messaging/profile.js.map +1 -0
  151. package/dist/messaging/quick-replies.d.ts +13 -0
  152. package/dist/messaging/quick-replies.d.ts.map +1 -0
  153. package/dist/messaging/quick-replies.js +45 -0
  154. package/dist/messaging/quick-replies.js.map +1 -0
  155. package/dist/messaging/quicklinks.d.ts +69 -0
  156. package/dist/messaging/quicklinks.d.ts.map +1 -0
  157. package/dist/messaging/quicklinks.js +45 -0
  158. package/dist/messaging/quicklinks.js.map +1 -0
  159. package/dist/messaging/session-configuration.d.ts +67 -0
  160. package/dist/messaging/session-configuration.d.ts.map +1 -0
  161. package/dist/messaging/session-configuration.js +2 -0
  162. package/dist/messaging/session-configuration.js.map +1 -0
  163. package/dist/messaging/sessions.d.ts +26 -0
  164. package/dist/messaging/sessions.d.ts.map +1 -0
  165. package/dist/messaging/sessions.js +108 -0
  166. package/dist/messaging/sessions.js.map +1 -0
  167. package/dist/messaging/templates.d.ts +15 -0
  168. package/dist/messaging/templates.d.ts.map +1 -0
  169. package/dist/messaging/templates.js +67 -0
  170. package/dist/messaging/templates.js.map +1 -0
  171. package/dist/messaging/testing-configuration.d.ts +89 -0
  172. package/dist/messaging/testing-configuration.d.ts.map +1 -0
  173. package/dist/messaging/testing-configuration.js +14 -0
  174. package/dist/messaging/testing-configuration.js.map +1 -0
  175. package/dist/messaging/types.d.ts +1793 -0
  176. package/dist/messaging/types.d.ts.map +1 -0
  177. package/dist/messaging/types.js +25 -0
  178. package/dist/messaging/types.js.map +1 -0
  179. package/dist/messaging/users.d.ts +9 -0
  180. package/dist/messaging/users.d.ts.map +1 -0
  181. package/dist/messaging/users.js +15 -0
  182. package/dist/messaging/users.js.map +1 -0
  183. package/dist/messaging/voip.d.ts +51 -0
  184. package/dist/messaging/voip.d.ts.map +1 -0
  185. package/dist/messaging/voip.js +293 -0
  186. package/dist/messaging/voip.js.map +1 -0
  187. package/dist/messaging/webhooks.d.ts +13 -0
  188. package/dist/messaging/webhooks.d.ts.map +1 -0
  189. package/dist/messaging/webhooks.js +48 -0
  190. package/dist/messaging/webhooks.js.map +1 -0
  191. package/dist/node.d.ts +55 -0
  192. package/dist/node.d.ts.map +1 -0
  193. package/dist/node.js +237 -0
  194. package/dist/node.js.map +1 -0
  195. package/dist/pagination.d.ts +24 -0
  196. package/dist/pagination.d.ts.map +1 -0
  197. package/dist/pagination.js +28 -0
  198. package/dist/pagination.js.map +1 -0
  199. package/dist/platform/api-keys.d.ts +10 -0
  200. package/dist/platform/api-keys.d.ts.map +1 -0
  201. package/dist/platform/api-keys.js +22 -0
  202. package/dist/platform/api-keys.js.map +1 -0
  203. package/dist/platform/audiences.d.ts +16 -0
  204. package/dist/platform/audiences.d.ts.map +1 -0
  205. package/dist/platform/audiences.js +46 -0
  206. package/dist/platform/audiences.js.map +1 -0
  207. package/dist/platform/audit-logs.d.ts +9 -0
  208. package/dist/platform/audit-logs.d.ts.map +1 -0
  209. package/dist/platform/audit-logs.js +20 -0
  210. package/dist/platform/audit-logs.js.map +1 -0
  211. package/dist/platform/bansafe.d.ts +24 -0
  212. package/dist/platform/bansafe.d.ts.map +1 -0
  213. package/dist/platform/bansafe.js +70 -0
  214. package/dist/platform/bansafe.js.map +1 -0
  215. package/dist/platform/billing.d.ts +13 -0
  216. package/dist/platform/billing.d.ts.map +1 -0
  217. package/dist/platform/billing.js +27 -0
  218. package/dist/platform/billing.js.map +1 -0
  219. package/dist/platform/campaigns.d.ts +28 -0
  220. package/dist/platform/campaigns.d.ts.map +1 -0
  221. package/dist/platform/campaigns.js +92 -0
  222. package/dist/platform/campaigns.js.map +1 -0
  223. package/dist/platform/customers.d.ts +23 -0
  224. package/dist/platform/customers.d.ts.map +1 -0
  225. package/dist/platform/customers.js +140 -0
  226. package/dist/platform/customers.js.map +1 -0
  227. package/dist/platform/developer-resources.d.ts +101 -0
  228. package/dist/platform/developer-resources.d.ts.map +1 -0
  229. package/dist/platform/developer-resources.js +250 -0
  230. package/dist/platform/developer-resources.js.map +1 -0
  231. package/dist/platform/developer-types.d.ts +378 -0
  232. package/dist/platform/developer-types.d.ts.map +1 -0
  233. package/dist/platform/developer-types.js +2 -0
  234. package/dist/platform/developer-types.js.map +1 -0
  235. package/dist/platform/event-stream.d.ts +114 -0
  236. package/dist/platform/event-stream.d.ts.map +1 -0
  237. package/dist/platform/event-stream.js +308 -0
  238. package/dist/platform/event-stream.js.map +1 -0
  239. package/dist/platform/media.d.ts +13 -0
  240. package/dist/platform/media.d.ts.map +1 -0
  241. package/dist/platform/media.js +33 -0
  242. package/dist/platform/media.js.map +1 -0
  243. package/dist/platform/members.d.ts +9 -0
  244. package/dist/platform/members.d.ts.map +1 -0
  245. package/dist/platform/members.js +15 -0
  246. package/dist/platform/members.js.map +1 -0
  247. package/dist/platform/opt-outs.d.ts +15 -0
  248. package/dist/platform/opt-outs.d.ts.map +1 -0
  249. package/dist/platform/opt-outs.js +36 -0
  250. package/dist/platform/opt-outs.js.map +1 -0
  251. package/dist/platform/organizations.d.ts +9 -0
  252. package/dist/platform/organizations.d.ts.map +1 -0
  253. package/dist/platform/organizations.js +15 -0
  254. package/dist/platform/organizations.js.map +1 -0
  255. package/dist/platform/project-tokens.d.ts +9 -0
  256. package/dist/platform/project-tokens.d.ts.map +1 -0
  257. package/dist/platform/project-tokens.js +16 -0
  258. package/dist/platform/project-tokens.js.map +1 -0
  259. package/dist/platform/projects.d.ts +24 -0
  260. package/dist/platform/projects.d.ts.map +1 -0
  261. package/dist/platform/projects.js +83 -0
  262. package/dist/platform/projects.js.map +1 -0
  263. package/dist/platform/quicklink-settings.d.ts +85 -0
  264. package/dist/platform/quicklink-settings.d.ts.map +1 -0
  265. package/dist/platform/quicklink-settings.js +35 -0
  266. package/dist/platform/quicklink-settings.js.map +1 -0
  267. package/dist/platform/response.d.ts +10 -0
  268. package/dist/platform/response.d.ts.map +1 -0
  269. package/dist/platform/response.js +41 -0
  270. package/dist/platform/response.js.map +1 -0
  271. package/dist/platform/security-incidents.d.ts +10 -0
  272. package/dist/platform/security-incidents.d.ts.map +1 -0
  273. package/dist/platform/security-incidents.js +22 -0
  274. package/dist/platform/security-incidents.js.map +1 -0
  275. package/dist/platform/session-bans.d.ts +13 -0
  276. package/dist/platform/session-bans.d.ts.map +1 -0
  277. package/dist/platform/session-bans.js +21 -0
  278. package/dist/platform/session-bans.js.map +1 -0
  279. package/dist/platform/session-configuration.d.ts +15 -0
  280. package/dist/platform/session-configuration.d.ts.map +1 -0
  281. package/dist/platform/session-configuration.js +36 -0
  282. package/dist/platform/session-configuration.js.map +1 -0
  283. package/dist/platform/sessions.d.ts +26 -0
  284. package/dist/platform/sessions.d.ts.map +1 -0
  285. package/dist/platform/sessions.js +121 -0
  286. package/dist/platform/sessions.js.map +1 -0
  287. package/dist/platform/sip-trunks.d.ts +141 -0
  288. package/dist/platform/sip-trunks.d.ts.map +1 -0
  289. package/dist/platform/sip-trunks.js +140 -0
  290. package/dist/platform/sip-trunks.js.map +1 -0
  291. package/dist/platform/types.d.ts +897 -0
  292. package/dist/platform/types.d.ts.map +1 -0
  293. package/dist/platform/types.js +2 -0
  294. package/dist/platform/types.js.map +1 -0
  295. package/dist/raw.d.ts +26 -0
  296. package/dist/raw.d.ts.map +1 -0
  297. package/dist/raw.js +79 -0
  298. package/dist/raw.js.map +1 -0
  299. package/dist/system.d.ts +37 -0
  300. package/dist/system.d.ts.map +1 -0
  301. package/dist/system.js +45 -0
  302. package/dist/system.js.map +1 -0
  303. package/dist/transport/body.d.ts +6 -0
  304. package/dist/transport/body.d.ts.map +1 -0
  305. package/dist/transport/body.js +33 -0
  306. package/dist/transport/body.js.map +1 -0
  307. package/dist/transport/content-disposition.d.ts +9 -0
  308. package/dist/transport/content-disposition.d.ts.map +1 -0
  309. package/dist/transport/content-disposition.js +107 -0
  310. package/dist/transport/content-disposition.js.map +1 -0
  311. package/dist/transport/http.d.ts +35 -0
  312. package/dist/transport/http.d.ts.map +1 -0
  313. package/dist/transport/http.js +660 -0
  314. package/dist/transport/http.js.map +1 -0
  315. package/dist/transport/idempotency.d.ts +10 -0
  316. package/dist/transport/idempotency.d.ts.map +1 -0
  317. package/dist/transport/idempotency.js +13 -0
  318. package/dist/transport/idempotency.js.map +1 -0
  319. package/dist/transport/retry.d.ts +11 -0
  320. package/dist/transport/retry.d.ts.map +1 -0
  321. package/dist/transport/retry.js +45 -0
  322. package/dist/transport/retry.js.map +1 -0
  323. package/dist/transport/types.d.ts +58 -0
  324. package/dist/transport/types.d.ts.map +1 -0
  325. package/dist/transport/types.js +2 -0
  326. package/dist/transport/types.js.map +1 -0
  327. package/dist/version.d.ts +2 -0
  328. package/dist/version.d.ts.map +1 -0
  329. package/dist/version.js +2 -0
  330. package/dist/version.js.map +1 -0
  331. package/dist/webhooks/events.d.ts +741 -0
  332. package/dist/webhooks/events.d.ts.map +1 -0
  333. package/dist/webhooks/events.js +80 -0
  334. package/dist/webhooks/events.js.map +1 -0
  335. package/dist/webhooks/index.d.ts +4 -0
  336. package/dist/webhooks/index.d.ts.map +1 -0
  337. package/dist/webhooks/index.js +4 -0
  338. package/dist/webhooks/index.js.map +1 -0
  339. package/dist/webhooks/utilities.d.ts +33 -0
  340. package/dist/webhooks/utilities.d.ts.map +1 -0
  341. package/dist/webhooks/utilities.js +67 -0
  342. package/dist/webhooks/utilities.js.map +1 -0
  343. package/dist/webhooks/verify.d.ts +9 -0
  344. package/dist/webhooks/verify.d.ts.map +1 -0
  345. package/dist/webhooks/verify.js +77 -0
  346. package/dist/webhooks/verify.js.map +1 -0
  347. package/package.json +58 -0
@@ -0,0 +1,660 @@
1
+ import { PolymorfaAuthenticationError, PolymorfaAuthorizationError, PolymorfaCancelledError, PolymorfaConflictError, PolymorfaPaymentRequiredError, PolymorfaConfigurationError, PolymorfaConnectionError, PolymorfaError, PolymorfaNotFoundError, PolymorfaRateLimitError, PolymorfaServerError, PolymorfaTimeoutError, PolymorfaValidationError, } from "../errors.js";
2
+ import { SDK_VERSION } from "../version.js";
3
+ import { decodeResponseBody, encodeRequestBody } from "./body.js";
4
+ import { parseContentDispositionFilename } from "./content-disposition.js";
5
+ import { canRetryRequest, defaultSleep, isIdempotentReplay, isRetryableStatus, retryDelayMs, } from "./retry.js";
6
+ export class HttpTransport {
7
+ #baseUrl;
8
+ #authorization;
9
+ #apiVersion;
10
+ #timeoutMs;
11
+ #maxNetworkRetries;
12
+ #fetch;
13
+ #sleep;
14
+ #random;
15
+ constructor(options) {
16
+ this.#baseUrl = validateBaseUrl(options.baseUrl, options.authorization);
17
+ this.#authorization = options.authorization;
18
+ this.#apiVersion = options.apiVersion;
19
+ this.#timeoutMs = assertNonNegativeInteger(options.timeoutMs, "timeoutMs", false);
20
+ this.#maxNetworkRetries = assertNonNegativeInteger(options.maxNetworkRetries, "maxNetworkRetries", true);
21
+ this.#fetch = options.fetch ?? globalThis.fetch;
22
+ this.#sleep = options.sleep ?? defaultSleep;
23
+ this.#random = options.random ?? Math.random;
24
+ }
25
+ async request(request) {
26
+ return this.#request(request, decodeResponseBody, "application/json");
27
+ }
28
+ async requestBinary(request) {
29
+ return this.#request(request, async (response) => response.ok ? response.arrayBuffer() : decodeResponseBody(response), "application/octet-stream, */*");
30
+ }
31
+ /**
32
+ * Returns a successful response body as an unbuffered stream.
33
+ *
34
+ * Retries apply only before a body is handed to the caller. `timeoutMs`
35
+ * bounds the time until response headers arrive; the caller's `signal`
36
+ * stays linked to the body until it is fully read or cancelled. A redirect
37
+ * is followed manually, without Polymorfa credentials or caller headers.
38
+ */
39
+ async requestStream(request) {
40
+ const result = await this.#stream(request, false);
41
+ if (!("body" in result)) {
42
+ throw new PolymorfaError("Unexpected unresolved redirect.");
43
+ }
44
+ return result;
45
+ }
46
+ /**
47
+ * Like `requestStream`, but returns a redirect target instead of following
48
+ * it. The body of a redirect response is discarded.
49
+ */
50
+ async requestStreamOrRedirect(request) {
51
+ return this.#stream(request, true);
52
+ }
53
+ async #stream(request, returnRedirect) {
54
+ validatePath(request.path);
55
+ const retries = assertNonNegativeInteger(request.maxNetworkRetries ?? this.#maxNetworkRetries, "maxNetworkRetries", true);
56
+ const eligible = canRetryRequest(request.method, request.idempotencyKey);
57
+ const apiUrl = requestUrl(this.#baseUrl, request.path, request.query);
58
+ let attempt = 0;
59
+ while (true) {
60
+ attempt += 1;
61
+ let opened;
62
+ try {
63
+ opened = await this.#open(request, apiUrl, {
64
+ headers: this.#headers(request, "application/octet-stream, */*"),
65
+ redirect: "manual",
66
+ });
67
+ let response = opened.response;
68
+ const metadata = responseMetadata(response, attempt);
69
+ if (response.type === "opaqueredirect" || response.status === 0) {
70
+ await opened.discard();
71
+ throw new PolymorfaConfigurationError("This runtime does not expose manual redirect responses.", "fetch");
72
+ }
73
+ if (REDIRECT_STATUSES.has(response.status)) {
74
+ let location;
75
+ try {
76
+ location = redirectLocation(response, apiUrl, metadata);
77
+ }
78
+ finally {
79
+ // Cancel the redirect body whether or not its location is valid.
80
+ await opened.discard();
81
+ }
82
+ if (returnRedirect) {
83
+ return Object.freeze({ location, metadata });
84
+ }
85
+ opened = await this.#open(request, new URL(location), {
86
+ headers: new Headers({
87
+ accept: "application/octet-stream, */*",
88
+ "user-agent": `polymorfa-node/${SDK_VERSION}`,
89
+ }),
90
+ redirect: "follow",
91
+ credentials: "omit",
92
+ referrerPolicy: "no-referrer",
93
+ });
94
+ response = opened.response;
95
+ if (!response.ok) {
96
+ await opened.discard();
97
+ if (eligible &&
98
+ attempt <= retries &&
99
+ isRetryableStatus(response.status)) {
100
+ await this.#sleep(retryDelayMs(response, attempt, this.#random), request.signal);
101
+ continue;
102
+ }
103
+ throw new PolymorfaError(`Media storage returned status ${response.status}.`, {
104
+ status: response.status,
105
+ code: "media_storage_error",
106
+ ...(metadata.requestId === undefined
107
+ ? {}
108
+ : { requestId: metadata.requestId }),
109
+ metadata,
110
+ });
111
+ }
112
+ return streamResult(opened, apiUrl, metadata, true);
113
+ }
114
+ if (response.ok) {
115
+ return streamResult(opened, apiUrl, metadata, false);
116
+ }
117
+ const data = await opened.readError();
118
+ if (eligible &&
119
+ attempt <= retries &&
120
+ isRetryableStatus(response.status)) {
121
+ await this.#sleep(retryDelayMs(response, attempt, this.#random), request.signal);
122
+ continue;
123
+ }
124
+ throw apiError(response, data, metadata);
125
+ }
126
+ catch (error) {
127
+ opened?.release();
128
+ if (error instanceof PolymorfaError) {
129
+ throw error;
130
+ }
131
+ if (request.signal?.aborted === true) {
132
+ throw cancelledError(error);
133
+ }
134
+ if (error instanceof RequestTimeout) {
135
+ if (eligible && attempt <= retries) {
136
+ await this.#sleep(retryDelayMs(undefined, attempt, this.#random), request.signal);
137
+ continue;
138
+ }
139
+ throw new PolymorfaTimeoutError(`The request exceeded its ${error.timeoutMs}ms timeout.`, { code: "request_timeout", cause: error });
140
+ }
141
+ if (eligible && attempt <= retries) {
142
+ await this.#sleep(retryDelayMs(undefined, attempt, this.#random), request.signal);
143
+ continue;
144
+ }
145
+ throw new PolymorfaConnectionError("The request could not reach the Polymorfa API.", { code: "connection_error", cause: error });
146
+ }
147
+ }
148
+ }
149
+ #headers(request, accept) {
150
+ const headers = new Headers(request.headers);
151
+ if (!headers.has("accept"))
152
+ headers.set("accept", accept);
153
+ if (this.#authorization !== undefined) {
154
+ headers.set("authorization", this.#authorization);
155
+ }
156
+ headers.set("user-agent", `polymorfa-node/${SDK_VERSION}`);
157
+ const apiVersion = request.apiVersion ?? this.#apiVersion;
158
+ if (apiVersion !== undefined) {
159
+ headers.set("polymorfa-version", apiVersion);
160
+ }
161
+ if (request.idempotencyKey !== undefined) {
162
+ headers.set("idempotency-key", request.idempotencyKey);
163
+ }
164
+ return headers;
165
+ }
166
+ async #open(request, url, init) {
167
+ const timeoutMs = assertNonNegativeInteger(request.timeoutMs ?? this.#timeoutMs, "timeoutMs", false);
168
+ const controller = new AbortController();
169
+ let timedOut = false;
170
+ const timer = setTimeout(() => {
171
+ timedOut = true;
172
+ controller.abort();
173
+ }, timeoutMs);
174
+ const cancel = () => controller.abort(request.signal?.reason);
175
+ let released = false;
176
+ const release = () => {
177
+ if (released)
178
+ return;
179
+ released = true;
180
+ clearTimeout(timer);
181
+ request.signal?.removeEventListener("abort", cancel);
182
+ };
183
+ if (request.signal?.aborted === true) {
184
+ cancel();
185
+ }
186
+ else {
187
+ request.signal?.addEventListener("abort", cancel, { once: true });
188
+ }
189
+ const guard = async (operation) => {
190
+ try {
191
+ return await operation();
192
+ }
193
+ catch (error) {
194
+ if (timedOut)
195
+ throw new RequestTimeout(timeoutMs, error);
196
+ throw error;
197
+ }
198
+ };
199
+ let response;
200
+ try {
201
+ response = await guard(() => this.#fetch(url, {
202
+ method: request.method,
203
+ ...init,
204
+ signal: controller.signal,
205
+ }));
206
+ }
207
+ catch (error) {
208
+ release();
209
+ throw error;
210
+ }
211
+ return {
212
+ response,
213
+ signal: request.signal,
214
+ release,
215
+ startBody: () => clearTimeout(timer),
216
+ discard: async () => {
217
+ try {
218
+ await response.body?.cancel();
219
+ }
220
+ catch {
221
+ // The body is discarded; a cancellation failure changes nothing.
222
+ }
223
+ finally {
224
+ release();
225
+ }
226
+ },
227
+ readError: async () => {
228
+ try {
229
+ return await guard(() => decodeResponseBody(response));
230
+ }
231
+ finally {
232
+ release();
233
+ }
234
+ },
235
+ };
236
+ }
237
+ async #request(request, decode, accept) {
238
+ validatePath(request.path);
239
+ const retries = assertNonNegativeInteger(request.maxNetworkRetries ?? this.#maxNetworkRetries, "maxNetworkRetries", true);
240
+ const eligible = canRetryRequest(request.method, request.idempotencyKey);
241
+ let attempt = 0;
242
+ while (true) {
243
+ attempt += 1;
244
+ let response;
245
+ try {
246
+ const performed = await this.#perform(request, decode, accept);
247
+ response = performed.response;
248
+ const data = performed.data;
249
+ const metadata = responseMetadata(response, attempt);
250
+ if (response.ok) {
251
+ return Object.freeze({ data: data, metadata });
252
+ }
253
+ if (eligible &&
254
+ attempt <= retries &&
255
+ isRetryableStatus(response.status) &&
256
+ !isIdempotentReplay(response)) {
257
+ await this.#sleep(retryDelayMs(response, attempt, this.#random), request.signal);
258
+ continue;
259
+ }
260
+ throw apiError(response, data, metadata);
261
+ }
262
+ catch (error) {
263
+ if (error instanceof PolymorfaError) {
264
+ throw error;
265
+ }
266
+ if (request.signal?.aborted === true) {
267
+ throw new PolymorfaCancelledError("The request was cancelled by the caller.", {
268
+ code: "request_cancelled",
269
+ cause: error,
270
+ });
271
+ }
272
+ if (error instanceof RequestTimeout) {
273
+ if (eligible && attempt <= retries) {
274
+ await this.#sleep(retryDelayMs(undefined, attempt, this.#random), request.signal);
275
+ continue;
276
+ }
277
+ throw new PolymorfaTimeoutError(`The request exceeded its ${error.timeoutMs}ms timeout.`, {
278
+ code: "request_timeout",
279
+ cause: error,
280
+ });
281
+ }
282
+ if (eligible && attempt <= retries) {
283
+ await this.#sleep(retryDelayMs(undefined, attempt, this.#random), request.signal);
284
+ continue;
285
+ }
286
+ throw new PolymorfaConnectionError("The request could not reach the Polymorfa API.", {
287
+ code: "connection_error",
288
+ cause: error,
289
+ });
290
+ }
291
+ }
292
+ }
293
+ #requestHeaders(request, accept) {
294
+ const headers = new Headers(request.headers);
295
+ if (!headers.has("accept"))
296
+ headers.set("accept", accept);
297
+ if (this.#authorization !== undefined) {
298
+ headers.set("authorization", this.#authorization);
299
+ }
300
+ headers.set("user-agent", `polymorfa-node/${SDK_VERSION}`);
301
+ const apiVersion = request.apiVersion ?? this.#apiVersion;
302
+ if (apiVersion !== undefined) {
303
+ headers.set("polymorfa-version", apiVersion);
304
+ }
305
+ if (request.idempotencyKey !== undefined) {
306
+ headers.set("idempotency-key", request.idempotencyKey);
307
+ }
308
+ const encoded = encodeRequestBody(request.body);
309
+ if (encoded.contentType !== undefined && !headers.has("content-type")) {
310
+ headers.set("content-type", encoded.contentType);
311
+ }
312
+ return { headers, encoded };
313
+ }
314
+ /**
315
+ * Opens a long-lived streaming response, such as `text/event-stream`.
316
+ * `timeoutMs` bounds only the wait for response headers; the body stays
317
+ * open until the server ends it or `signal` aborts. A non-2xx response
318
+ * throws the same typed errors as `request`. Streams are never retried
319
+ * here; the caller owns reconnection.
320
+ */
321
+ async openStream(request) {
322
+ validatePath(request.path);
323
+ const url = requestUrl(this.#baseUrl, request.path, request.query);
324
+ const { headers, encoded } = this.#requestHeaders(request, request.accept);
325
+ const timeoutMs = assertNonNegativeInteger(request.timeoutMs ?? this.#timeoutMs, "timeoutMs", false);
326
+ const controller = new AbortController();
327
+ let timedOut = false;
328
+ const timer = setTimeout(() => {
329
+ timedOut = true;
330
+ controller.abort();
331
+ }, timeoutMs);
332
+ const cancel = () => controller.abort(request.signal?.reason);
333
+ if (request.signal?.aborted === true)
334
+ cancel();
335
+ else
336
+ request.signal?.addEventListener("abort", cancel, { once: true });
337
+ let response;
338
+ try {
339
+ response = await this.#fetch(url, {
340
+ method: request.method,
341
+ headers,
342
+ redirect: this.#authorization === undefined ? "follow" : "error",
343
+ ...(encoded.body === undefined ? {} : { body: encoded.body }),
344
+ signal: controller.signal,
345
+ });
346
+ }
347
+ catch (error) {
348
+ request.signal?.removeEventListener("abort", cancel);
349
+ if (request.signal?.aborted === true) {
350
+ throw new PolymorfaCancelledError("The request was cancelled by the caller.", { code: "request_cancelled", cause: error });
351
+ }
352
+ if (timedOut) {
353
+ throw new PolymorfaTimeoutError(`The stream did not open within ${timeoutMs}ms.`, { code: "request_timeout", cause: error });
354
+ }
355
+ throw new PolymorfaConnectionError("The request could not reach the Polymorfa API.", { code: "connection_error", cause: error });
356
+ }
357
+ finally {
358
+ clearTimeout(timer);
359
+ }
360
+ const metadata = responseMetadata(response, 1);
361
+ if (!response.ok) {
362
+ request.signal?.removeEventListener("abort", cancel);
363
+ const data = await decodeResponseBody(response).catch(() => null);
364
+ throw apiError(response, data, metadata);
365
+ }
366
+ return Object.freeze({ response, metadata });
367
+ }
368
+ async #perform(request, decode, accept) {
369
+ const url = requestUrl(this.#baseUrl, request.path, request.query);
370
+ const { headers, encoded } = this.#requestHeaders(request, accept);
371
+ const timeoutMs = assertNonNegativeInteger(request.timeoutMs ?? this.#timeoutMs, "timeoutMs", false);
372
+ const controller = new AbortController();
373
+ let timedOut = false;
374
+ const timer = setTimeout(() => {
375
+ timedOut = true;
376
+ controller.abort();
377
+ }, timeoutMs);
378
+ const cancel = () => controller.abort(request.signal?.reason);
379
+ if (request.signal?.aborted === true) {
380
+ cancel();
381
+ }
382
+ else {
383
+ request.signal?.addEventListener("abort", cancel, { once: true });
384
+ }
385
+ try {
386
+ const response = await this.#fetch(url, {
387
+ method: request.method,
388
+ headers,
389
+ redirect: this.#authorization === undefined ? "follow" : "error",
390
+ ...(encoded.body === undefined ? {} : { body: encoded.body }),
391
+ signal: controller.signal,
392
+ });
393
+ return { response, data: await decode(response) };
394
+ }
395
+ catch (error) {
396
+ if (timedOut) {
397
+ throw new RequestTimeout(timeoutMs, error);
398
+ }
399
+ throw error;
400
+ }
401
+ finally {
402
+ clearTimeout(timer);
403
+ request.signal?.removeEventListener("abort", cancel);
404
+ }
405
+ }
406
+ }
407
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
408
+ function cancelledError(cause) {
409
+ return new PolymorfaCancelledError("The request was cancelled by the caller.", { code: "request_cancelled", cause });
410
+ }
411
+ function isLoopbackHost(hostname) {
412
+ return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]");
413
+ }
414
+ function redirectLocation(response, apiUrl, metadata) {
415
+ const header = response.headers.get("location");
416
+ let target;
417
+ try {
418
+ target = header === null ? undefined : new URL(header, apiUrl);
419
+ }
420
+ catch {
421
+ target = undefined;
422
+ }
423
+ if (target === undefined ||
424
+ target.username !== "" ||
425
+ target.password !== "" ||
426
+ !(target.protocol === "https:" ||
427
+ (target.protocol === "http:" && isLoopbackHost(target.hostname)))) {
428
+ throw new PolymorfaError("The API returned a redirect without a valid HTTPS location.", {
429
+ status: response.status,
430
+ code: "invalid_redirect",
431
+ ...(metadata.requestId === undefined
432
+ ? {}
433
+ : { requestId: metadata.requestId }),
434
+ metadata,
435
+ });
436
+ }
437
+ return target.toString();
438
+ }
439
+ function streamResult(opened, apiUrl, metadata, redirected) {
440
+ const { response } = opened;
441
+ opened.startBody();
442
+ const contentType = response.headers.get("content-type") ?? undefined;
443
+ const lengthHeader = response.headers.get("content-length")?.trim();
444
+ const contentLength = lengthHeader !== undefined && /^[0-9]{1,15}$/.test(lengthHeader)
445
+ ? Number(lengthHeader)
446
+ : undefined;
447
+ const filename = parseContentDispositionFilename(response.headers.get("content-disposition"));
448
+ return Object.freeze({
449
+ body: guardedBody(response.body, opened),
450
+ ...(contentType === undefined ? {} : { contentType }),
451
+ ...(contentLength === undefined ? {} : { contentLength }),
452
+ ...(filename === undefined ? {} : { filename }),
453
+ url: apiUrl.toString(),
454
+ redirected,
455
+ metadata,
456
+ });
457
+ }
458
+ function guardedBody(body, opened) {
459
+ if (body === null) {
460
+ opened.release();
461
+ return new ReadableStream({
462
+ start(controller) {
463
+ controller.close();
464
+ },
465
+ });
466
+ }
467
+ const reader = body.getReader();
468
+ return new ReadableStream({
469
+ async pull(controller) {
470
+ try {
471
+ const { done, value } = await reader.read();
472
+ if (done) {
473
+ opened.release();
474
+ controller.close();
475
+ return;
476
+ }
477
+ controller.enqueue(value);
478
+ }
479
+ catch (error) {
480
+ const aborted = opened.signal?.aborted === true;
481
+ opened.release();
482
+ controller.error(aborted
483
+ ? cancelledError(error)
484
+ : new PolymorfaConnectionError("The media response body could not be read.", { code: "connection_error", cause: error }));
485
+ }
486
+ },
487
+ async cancel(reason) {
488
+ opened.release();
489
+ await reader.cancel(reason);
490
+ },
491
+ }, { highWaterMark: 0 });
492
+ }
493
+ function validateBaseUrl(value, authorization) {
494
+ let url;
495
+ try {
496
+ url = new URL(value);
497
+ }
498
+ catch {
499
+ throw new PolymorfaConfigurationError("baseUrl must be an absolute HTTP or HTTPS URL.", "baseUrl");
500
+ }
501
+ if (url.username !== "" || url.password !== "") {
502
+ throw new PolymorfaConfigurationError("baseUrl must not contain credentials.", "baseUrl");
503
+ }
504
+ const isLoopbackHttp = url.protocol === "http:" &&
505
+ (url.hostname === "localhost" ||
506
+ url.hostname === "127.0.0.1" ||
507
+ url.hostname === "[::1]");
508
+ if (authorization !== undefined &&
509
+ url.protocol !== "https:" &&
510
+ !isLoopbackHttp) {
511
+ throw new PolymorfaConfigurationError("Credentialed clients require HTTPS, except for loopback development servers.", "baseUrl");
512
+ }
513
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
514
+ throw new PolymorfaConfigurationError("baseUrl must use HTTP or HTTPS.", "baseUrl");
515
+ }
516
+ return value.replace(/\/+$/, "");
517
+ }
518
+ class RequestTimeout extends Error {
519
+ timeoutMs;
520
+ constructor(timeoutMs, cause) {
521
+ super("Request timed out.", { cause });
522
+ this.timeoutMs = timeoutMs;
523
+ }
524
+ }
525
+ function requestUrl(baseUrl, path, query) {
526
+ const url = new URL(path, `${baseUrl}/`);
527
+ for (const [name, value] of Object.entries(query ?? {})) {
528
+ if (value === undefined || value === null)
529
+ continue;
530
+ const values = Array.isArray(value) ? value : [value];
531
+ for (const item of values) {
532
+ url.searchParams.append(name, String(item));
533
+ }
534
+ }
535
+ return url;
536
+ }
537
+ function validatePath(path) {
538
+ if (!path.startsWith("/") ||
539
+ path.startsWith("//") ||
540
+ path.includes("\\") ||
541
+ URL.canParse(path)) {
542
+ throw new PolymorfaValidationError("Raw request paths must be relative and begin with a single slash.", {
543
+ code: "invalid_request_path",
544
+ });
545
+ }
546
+ }
547
+ function responseMetadata(response, attempts) {
548
+ const headerRecord = {};
549
+ for (const key of SAFE_RESPONSE_HEADERS) {
550
+ const value = response.headers.get(key);
551
+ if (value !== null)
552
+ headerRecord[key] = value;
553
+ }
554
+ const requestId = response.headers.get("x-request-id") ??
555
+ response.headers.get("request-id") ??
556
+ undefined;
557
+ const apiVersion = response.headers.get("polymorfa-version") ?? undefined;
558
+ return Object.freeze({
559
+ status: response.status,
560
+ ...(requestId === undefined ? {} : { requestId }),
561
+ ...(apiVersion === undefined ? {} : { apiVersion }),
562
+ attempts,
563
+ headers: Object.freeze(headerRecord),
564
+ });
565
+ }
566
+ const SAFE_RESPONSE_HEADERS = [
567
+ "content-type",
568
+ "x-request-id",
569
+ "polymorfa-version",
570
+ "retry-after",
571
+ "x-ratelimit-limit",
572
+ "x-ratelimit-remaining",
573
+ "polymorfa-ratelimit-reason",
574
+ ];
575
+ function apiError(response, body, metadata) {
576
+ const fields = errorFields(body);
577
+ const requestId = fields.requestId ?? metadata.requestId;
578
+ const rateLimitReason = response.headers.get("polymorfa-ratelimit-reason") ?? undefined;
579
+ const options = {
580
+ status: response.status,
581
+ ...(requestId === undefined ? {} : { requestId }),
582
+ ...(fields.requestLogUrl === undefined
583
+ ? {}
584
+ : { requestLogUrl: fields.requestLogUrl }),
585
+ ...(fields.docUrl === undefined ? {} : { docUrl: fields.docUrl }),
586
+ ...(rateLimitReason === undefined ? {} : { rateLimitReason }),
587
+ details: body,
588
+ metadata,
589
+ ...(fields.code === undefined ? {} : { code: fields.code }),
590
+ };
591
+ const message = errorMessage(body, response.status);
592
+ if (response.status === 400 ||
593
+ response.status === 413 ||
594
+ response.status === 422)
595
+ return new PolymorfaValidationError(message, options);
596
+ if (response.status === 401)
597
+ return new PolymorfaAuthenticationError(message, options);
598
+ if (response.status === 403)
599
+ return new PolymorfaAuthorizationError(message, options);
600
+ if (response.status === 402)
601
+ return new PolymorfaPaymentRequiredError(message, options);
602
+ if (response.status === 404)
603
+ return new PolymorfaNotFoundError(message, options);
604
+ if (response.status === 409)
605
+ return new PolymorfaConflictError(message, options);
606
+ if (response.status === 429)
607
+ return new PolymorfaRateLimitError(message, options);
608
+ if (response.status >= 500)
609
+ return new PolymorfaServerError(message, options);
610
+ return new PolymorfaError(message, options);
611
+ }
612
+ function errorMessage(body, status) {
613
+ if (typeof body === "object" && body !== null) {
614
+ const record = body;
615
+ if (typeof record.error === "object" && record.error !== null) {
616
+ const message = record.error.message;
617
+ if (typeof message === "string")
618
+ return message;
619
+ }
620
+ if (typeof record.error === "string")
621
+ return record.error;
622
+ if (typeof record.message === "string")
623
+ return record.message;
624
+ }
625
+ if (typeof body === "string" && body.length > 0)
626
+ return body;
627
+ return `Polymorfa API request failed with status ${status}.`;
628
+ }
629
+ /**
630
+ * Reads the documented error object, `{ error: { code, request_id,
631
+ * request_log_url }, docs }`, and the older `{ error, code }` shape.
632
+ */
633
+ function errorFields(body) {
634
+ if (typeof body !== "object" || body === null)
635
+ return {};
636
+ const record = body;
637
+ const error = typeof record.error === "object" && record.error !== null
638
+ ? record.error
639
+ : undefined;
640
+ const text = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
641
+ const code = text(error ? error.code : record.code);
642
+ const requestId = text(error?.request_id);
643
+ const requestLogUrl = text(error?.request_log_url);
644
+ const docUrl = text(record.docs);
645
+ return {
646
+ ...(code === undefined ? {} : { code }),
647
+ ...(requestId === undefined ? {} : { requestId }),
648
+ ...(requestLogUrl === undefined ? {} : { requestLogUrl }),
649
+ ...(docUrl === undefined ? {} : { docUrl }),
650
+ };
651
+ }
652
+ function assertNonNegativeInteger(value, field, allowZero) {
653
+ if (!Number.isInteger(value) || value < 0 || (!allowZero && value === 0)) {
654
+ throw new PolymorfaValidationError(`${field} must be ${allowZero ? "a non-negative" : "a positive"} integer.`, {
655
+ code: "invalid_request_option",
656
+ });
657
+ }
658
+ return value;
659
+ }
660
+ //# sourceMappingURL=http.js.map