@teever/ez-hook-effect 0.4.4
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.
- package/README.md +265 -0
- package/dist/errors/WebhookError.d.ts +156 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +952 -0
- package/dist/index.js.map +20 -0
- package/dist/layers/Config.d.ts +48 -0
- package/dist/layers/HttpClient.d.ts +80 -0
- package/dist/layers/index.d.ts +2 -0
- package/dist/pipes/Embed.d.ts +51 -0
- package/dist/pipes/Webhook.d.ts +37 -0
- package/dist/pipes/index.d.ts +2 -0
- package/dist/schemas/Common.d.ts +10 -0
- package/dist/schemas/Discord.d.ts +16 -0
- package/dist/schemas/Embed.d.ts +314 -0
- package/dist/schemas/Field.d.ts +45 -0
- package/dist/schemas/Webhook.d.ts +206 -0
- package/dist/schemas/index.d.ts +5 -0
- package/dist/services/WebhookService.d.ts +72 -0
- package/dist/services/index.d.ts +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# @teever/ez-hook-effect
|
|
2
|
+
|
|
3
|
+
A Discord webhook library built with [Effect](https://effect.website/) - type-safe, composable, and resilient.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-safe** - Runtime validation with Effect schemas
|
|
8
|
+
- **Composable** - Functional programming patterns with Effect
|
|
9
|
+
- **Error Handling** - Comprehensive error tracking with structured validation issues
|
|
10
|
+
- **Retry Logic** - Built-in exponential backoff with jitter
|
|
11
|
+
- **Dependency Injection** - Layer-based architecture
|
|
12
|
+
- **Validated** - All Discord webhook constraints enforced
|
|
13
|
+
- **One Dependency** - Only requires Effect
|
|
14
|
+
- **Environment Support** - Configure via environment variables
|
|
15
|
+
- **CRUD Operations** - Send, get, modify, delete, and validate webhooks
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bunx jsr add @teever/ez-hook-effect
|
|
21
|
+
# or
|
|
22
|
+
npx jsr add @teever/ez-hook-effect
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Effect, Layer, pipe } from 'effect'
|
|
29
|
+
import { sendWebhook, makeConfigLayer, HttpClientLive, WebhookServiceLive, Webhook } from '@teever/ez-hook-effect'
|
|
30
|
+
|
|
31
|
+
// Configure your webhook
|
|
32
|
+
const WEBHOOK_URL = 'https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN'
|
|
33
|
+
|
|
34
|
+
// Create service layers
|
|
35
|
+
const AppLayer = WebhookServiceLive.pipe(
|
|
36
|
+
Layer.provide(makeConfigLayer(WEBHOOK_URL)),
|
|
37
|
+
Layer.provide(HttpClientLive)
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
// Send a simple message
|
|
41
|
+
const program = pipe(
|
|
42
|
+
Webhook.make,
|
|
43
|
+
Webhook.setContent('Hello from Effect!'),
|
|
44
|
+
Webhook.setUsername('My Bot'),
|
|
45
|
+
Webhook.build,
|
|
46
|
+
Effect.flatMap(sendWebhook)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// Run the program
|
|
50
|
+
Effect.runPromise(Effect.provide(program, AppLayer))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pipe-First API (Recommended)
|
|
54
|
+
|
|
55
|
+
Prefer composing with `pipe` for ergonomic, Effect-style data assembly. This library uses a single, pipe-first API (no OO builders).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { Effect, pipe } from 'effect'
|
|
59
|
+
import { Webhook, Embed, sendWebhook } from '@teever/ez-hook-effect'
|
|
60
|
+
|
|
61
|
+
const program = pipe(
|
|
62
|
+
Embed.make,
|
|
63
|
+
Embed.setTitle('System Status'),
|
|
64
|
+
Embed.setDescription('All systems operational'),
|
|
65
|
+
Embed.setColor(0x00ff00),
|
|
66
|
+
Embed.addField('CPU', '45%', true),
|
|
67
|
+
Embed.setTimestamp(),
|
|
68
|
+
Embed.build,
|
|
69
|
+
Effect.flatMap((embed) =>
|
|
70
|
+
pipe(
|
|
71
|
+
Webhook.make,
|
|
72
|
+
Webhook.setContent('📊 Status Update'),
|
|
73
|
+
Webhook.setUsername('Ez-Hook Bot'),
|
|
74
|
+
Webhook.addEmbed(embed),
|
|
75
|
+
Webhook.build
|
|
76
|
+
)
|
|
77
|
+
),
|
|
78
|
+
Effect.flatMap(sendWebhook)
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Building Rich Embeds
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
const embedProgram = pipe(
|
|
86
|
+
Embed.make,
|
|
87
|
+
Embed.setTitle('Server Status'),
|
|
88
|
+
Embed.setDescription('All systems operational'),
|
|
89
|
+
Embed.setColor(0x00FF00),
|
|
90
|
+
Embed.addField('CPU', '45%', true),
|
|
91
|
+
Embed.addField('Memory', '2.1GB', true),
|
|
92
|
+
Embed.addField('Uptime', '15 days', true),
|
|
93
|
+
Embed.build,
|
|
94
|
+
Effect.flatMap((embed) =>
|
|
95
|
+
pipe(
|
|
96
|
+
Webhook.make,
|
|
97
|
+
Webhook.setContent('Daily Report'),
|
|
98
|
+
Webhook.addEmbed(embed),
|
|
99
|
+
Webhook.build
|
|
100
|
+
)
|
|
101
|
+
),
|
|
102
|
+
Effect.flatMap(sendWebhook)
|
|
103
|
+
)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Error Handling
|
|
107
|
+
|
|
108
|
+
Retries for rate limits (429) and transient errors (5xx) are handled automatically using configurable exponential backoff. You only need to handle errors that persist after retries are exhausted:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { Effect, pipe } from 'effect'
|
|
112
|
+
import { Webhook, sendWebhook, ValidationError, HttpError, NetworkError, RateLimitError, WebhookError } from '@teever/ez-hook-effect'
|
|
113
|
+
|
|
114
|
+
const program = pipe(
|
|
115
|
+
Webhook.make,
|
|
116
|
+
Webhook.setContent('Hello!'),
|
|
117
|
+
Webhook.build,
|
|
118
|
+
Effect.flatMap(sendWebhook),
|
|
119
|
+
Effect.catchTag('ValidationError', (error) =>
|
|
120
|
+
Effect.logError(`Validation failed: ${error.message}`)
|
|
121
|
+
),
|
|
122
|
+
Effect.catchTag('RateLimitError', (error) =>
|
|
123
|
+
Effect.logError(`Rate limited: retry after ${error.retryAfter}ms`)
|
|
124
|
+
),
|
|
125
|
+
Effect.catchTag('HttpError', (error) =>
|
|
126
|
+
Effect.logError(`Request failed: ${error.message}`)
|
|
127
|
+
),
|
|
128
|
+
Effect.catchTag('NetworkError', (error) =>
|
|
129
|
+
Effect.logError(`Network error: ${error.message}`)
|
|
130
|
+
),
|
|
131
|
+
Effect.catchTag('WebhookError', (error) =>
|
|
132
|
+
Effect.logError(`Webhook error: ${error.message}`)
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Structured Validation Errors
|
|
138
|
+
|
|
139
|
+
Validation errors include detailed issue information:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
Effect.catchTag('ValidationError', (error) =>
|
|
143
|
+
Effect.logError(`Validation failed:\n${error.format()}`)
|
|
144
|
+
)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Configuration Options
|
|
148
|
+
|
|
149
|
+
### Programmatic Configuration
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const AppLayer = WebhookServiceLive.pipe(
|
|
153
|
+
Layer.provide(makeConfigLayer(WEBHOOK_URL, {
|
|
154
|
+
maxRetries: 5, // Maximum retry attempts
|
|
155
|
+
baseDelayMs: 1000, // Base delay for exponential backoff
|
|
156
|
+
maxDelayMs: 60000, // Maximum delay between retries
|
|
157
|
+
enableJitter: true // Add randomness to retry delays
|
|
158
|
+
})),
|
|
159
|
+
Layer.provide(HttpClientLive)
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Environment Variables
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { ConfigFromEnv, HttpClientLive, WebhookServiceLive } from '@teever/ez-hook-effect'
|
|
167
|
+
|
|
168
|
+
// Reads from DISCORD_WEBHOOK_URL, WEBHOOK_MAX_RETRIES, etc.
|
|
169
|
+
const AppLayer = WebhookServiceLive.pipe(
|
|
170
|
+
Layer.provide(ConfigFromEnv),
|
|
171
|
+
Layer.provide(HttpClientLive)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Available environment variables:
|
|
175
|
+
# DISCORD_WEBHOOK_URL - Webhook URL (required)
|
|
176
|
+
# WEBHOOK_MAX_RETRIES - Maximum retry attempts (default: 3)
|
|
177
|
+
# WEBHOOK_BASE_DELAY_MS - Base delay in ms (default: 1000)
|
|
178
|
+
# WEBHOOK_MAX_DELAY_MS - Maximum delay in ms (default: 60000)
|
|
179
|
+
# WEBHOOK_ENABLE_JITTER - Enable jitter (default: true)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Parse Webhook URL
|
|
183
|
+
|
|
184
|
+
Extract webhook ID and token from a URL:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import { parseWebhookUrl } from '@teever/ez-hook-effect'
|
|
188
|
+
|
|
189
|
+
const result = await Effect.runPromise(
|
|
190
|
+
parseWebhookUrl('https://discord.com/api/webhooks/123456789/token')
|
|
191
|
+
)
|
|
192
|
+
// result: { id: '123456789', token: 'token' }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Webhook Operations
|
|
196
|
+
|
|
197
|
+
Beyond sending messages, the library supports full webhook CRUD:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import { Effect, Layer, pipe } from 'effect'
|
|
201
|
+
import { getWebhook, modifyWebhook, deleteWebhook, validateWebhook, WebhookServiceLive, makeConfigLayer, HttpClientLive } from '@teever/ez-hook-effect'
|
|
202
|
+
|
|
203
|
+
const AppLayer = WebhookServiceLive.pipe(
|
|
204
|
+
Layer.provide(makeConfigLayer(WEBHOOK_URL)),
|
|
205
|
+
Layer.provide(HttpClientLive)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
const program = Effect.gen(function* () {
|
|
209
|
+
const service = yield* WebhookService
|
|
210
|
+
|
|
211
|
+
// Check if webhook is valid and accessible
|
|
212
|
+
const isValid = yield* service.validateWebhook()
|
|
213
|
+
|
|
214
|
+
// Get webhook information
|
|
215
|
+
const info = yield* service.getWebhook()
|
|
216
|
+
|
|
217
|
+
// Modify webhook settings
|
|
218
|
+
const modified = yield* service.modifyWebhook({
|
|
219
|
+
name: 'New Name',
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// Delete the webhook
|
|
223
|
+
const deleted = yield* service.deleteWebhook()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
Effect.runPromise(Effect.provide(program, AppLayer))
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
bun run lint # Biome lint + format
|
|
233
|
+
bun run typecheck # TypeScript type checking
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Testing
|
|
237
|
+
|
|
238
|
+
The library includes comprehensive tests for all features:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
bun test # Run all tests
|
|
242
|
+
bun test:watch # Watch mode
|
|
243
|
+
bun test:coverage # Coverage report
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Building
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
bun run build # Build the library
|
|
250
|
+
bun run build:standalone # Create standalone executable
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Examples
|
|
254
|
+
|
|
255
|
+
Check out the `examples/` directory:
|
|
256
|
+
|
|
257
|
+
- `01-basic-webhook.ts` - Send a simple message
|
|
258
|
+
- `02-rich-embeds.ts` - Build embeds with fields, colors, metadata
|
|
259
|
+
- `03-error-handling.ts` - Validation errors and recovery patterns
|
|
260
|
+
- `04-multiple-embeds.ts` - Add several embeds to one webhook
|
|
261
|
+
- `05-service-layer.ts` - Dependency injection with layers and CRUD operations
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { type ParseResult } from "effect";
|
|
2
|
+
declare const WebhookError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
3
|
+
readonly _tag: "WebhookError";
|
|
4
|
+
} & Readonly<A>;
|
|
5
|
+
/**
|
|
6
|
+
* Base error class for all webhook-related errors
|
|
7
|
+
*/
|
|
8
|
+
export declare class WebhookError extends WebhookError_base<{
|
|
9
|
+
message: string;
|
|
10
|
+
cause?: unknown;
|
|
11
|
+
}> {
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Structured validation issue with path and constraint details
|
|
15
|
+
*/
|
|
16
|
+
export interface ValidationIssue {
|
|
17
|
+
/** JSONPath-style path to the invalid field, e.g. "$.embeds[0].fields[2].name" */
|
|
18
|
+
readonly path: string;
|
|
19
|
+
/** Human-readable error message */
|
|
20
|
+
readonly message: string;
|
|
21
|
+
/** Constraint name, e.g. "MaxLength", "MinItems", "Required" */
|
|
22
|
+
readonly constraint?: string;
|
|
23
|
+
/** Expected value or constraint description */
|
|
24
|
+
readonly expected?: string;
|
|
25
|
+
/** Actual value (truncated for large values) */
|
|
26
|
+
readonly actual?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert Effect Schema ParseError to ValidationIssue array
|
|
30
|
+
*/
|
|
31
|
+
export declare const parseErrorToIssues: (error: ParseResult.ParseError) => ValidationIssue[];
|
|
32
|
+
/**
|
|
33
|
+
* Create a single ValidationIssue from simple field validation
|
|
34
|
+
*/
|
|
35
|
+
export declare const makeIssue: (field: string, message: string, options?: {
|
|
36
|
+
constraint?: string;
|
|
37
|
+
expected?: string;
|
|
38
|
+
actual?: unknown;
|
|
39
|
+
}) => ValidationIssue;
|
|
40
|
+
declare const ValidationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
41
|
+
readonly _tag: "ValidationError";
|
|
42
|
+
} & Readonly<A>;
|
|
43
|
+
/**
|
|
44
|
+
* Validation errors for webhook data with structured issues
|
|
45
|
+
*/
|
|
46
|
+
export declare class ValidationError extends ValidationError_base<{
|
|
47
|
+
/** Human-readable summary message */
|
|
48
|
+
message: string;
|
|
49
|
+
/** Structured validation issues */
|
|
50
|
+
issues: ReadonlyArray<ValidationIssue>;
|
|
51
|
+
/** Legacy: primary field that failed (for backward compatibility) */
|
|
52
|
+
field?: string;
|
|
53
|
+
/** Legacy: value that failed validation (for backward compatibility) */
|
|
54
|
+
value?: unknown;
|
|
55
|
+
}> {
|
|
56
|
+
/**
|
|
57
|
+
* Format issues as a human-readable string
|
|
58
|
+
*/
|
|
59
|
+
format(options?: {
|
|
60
|
+
maxIssues?: number;
|
|
61
|
+
}): string;
|
|
62
|
+
/**
|
|
63
|
+
* Create ValidationError from Effect Schema ParseError
|
|
64
|
+
*/
|
|
65
|
+
static fromParseError(parseError: ParseResult.ParseError, context?: {
|
|
66
|
+
field?: string;
|
|
67
|
+
value?: unknown;
|
|
68
|
+
}): ValidationError;
|
|
69
|
+
/**
|
|
70
|
+
* Create ValidationError from a single issue
|
|
71
|
+
*/
|
|
72
|
+
static fromIssue(field: string, message: string, options?: {
|
|
73
|
+
constraint?: string;
|
|
74
|
+
expected?: string;
|
|
75
|
+
actual?: unknown;
|
|
76
|
+
}): ValidationError;
|
|
77
|
+
}
|
|
78
|
+
declare const NetworkError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
79
|
+
readonly _tag: "NetworkError";
|
|
80
|
+
} & Readonly<A>;
|
|
81
|
+
/**
|
|
82
|
+
* Network-related errors
|
|
83
|
+
*/
|
|
84
|
+
export declare class NetworkError extends NetworkError_base<{
|
|
85
|
+
message: string;
|
|
86
|
+
statusCode?: number;
|
|
87
|
+
response?: unknown;
|
|
88
|
+
}> {
|
|
89
|
+
}
|
|
90
|
+
declare const RateLimitError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
91
|
+
readonly _tag: "RateLimitError";
|
|
92
|
+
} & Readonly<A>;
|
|
93
|
+
/**
|
|
94
|
+
* Rate limit errors
|
|
95
|
+
*/
|
|
96
|
+
export declare class RateLimitError extends RateLimitError_base<{
|
|
97
|
+
message: string;
|
|
98
|
+
retryAfter: number;
|
|
99
|
+
limit?: number;
|
|
100
|
+
remaining?: number;
|
|
101
|
+
reset?: Date;
|
|
102
|
+
}> {
|
|
103
|
+
}
|
|
104
|
+
declare const ConfigError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
105
|
+
readonly _tag: "ConfigError";
|
|
106
|
+
} & Readonly<A>;
|
|
107
|
+
/**
|
|
108
|
+
* Configuration errors
|
|
109
|
+
*/
|
|
110
|
+
export declare class ConfigError extends ConfigError_base<{
|
|
111
|
+
message: string;
|
|
112
|
+
parameter: string;
|
|
113
|
+
}> {
|
|
114
|
+
}
|
|
115
|
+
declare const FileError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
116
|
+
readonly _tag: "FileError";
|
|
117
|
+
} & Readonly<A>;
|
|
118
|
+
/**
|
|
119
|
+
* File-related errors
|
|
120
|
+
*/
|
|
121
|
+
export declare class FileError extends FileError_base<{
|
|
122
|
+
message: string;
|
|
123
|
+
filename?: string;
|
|
124
|
+
size?: number;
|
|
125
|
+
maxSize?: number;
|
|
126
|
+
}> {
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* HTTP response error details
|
|
130
|
+
*/
|
|
131
|
+
export interface HttpErrorResponse {
|
|
132
|
+
status: number;
|
|
133
|
+
statusText: string;
|
|
134
|
+
headers: Record<string, string>;
|
|
135
|
+
body?: unknown;
|
|
136
|
+
}
|
|
137
|
+
declare const HttpError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
138
|
+
readonly _tag: "HttpError";
|
|
139
|
+
} & Readonly<A>;
|
|
140
|
+
/**
|
|
141
|
+
* HTTP errors with detailed response information
|
|
142
|
+
*/
|
|
143
|
+
export declare class HttpError extends HttpError_base<{
|
|
144
|
+
message: string;
|
|
145
|
+
request: {
|
|
146
|
+
method: string;
|
|
147
|
+
url: string;
|
|
148
|
+
};
|
|
149
|
+
response?: HttpErrorResponse;
|
|
150
|
+
}> {
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* All possible webhook errors
|
|
154
|
+
*/
|
|
155
|
+
export type WebhookErrors = WebhookError | ValidationError | NetworkError | RateLimitError | ConfigError | FileError | HttpError;
|
|
156
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./WebhookError";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ez-hook-effect - Discord webhook library built with Effect
|
|
3
|
+
*
|
|
4
|
+
* Type-safe, composable, and resilient Discord webhook client.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Webhook, Embed, sendWebhook, WebhookServiceLive, makeConfigLayer, HttpClientLive } from "ez-hook-effect";
|
|
9
|
+
* import { Effect, Layer, pipe } from "effect";
|
|
10
|
+
*
|
|
11
|
+
* const webhook = pipe(
|
|
12
|
+
* Webhook.make,
|
|
13
|
+
* Webhook.setContent("Hello from Effect!"),
|
|
14
|
+
* Webhook.build
|
|
15
|
+
* );
|
|
16
|
+
*
|
|
17
|
+
* const layer = WebhookServiceLive.pipe(
|
|
18
|
+
* Layer.provide(makeConfigLayer("https://discord.com/api/webhooks/...")),
|
|
19
|
+
* Layer.provide(HttpClientLive)
|
|
20
|
+
* );
|
|
21
|
+
*
|
|
22
|
+
* Effect.gen(function* () {
|
|
23
|
+
* const msg = yield* webhook;
|
|
24
|
+
* yield* sendWebhook(msg);
|
|
25
|
+
* }).pipe(Effect.provide(layer), Effect.runPromise);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
/** Error types for handling webhook failures */
|
|
31
|
+
export { ConfigError, FileError, HttpError, type HttpErrorResponse, makeIssue, NetworkError, parseErrorToIssues, RateLimitError, ValidationError, type ValidationIssue, WebhookError, type WebhookErrors, } from "./errors";
|
|
32
|
+
/**
|
|
33
|
+
* Configuration service and layers.
|
|
34
|
+
* Use makeConfigLayer for programmatic config or ConfigFromEnv for environment variables.
|
|
35
|
+
*/
|
|
36
|
+
export { Config, ConfigFromEnv, makeConfig, makeConfigLayer, parseWebhookUrl, type WebhookConfig, } from "./layers/Config";
|
|
37
|
+
/**
|
|
38
|
+
* HTTP client service and layers.
|
|
39
|
+
* Provides fetch-based HTTP client with retry and rate limit support.
|
|
40
|
+
*/
|
|
41
|
+
export { createRetrySchedule, defaultRetryConfig, FetchHttpClient, HttpClient, HttpClientLive, type HttpRequest, type HttpResponse, makeHttpClientLayer, parseRateLimitHeaders, type RateLimitInfo, type RetryConfig, } from "./layers/HttpClient";
|
|
42
|
+
/**
|
|
43
|
+
* Embed builder namespace.
|
|
44
|
+
* Pipe-first API for building Discord embed objects.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { Embed } from "ez-hook-effect";
|
|
49
|
+
* import { pipe } from "effect";
|
|
50
|
+
*
|
|
51
|
+
* const embed = pipe(
|
|
52
|
+
* Embed.make,
|
|
53
|
+
* Embed.setTitle("Hello"),
|
|
54
|
+
* Embed.setDescription("World"),
|
|
55
|
+
* Embed.setColor("#5865F2"),
|
|
56
|
+
* Embed.build
|
|
57
|
+
* );
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export * as Embed from "./pipes/Embed";
|
|
61
|
+
/**
|
|
62
|
+
* Webhook builder namespace.
|
|
63
|
+
* Pipe-first API for building Discord webhook payloads.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* import { Webhook } from "ez-hook-effect";
|
|
68
|
+
* import { pipe } from "effect";
|
|
69
|
+
*
|
|
70
|
+
* const webhook = pipe(
|
|
71
|
+
* Webhook.make,
|
|
72
|
+
* Webhook.setUsername("Bot"),
|
|
73
|
+
* Webhook.setContent("Hello!"),
|
|
74
|
+
* Webhook.build
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export * as Webhook from "./pipes/Webhook";
|
|
79
|
+
/** Schema types for Discord webhook payloads */
|
|
80
|
+
export * from "./schemas";
|
|
81
|
+
/**
|
|
82
|
+
* Webhook service and module-level accessors.
|
|
83
|
+
* Use these with Effect.flatMap or Effect.gen for sending webhooks.
|
|
84
|
+
*/
|
|
85
|
+
export { deleteWebhook, getWebhook, makeWebhookService, modifyWebhook, sendWebhook, validateWebhook, WebhookService, WebhookServiceLive, } from "./services/WebhookService";
|