@teever/ez-hook-effect 0.5.1 → 0.5.19
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/dist/errors/WebhookError.d.ts +7 -7
- package/dist/index.js +17 -965
- package/dist/index.js.map +5 -13
- package/dist/layers/Config.d.ts +2 -2
- package/dist/layers/Config.js +2 -2
- package/dist/layers/Config.js.map +1 -1
- package/dist/layers/HttpClient.d.ts +3 -3
- package/dist/layers/HttpClient.js +2 -2
- package/dist/layers/HttpClient.js.map +1 -1
- package/dist/schemas/Embed.d.ts +8 -8
- package/dist/schemas/Field.d.ts +1 -1
- package/dist/schemas/Webhook.d.ts +3 -3
- package/dist/services/WebhookService.d.ts +2 -2
- package/dist/services/WebhookService.js +2 -2
- package/dist/services/WebhookService.js.map +1 -1
- package/package.json +56 -56
package/dist/index.js
CHANGED
|
@@ -1,615 +1,8 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// src/errors/WebhookError.ts
|
|
13
|
-
import { Data } from "effect";
|
|
14
|
-
|
|
15
|
-
class WebhookError extends Data.TaggedError("WebhookError") {
|
|
16
|
-
}
|
|
17
|
-
var extractConstraint = (message) => {
|
|
18
|
-
if (message.includes("at most") || message.includes("maximum"))
|
|
19
|
-
return "MaxLength";
|
|
20
|
-
if (message.includes("at least") || message.includes("minimum"))
|
|
21
|
-
return "MinLength";
|
|
22
|
-
if (message.includes("required") || message.includes("missing"))
|
|
23
|
-
return "Required";
|
|
24
|
-
if (message.includes("expected") && message.includes("to be"))
|
|
25
|
-
return "Type";
|
|
26
|
-
if (message.includes("URL") || message.includes("url"))
|
|
27
|
-
return "URL";
|
|
28
|
-
if (message.includes("integer") || message.includes("number"))
|
|
29
|
-
return "Number";
|
|
30
|
-
return;
|
|
31
|
-
};
|
|
32
|
-
var truncateValue = (value, maxLength = 100) => {
|
|
33
|
-
if (typeof value === "string" && value.length > maxLength) {
|
|
34
|
-
return `${value.slice(0, maxLength)}... (${value.length} chars)`;
|
|
35
|
-
}
|
|
36
|
-
if (Array.isArray(value) && value.length > 5) {
|
|
37
|
-
return `[Array with ${value.length} items]`;
|
|
38
|
-
}
|
|
39
|
-
if (typeof value === "object" && value !== null) {
|
|
40
|
-
const keys = Object.keys(value);
|
|
41
|
-
if (keys.length > 5) {
|
|
42
|
-
return `{Object with ${keys.length} keys}`;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return value;
|
|
46
|
-
};
|
|
47
|
-
var pathToString = (path) => {
|
|
48
|
-
if (path.length === 0)
|
|
49
|
-
return "$";
|
|
50
|
-
return path.reduce((acc, segment) => {
|
|
51
|
-
if (typeof segment === "number") {
|
|
52
|
-
return `${acc}[${segment}]`;
|
|
53
|
-
}
|
|
54
|
-
return `${acc}.${String(segment)}`;
|
|
55
|
-
}, "$");
|
|
56
|
-
};
|
|
57
|
-
var toArray = (value) => Array.isArray(value) ? value : [value];
|
|
58
|
-
var getIssueProp = (issue, key) => issue[key];
|
|
59
|
-
var extractIssuesFromParseIssue = (issue, currentPath = []) => {
|
|
60
|
-
const issues = [];
|
|
61
|
-
switch (issue._tag) {
|
|
62
|
-
case "InvalidType": {
|
|
63
|
-
const ast = getIssueProp(issue, "ast");
|
|
64
|
-
const message = getIssueProp(issue, "message") ?? `Expected ${ast?._tag}`;
|
|
65
|
-
issues.push({
|
|
66
|
-
path: pathToString(currentPath),
|
|
67
|
-
message,
|
|
68
|
-
constraint: extractConstraint(message) ?? "Type",
|
|
69
|
-
actual: truncateValue(getIssueProp(issue, "actual"))
|
|
70
|
-
});
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
case "Forbidden": {
|
|
74
|
-
issues.push({
|
|
75
|
-
path: pathToString(currentPath),
|
|
76
|
-
message: getIssueProp(issue, "message") ?? "Value is forbidden",
|
|
77
|
-
constraint: "Forbidden"
|
|
78
|
-
});
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
case "MissingKey": {
|
|
82
|
-
issues.push({
|
|
83
|
-
path: pathToString(currentPath),
|
|
84
|
-
message: "Required field is missing",
|
|
85
|
-
constraint: "Required"
|
|
86
|
-
});
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
case "UnexpectedKey": {
|
|
90
|
-
issues.push({
|
|
91
|
-
path: pathToString(currentPath),
|
|
92
|
-
message: getIssueProp(issue, "message") ?? "Unexpected field",
|
|
93
|
-
constraint: "Unexpected",
|
|
94
|
-
actual: truncateValue(getIssueProp(issue, "actual"))
|
|
95
|
-
});
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
case "Pointer": {
|
|
99
|
-
const pathSegments = getIssueProp(issue, "path") ?? [];
|
|
100
|
-
const newPath = [...currentPath, ...pathSegments];
|
|
101
|
-
const nested = getIssueProp(issue, "issue");
|
|
102
|
-
if (nested)
|
|
103
|
-
issues.push(...extractIssuesFromParseIssue(nested, newPath));
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case "Composite": {
|
|
107
|
-
const rawIssues = getIssueProp(issue, "issues");
|
|
108
|
-
const subIssues = rawIssues ? toArray(rawIssues) : [];
|
|
109
|
-
for (const subIssue of subIssues) {
|
|
110
|
-
issues.push(...extractIssuesFromParseIssue(subIssue, currentPath));
|
|
111
|
-
}
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
case "Filter":
|
|
115
|
-
case "Encoding": {
|
|
116
|
-
const nested = getIssueProp(issue, "issue");
|
|
117
|
-
if (nested)
|
|
118
|
-
issues.push(...extractIssuesFromParseIssue(nested, currentPath));
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
case "InvalidValue":
|
|
122
|
-
case "OneOf":
|
|
123
|
-
case "AnyOf": {
|
|
124
|
-
issues.push({
|
|
125
|
-
path: pathToString(currentPath),
|
|
126
|
-
message: getIssueProp(issue, "message") ?? "Invalid value",
|
|
127
|
-
constraint: "Value",
|
|
128
|
-
actual: truncateValue(getIssueProp(issue, "actual"))
|
|
129
|
-
});
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
default: {
|
|
133
|
-
const message = getIssueProp(issue, "message") ?? "Invalid value";
|
|
134
|
-
const constraint = extractConstraint(message);
|
|
135
|
-
issues.push({
|
|
136
|
-
path: pathToString(currentPath),
|
|
137
|
-
message,
|
|
138
|
-
...constraint !== undefined && { constraint },
|
|
139
|
-
...getIssueProp(issue, "actual") !== undefined && {
|
|
140
|
-
actual: truncateValue(getIssueProp(issue, "actual"))
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
return issues;
|
|
147
|
-
};
|
|
148
|
-
var parseErrorToIssues = (error) => {
|
|
149
|
-
return extractIssuesFromParseIssue(error.issue);
|
|
150
|
-
};
|
|
151
|
-
var makeIssue = (field, message, options) => {
|
|
152
|
-
const constraint = options?.constraint ?? extractConstraint(message);
|
|
153
|
-
return {
|
|
154
|
-
path: field.startsWith("$") ? field : `$.${field}`,
|
|
155
|
-
message,
|
|
156
|
-
...constraint !== undefined && { constraint },
|
|
157
|
-
...options?.expected !== undefined && { expected: options.expected },
|
|
158
|
-
...options?.actual !== undefined && {
|
|
159
|
-
actual: truncateValue(options.actual)
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
class ValidationError extends Data.TaggedError("ValidationError") {
|
|
165
|
-
format(options) {
|
|
166
|
-
const maxIssues = options?.maxIssues ?? 10;
|
|
167
|
-
const displayIssues = this.issues.slice(0, maxIssues);
|
|
168
|
-
const lines = displayIssues.map((issue) => {
|
|
169
|
-
let line = ` - ${issue.path}: ${issue.message}`;
|
|
170
|
-
if (issue.constraint) {
|
|
171
|
-
line += ` [${issue.constraint}]`;
|
|
172
|
-
}
|
|
173
|
-
return line;
|
|
174
|
-
});
|
|
175
|
-
if (this.issues.length > maxIssues) {
|
|
176
|
-
lines.push(` ... and ${this.issues.length - maxIssues} more issues`);
|
|
177
|
-
}
|
|
178
|
-
return `${this.message}
|
|
179
|
-
${lines.join(`
|
|
180
|
-
`)}`;
|
|
181
|
-
}
|
|
182
|
-
static fromParseError(parseError, context) {
|
|
183
|
-
const issues = parseErrorToIssues(parseError);
|
|
184
|
-
const firstIssue = issues[0];
|
|
185
|
-
const message = issues.length === 1 && firstIssue ? firstIssue.message : `Validation failed (${issues.length} issues)`;
|
|
186
|
-
return new ValidationError({
|
|
187
|
-
message,
|
|
188
|
-
issues,
|
|
189
|
-
...context?.field !== undefined && { field: context.field },
|
|
190
|
-
...context?.value !== undefined && { value: context.value }
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
static fromIssue(field, message, options) {
|
|
194
|
-
const issue = makeIssue(field, message, options);
|
|
195
|
-
return new ValidationError({
|
|
196
|
-
message,
|
|
197
|
-
issues: [issue],
|
|
198
|
-
field,
|
|
199
|
-
value: options?.actual
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
class NetworkError extends Data.TaggedError("NetworkError") {
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
class RateLimitError extends Data.TaggedError("RateLimitError") {
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
class ConfigError extends Data.TaggedError("ConfigError") {
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
class FileError extends Data.TaggedError("FileError") {
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
class HttpError extends Data.TaggedError("HttpError") {
|
|
217
|
-
}
|
|
218
|
-
var formatWebhookError = (error, options) => {
|
|
219
|
-
switch (error._tag) {
|
|
220
|
-
case "ValidationError":
|
|
221
|
-
return error.format(options);
|
|
222
|
-
case "RateLimitError":
|
|
223
|
-
return `${error.message} (retryAfter=${error.retryAfter}ms)`;
|
|
224
|
-
case "HttpError": {
|
|
225
|
-
const status = error.response?.status;
|
|
226
|
-
return status ? `${error.message} (status=${status})` : error.message;
|
|
227
|
-
}
|
|
228
|
-
default:
|
|
229
|
-
return error.message;
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
var webhookErrorToLogObject = (error) => {
|
|
233
|
-
switch (error._tag) {
|
|
234
|
-
case "ValidationError":
|
|
235
|
-
return {
|
|
236
|
-
tag: error._tag,
|
|
237
|
-
message: error.message,
|
|
238
|
-
issues: error.issues,
|
|
239
|
-
field: error.field,
|
|
240
|
-
value: error.value
|
|
241
|
-
};
|
|
242
|
-
case "RateLimitError":
|
|
243
|
-
return {
|
|
244
|
-
tag: error._tag,
|
|
245
|
-
message: error.message,
|
|
246
|
-
retryAfter: error.retryAfter,
|
|
247
|
-
limit: error.limit,
|
|
248
|
-
remaining: error.remaining,
|
|
249
|
-
reset: error.reset
|
|
250
|
-
};
|
|
251
|
-
case "HttpError":
|
|
252
|
-
return {
|
|
253
|
-
tag: error._tag,
|
|
254
|
-
message: error.message,
|
|
255
|
-
request: error.request,
|
|
256
|
-
response: error.response
|
|
257
|
-
};
|
|
258
|
-
case "NetworkError":
|
|
259
|
-
return {
|
|
260
|
-
tag: error._tag,
|
|
261
|
-
message: error.message,
|
|
262
|
-
statusCode: error.statusCode,
|
|
263
|
-
response: error.response
|
|
264
|
-
};
|
|
265
|
-
case "ConfigError":
|
|
266
|
-
return {
|
|
267
|
-
tag: error._tag,
|
|
268
|
-
message: error.message,
|
|
269
|
-
parameter: error.parameter
|
|
270
|
-
};
|
|
271
|
-
case "FileError":
|
|
272
|
-
return {
|
|
273
|
-
tag: error._tag,
|
|
274
|
-
message: error.message,
|
|
275
|
-
filename: error.filename,
|
|
276
|
-
size: error.size,
|
|
277
|
-
maxSize: error.maxSize
|
|
278
|
-
};
|
|
279
|
-
default:
|
|
280
|
-
return {
|
|
281
|
-
tag: error._tag,
|
|
282
|
-
message: error.message,
|
|
283
|
-
cause: error.cause
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
// src/layers/Config.ts
|
|
288
|
-
import { Effect, Layer, ServiceMap } from "effect";
|
|
289
|
-
|
|
290
|
-
// src/schemas/Discord.ts
|
|
291
|
-
import { Schema } from "effect";
|
|
292
|
-
var DISCORD_WEBHOOK_REGEX = /^https:\/\/(?:(?:canary|ptb)\.)?discord(?:app)?\.com\/api(?:\/v\d+)?\/webhooks\/(?<id>\d+)\/(?<token>[\w-]+)\/?$/;
|
|
293
|
-
var DiscordWebhookUrl = Schema.String.check(Schema.isPattern(DISCORD_WEBHOOK_REGEX, {
|
|
294
|
-
message: "Invalid Discord webhook URL"
|
|
295
|
-
}));
|
|
296
|
-
|
|
297
|
-
// src/layers/Config.ts
|
|
298
|
-
class Config extends ServiceMap.Service()("Config") {
|
|
299
|
-
}
|
|
300
|
-
var validateWebhookUrl = (url) => {
|
|
301
|
-
if (!url || url.trim() === "") {
|
|
302
|
-
return Effect.fail(new ConfigError({
|
|
303
|
-
message: `Webhook URL is empty. Please provide a valid Discord webhook URL.
|
|
304
|
-
` + `Format: https://discord.com/api/webhooks/{webhook_id}/{webhook_token}
|
|
305
|
-
` + "You can create a webhook in Discord: Server Settings > Integrations > Webhooks",
|
|
306
|
-
parameter: "webhookUrl"
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
if (!url.startsWith("https://")) {
|
|
310
|
-
return Effect.fail(new ConfigError({
|
|
311
|
-
message: `Webhook URL must use HTTPS protocol.
|
|
312
|
-
` + `Received: ${url.substring(0, 50)}${url.length > 50 ? "..." : ""}
|
|
313
|
-
` + "Expected format: https://discord.com/api/webhooks/{webhook_id}/{webhook_token}",
|
|
314
|
-
parameter: "webhookUrl"
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
if (!DISCORD_WEBHOOK_REGEX.test(url)) {
|
|
318
|
-
const isDiscordDomain = url.includes("discord.com") || url.includes("discordapp.com");
|
|
319
|
-
const hasWebhooksPath = url.includes("/webhooks/");
|
|
320
|
-
let hint = "";
|
|
321
|
-
if (!isDiscordDomain) {
|
|
322
|
-
hint = "URL must be from discord.com or discordapp.com domain.";
|
|
323
|
-
} else if (!hasWebhooksPath) {
|
|
324
|
-
hint = "URL must include /api/webhooks/ path.";
|
|
325
|
-
} else {
|
|
326
|
-
hint = "URL must include both webhook ID (numeric) and token after /webhooks/.";
|
|
327
|
-
}
|
|
328
|
-
return Effect.fail(new ConfigError({
|
|
329
|
-
message: `Invalid Discord webhook URL format.
|
|
330
|
-
` + `${hint}
|
|
331
|
-
` + `Received: ${url.substring(0, 80)}${url.length > 80 ? "..." : ""}
|
|
332
|
-
` + `Expected format: https://discord.com/api/webhooks/{webhook_id}/{webhook_token}`,
|
|
333
|
-
parameter: "webhookUrl"
|
|
334
|
-
}));
|
|
335
|
-
}
|
|
336
|
-
return Effect.succeed(url);
|
|
337
|
-
};
|
|
338
|
-
var validateRetryOptions = (options) => {
|
|
339
|
-
const errors = [];
|
|
340
|
-
if (options?.maxRetries !== undefined) {
|
|
341
|
-
if (!Number.isInteger(options.maxRetries) || options.maxRetries < 0) {
|
|
342
|
-
errors.push(`maxRetries must be a non-negative integer (received: ${options.maxRetries})`);
|
|
343
|
-
}
|
|
344
|
-
if (options.maxRetries > 10) {
|
|
345
|
-
errors.push(`maxRetries should not exceed 10 to avoid excessive retries (received: ${options.maxRetries})`);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
if (options?.baseDelayMs !== undefined) {
|
|
349
|
-
if (!Number.isInteger(options.baseDelayMs) || options.baseDelayMs < 0) {
|
|
350
|
-
errors.push(`baseDelayMs must be a non-negative integer (received: ${options.baseDelayMs})`);
|
|
351
|
-
}
|
|
352
|
-
if (options.baseDelayMs > 60000) {
|
|
353
|
-
errors.push(`baseDelayMs should not exceed 60000ms (received: ${options.baseDelayMs})`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (options?.maxDelayMs !== undefined) {
|
|
357
|
-
if (!Number.isInteger(options.maxDelayMs) || options.maxDelayMs < 0) {
|
|
358
|
-
errors.push(`maxDelayMs must be a non-negative integer (received: ${options.maxDelayMs})`);
|
|
359
|
-
}
|
|
360
|
-
if (options.baseDelayMs !== undefined && options.maxDelayMs < options.baseDelayMs) {
|
|
361
|
-
errors.push(`maxDelayMs (${options.maxDelayMs}) must be >= baseDelayMs (${options.baseDelayMs})`);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (errors.length > 0) {
|
|
365
|
-
return Effect.fail(new ConfigError({
|
|
366
|
-
message: `Invalid retry configuration:
|
|
367
|
-
${errors.map((e) => ` - ${e}`).join(`
|
|
368
|
-
`)}`,
|
|
369
|
-
parameter: "retryConfig"
|
|
370
|
-
}));
|
|
371
|
-
}
|
|
372
|
-
return Effect.succeed(options);
|
|
373
|
-
};
|
|
374
|
-
var makeConfig = (webhookUrl, options) => Effect.gen(function* () {
|
|
375
|
-
const validatedUrl = yield* validateWebhookUrl(webhookUrl);
|
|
376
|
-
yield* validateRetryOptions(options);
|
|
377
|
-
return {
|
|
378
|
-
webhook: {
|
|
379
|
-
webhookUrl: validatedUrl,
|
|
380
|
-
maxRetries: options?.maxRetries ?? 3,
|
|
381
|
-
baseDelayMs: options?.baseDelayMs ?? 1000,
|
|
382
|
-
maxDelayMs: options?.maxDelayMs ?? 60000,
|
|
383
|
-
enableJitter: options?.enableJitter ?? true
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
});
|
|
387
|
-
var makeConfigLayer = (webhookUrl, options) => Layer.effect(Config, makeConfig(webhookUrl, options));
|
|
388
|
-
var ConfigFromEnv = Layer.effect(Config, Effect.gen(function* () {
|
|
389
|
-
const rawWebhookUrl = yield* Effect.sync(() => process.env.DISCORD_WEBHOOK_URL ?? "");
|
|
390
|
-
if (!rawWebhookUrl) {
|
|
391
|
-
return yield* Effect.fail(new ConfigError({
|
|
392
|
-
message: `DISCORD_WEBHOOK_URL environment variable not set.
|
|
393
|
-
` + `Please set DISCORD_WEBHOOK_URL in your environment or .env file.
|
|
394
|
-
` + "See .env.example for all available configuration options.",
|
|
395
|
-
parameter: "DISCORD_WEBHOOK_URL"
|
|
396
|
-
}));
|
|
397
|
-
}
|
|
398
|
-
const webhookUrl = yield* validateWebhookUrl(rawWebhookUrl);
|
|
399
|
-
const maxRetries = yield* Effect.sync(() => {
|
|
400
|
-
const v = process.env.WEBHOOK_MAX_RETRIES;
|
|
401
|
-
return v ? Number.parseInt(v, 10) : 3;
|
|
402
|
-
});
|
|
403
|
-
const baseDelayMs = yield* Effect.sync(() => {
|
|
404
|
-
const v = process.env.WEBHOOK_BASE_DELAY_MS;
|
|
405
|
-
return v ? Number.parseInt(v, 10) : 1000;
|
|
406
|
-
});
|
|
407
|
-
const maxDelayMs = yield* Effect.sync(() => {
|
|
408
|
-
const v = process.env.WEBHOOK_MAX_DELAY_MS;
|
|
409
|
-
return v ? Number.parseInt(v, 10) : 60000;
|
|
410
|
-
});
|
|
411
|
-
const enableJitter = yield* Effect.sync(() => {
|
|
412
|
-
const v = process.env.WEBHOOK_ENABLE_JITTER;
|
|
413
|
-
return v ? v === "true" : true;
|
|
414
|
-
});
|
|
415
|
-
yield* validateRetryOptions({ maxRetries, baseDelayMs, maxDelayMs });
|
|
416
|
-
return {
|
|
417
|
-
webhook: {
|
|
418
|
-
webhookUrl,
|
|
419
|
-
maxRetries,
|
|
420
|
-
baseDelayMs,
|
|
421
|
-
maxDelayMs,
|
|
422
|
-
enableJitter
|
|
423
|
-
}
|
|
424
|
-
};
|
|
425
|
-
}));
|
|
426
|
-
var parseWebhookUrl = (url) => Effect.gen(function* () {
|
|
427
|
-
const match = url.match(DISCORD_WEBHOOK_REGEX);
|
|
428
|
-
if (!match?.groups?.id || !match.groups.token) {
|
|
429
|
-
return yield* Effect.fail(new ConfigError({
|
|
430
|
-
message: "Invalid webhook URL format",
|
|
431
|
-
parameter: "webhookUrl"
|
|
432
|
-
}));
|
|
433
|
-
}
|
|
434
|
-
const { id, token } = match.groups;
|
|
435
|
-
return { id, token };
|
|
436
|
-
});
|
|
437
|
-
// src/layers/HttpClient.ts
|
|
438
|
-
import { Duration, Effect as Effect2, Layer as Layer2, Schedule, ServiceMap as ServiceMap2 } from "effect";
|
|
439
|
-
class HttpClient extends ServiceMap2.Service()("HttpClient") {
|
|
440
|
-
}
|
|
441
|
-
var parseRateLimitHeaders = (headers) => {
|
|
442
|
-
const retryAfterHeader = headers["retry-after"];
|
|
443
|
-
const rateLimitRemaining = headers["x-ratelimit-remaining"];
|
|
444
|
-
if (!retryAfterHeader && rateLimitRemaining !== "0") {
|
|
445
|
-
return null;
|
|
446
|
-
}
|
|
447
|
-
let retryAfter = 0;
|
|
448
|
-
if (retryAfterHeader) {
|
|
449
|
-
const parsed = Number.parseFloat(retryAfterHeader);
|
|
450
|
-
if (!Number.isNaN(parsed)) {
|
|
451
|
-
retryAfter = Math.ceil(parsed * 1000);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
const limit = headers["x-ratelimit-limit"];
|
|
455
|
-
const remaining = headers["x-ratelimit-remaining"];
|
|
456
|
-
const resetHeader = headers["x-ratelimit-reset"];
|
|
457
|
-
const resetAfterHeader = headers["x-ratelimit-reset-after"];
|
|
458
|
-
const globalHeader = headers["x-ratelimit-global"];
|
|
459
|
-
let reset;
|
|
460
|
-
if (resetHeader) {
|
|
461
|
-
const resetTimestamp = Number.parseFloat(resetHeader);
|
|
462
|
-
if (!Number.isNaN(resetTimestamp)) {
|
|
463
|
-
reset = new Date(resetTimestamp * 1000);
|
|
464
|
-
}
|
|
465
|
-
} else if (resetAfterHeader) {
|
|
466
|
-
const resetAfter = Number.parseFloat(resetAfterHeader);
|
|
467
|
-
if (!Number.isNaN(resetAfter)) {
|
|
468
|
-
reset = new Date(Date.now() + resetAfter * 1000);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
if (retryAfter === 0 && reset) {
|
|
472
|
-
retryAfter = Math.max(0, reset.getTime() - Date.now());
|
|
473
|
-
}
|
|
474
|
-
if (retryAfter === 0) {
|
|
475
|
-
retryAfter = 1000;
|
|
476
|
-
}
|
|
477
|
-
return {
|
|
478
|
-
retryAfter,
|
|
479
|
-
...limit && { limit: Number.parseInt(limit, 10) },
|
|
480
|
-
...remaining && { remaining: Number.parseInt(remaining, 10) },
|
|
481
|
-
...reset && { reset },
|
|
482
|
-
global: globalHeader === "true"
|
|
483
|
-
};
|
|
484
|
-
};
|
|
485
|
-
var defaultRetryConfig = {
|
|
486
|
-
maxRetries: 3,
|
|
487
|
-
baseDelay: Duration.seconds(1),
|
|
488
|
-
maxDelay: Duration.seconds(60),
|
|
489
|
-
jitter: true
|
|
490
|
-
};
|
|
491
|
-
var createRetrySchedule = (config) => {
|
|
492
|
-
const capDelay = (delay) => Duration.isLessThanOrEqualTo(delay, config.maxDelay) ? delay : config.maxDelay;
|
|
493
|
-
let policy = Schedule.exponential(config.baseDelay, 2);
|
|
494
|
-
if (config.jitter) {
|
|
495
|
-
policy = policy.pipe(Schedule.modifyDelay((_, delay) => Effect2.sync(() => {
|
|
496
|
-
const millis = Duration.toMillis(delay);
|
|
497
|
-
return Duration.millis(millis + Math.random() * millis);
|
|
498
|
-
})));
|
|
499
|
-
}
|
|
500
|
-
policy = policy.pipe(Schedule.modifyDelay((_, delay) => Effect2.succeed(capDelay(delay))));
|
|
501
|
-
const capped = Schedule.both(policy, Schedule.recurs(config.maxRetries));
|
|
502
|
-
return capped.pipe(Schedule.map(([delay]) => Effect2.succeed(delay)));
|
|
503
|
-
};
|
|
504
|
-
var FetchHttpClient = (retryConfig = defaultRetryConfig) => ({
|
|
505
|
-
retryConfig,
|
|
506
|
-
request: (request) => Effect2.gen(function* () {
|
|
507
|
-
const controller = new AbortController;
|
|
508
|
-
const timeout = request.timeout ? setTimeout(() => controller.abort(), Duration.toMillis(request.timeout)) : undefined;
|
|
509
|
-
const requestHeaders = new Headers({
|
|
510
|
-
"Content-Type": "application/json"
|
|
511
|
-
});
|
|
512
|
-
for (const [key, value] of Object.entries(request.headers ?? {})) {
|
|
513
|
-
requestHeaders.set(key, value);
|
|
514
|
-
}
|
|
515
|
-
const response = yield* Effect2.tryPromise({
|
|
516
|
-
try: () => fetch(request.url, {
|
|
517
|
-
method: request.method,
|
|
518
|
-
headers: requestHeaders,
|
|
519
|
-
body: request.body ? JSON.stringify(request.body) : undefined,
|
|
520
|
-
signal: controller.signal
|
|
521
|
-
}),
|
|
522
|
-
catch: (error) => new NetworkError({
|
|
523
|
-
message: `Network request failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
524
|
-
statusCode: 0
|
|
525
|
-
})
|
|
526
|
-
});
|
|
527
|
-
if (timeout)
|
|
528
|
-
clearTimeout(timeout);
|
|
529
|
-
const rawTextOrFn = yield* Effect2.tryPromise({
|
|
530
|
-
try: () => response.text(),
|
|
531
|
-
catch: () => new NetworkError({ message: "Failed to read response body" })
|
|
532
|
-
});
|
|
533
|
-
const text = typeof rawTextOrFn === "function" ? yield* Effect2.tryPromise({
|
|
534
|
-
try: () => rawTextOrFn(),
|
|
535
|
-
catch: () => new NetworkError({ message: "Failed to read response body" })
|
|
536
|
-
}) : String(rawTextOrFn ?? "");
|
|
537
|
-
const headers = {};
|
|
538
|
-
response.headers.forEach((value, key) => {
|
|
539
|
-
headers[key.toLowerCase()] = value;
|
|
540
|
-
});
|
|
541
|
-
let body = text;
|
|
542
|
-
if (headers["content-type"]?.includes("application/json") && text) {
|
|
543
|
-
try {
|
|
544
|
-
body = JSON.parse(text);
|
|
545
|
-
} catch {}
|
|
546
|
-
}
|
|
547
|
-
const httpResponse = {
|
|
548
|
-
status: response.status,
|
|
549
|
-
statusText: response.statusText,
|
|
550
|
-
headers,
|
|
551
|
-
body,
|
|
552
|
-
text
|
|
553
|
-
};
|
|
554
|
-
if (!response.ok) {
|
|
555
|
-
if (response.status === 429) {
|
|
556
|
-
const rateLimitInfo = parseRateLimitHeaders(headers);
|
|
557
|
-
return yield* Effect2.fail(new RateLimitError({
|
|
558
|
-
message: `Rate limited: retry after ${rateLimitInfo?.retryAfter ?? 1000}ms`,
|
|
559
|
-
retryAfter: rateLimitInfo?.retryAfter ?? 1000,
|
|
560
|
-
...rateLimitInfo?.limit !== undefined && {
|
|
561
|
-
limit: rateLimitInfo.limit
|
|
562
|
-
},
|
|
563
|
-
...rateLimitInfo?.remaining !== undefined && {
|
|
564
|
-
remaining: rateLimitInfo.remaining
|
|
565
|
-
},
|
|
566
|
-
...rateLimitInfo?.reset !== undefined && {
|
|
567
|
-
reset: rateLimitInfo.reset
|
|
568
|
-
}
|
|
569
|
-
}));
|
|
570
|
-
}
|
|
571
|
-
return yield* Effect2.fail(new HttpError({
|
|
572
|
-
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
573
|
-
request: {
|
|
574
|
-
method: request.method,
|
|
575
|
-
url: request.url
|
|
576
|
-
},
|
|
577
|
-
response: {
|
|
578
|
-
status: response.status,
|
|
579
|
-
statusText: response.statusText,
|
|
580
|
-
headers,
|
|
581
|
-
body
|
|
582
|
-
}
|
|
583
|
-
}));
|
|
584
|
-
}
|
|
585
|
-
return httpResponse;
|
|
586
|
-
})
|
|
587
|
-
});
|
|
588
|
-
var HttpClientLive = Layer2.succeed(HttpClient, FetchHttpClient());
|
|
589
|
-
var makeHttpClientLayer = (retryConfig) => Layer2.succeed(HttpClient, FetchHttpClient({ ...defaultRetryConfig, ...retryConfig }));
|
|
590
|
-
var makeTestHttpClient = (resolver, retryConfig = defaultRetryConfig) => Layer2.succeed(HttpClient, {
|
|
591
|
-
request: resolver,
|
|
592
|
-
retryConfig
|
|
593
|
-
});
|
|
594
|
-
// src/layers/Default.ts
|
|
595
|
-
import { Layer as Layer4 } from "effect";
|
|
596
|
-
|
|
597
|
-
// src/services/WebhookService.ts
|
|
598
|
-
import {
|
|
599
|
-
Duration as Duration2,
|
|
600
|
-
Effect as Effect3,
|
|
601
|
-
Layer as Layer3,
|
|
602
|
-
pipe,
|
|
603
|
-
Schedule as Schedule2,
|
|
604
|
-
Schema as Schema6,
|
|
605
|
-
ServiceMap as ServiceMap3
|
|
606
|
-
} from "effect";
|
|
607
|
-
|
|
608
1
|
// src/schemas/Common.ts
|
|
609
|
-
import { Schema
|
|
610
|
-
var maxLength = (max) =>
|
|
611
|
-
var positiveInt =
|
|
612
|
-
var UrlString =
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
var maxLength = (max) => Schema.String.check(Schema.isMaxLength(max));
|
|
4
|
+
var positiveInt = Schema.Number.check(Schema.isInt(), Schema.isGreaterThan(0));
|
|
5
|
+
var UrlString = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => {
|
|
613
6
|
try {
|
|
614
7
|
new URL(value);
|
|
615
8
|
return true;
|
|
@@ -617,12 +10,19 @@ var UrlString = Schema2.String.pipe(Schema2.check(Schema2.makeFilter((value) =>
|
|
|
617
10
|
return "Invalid URL format";
|
|
618
11
|
}
|
|
619
12
|
})));
|
|
620
|
-
var ISO8601Timestamp =
|
|
13
|
+
var ISO8601Timestamp = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => {
|
|
621
14
|
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
|
|
622
15
|
return iso8601Regex.test(value) ? true : "Invalid ISO8601 timestamp format";
|
|
623
16
|
})));
|
|
624
|
-
var ColorCode =
|
|
625
|
-
var Uint8ArraySchema =
|
|
17
|
+
var ColorCode = Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(16777215));
|
|
18
|
+
var Uint8ArraySchema = Schema.instanceOf(Uint8Array);
|
|
19
|
+
|
|
20
|
+
// src/schemas/Discord.ts
|
|
21
|
+
import { Schema as Schema2 } from "effect";
|
|
22
|
+
var DISCORD_WEBHOOK_REGEX = /^https:\/\/(?:(?:canary|ptb)\.)?discord(?:app)?\.com\/api(?:\/v\d+)?\/webhooks\/(?<id>\d+)\/(?<token>[\w-]+)\/?$/;
|
|
23
|
+
var DiscordWebhookUrl = Schema2.String.check(Schema2.isPattern(DISCORD_WEBHOOK_REGEX, {
|
|
24
|
+
message: "Invalid Discord webhook URL"
|
|
25
|
+
}));
|
|
626
26
|
|
|
627
27
|
// src/schemas/Embed.ts
|
|
628
28
|
import { Schema as Schema4 } from "effect";
|
|
@@ -768,354 +168,6 @@ class WebhookResponse extends Schema5.Class("WebhookResponse")({
|
|
|
768
168
|
url: Schema5.optionalKey(UrlString)
|
|
769
169
|
}) {
|
|
770
170
|
}
|
|
771
|
-
|
|
772
|
-
// src/utils/normalize.ts
|
|
773
|
-
var stripUndefined = (input) => {
|
|
774
|
-
if (Array.isArray(input)) {
|
|
775
|
-
return input.map(stripUndefined);
|
|
776
|
-
}
|
|
777
|
-
if (input && typeof input === "object") {
|
|
778
|
-
const proto = Object.getPrototypeOf(input);
|
|
779
|
-
if (proto !== Object.prototype && proto !== null) {
|
|
780
|
-
return input;
|
|
781
|
-
}
|
|
782
|
-
const entries = Object.entries(input).filter(([, value]) => value !== undefined).map(([key, value]) => [key, stripUndefined(value)]);
|
|
783
|
-
return Object.fromEntries(entries);
|
|
784
|
-
}
|
|
785
|
-
return input;
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
// src/services/WebhookService.ts
|
|
789
|
-
class WebhookService extends ServiceMap3.Service()("WebhookService") {
|
|
790
|
-
}
|
|
791
|
-
var makeWebhookService = Effect3.gen(function* () {
|
|
792
|
-
const config = yield* Config;
|
|
793
|
-
const httpClient = yield* HttpClient;
|
|
794
|
-
const webhookUrl = config.webhook.webhookUrl;
|
|
795
|
-
const retrySchedule = createRetrySchedule({
|
|
796
|
-
maxRetries: config.webhook.maxRetries ?? httpClient.retryConfig.maxRetries,
|
|
797
|
-
baseDelay: Duration2.millis(config.webhook.baseDelayMs ?? Duration2.toMillis(httpClient.retryConfig.baseDelay)),
|
|
798
|
-
maxDelay: Duration2.millis(config.webhook.maxDelayMs ?? Duration2.toMillis(httpClient.retryConfig.maxDelay)),
|
|
799
|
-
jitter: config.webhook.enableJitter ?? httpClient.retryConfig.jitter
|
|
800
|
-
});
|
|
801
|
-
const sendWebhookRaw = (webhook) => pipe(Schema6.decodeUnknownEffect(Webhook)(stripUndefined(webhook)), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "webhook", value: webhook })), Effect3.flatMap((validatedWebhook) => httpClient.request({
|
|
802
|
-
method: "POST",
|
|
803
|
-
url: webhookUrl,
|
|
804
|
-
body: validatedWebhook
|
|
805
|
-
}).pipe(Effect3.retry(Schedule2.while(retrySchedule, (meta) => Effect3.succeed(meta.input instanceof NetworkError || meta.input instanceof RateLimitError || meta.input instanceof HttpError && (meta.input.response?.status ?? 0) >= 500))))), Effect3.map((response) => ({
|
|
806
|
-
status: response.status,
|
|
807
|
-
ok: response.status >= 200 && response.status < 300,
|
|
808
|
-
headers: response.headers,
|
|
809
|
-
body: response.body,
|
|
810
|
-
text: response.text
|
|
811
|
-
})));
|
|
812
|
-
const sendWebhook = (webhook) => pipe(sendWebhookRaw(webhook), Effect3.flatMap((response) => response.status === 204 && response.text === "" ? Effect3.void : Effect3.fail(new WebhookError({
|
|
813
|
-
message: `Unexpected webhook response: ${response.status}`,
|
|
814
|
-
cause: response
|
|
815
|
-
}))));
|
|
816
|
-
const modifyWebhook = (params) => pipe(Schema6.decodeUnknownEffect(WebhookParameter)(stripUndefined(params)), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "params", value: params })), Effect3.flatMap((validatedParams) => httpClient.request({
|
|
817
|
-
method: "PATCH",
|
|
818
|
-
url: webhookUrl,
|
|
819
|
-
body: validatedParams
|
|
820
|
-
})), Effect3.flatMap((response) => Schema6.decodeUnknownEffect(WebhookResponse)(response.body).pipe(Effect3.mapError(() => new WebhookError({
|
|
821
|
-
message: "Invalid webhook response",
|
|
822
|
-
cause: response.body
|
|
823
|
-
})))));
|
|
824
|
-
const getWebhook = () => pipe(httpClient.request({
|
|
825
|
-
method: "GET",
|
|
826
|
-
url: webhookUrl
|
|
827
|
-
}), Effect3.flatMap((response) => Schema6.decodeUnknownEffect(WebhookResponse)(response.body).pipe(Effect3.mapError(() => new WebhookError({
|
|
828
|
-
message: "Invalid webhook response",
|
|
829
|
-
cause: response.body
|
|
830
|
-
})))));
|
|
831
|
-
const validateWebhook = () => pipe(httpClient.request({
|
|
832
|
-
method: "GET",
|
|
833
|
-
url: webhookUrl
|
|
834
|
-
}), Effect3.as(true), Effect3.catch(() => Effect3.succeed(false)));
|
|
835
|
-
const deleteWebhook = () => pipe(httpClient.request({
|
|
836
|
-
method: "DELETE",
|
|
837
|
-
url: webhookUrl
|
|
838
|
-
}), Effect3.map((response) => response.status === 204));
|
|
839
|
-
return {
|
|
840
|
-
sendWebhook,
|
|
841
|
-
sendWebhookRaw,
|
|
842
|
-
modifyWebhook,
|
|
843
|
-
getWebhook,
|
|
844
|
-
deleteWebhook,
|
|
845
|
-
validateWebhook
|
|
846
|
-
};
|
|
847
|
-
});
|
|
848
|
-
var WebhookServiceLive = Layer3.effect(WebhookService, makeWebhookService);
|
|
849
|
-
var sendWebhook = (webhook) => WebhookService.use((service) => service.sendWebhook(webhook));
|
|
850
|
-
var sendWebhookRaw = (webhook) => WebhookService.use((service) => service.sendWebhookRaw(webhook));
|
|
851
|
-
var modifyWebhook = (params) => WebhookService.use((service) => service.modifyWebhook(params));
|
|
852
|
-
var getWebhook = () => WebhookService.use((service) => service.getWebhook());
|
|
853
|
-
var deleteWebhook = () => WebhookService.use((service) => service.deleteWebhook());
|
|
854
|
-
var validateWebhook = () => WebhookService.use((service) => service.validateWebhook());
|
|
855
|
-
|
|
856
|
-
// src/layers/Default.ts
|
|
857
|
-
var makeDefaultLayer = (url, options) => WebhookServiceLive.pipe(Layer4.provide(makeConfigLayer(url, options)), Layer4.provide(HttpClientLive));
|
|
858
|
-
// src/pipes/Embed.ts
|
|
859
|
-
var exports_Embed = {};
|
|
860
|
-
__export(exports_Embed, {
|
|
861
|
-
setVideo: () => setVideo,
|
|
862
|
-
setURL: () => setURL,
|
|
863
|
-
setTitle: () => setTitle,
|
|
864
|
-
setTimestamp: () => setTimestamp,
|
|
865
|
-
setThumbnail: () => setThumbnail,
|
|
866
|
-
setProvider: () => setProvider,
|
|
867
|
-
setImage: () => setImage,
|
|
868
|
-
setFooter: () => setFooter,
|
|
869
|
-
setFields: () => setFields,
|
|
870
|
-
setDescription: () => setDescription,
|
|
871
|
-
setColor: () => setColor,
|
|
872
|
-
setAuthor: () => setAuthor,
|
|
873
|
-
make: () => make,
|
|
874
|
-
buildDirect: () => buildDirect,
|
|
875
|
-
build: () => build,
|
|
876
|
-
addFieldImpl: () => addFieldImpl,
|
|
877
|
-
addFieldDirect: () => addFieldDirect,
|
|
878
|
-
addField: () => addField
|
|
879
|
-
});
|
|
880
|
-
import { Effect as Effect4, pipe as pipe2, Schema as Schema7 } from "effect";
|
|
881
|
-
var createDraft = () => ({});
|
|
882
|
-
var setTitleImpl = (title) => (d) => pipe2(Schema7.decodeUnknownEffect(maxLength(256))(title), Effect4.mapError(() => ValidationError.fromIssue("title", "Title must be 256 characters or less", {
|
|
883
|
-
constraint: "MaxLength",
|
|
884
|
-
expected: "256 characters or less",
|
|
885
|
-
actual: title.length
|
|
886
|
-
})), Effect4.as({ ...d, title }));
|
|
887
|
-
var setURLImpl = (url) => (d) => pipe2(Schema7.decodeUnknownEffect(UrlString)(url), Effect4.mapError(() => ValidationError.fromIssue("url", "Invalid URL", {
|
|
888
|
-
constraint: "URL",
|
|
889
|
-
expected: "valid URL",
|
|
890
|
-
actual: url
|
|
891
|
-
})), Effect4.as({ ...d, url }));
|
|
892
|
-
var setDescriptionImpl = (description) => (d) => pipe2(Schema7.decodeUnknownEffect(maxLength(4096))(description), Effect4.mapError(() => ValidationError.fromIssue("description", "Description must be 4096 characters or less", {
|
|
893
|
-
constraint: "MaxLength",
|
|
894
|
-
expected: "4096 characters or less",
|
|
895
|
-
actual: description.length
|
|
896
|
-
})), Effect4.as({ ...d, description }));
|
|
897
|
-
var setTimestampImpl = (date) => (d) => Effect4.succeed({ ...d, timestamp: (date ?? new Date).toISOString() });
|
|
898
|
-
var setColorImpl = (color) => (d) => {
|
|
899
|
-
const colorValue = typeof color === "string" ? parseInt(color.replace("#", ""), 16) : color;
|
|
900
|
-
return pipe2(Schema7.decodeUnknownEffect(ColorCode)(colorValue), Effect4.mapError(() => ValidationError.fromIssue("color", "Invalid color code (must be 0-16777215)", {
|
|
901
|
-
constraint: "Range",
|
|
902
|
-
expected: "0-16777215",
|
|
903
|
-
actual: colorValue
|
|
904
|
-
})), Effect4.as({ ...d, color: colorValue }));
|
|
905
|
-
};
|
|
906
|
-
var setFooterImpl = (footerOrText, icon_url, proxy_icon_url) => (d) => {
|
|
907
|
-
const footer = typeof footerOrText === "string" ? {
|
|
908
|
-
text: footerOrText,
|
|
909
|
-
...icon_url === undefined ? {} : { icon_url },
|
|
910
|
-
...proxy_icon_url === undefined ? {} : { proxy_icon_url }
|
|
911
|
-
} : (() => {
|
|
912
|
-
const { text, icon_url: icon_url2, proxy_icon_url: proxy_icon_url2 } = footerOrText;
|
|
913
|
-
return {
|
|
914
|
-
text,
|
|
915
|
-
...icon_url2 === undefined ? {} : { icon_url: icon_url2 },
|
|
916
|
-
...proxy_icon_url2 === undefined ? {} : { proxy_icon_url: proxy_icon_url2 }
|
|
917
|
-
};
|
|
918
|
-
})();
|
|
919
|
-
return pipe2(Schema7.decodeUnknownEffect(Footer)(footer), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "footer", value: footer })), Effect4.as({ ...d, footer }));
|
|
920
|
-
};
|
|
921
|
-
var setImageImpl = (imageOrUrl, height, width) => (d) => {
|
|
922
|
-
const image = typeof imageOrUrl === "string" ? {
|
|
923
|
-
url: imageOrUrl,
|
|
924
|
-
...height === undefined ? {} : { height },
|
|
925
|
-
...width === undefined ? {} : { width }
|
|
926
|
-
} : (() => {
|
|
927
|
-
const { url, proxy_url, height: height2, width: width2 } = imageOrUrl;
|
|
928
|
-
return {
|
|
929
|
-
url,
|
|
930
|
-
...proxy_url === undefined ? {} : { proxy_url },
|
|
931
|
-
...height2 === undefined ? {} : { height: height2 },
|
|
932
|
-
...width2 === undefined ? {} : { width: width2 }
|
|
933
|
-
};
|
|
934
|
-
})();
|
|
935
|
-
return pipe2(Schema7.decodeUnknownEffect(Image)(image), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "image", value: image })), Effect4.as({ ...d, image }));
|
|
936
|
-
};
|
|
937
|
-
var setThumbnailImpl = (thumbnailOrUrl, height, width) => (d) => {
|
|
938
|
-
const thumbnail = typeof thumbnailOrUrl === "string" ? {
|
|
939
|
-
url: thumbnailOrUrl,
|
|
940
|
-
...height === undefined ? {} : { height },
|
|
941
|
-
...width === undefined ? {} : { width }
|
|
942
|
-
} : (() => {
|
|
943
|
-
const { url, proxy_url, height: height2, width: width2 } = thumbnailOrUrl;
|
|
944
|
-
return {
|
|
945
|
-
url,
|
|
946
|
-
...proxy_url === undefined ? {} : { proxy_url },
|
|
947
|
-
...height2 === undefined ? {} : { height: height2 },
|
|
948
|
-
...width2 === undefined ? {} : { width: width2 }
|
|
949
|
-
};
|
|
950
|
-
})();
|
|
951
|
-
return pipe2(Schema7.decodeUnknownEffect(Thumbnail)(thumbnail), Effect4.mapError((e) => ValidationError.fromParseError(e, {
|
|
952
|
-
field: "thumbnail",
|
|
953
|
-
value: thumbnail
|
|
954
|
-
})), Effect4.as({ ...d, thumbnail }));
|
|
955
|
-
};
|
|
956
|
-
var setVideoImpl = (videoOrUrl, height, width) => (d) => {
|
|
957
|
-
const video = typeof videoOrUrl === "string" ? {
|
|
958
|
-
url: videoOrUrl,
|
|
959
|
-
...height === undefined ? {} : { height },
|
|
960
|
-
...width === undefined ? {} : { width }
|
|
961
|
-
} : (() => {
|
|
962
|
-
const { url, proxy_url, height: height2, width: width2 } = videoOrUrl;
|
|
963
|
-
return {
|
|
964
|
-
...url === undefined ? {} : { url },
|
|
965
|
-
...proxy_url === undefined ? {} : { proxy_url },
|
|
966
|
-
...height2 === undefined ? {} : { height: height2 },
|
|
967
|
-
...width2 === undefined ? {} : { width: width2 }
|
|
968
|
-
};
|
|
969
|
-
})();
|
|
970
|
-
return pipe2(Schema7.decodeUnknownEffect(Video)(video), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "video", value: video })), Effect4.as({ ...d, video }));
|
|
971
|
-
};
|
|
972
|
-
var setProviderImpl = (providerOrName, url) => (d) => {
|
|
973
|
-
const provider = typeof providerOrName === "string" ? {
|
|
974
|
-
name: providerOrName,
|
|
975
|
-
...url === undefined ? {} : { url }
|
|
976
|
-
} : (() => {
|
|
977
|
-
const { name, url: url2 } = providerOrName;
|
|
978
|
-
return {
|
|
979
|
-
...name === undefined ? {} : { name },
|
|
980
|
-
...url2 === undefined ? {} : { url: url2 }
|
|
981
|
-
};
|
|
982
|
-
})();
|
|
983
|
-
return pipe2(Schema7.decodeUnknownEffect(Provider)(provider), Effect4.mapError((e) => ValidationError.fromParseError(e, {
|
|
984
|
-
field: "provider",
|
|
985
|
-
value: provider
|
|
986
|
-
})), Effect4.as({ ...d, provider }));
|
|
987
|
-
};
|
|
988
|
-
var setAuthorImpl = (authorOrName, url, icon_url) => (d) => {
|
|
989
|
-
const author = typeof authorOrName === "string" ? {
|
|
990
|
-
name: authorOrName,
|
|
991
|
-
...url === undefined ? {} : { url },
|
|
992
|
-
...icon_url === undefined ? {} : { icon_url }
|
|
993
|
-
} : (() => {
|
|
994
|
-
const { name, url: url2, icon_url: icon_url2, proxy_icon_url } = authorOrName;
|
|
995
|
-
return {
|
|
996
|
-
...name === undefined ? {} : { name },
|
|
997
|
-
...url2 === undefined ? {} : { url: url2 },
|
|
998
|
-
...icon_url2 === undefined ? {} : { icon_url: icon_url2 },
|
|
999
|
-
...proxy_icon_url === undefined ? {} : { proxy_icon_url }
|
|
1000
|
-
};
|
|
1001
|
-
})();
|
|
1002
|
-
return pipe2(Schema7.decodeUnknownEffect(Author)(author), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "author", value: author })), Effect4.as({ ...d, author }));
|
|
1003
|
-
};
|
|
1004
|
-
var addFieldImpl = (fieldOrName, value, inline = false) => (d) => {
|
|
1005
|
-
const currentFields = d.fields || [];
|
|
1006
|
-
if (currentFields.length >= 25) {
|
|
1007
|
-
return Effect4.fail(ValidationError.fromIssue("fields", "Cannot add more than 25 fields", {
|
|
1008
|
-
constraint: "MaxItems",
|
|
1009
|
-
expected: "25 fields or less",
|
|
1010
|
-
actual: currentFields.length + 1
|
|
1011
|
-
}));
|
|
1012
|
-
}
|
|
1013
|
-
const field = typeof fieldOrName === "string" ? { name: fieldOrName, value: value ?? "", inline } : (() => {
|
|
1014
|
-
const { name, value: value2, inline: inline2 } = fieldOrName;
|
|
1015
|
-
return {
|
|
1016
|
-
name,
|
|
1017
|
-
value: value2,
|
|
1018
|
-
...inline2 === undefined ? {} : { inline: inline2 }
|
|
1019
|
-
};
|
|
1020
|
-
})();
|
|
1021
|
-
return pipe2(Schema7.decodeUnknownEffect(Field)(field), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "fields", value: field })), Effect4.map(() => ({ ...d, fields: [...currentFields, field] })));
|
|
1022
|
-
};
|
|
1023
|
-
var setFieldsImpl = (fields) => (d) => {
|
|
1024
|
-
if (fields.length > 25) {
|
|
1025
|
-
return Effect4.fail(ValidationError.fromIssue("fields", "Cannot have more than 25 fields", {
|
|
1026
|
-
constraint: "MaxItems",
|
|
1027
|
-
expected: "25 fields or less",
|
|
1028
|
-
actual: fields.length
|
|
1029
|
-
}));
|
|
1030
|
-
}
|
|
1031
|
-
const normalized = fields.map((field) => {
|
|
1032
|
-
const { name, value, inline } = field;
|
|
1033
|
-
return {
|
|
1034
|
-
name,
|
|
1035
|
-
value,
|
|
1036
|
-
...inline === undefined ? {} : { inline }
|
|
1037
|
-
};
|
|
1038
|
-
});
|
|
1039
|
-
return pipe2(Effect4.forEach(normalized, (f) => Schema7.decodeUnknownEffect(Field)(f)), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "fields", value: fields })), Effect4.map((decoded) => ({ ...d, fields: decoded })));
|
|
1040
|
-
};
|
|
1041
|
-
var buildImpl = (d) => pipe2(Schema7.decodeUnknownEffect(Embed)(stripUndefined({ ...d, type: "rich" })), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "embed", value: d })));
|
|
1042
|
-
var buildDirect = buildImpl;
|
|
1043
|
-
var addFieldDirect = addFieldImpl;
|
|
1044
|
-
var make = Effect4.succeed(createDraft());
|
|
1045
|
-
var chain = (f) => (self) => pipe2(self, Effect4.flatMap(f));
|
|
1046
|
-
var setTitle = (title) => chain(setTitleImpl(title));
|
|
1047
|
-
var setURL = (url) => chain(setURLImpl(url));
|
|
1048
|
-
var setDescription = (description) => chain(setDescriptionImpl(description));
|
|
1049
|
-
var setTimestamp = (date) => chain(setTimestampImpl(date));
|
|
1050
|
-
var setColor = (color) => chain(setColorImpl(color));
|
|
1051
|
-
var setFooter = (footer, icon_url, proxy_icon_url) => chain(setFooterImpl(footer, icon_url, proxy_icon_url));
|
|
1052
|
-
var setImage = (image, height, width) => chain(setImageImpl(image, height, width));
|
|
1053
|
-
var setThumbnail = (thumbnail, height, width) => chain(setThumbnailImpl(thumbnail, height, width));
|
|
1054
|
-
var setVideo = (video, height, width) => chain(setVideoImpl(video, height, width));
|
|
1055
|
-
var setProvider = (provider, url) => chain(setProviderImpl(provider, url));
|
|
1056
|
-
var setAuthor = (author, url, icon_url) => chain(setAuthorImpl(author, url, icon_url));
|
|
1057
|
-
var addField = (field, value, inline = false) => chain(addFieldImpl(field, value, inline));
|
|
1058
|
-
var setFields = (fields) => chain(setFieldsImpl(fields));
|
|
1059
|
-
var build = (self) => pipe2(self, Effect4.flatMap(buildImpl));
|
|
1060
|
-
// src/pipes/Webhook.ts
|
|
1061
|
-
var exports_Webhook = {};
|
|
1062
|
-
__export(exports_Webhook, {
|
|
1063
|
-
validate: () => validate,
|
|
1064
|
-
setUsername: () => setUsername,
|
|
1065
|
-
setTTS: () => setTTS,
|
|
1066
|
-
setFile: () => setFile,
|
|
1067
|
-
setContent: () => setContent,
|
|
1068
|
-
setAvatarUrl: () => setAvatarUrl,
|
|
1069
|
-
modifyParams: () => modifyParams,
|
|
1070
|
-
make: () => make2,
|
|
1071
|
-
buildDirect: () => buildDirect2,
|
|
1072
|
-
build: () => build2,
|
|
1073
|
-
addEmbedImpl: () => addEmbedImpl,
|
|
1074
|
-
addEmbedDirect: () => addEmbedDirect,
|
|
1075
|
-
addEmbed: () => addEmbed
|
|
1076
|
-
});
|
|
1077
|
-
import { Effect as Effect5, pipe as pipe3, Schema as Schema8 } from "effect";
|
|
1078
|
-
var createDraft2 = () => ({});
|
|
1079
|
-
var setUsernameImpl = (username) => (d) => pipe3(Schema8.decodeUnknownEffect(maxLength(80))(username), Effect5.mapError(() => ValidationError.fromIssue("username", "Username must be 80 characters or less", {
|
|
1080
|
-
constraint: "MaxLength",
|
|
1081
|
-
expected: "80 characters or less",
|
|
1082
|
-
actual: username.length
|
|
1083
|
-
})), Effect5.as({ ...d, username }));
|
|
1084
|
-
var setAvatarUrlImpl = (avatar_url) => (d) => pipe3(Schema8.decodeUnknownEffect(UrlString)(avatar_url), Effect5.mapError(() => ValidationError.fromIssue("avatar_url", "Invalid avatar URL", {
|
|
1085
|
-
constraint: "URL",
|
|
1086
|
-
expected: "valid URL",
|
|
1087
|
-
actual: avatar_url
|
|
1088
|
-
})), Effect5.as({ ...d, avatar_url }));
|
|
1089
|
-
var setContentImpl = (content) => (d) => pipe3(Schema8.decodeUnknownEffect(maxLength(2000))(content), Effect5.mapError(() => ValidationError.fromIssue("content", "Content must be 2000 characters or less", {
|
|
1090
|
-
constraint: "MaxLength",
|
|
1091
|
-
expected: "2000 characters or less",
|
|
1092
|
-
actual: content.length
|
|
1093
|
-
})), Effect5.as({ ...d, content }));
|
|
1094
|
-
var setTTSImpl = (tts) => (d) => Effect5.succeed({ ...d, tts });
|
|
1095
|
-
var addEmbedImpl = (embed) => (d) => {
|
|
1096
|
-
const current = d.embeds || [];
|
|
1097
|
-
const next = [...current, embed];
|
|
1098
|
-
return pipe3(Schema8.decodeUnknownEffect(EmbedArray)(stripUndefined(next)), Effect5.mapError(() => ValidationError.fromIssue("embeds", "Cannot have more than 10 embeds", {
|
|
1099
|
-
constraint: "MaxItems",
|
|
1100
|
-
expected: "10 embeds or less",
|
|
1101
|
-
actual: next.length
|
|
1102
|
-
})), Effect5.map((decoded) => ({ ...d, embeds: decoded })));
|
|
1103
|
-
};
|
|
1104
|
-
var setFileImpl = (file) => (d) => Effect5.succeed({ ...d, file });
|
|
1105
|
-
var buildImpl2 = (d) => pipe3(Schema8.decodeUnknownEffect(Webhook)(stripUndefined(d)), Effect5.mapError((e) => ValidationError.fromParseError(e, { field: "webhook", value: d })));
|
|
1106
|
-
var buildDirect2 = buildImpl2;
|
|
1107
|
-
var addEmbedDirect = addEmbedImpl;
|
|
1108
|
-
var validate = (input) => pipe3(Schema8.decodeUnknownEffect(Webhook)(stripUndefined(input)), Effect5.mapError((e) => ValidationError.fromParseError(e, { field: "webhook", value: input })));
|
|
1109
|
-
var modifyParams = (p) => pipe3(Schema8.decodeUnknownEffect(WebhookParameter)(stripUndefined(p)), Effect5.mapError((e) => ValidationError.fromParseError(e, { field: "params", value: p })));
|
|
1110
|
-
var make2 = Effect5.succeed(createDraft2());
|
|
1111
|
-
var chain2 = (f) => (self) => pipe3(self, Effect5.flatMap(f));
|
|
1112
|
-
var setUsername = (username) => chain2(setUsernameImpl(username));
|
|
1113
|
-
var setAvatarUrl = (url) => chain2(setAvatarUrlImpl(url));
|
|
1114
|
-
var setContent = (content) => chain2(setContentImpl(content));
|
|
1115
|
-
var setTTS = (tts) => chain2(setTTSImpl(tts));
|
|
1116
|
-
var addEmbed = (embed) => chain2(addEmbedImpl(embed));
|
|
1117
|
-
var setFile = (file) => chain2(setFileImpl(file));
|
|
1118
|
-
var build2 = (self) => pipe3(self, Effect5.flatMap(buildImpl2));
|
|
1119
171
|
export {
|
|
1120
172
|
webhookErrorToLogObject,
|
|
1121
173
|
validateWebhook,
|
|
@@ -1145,7 +197,7 @@ export {
|
|
|
1145
197
|
WebhookParameter,
|
|
1146
198
|
WebhookFile,
|
|
1147
199
|
WebhookError,
|
|
1148
|
-
|
|
200
|
+
Webhook2 as Webhook,
|
|
1149
201
|
Video,
|
|
1150
202
|
ValidationError,
|
|
1151
203
|
UrlString,
|
|
@@ -1166,7 +218,7 @@ export {
|
|
|
1166
218
|
Field,
|
|
1167
219
|
FetchHttpClient,
|
|
1168
220
|
EmbedArray,
|
|
1169
|
-
|
|
221
|
+
Embed2 as Embed,
|
|
1170
222
|
DiscordWebhookUrl,
|
|
1171
223
|
DISCORD_WEBHOOK_REGEX,
|
|
1172
224
|
ConfigFromEnv,
|
|
@@ -1177,5 +229,5 @@ export {
|
|
|
1177
229
|
Attachment
|
|
1178
230
|
};
|
|
1179
231
|
|
|
1180
|
-
//# debugId=
|
|
232
|
+
//# debugId=77B5F8F8A236B81364756E2164756E21
|
|
1181
233
|
//# sourceMappingURL=index.js.map
|