effect-slack 0.1.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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +322 -0
  3. package/dist/Errors.d.ts +103 -0
  4. package/dist/Errors.d.ts.map +1 -0
  5. package/dist/Errors.js +84 -0
  6. package/dist/Errors.js.map +1 -0
  7. package/dist/Retry.d.ts +63 -0
  8. package/dist/Retry.d.ts.map +1 -0
  9. package/dist/Retry.js +77 -0
  10. package/dist/Retry.js.map +1 -0
  11. package/dist/SlackClient.d.ts +19 -0
  12. package/dist/SlackClient.d.ts.map +1 -0
  13. package/dist/SlackClient.js +21 -0
  14. package/dist/SlackClient.js.map +1 -0
  15. package/dist/SlackConfig.d.ts +26 -0
  16. package/dist/SlackConfig.d.ts.map +1 -0
  17. package/dist/SlackConfig.js +19 -0
  18. package/dist/SlackConfig.js.map +1 -0
  19. package/dist/SlackService.d.ts +32 -0
  20. package/dist/SlackService.d.ts.map +1 -0
  21. package/dist/SlackService.js +135 -0
  22. package/dist/SlackService.js.map +1 -0
  23. package/dist/generated/AdminService.d.ts +174 -0
  24. package/dist/generated/AdminService.d.ts.map +1 -0
  25. package/dist/generated/AdminService.js +1029 -0
  26. package/dist/generated/AdminService.js.map +1 -0
  27. package/dist/generated/ApiService.d.ts +18 -0
  28. package/dist/generated/ApiService.d.ts.map +1 -0
  29. package/dist/generated/ApiService.js +27 -0
  30. package/dist/generated/ApiService.js.map +1 -0
  31. package/dist/generated/AppsService.d.ts +33 -0
  32. package/dist/generated/AppsService.d.ts.map +1 -0
  33. package/dist/generated/AppsService.js +105 -0
  34. package/dist/generated/AppsService.js.map +1 -0
  35. package/dist/generated/AssistantService.d.ts +22 -0
  36. package/dist/generated/AssistantService.d.ts.map +1 -0
  37. package/dist/generated/AssistantService.js +49 -0
  38. package/dist/generated/AssistantService.js.map +1 -0
  39. package/dist/generated/AuthService.d.ts +22 -0
  40. package/dist/generated/AuthService.d.ts.map +1 -0
  41. package/dist/generated/AuthService.js +46 -0
  42. package/dist/generated/AuthService.js.map +1 -0
  43. package/dist/generated/BookmarksService.d.ts +21 -0
  44. package/dist/generated/BookmarksService.d.ts.map +1 -0
  45. package/dist/generated/BookmarksService.js +57 -0
  46. package/dist/generated/BookmarksService.js.map +1 -0
  47. package/dist/generated/BotsService.d.ts +18 -0
  48. package/dist/generated/BotsService.d.ts.map +1 -0
  49. package/dist/generated/BotsService.js +27 -0
  50. package/dist/generated/BotsService.js.map +1 -0
  51. package/dist/generated/CallsService.d.ts +25 -0
  52. package/dist/generated/CallsService.d.ts.map +1 -0
  53. package/dist/generated/CallsService.js +76 -0
  54. package/dist/generated/CallsService.js.map +1 -0
  55. package/dist/generated/CanvasesService.d.ts +27 -0
  56. package/dist/generated/CanvasesService.d.ts.map +1 -0
  57. package/dist/generated/CanvasesService.js +81 -0
  58. package/dist/generated/CanvasesService.js.map +1 -0
  59. package/dist/generated/ChatService.d.ts +32 -0
  60. package/dist/generated/ChatService.d.ts.map +1 -0
  61. package/dist/generated/ChatService.js +149 -0
  62. package/dist/generated/ChatService.js.map +1 -0
  63. package/dist/generated/ConversationsService.d.ts +51 -0
  64. package/dist/generated/ConversationsService.d.ts.map +1 -0
  65. package/dist/generated/ConversationsService.js +303 -0
  66. package/dist/generated/ConversationsService.js.map +1 -0
  67. package/dist/generated/DialogService.d.ts +18 -0
  68. package/dist/generated/DialogService.d.ts.map +1 -0
  69. package/dist/generated/DialogService.js +27 -0
  70. package/dist/generated/DialogService.js.map +1 -0
  71. package/dist/generated/DndService.d.ts +22 -0
  72. package/dist/generated/DndService.d.ts.map +1 -0
  73. package/dist/generated/DndService.js +67 -0
  74. package/dist/generated/DndService.js.map +1 -0
  75. package/dist/generated/EmojiService.d.ts +18 -0
  76. package/dist/generated/EmojiService.d.ts.map +1 -0
  77. package/dist/generated/EmojiService.js +27 -0
  78. package/dist/generated/EmojiService.js.map +1 -0
  79. package/dist/generated/EntityService.d.ts +18 -0
  80. package/dist/generated/EntityService.d.ts.map +1 -0
  81. package/dist/generated/EntityService.js +27 -0
  82. package/dist/generated/EntityService.js.map +1 -0
  83. package/dist/generated/FilesService.d.ts +37 -0
  84. package/dist/generated/FilesService.d.ts.map +1 -0
  85. package/dist/generated/FilesService.js +182 -0
  86. package/dist/generated/FilesService.js.map +1 -0
  87. package/dist/generated/FunctionsService.d.ts +19 -0
  88. package/dist/generated/FunctionsService.d.ts.map +1 -0
  89. package/dist/generated/FunctionsService.js +37 -0
  90. package/dist/generated/FunctionsService.js.map +1 -0
  91. package/dist/generated/MigrationService.d.ts +18 -0
  92. package/dist/generated/MigrationService.d.ts.map +1 -0
  93. package/dist/generated/MigrationService.js +27 -0
  94. package/dist/generated/MigrationService.js.map +1 -0
  95. package/dist/generated/OauthService.d.ts +22 -0
  96. package/dist/generated/OauthService.d.ts.map +1 -0
  97. package/dist/generated/OauthService.js +50 -0
  98. package/dist/generated/OauthService.js.map +1 -0
  99. package/dist/generated/OpenidService.d.ts +21 -0
  100. package/dist/generated/OpenidService.d.ts.map +1 -0
  101. package/dist/generated/OpenidService.js +39 -0
  102. package/dist/generated/OpenidService.js.map +1 -0
  103. package/dist/generated/PinsService.d.ts +20 -0
  104. package/dist/generated/PinsService.d.ts.map +1 -0
  105. package/dist/generated/PinsService.js +47 -0
  106. package/dist/generated/PinsService.js.map +1 -0
  107. package/dist/generated/ReactionsService.d.ts +21 -0
  108. package/dist/generated/ReactionsService.d.ts.map +1 -0
  109. package/dist/generated/ReactionsService.js +57 -0
  110. package/dist/generated/ReactionsService.js.map +1 -0
  111. package/dist/generated/RemindersService.d.ts +22 -0
  112. package/dist/generated/RemindersService.d.ts.map +1 -0
  113. package/dist/generated/RemindersService.js +67 -0
  114. package/dist/generated/RemindersService.js.map +1 -0
  115. package/dist/generated/RtmService.d.ts +19 -0
  116. package/dist/generated/RtmService.d.ts.map +1 -0
  117. package/dist/generated/RtmService.js +38 -0
  118. package/dist/generated/RtmService.js.map +1 -0
  119. package/dist/generated/SearchService.d.ts +20 -0
  120. package/dist/generated/SearchService.d.ts.map +1 -0
  121. package/dist/generated/SearchService.js +47 -0
  122. package/dist/generated/SearchService.js.map +1 -0
  123. package/dist/generated/SlackListsService.d.ts +35 -0
  124. package/dist/generated/SlackListsService.d.ts.map +1 -0
  125. package/dist/generated/SlackListsService.js +143 -0
  126. package/dist/generated/SlackListsService.js.map +1 -0
  127. package/dist/generated/StarsService.d.ts +20 -0
  128. package/dist/generated/StarsService.d.ts.map +1 -0
  129. package/dist/generated/StarsService.js +50 -0
  130. package/dist/generated/StarsService.js.map +1 -0
  131. package/dist/generated/TeamService.d.ts +34 -0
  132. package/dist/generated/TeamService.d.ts.map +1 -0
  133. package/dist/generated/TeamService.js +115 -0
  134. package/dist/generated/TeamService.js.map +1 -0
  135. package/dist/generated/ToolingService.d.ts +20 -0
  136. package/dist/generated/ToolingService.d.ts.map +1 -0
  137. package/dist/generated/ToolingService.js +29 -0
  138. package/dist/generated/ToolingService.js.map +1 -0
  139. package/dist/generated/UsergroupsService.d.ts +26 -0
  140. package/dist/generated/UsergroupsService.d.ts.map +1 -0
  141. package/dist/generated/UsergroupsService.js +89 -0
  142. package/dist/generated/UsergroupsService.js.map +1 -0
  143. package/dist/generated/UsersService.d.ts +33 -0
  144. package/dist/generated/UsersService.d.ts.map +1 -0
  145. package/dist/generated/UsersService.js +141 -0
  146. package/dist/generated/UsersService.js.map +1 -0
  147. package/dist/generated/ViewsService.d.ts +21 -0
  148. package/dist/generated/ViewsService.d.ts.map +1 -0
  149. package/dist/generated/ViewsService.js +57 -0
  150. package/dist/generated/ViewsService.js.map +1 -0
  151. package/dist/generated/WorkflowsService.d.ts +26 -0
  152. package/dist/generated/WorkflowsService.d.ts.map +1 -0
  153. package/dist/generated/WorkflowsService.js +92 -0
  154. package/dist/generated/WorkflowsService.js.map +1 -0
  155. package/dist/generated/index.d.ts +38 -0
  156. package/dist/generated/index.d.ts.map +1 -0
  157. package/dist/generated/index.js +38 -0
  158. package/dist/generated/index.js.map +1 -0
  159. package/dist/index.d.ts +8 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +11 -0
  162. package/dist/index.js.map +1 -0
  163. package/dist/internal/errors.d.ts +13 -0
  164. package/dist/internal/errors.d.ts.map +1 -0
  165. package/dist/internal/errors.js +68 -0
  166. package/dist/internal/errors.js.map +1 -0
  167. package/package.json +80 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mateo Kruk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,322 @@
1
+ # effect-slack
2
+
3
+ An Effect-native Slack SDK ✨
4
+
5
+ ## Features
6
+
7
+ - **100% Type-safe** — Full TypeScript types for all 272 methods, arguments, and responses
8
+ - **Typed errors** — Discriminated unions with `catchTag`/`catchTags` for precise error handling
9
+ - **Observability built-in** — OpenTelemetry spans with rich attributes (method, channel, user, timestamps)
10
+ - **Smart retries** — Rate limit aware with exponential backoff and jitter
11
+ - **Testable by design** — Dependency injection via Effect layers, easily mockable
12
+ - **Always up-to-date** — Auto-generated from official `@slack/web-api` types
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ bun add effect-slack effect
18
+ # or
19
+ npm install effect-slack effect
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { Effect } from "effect"
26
+ import { SlackService } from "effect-slack"
27
+
28
+ // Using environment variables (SLACK_TOKEN)
29
+ const program = Effect.gen(function* () {
30
+ const slack = yield* SlackService
31
+
32
+ const result = yield* slack.postMessage({
33
+ channel: "C1234567890",
34
+ text: "Hello from Effect!"
35
+ })
36
+
37
+ console.log("Message sent:", result.ts)
38
+ }).pipe(Effect.provide(SlackService.Live))
39
+
40
+ Effect.runPromise(program)
41
+ ```
42
+
43
+ ## Custom Configuration
44
+
45
+ ```typescript
46
+ import { Effect, Layer, Redacted } from "effect"
47
+ import { SlackService, SlackConfig, SlackClient } from "effect-slack"
48
+
49
+ const customConfig = SlackConfig.make({
50
+ token: Redacted.make(process.env.MY_SLACK_TOKEN!),
51
+ options: {
52
+ retryConfig: { retries: 5 }
53
+ }
54
+ })
55
+
56
+ const CustomSlackLayer = SlackService.Default.pipe(
57
+ Layer.provide(SlackClient.Default),
58
+ Layer.provide(customConfig)
59
+ )
60
+
61
+ const program = Effect.gen(function* () {
62
+ const slack = yield* SlackService
63
+ // ... use slack methods
64
+ }).pipe(Effect.provide(CustomSlackLayer))
65
+ ```
66
+
67
+ ## Error Handling
68
+
69
+ All Slack API errors are mapped to typed Effect errors that can be handled with `catchTag` or `catchTags`:
70
+
71
+ ```typescript
72
+ import { Effect } from "effect"
73
+ import { SlackService } from "effect-slack"
74
+
75
+ const program = Effect.gen(function* () {
76
+ const slack = yield* SlackService
77
+ return yield* slack.postMessage({ channel: "C123", text: "Hi" })
78
+ }).pipe(
79
+ Effect.catchTags({
80
+ SlackRateLimitedError: (e) =>
81
+ Effect.log(`Rate limited, retry in ${e.retryAfter}s`),
82
+ SlackPlatformError: (e) =>
83
+ e.isAuthError
84
+ ? Effect.logError(`Auth failed: ${e.error}`)
85
+ : Effect.logError(`API error: ${e.error}`),
86
+ SlackHttpError: (e) =>
87
+ Effect.logError(`HTTP ${e.statusCode}: ${e.statusMessage}`)
88
+ }),
89
+ Effect.provide(SlackService.Live)
90
+ )
91
+ ```
92
+
93
+ ### Error Types
94
+
95
+ | Error Type | Description |
96
+ | -------------------------------------- | -------------------------------------------------------------- |
97
+ | `SlackRequestError` | Network failures, DNS errors |
98
+ | `SlackHttpError` | Non-200 HTTP responses with `statusCode` and `body` |
99
+ | `SlackPlatformError` | Slack API errors with `error` code (e.g., `channel_not_found`) |
100
+ | `SlackRateLimitedError` | Rate limit exceeded, includes `retryAfter` (seconds) |
101
+ | `SlackFileUploadInvalidArgumentsError` | Invalid file upload arguments |
102
+ | `SlackFileUploadReadError` | Failed to read file data |
103
+ | `SlackUnknownError` | Unexpected errors |
104
+
105
+ `SlackPlatformError` includes an `isAuthError` getter that returns `true` for auth-related errors (`invalid_auth`, `not_authed`, `token_revoked`, `token_expired`, `account_inactive`).
106
+
107
+ ## Retry Support
108
+
109
+ The library provides two approaches to retry handling:
110
+
111
+ ### SDK-Level Retries (Default)
112
+
113
+ The underlying `@slack/web-api` SDK handles retries automatically. You can configure it via `SlackConfig`:
114
+
115
+ ```typescript
116
+ import { Redacted } from "effect"
117
+ import { SlackConfig } from "effect-slack"
118
+
119
+ const config = SlackConfig.make({
120
+ token: Redacted.make("xoxb-..."),
121
+ options: {
122
+ retryConfig: { retries: 5 }
123
+ }
124
+ })
125
+ ```
126
+
127
+ ### Disabling SDK Retries
128
+
129
+ To use Effect-level retries exclusively, disable SDK retries:
130
+
131
+ ```typescript
132
+ import { Redacted } from "effect"
133
+ import { SlackConfig } from "effect-slack"
134
+
135
+ const config = SlackConfig.make({
136
+ token: Redacted.make("xoxb-..."),
137
+ options: {
138
+ retryConfig: { retries: 0 }, // Disable SDK retries
139
+ rejectRateLimitedCalls: true // Don't auto-handle rate limits
140
+ }
141
+ })
142
+ ```
143
+
144
+ ### Effect-Level Retries
145
+
146
+ For more control, use the Effect-native retry utilities:
147
+
148
+ ```typescript
149
+ import { Effect, pipe } from "effect"
150
+ import {
151
+ SlackService,
152
+ withDefaultRetry,
153
+ withRateLimitRetry,
154
+ rapidRetryPolicy,
155
+ isRetryableError
156
+ } from "effect-slack"
157
+
158
+ // Apply default retry policy (10 retries with exponential backoff)
159
+ const program = pipe(
160
+ Effect.flatMap(SlackService, (slack) =>
161
+ slack.postMessage({ channel: "#general", text: "Hello!" })
162
+ ),
163
+ withDefaultRetry
164
+ )
165
+
166
+ // Or use a custom schedule
167
+ const programWithCustomRetry = pipe(
168
+ Effect.flatMap(SlackService, (slack) =>
169
+ slack.postMessage({ channel: "#general", text: "Hello!" })
170
+ ),
171
+ Effect.retry(rapidRetryPolicy)
172
+ )
173
+ ```
174
+
175
+ ### Pre-built Schedules
176
+
177
+ | Schedule | Description |
178
+ | -------------------------------- | --------------------------------------------- |
179
+ | `tenRetriesInAboutThirtyMinutes` | 10 retries with exponential backoff + jitter |
180
+ | `fiveRetriesInFiveMinutes` | 5 retries with exponential backoff + jitter |
181
+ | `rapidRetryPolicy` | 3 retries with 100ms delay (for testing) |
182
+ | `rateLimitAwareSchedule(opts)` | Configurable retries with exponential backoff |
183
+
184
+ ### Retryable Errors
185
+
186
+ The `isRetryableError` function determines which errors are safe to retry:
187
+
188
+ | Error Type | Retryable | Reason |
189
+ | ----------------------- | --------- | --------------------------- |
190
+ | `SlackRateLimitedError` | Yes | Transient, has `retryAfter` |
191
+ | `SlackRequestError` | Yes | Network failures |
192
+ | `SlackHttpError` (5xx) | Yes | Server errors |
193
+ | `SlackHttpError` (4xx) | No | Client errors |
194
+ | `SlackPlatformError` | Partial | Only `service_unavailable` |
195
+ | Auth errors | No | Won't resolve with retry |
196
+
197
+ ## Observability
198
+
199
+ All SlackService methods are instrumented with OpenTelemetry-compatible spans.
200
+
201
+ ### Span Attributes
202
+
203
+ | Attribute | Description |
204
+ | ------------------- | -------------------------------------------------- |
205
+ | `slack.method` | Slack API method (e.g., `chat.postMessage`) |
206
+ | `slack.channel` | Channel ID (where applicable) |
207
+ | `slack.user` | User ID (for user operations) |
208
+ | `slack.ts` | Message timestamp (for updates/deletes) |
209
+ | `slack.reaction` | Reaction name (for reaction operations) |
210
+ | `error.type` | Error tag on failures (e.g., `SlackPlatformError`) |
211
+ | `slack.error` | Platform error code (e.g., `channel_not_found`) |
212
+ | `http.status_code` | HTTP status code on HTTP errors |
213
+ | `slack.retry_after` | Retry-After seconds on rate limit errors |
214
+
215
+ ### Exporting Traces
216
+
217
+ Use `@effect/opentelemetry` to export traces to your observability backend:
218
+
219
+ ```typescript
220
+ import { Effect } from "effect"
221
+ import { NodeSdk } from "@effect/opentelemetry"
222
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base"
223
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"
224
+ import { SlackService } from "effect-slack"
225
+
226
+ const TracingLive = NodeSdk.layer(() => ({
227
+ resource: { serviceName: "my-slack-app" },
228
+ spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter())
229
+ }))
230
+
231
+ const program = Effect.gen(function* () {
232
+ const slack = yield* SlackService
233
+ yield* slack.postMessage({ channel: "#general", text: "Hello!" })
234
+ })
235
+
236
+ Effect.runPromise(
237
+ program.pipe(
238
+ Effect.provide(SlackService.Live),
239
+ Effect.provide(TracingLive)
240
+ )
241
+ )
242
+ ```
243
+
244
+ ## Available Methods
245
+
246
+ The library provides **272 methods** across **33 services**, auto-generated from the official `@slack/web-api` types. Services include:
247
+
248
+ - **ChatService** - Messages, threads, scheduled messages
249
+ - **ConversationsService** - Channels, DMs, group conversations
250
+ - **UsersService** - User profiles, presence, identity
251
+ - **ReactionsService** - Emoji reactions
252
+ - **FilesService** - File uploads and management
253
+ - **AdminService** - Workspace administration (100+ methods)
254
+ - **AppsService**, **AuthService**, **BookmarksService**, **CallsService**, **ViewsService**, and more...
255
+
256
+ Each service is available as an Effect service with full TypeScript types. See [`src/generated/`](./src/generated/) for the complete API.
257
+
258
+ ## Testing
259
+
260
+ The library is designed to be easily testable by providing mock implementations:
261
+
262
+ ```typescript
263
+ import { Effect, Layer } from "effect"
264
+ import { SlackService, SlackClient } from "effect-slack"
265
+ import type { WebClient } from "@slack/web-api"
266
+
267
+ // Create a mock WebClient
268
+ const mockClient = {
269
+ chat: {
270
+ postMessage: async () => ({ ok: true, ts: "1234.5678" })
271
+ }
272
+ } as unknown as WebClient
273
+
274
+ // Create test layer
275
+ const TestLayer = SlackService.Default.pipe(
276
+ Layer.provide(SlackClient.make(mockClient))
277
+ )
278
+
279
+ // Use in tests
280
+ const testProgram = Effect.gen(function* () {
281
+ const slack = yield* SlackService
282
+ const result = yield* slack.postMessage({
283
+ channel: "C123",
284
+ text: "Test message"
285
+ })
286
+ return result
287
+ }).pipe(Effect.provide(TestLayer))
288
+ ```
289
+
290
+ ## Architecture
291
+
292
+ Services are auto-generated from the `@slack/web-api` TypeScript definitions:
293
+
294
+ 1. **Parse** — Extract method signatures from `@slack/web-api/dist/methods.d.ts`
295
+ 2. **Generate** — Create Effect-wrapped services with typed arguments and responses
296
+ 3. **Instrument** — Add OpenTelemetry spans and error mapping to each method
297
+
298
+ ```
299
+ @slack/web-api types → Parser → Code Generator → Effect Services
300
+ ```
301
+
302
+ Generated services follow a consistent pattern:
303
+
304
+ ```typescript
305
+ // Each method is wrapped with Effect.tryPromise, error mapping, and tracing
306
+ const postMessage = (args: ChatPostMessageArguments): Effect.Effect<ChatPostMessageResponse, SlackError> =>
307
+ Effect.tryPromise({
308
+ try: () => client.chat.postMessage(args),
309
+ catch: mapSlackError
310
+ }).pipe(
311
+ Effect.tapError(annotateSpanWithError),
312
+ Effect.withSpan("ChatService.postMessage", {
313
+ attributes: { "slack.method": "chat.postMessage" }
314
+ })
315
+ )
316
+ ```
317
+
318
+ Run `bun run generate` to regenerate services when updating `@slack/web-api`.
319
+
320
+ ## License
321
+
322
+ MIT
@@ -0,0 +1,103 @@
1
+ import { Schema } from "effect";
2
+ import { ErrorCode } from "@slack/web-api";
3
+ /**
4
+ * Re-export ErrorCode for consumers to use in type guards
5
+ */
6
+ export { ErrorCode };
7
+ declare const SlackRequestError_base: Schema.TaggedErrorClass<SlackRequestError, "SlackRequestError", {
8
+ readonly _tag: Schema.tag<"SlackRequestError">;
9
+ } & {
10
+ message: typeof Schema.String;
11
+ cause: Schema.optional<typeof Schema.Defect>;
12
+ }>;
13
+ /**
14
+ * Request error - network failures, DNS errors, etc.
15
+ * Maps to: slack_webapi_request_error
16
+ */
17
+ export declare class SlackRequestError extends SlackRequestError_base {
18
+ }
19
+ declare const SlackHttpError_base: Schema.TaggedErrorClass<SlackHttpError, "SlackHttpError", {
20
+ readonly _tag: Schema.tag<"SlackHttpError">;
21
+ } & {
22
+ statusCode: typeof Schema.Number;
23
+ statusMessage: typeof Schema.String;
24
+ message: typeof Schema.String;
25
+ body: Schema.optional<typeof Schema.Unknown>;
26
+ }>;
27
+ /**
28
+ * HTTP error - non-200 responses from Slack API
29
+ * Maps to: slack_webapi_http_error
30
+ */
31
+ export declare class SlackHttpError extends SlackHttpError_base {
32
+ }
33
+ declare const SlackPlatformError_base: Schema.TaggedErrorClass<SlackPlatformError, "SlackPlatformError", {
34
+ readonly _tag: Schema.tag<"SlackPlatformError">;
35
+ } & {
36
+ error: typeof Schema.String;
37
+ message: typeof Schema.String;
38
+ data: Schema.optional<typeof Schema.Unknown>;
39
+ }>;
40
+ /**
41
+ * Platform error - Slack API returned an error response
42
+ * Maps to: slack_webapi_platform_error
43
+ *
44
+ * The `error` field contains the Slack error code (e.g., "channel_not_found", "invalid_auth")
45
+ */
46
+ export declare class SlackPlatformError extends SlackPlatformError_base {
47
+ /**
48
+ * Check if this is an authentication error
49
+ */
50
+ get isAuthError(): boolean;
51
+ }
52
+ declare const SlackRateLimitedError_base: Schema.TaggedErrorClass<SlackRateLimitedError, "SlackRateLimitedError", {
53
+ readonly _tag: Schema.tag<"SlackRateLimitedError">;
54
+ } & {
55
+ retryAfter: typeof Schema.Number;
56
+ message: typeof Schema.String;
57
+ }>;
58
+ /**
59
+ * Rate limited error - too many requests
60
+ * Maps to: slack_webapi_rate_limited_error
61
+ */
62
+ export declare class SlackRateLimitedError extends SlackRateLimitedError_base {
63
+ }
64
+ declare const SlackFileUploadInvalidArgumentsError_base: Schema.TaggedErrorClass<SlackFileUploadInvalidArgumentsError, "SlackFileUploadInvalidArgumentsError", {
65
+ readonly _tag: Schema.tag<"SlackFileUploadInvalidArgumentsError">;
66
+ } & {
67
+ message: typeof Schema.String;
68
+ data: Schema.optional<typeof Schema.Unknown>;
69
+ }>;
70
+ /**
71
+ * File upload error - invalid arguments for file upload
72
+ * Maps to: slack_webapi_file_upload_invalid_args_error
73
+ */
74
+ export declare class SlackFileUploadInvalidArgumentsError extends SlackFileUploadInvalidArgumentsError_base {
75
+ }
76
+ declare const SlackFileUploadReadError_base: Schema.TaggedErrorClass<SlackFileUploadReadError, "SlackFileUploadReadError", {
77
+ readonly _tag: Schema.tag<"SlackFileUploadReadError">;
78
+ } & {
79
+ message: typeof Schema.String;
80
+ cause: Schema.optional<typeof Schema.Defect>;
81
+ }>;
82
+ /**
83
+ * File upload read error - failed to read file data
84
+ * Maps to: slack_webapi_file_upload_read_file_data_error
85
+ */
86
+ export declare class SlackFileUploadReadError extends SlackFileUploadReadError_base {
87
+ }
88
+ declare const SlackUnknownError_base: Schema.TaggedErrorClass<SlackUnknownError, "SlackUnknownError", {
89
+ readonly _tag: Schema.tag<"SlackUnknownError">;
90
+ } & {
91
+ message: typeof Schema.String;
92
+ cause: typeof Schema.Defect;
93
+ }>;
94
+ /**
95
+ * Unknown error - catch-all for unexpected errors
96
+ */
97
+ export declare class SlackUnknownError extends SlackUnknownError_base {
98
+ }
99
+ /**
100
+ * Union type of all Slack errors for exhaustive handling
101
+ */
102
+ export type SlackError = SlackRequestError | SlackHttpError | SlackPlatformError | SlackRateLimitedError | SlackFileUploadInvalidArgumentsError | SlackFileUploadReadError | SlackUnknownError;
103
+ //# sourceMappingURL=Errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../src/Errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,CAAA;;;;;;;AAEpB;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,sBAMtC;CAAG;;;;;;;;;AAEJ;;;GAGG;AACH,qBAAa,cAAe,SAAQ,mBAKlC;CAAG;;;;;;;;AAEL;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,uBAOvC;IACC;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAQzB;CACF;;;;;;;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,0BAM1C;CAAG;;;;;;;AAEJ;;;GAGG;AACH,qBAAa,oCAAqC,SAAQ,yCAMzD;CAAG;;;;;;;AAEJ;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,6BAM7C;CAAG;;;;;;;AAEJ;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,sBAMtC;CAAG;AAEJ;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,iBAAiB,GACjB,cAAc,GACd,kBAAkB,GAClB,qBAAqB,GACrB,oCAAoC,GACpC,wBAAwB,GACxB,iBAAiB,CAAA"}
package/dist/Errors.js ADDED
@@ -0,0 +1,84 @@
1
+ import { Schema } from "effect";
2
+ import { ErrorCode } from "@slack/web-api";
3
+ /**
4
+ * Re-export ErrorCode for consumers to use in type guards
5
+ */
6
+ export { ErrorCode };
7
+ /**
8
+ * Request error - network failures, DNS errors, etc.
9
+ * Maps to: slack_webapi_request_error
10
+ */
11
+ export class SlackRequestError extends Schema.TaggedError()("SlackRequestError", {
12
+ message: Schema.String,
13
+ cause: Schema.optional(Schema.Defect)
14
+ }) {
15
+ }
16
+ /**
17
+ * HTTP error - non-200 responses from Slack API
18
+ * Maps to: slack_webapi_http_error
19
+ */
20
+ export class SlackHttpError extends Schema.TaggedError()("SlackHttpError", {
21
+ statusCode: Schema.Number,
22
+ statusMessage: Schema.String,
23
+ message: Schema.String,
24
+ body: Schema.optional(Schema.Unknown)
25
+ }) {
26
+ }
27
+ /**
28
+ * Platform error - Slack API returned an error response
29
+ * Maps to: slack_webapi_platform_error
30
+ *
31
+ * The `error` field contains the Slack error code (e.g., "channel_not_found", "invalid_auth")
32
+ */
33
+ export class SlackPlatformError extends Schema.TaggedError()("SlackPlatformError", {
34
+ error: Schema.String,
35
+ message: Schema.String,
36
+ data: Schema.optional(Schema.Unknown)
37
+ }) {
38
+ /**
39
+ * Check if this is an authentication error
40
+ */
41
+ get isAuthError() {
42
+ return (this.error === "invalid_auth" ||
43
+ this.error === "not_authed" ||
44
+ this.error === "token_revoked" ||
45
+ this.error === "token_expired" ||
46
+ this.error === "account_inactive");
47
+ }
48
+ }
49
+ /**
50
+ * Rate limited error - too many requests
51
+ * Maps to: slack_webapi_rate_limited_error
52
+ */
53
+ export class SlackRateLimitedError extends Schema.TaggedError()("SlackRateLimitedError", {
54
+ retryAfter: Schema.Number,
55
+ message: Schema.String
56
+ }) {
57
+ }
58
+ /**
59
+ * File upload error - invalid arguments for file upload
60
+ * Maps to: slack_webapi_file_upload_invalid_args_error
61
+ */
62
+ export class SlackFileUploadInvalidArgumentsError extends Schema.TaggedError()("SlackFileUploadInvalidArgumentsError", {
63
+ message: Schema.String,
64
+ data: Schema.optional(Schema.Unknown)
65
+ }) {
66
+ }
67
+ /**
68
+ * File upload read error - failed to read file data
69
+ * Maps to: slack_webapi_file_upload_read_file_data_error
70
+ */
71
+ export class SlackFileUploadReadError extends Schema.TaggedError()("SlackFileUploadReadError", {
72
+ message: Schema.String,
73
+ cause: Schema.optional(Schema.Defect)
74
+ }) {
75
+ }
76
+ /**
77
+ * Unknown error - catch-all for unexpected errors
78
+ */
79
+ export class SlackUnknownError extends Schema.TaggedError()("SlackUnknownError", {
80
+ message: Schema.String,
81
+ cause: Schema.Defect
82
+ }) {
83
+ }
84
+ //# sourceMappingURL=Errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Errors.js","sourceRoot":"","sources":["../src/Errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C;;GAEG;AACH,OAAO,EAAE,SAAS,EAAE,CAAA;AAEpB;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,WAAW,EAAqB,CAC5E,mBAAmB,EACnB;IACE,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;CACtC,CACF;CAAG;AAEJ;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,EAAkB,CAAC,gBAAgB,EAAE;IACzF,UAAU,EAAE,MAAM,CAAC,MAAM;IACzB,aAAa,EAAE,MAAM,CAAC,MAAM;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;CACtC,CAAC;CAAG;AAEL;;;;;GAKG;AACH,MAAM,OAAO,kBAAmB,SAAQ,MAAM,CAAC,WAAW,EAAsB,CAC9E,oBAAoB,EACpB;IACE,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;CACtC,CACF;IACC;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,CACL,IAAI,CAAC,KAAK,KAAK,cAAc;YAC7B,IAAI,CAAC,KAAK,KAAK,YAAY;YAC3B,IAAI,CAAC,KAAK,KAAK,eAAe;YAC9B,IAAI,CAAC,KAAK,KAAK,eAAe;YAC9B,IAAI,CAAC,KAAK,KAAK,kBAAkB,CAClC,CAAA;IACH,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,qBAAsB,SAAQ,MAAM,CAAC,WAAW,EAAyB,CACpF,uBAAuB,EACvB;IACE,UAAU,EAAE,MAAM,CAAC,MAAM;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CACF;CAAG;AAEJ;;;GAGG;AACH,MAAM,OAAO,oCAAqC,SAAQ,MAAM,CAAC,WAAW,EAAwC,CAClH,sCAAsC,EACtC;IACE,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;CACtC,CACF;CAAG;AAEJ;;;GAGG;AACH,MAAM,OAAO,wBAAyB,SAAQ,MAAM,CAAC,WAAW,EAA4B,CAC1F,0BAA0B,EAC1B;IACE,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;CACtC,CACF;CAAG;AAEJ;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,WAAW,EAAqB,CAC5E,mBAAmB,EACnB;IACE,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM;CACrB,CACF;CAAG"}
@@ -0,0 +1,63 @@
1
+ import { Duration, Effect, Schedule } from "effect";
2
+ import type { SlackError } from "./Errors.js";
3
+ /**
4
+ * Options for creating retry schedules
5
+ */
6
+ export interface RetryOptions {
7
+ readonly maxRetries?: number;
8
+ readonly baseDelay?: Duration.DurationInput;
9
+ }
10
+ /**
11
+ * Check if an error is transient and safe to retry
12
+ */
13
+ export declare const isRetryableError: (error: SlackError) => boolean;
14
+ /**
15
+ * Effect equivalent of SDK's tenRetriesInAboutThirtyMinutes
16
+ * 10 retries with exponential backoff + jitter
17
+ */
18
+ export declare const tenRetriesInAboutThirtyMinutes: Schedule.Schedule<number, SlackError>;
19
+ /**
20
+ * Effect equivalent of SDK's fiveRetriesInFiveMinutes
21
+ * 5 retries with exponential backoff + jitter
22
+ */
23
+ export declare const fiveRetriesInFiveMinutes: Schedule.Schedule<number, SlackError>;
24
+ /**
25
+ * Rapid retry policy for testing
26
+ * 3 retries with 100ms fixed delay
27
+ */
28
+ export declare const rapidRetryPolicy: Schedule.Schedule<number, SlackError>;
29
+ /**
30
+ * Schedule that respects Slack's Retry-After header for rate limits
31
+ * Uses exponential backoff for non-rate-limit errors
32
+ */
33
+ export declare const rateLimitAwareSchedule: (options?: RetryOptions) => Schedule.Schedule<number, SlackError>;
34
+ /**
35
+ * Retry an effect with the default policy (tenRetriesInAboutThirtyMinutes)
36
+ */
37
+ export declare const withDefaultRetry: <A, E extends SlackError, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
38
+ /**
39
+ * Retry an effect with rate-limit awareness
40
+ * Automatically uses retryAfter from SlackRateLimitedError
41
+ */
42
+ export declare const withRateLimitRetry: <A, E extends SlackError, R>(effect: Effect.Effect<A, E, R>, options?: RetryOptions) => Effect.Effect<A, E, R>;
43
+ /**
44
+ * Retry only rate limit errors with exact retryAfter timing
45
+ * Does NOT retry other error types
46
+ */
47
+ export declare const withRateLimitOnlyRetry: <A, E extends SlackError, R>(effect: Effect.Effect<A, E, R>, options?: {
48
+ maxRetries?: number;
49
+ }) => Effect.Effect<A, E, R>;
50
+ /**
51
+ * Retry with fallback - returns a fallback value if all retries fail
52
+ */
53
+ export declare const withRetryOrElse: <A, B, E extends SlackError, R, R2>(effect: Effect.Effect<A, E, R>, fallback: (error: E, fiberId: unknown) => Effect.Effect<B, E, R2>, schedule?: Schedule.Schedule<unknown, E>) => Effect.Effect<A | B, E, R | R2>;
54
+ /**
55
+ * All pre-built schedules as a namespace
56
+ */
57
+ export declare const Schedules: {
58
+ readonly tenRetriesInAboutThirtyMinutes: Schedule.Schedule<number, SlackError, never>;
59
+ readonly fiveRetriesInFiveMinutes: Schedule.Schedule<number, SlackError, never>;
60
+ readonly rapidRetryPolicy: Schedule.Schedule<number, SlackError, never>;
61
+ readonly rateLimitAwareSchedule: (options?: RetryOptions) => Schedule.Schedule<number, SlackError>;
62
+ };
63
+ //# sourceMappingURL=Retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Retry.d.ts","sourceRoot":"","sources":["../src/Retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAQ,QAAQ,EAAE,MAAM,QAAQ,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE7C;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,aAAa,CAAA;CAC5C;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,UAAU,KAAG,OAgBpD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAMhF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAM1E,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAKlE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAU,YAAY,KACrB,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAWtC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,SAAS,UAAU,EAAE,CAAC,EACzD,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC7B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAC8D,CAAA;AAEtF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,CAAC,SAAS,UAAU,EAAE,CAAC,EAC3D,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAC+D,CAAA;AAEvF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,EAAE,CAAC,SAAS,UAAU,EAAE,CAAC,EAC/D,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,UAAU;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,KAChC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAIpB,CAAA;AAEJ;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,UAAU,EAAE,CAAC,EAAE,EAAE,EAC/D,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,EACjE,WAAW,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,KACvC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAK9B,CAAA;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS;;;;gDA9DV,YAAY,KACrB,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAkE9B,CAAA"}
package/dist/Retry.js ADDED
@@ -0,0 +1,77 @@
1
+ import { Duration, Effect, pipe, Schedule } from "effect";
2
+ /**
3
+ * Check if an error is transient and safe to retry
4
+ */
5
+ export const isRetryableError = (error) => {
6
+ switch (error._tag) {
7
+ case "SlackRateLimitedError":
8
+ return true;
9
+ case "SlackRequestError":
10
+ return true;
11
+ case "SlackHttpError":
12
+ return error.statusCode >= 500;
13
+ case "SlackPlatformError":
14
+ if (error.isAuthError)
15
+ return false;
16
+ return error.error === "service_unavailable" || error.error === "internal_error";
17
+ case "SlackFileUploadInvalidArgumentsError":
18
+ case "SlackFileUploadReadError":
19
+ case "SlackUnknownError":
20
+ return false;
21
+ }
22
+ };
23
+ /**
24
+ * Effect equivalent of SDK's tenRetriesInAboutThirtyMinutes
25
+ * 10 retries with exponential backoff + jitter
26
+ */
27
+ export const tenRetriesInAboutThirtyMinutes = pipe(Schedule.exponential("1 second", 2), Schedule.jittered, Schedule.intersect(Schedule.recurs(10)), Schedule.whileInput(isRetryableError), Schedule.map(([, count]) => count));
28
+ /**
29
+ * Effect equivalent of SDK's fiveRetriesInFiveMinutes
30
+ * 5 retries with exponential backoff + jitter
31
+ */
32
+ export const fiveRetriesInFiveMinutes = pipe(Schedule.exponential("1 second", 2), Schedule.jittered, Schedule.intersect(Schedule.recurs(5)), Schedule.whileInput(isRetryableError), Schedule.map(([, count]) => count));
33
+ /**
34
+ * Rapid retry policy for testing
35
+ * 3 retries with 100ms fixed delay
36
+ */
37
+ export const rapidRetryPolicy = pipe(Schedule.fixed("100 millis"), Schedule.intersect(Schedule.recurs(3)), Schedule.whileInput(isRetryableError), Schedule.map(([, count]) => count));
38
+ /**
39
+ * Schedule that respects Slack's Retry-After header for rate limits
40
+ * Uses exponential backoff for non-rate-limit errors
41
+ */
42
+ export const rateLimitAwareSchedule = (options) => {
43
+ const maxRetries = options?.maxRetries ?? 10;
44
+ const baseDelay = options?.baseDelay ?? "1 second";
45
+ return pipe(Schedule.exponential(baseDelay, 2), Schedule.jittered, Schedule.intersect(Schedule.recurs(maxRetries)), Schedule.whileInput(isRetryableError), Schedule.map(([, count]) => count));
46
+ };
47
+ /**
48
+ * Retry an effect with the default policy (tenRetriesInAboutThirtyMinutes)
49
+ */
50
+ export const withDefaultRetry = (effect) => Effect.retry(effect, tenRetriesInAboutThirtyMinutes);
51
+ /**
52
+ * Retry an effect with rate-limit awareness
53
+ * Automatically uses retryAfter from SlackRateLimitedError
54
+ */
55
+ export const withRateLimitRetry = (effect, options) => Effect.retry(effect, rateLimitAwareSchedule(options));
56
+ /**
57
+ * Retry only rate limit errors with exact retryAfter timing
58
+ * Does NOT retry other error types
59
+ */
60
+ export const withRateLimitOnlyRetry = (effect, options) => Effect.retry(effect, {
61
+ while: (error) => error._tag === "SlackRateLimitedError",
62
+ times: options?.maxRetries ?? 5
63
+ });
64
+ /**
65
+ * Retry with fallback - returns a fallback value if all retries fail
66
+ */
67
+ export const withRetryOrElse = (effect, fallback, schedule) => Effect.retryOrElse(effect, schedule ?? tenRetriesInAboutThirtyMinutes, fallback);
68
+ /**
69
+ * All pre-built schedules as a namespace
70
+ */
71
+ export const Schedules = {
72
+ tenRetriesInAboutThirtyMinutes,
73
+ fiveRetriesInFiveMinutes,
74
+ rapidRetryPolicy,
75
+ rateLimitAwareSchedule
76
+ };
77
+ //# sourceMappingURL=Retry.js.map