@teever/ez-hook-effect 0.4.4 → 0.5.1

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 (54) hide show
  1. package/README.md +88 -10
  2. package/dist/errors/WebhookError.d.ts +142 -3
  3. package/dist/errors/WebhookError.js +322 -0
  4. package/dist/errors/WebhookError.js.map +1 -0
  5. package/dist/errors/index.js +2 -0
  6. package/dist/errors/index.js.map +1 -0
  7. package/dist/index.d.ts +7 -3
  8. package/dist/index.js +493 -264
  9. package/dist/index.js.map +16 -14
  10. package/dist/layers/Config.d.ts +3 -3
  11. package/dist/layers/Config.js +166 -0
  12. package/dist/layers/Config.js.map +1 -0
  13. package/dist/layers/Default.d.ts +6 -0
  14. package/dist/layers/Default.js +9 -0
  15. package/dist/layers/Default.js.map +1 -0
  16. package/dist/layers/HttpClient.d.ts +8 -4
  17. package/dist/layers/HttpClient.js +209 -0
  18. package/dist/layers/HttpClient.js.map +1 -0
  19. package/dist/layers/index.js +3 -0
  20. package/dist/layers/index.js.map +1 -0
  21. package/dist/pipes/Embed.d.ts +2 -2
  22. package/dist/pipes/Embed.js +198 -0
  23. package/dist/pipes/Embed.js.map +1 -0
  24. package/dist/pipes/Webhook.d.ts +3 -2
  25. package/dist/pipes/Webhook.js +46 -0
  26. package/dist/pipes/Webhook.js.map +1 -0
  27. package/dist/pipes/index.js +3 -0
  28. package/dist/pipes/index.js.map +1 -0
  29. package/dist/schemas/Common.d.ts +6 -6
  30. package/dist/schemas/Common.js +30 -0
  31. package/dist/schemas/Common.js.map +1 -0
  32. package/dist/schemas/Discord.d.ts +1 -1
  33. package/dist/schemas/Discord.js +19 -0
  34. package/dist/schemas/Discord.js.map +1 -0
  35. package/dist/schemas/Embed.d.ts +58 -229
  36. package/dist/schemas/Embed.js +139 -0
  37. package/dist/schemas/Embed.js.map +1 -0
  38. package/dist/schemas/Field.d.ts +6 -27
  39. package/dist/schemas/Field.js +25 -0
  40. package/dist/schemas/Field.js.map +1 -0
  41. package/dist/schemas/Webhook.d.ts +42 -160
  42. package/dist/schemas/Webhook.js +87 -0
  43. package/dist/schemas/Webhook.js.map +1 -0
  44. package/dist/schemas/index.js +6 -0
  45. package/dist/schemas/index.js.map +1 -0
  46. package/dist/services/WebhookService.d.ts +27 -5
  47. package/dist/services/WebhookService.js +116 -0
  48. package/dist/services/WebhookService.js.map +1 -0
  49. package/dist/services/index.js +2 -0
  50. package/dist/services/index.js.map +1 -0
  51. package/dist/utils/normalize.d.ts +1 -0
  52. package/dist/utils/normalize.js +17 -0
  53. package/dist/utils/normalize.js.map +1 -0
  54. package/package.json +56 -54
package/dist/index.js CHANGED
@@ -55,28 +55,30 @@ var pathToString = (path) => {
55
55
  }, "$");
56
56
  };
57
57
  var toArray = (value) => Array.isArray(value) ? value : [value];
58
+ var getIssueProp = (issue, key) => issue[key];
58
59
  var extractIssuesFromParseIssue = (issue, currentPath = []) => {
59
60
  const issues = [];
60
61
  switch (issue._tag) {
61
- case "Type": {
62
- const message = issue.message ?? `Expected ${issue.ast._tag}`;
62
+ case "InvalidType": {
63
+ const ast = getIssueProp(issue, "ast");
64
+ const message = getIssueProp(issue, "message") ?? `Expected ${ast?._tag}`;
63
65
  issues.push({
64
66
  path: pathToString(currentPath),
65
67
  message,
66
68
  constraint: extractConstraint(message) ?? "Type",
67
- actual: truncateValue(issue.actual)
69
+ actual: truncateValue(getIssueProp(issue, "actual"))
68
70
  });
69
71
  break;
70
72
  }
71
73
  case "Forbidden": {
72
74
  issues.push({
73
75
  path: pathToString(currentPath),
74
- message: issue.message ?? "Value is forbidden",
76
+ message: getIssueProp(issue, "message") ?? "Value is forbidden",
75
77
  constraint: "Forbidden"
76
78
  });
77
79
  break;
78
80
  }
79
- case "Missing": {
81
+ case "MissingKey": {
80
82
  issues.push({
81
83
  path: pathToString(currentPath),
82
84
  message: "Required field is missing",
@@ -84,34 +86,60 @@ var extractIssuesFromParseIssue = (issue, currentPath = []) => {
84
86
  });
85
87
  break;
86
88
  }
87
- case "Unexpected": {
89
+ case "UnexpectedKey": {
88
90
  issues.push({
89
91
  path: pathToString(currentPath),
90
- message: issue.message ?? "Unexpected field",
92
+ message: getIssueProp(issue, "message") ?? "Unexpected field",
91
93
  constraint: "Unexpected",
92
- actual: truncateValue(issue.actual)
94
+ actual: truncateValue(getIssueProp(issue, "actual"))
93
95
  });
94
96
  break;
95
97
  }
96
98
  case "Pointer": {
97
- const pathSegments = toArray(issue.path);
99
+ const pathSegments = getIssueProp(issue, "path") ?? [];
98
100
  const newPath = [...currentPath, ...pathSegments];
99
- issues.push(...extractIssuesFromParseIssue(issue.issue, newPath));
101
+ const nested = getIssueProp(issue, "issue");
102
+ if (nested)
103
+ issues.push(...extractIssuesFromParseIssue(nested, newPath));
100
104
  break;
101
105
  }
102
106
  case "Composite": {
103
- const subIssues = toArray(issue.issues);
107
+ const rawIssues = getIssueProp(issue, "issues");
108
+ const subIssues = rawIssues ? toArray(rawIssues) : [];
104
109
  for (const subIssue of subIssues) {
105
110
  issues.push(...extractIssuesFromParseIssue(subIssue, currentPath));
106
111
  }
107
112
  break;
108
113
  }
109
- case "Refinement": {
110
- issues.push(...extractIssuesFromParseIssue(issue.issue, currentPath));
114
+ case "Filter":
115
+ case "Encoding": {
116
+ const nested = getIssueProp(issue, "issue");
117
+ if (nested)
118
+ issues.push(...extractIssuesFromParseIssue(nested, currentPath));
111
119
  break;
112
120
  }
113
- case "Transformation": {
114
- issues.push(...extractIssuesFromParseIssue(issue.issue, currentPath));
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
+ });
115
143
  break;
116
144
  }
117
145
  }
@@ -187,18 +215,87 @@ class FileError extends Data.TaggedError("FileError") {
187
215
 
188
216
  class HttpError extends Data.TaggedError("HttpError") {
189
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
+ };
190
287
  // src/layers/Config.ts
191
- import { Context, Effect, Config as EffectConfig, Layer } from "effect";
288
+ import { Effect, Layer, ServiceMap } from "effect";
192
289
 
193
290
  // src/schemas/Discord.ts
194
291
  import { Schema } from "effect";
195
292
  var DISCORD_WEBHOOK_REGEX = /^https:\/\/(?:(?:canary|ptb)\.)?discord(?:app)?\.com\/api(?:\/v\d+)?\/webhooks\/(?<id>\d+)\/(?<token>[\w-]+)\/?$/;
196
- var DiscordWebhookUrl = Schema.String.pipe(Schema.pattern(DISCORD_WEBHOOK_REGEX, {
197
- message: () => "Invalid Discord webhook URL"
293
+ var DiscordWebhookUrl = Schema.String.check(Schema.isPattern(DISCORD_WEBHOOK_REGEX, {
294
+ message: "Invalid Discord webhook URL"
198
295
  }));
199
296
 
200
297
  // src/layers/Config.ts
201
- class Config extends Context.Tag("Config")() {
298
+ class Config extends ServiceMap.Service()("Config") {
202
299
  }
203
300
  var validateWebhookUrl = (url) => {
204
301
  if (!url || url.trim() === "") {
@@ -289,16 +386,32 @@ var makeConfig = (webhookUrl, options) => Effect.gen(function* () {
289
386
  });
290
387
  var makeConfigLayer = (webhookUrl, options) => Layer.effect(Config, makeConfig(webhookUrl, options));
291
388
  var ConfigFromEnv = Layer.effect(Config, Effect.gen(function* () {
292
- const webhookUrl = yield* EffectConfig.string("DISCORD_WEBHOOK_URL").pipe(Effect.mapError(() => new ConfigError({
293
- message: `DISCORD_WEBHOOK_URL environment variable not set.
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.
294
393
  ` + `Please set DISCORD_WEBHOOK_URL in your environment or .env file.
295
394
  ` + "See .env.example for all available configuration options.",
296
- parameter: "DISCORD_WEBHOOK_URL"
297
- })), Effect.flatMap(validateWebhookUrl));
298
- const maxRetries = yield* EffectConfig.integer("WEBHOOK_MAX_RETRIES").pipe(EffectConfig.withDefault(3));
299
- const baseDelayMs = yield* EffectConfig.integer("WEBHOOK_BASE_DELAY_MS").pipe(EffectConfig.withDefault(1000));
300
- const maxDelayMs = yield* EffectConfig.integer("WEBHOOK_MAX_DELAY_MS").pipe(EffectConfig.withDefault(60000));
301
- const enableJitter = yield* EffectConfig.boolean("WEBHOOK_ENABLE_JITTER").pipe(EffectConfig.withDefault(true));
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
+ });
302
415
  yield* validateRetryOptions({ maxRetries, baseDelayMs, maxDelayMs });
303
416
  return {
304
417
  webhook: {
@@ -322,8 +435,8 @@ var parseWebhookUrl = (url) => Effect.gen(function* () {
322
435
  return { id, token };
323
436
  });
324
437
  // src/layers/HttpClient.ts
325
- import { Context as Context2, Duration, Effect as Effect2, Layer as Layer2, Schedule } from "effect";
326
- class HttpClient extends Context2.Tag("HttpClient")() {
438
+ import { Duration, Effect as Effect2, Layer as Layer2, Schedule, ServiceMap as ServiceMap2 } from "effect";
439
+ class HttpClient extends ServiceMap2.Service()("HttpClient") {
327
440
  }
328
441
  var parseRateLimitHeaders = (headers) => {
329
442
  const retryAfterHeader = headers["retry-after"];
@@ -376,13 +489,17 @@ var defaultRetryConfig = {
376
489
  jitter: true
377
490
  };
378
491
  var createRetrySchedule = (config) => {
492
+ const capDelay = (delay) => Duration.isLessThanOrEqualTo(delay, config.maxDelay) ? delay : config.maxDelay;
379
493
  let policy = Schedule.exponential(config.baseDelay, 2);
380
- policy = policy.pipe(Schedule.delayed((d) => Duration.lessThanOrEqualTo(d, config.maxDelay) ? d : config.maxDelay));
381
494
  if (config.jitter) {
382
- policy = policy.pipe(Schedule.jittered);
495
+ policy = policy.pipe(Schedule.modifyDelay((_, delay) => Effect2.sync(() => {
496
+ const millis = Duration.toMillis(delay);
497
+ return Duration.millis(millis + Math.random() * millis);
498
+ })));
383
499
  }
384
- const capped = Schedule.intersect(policy, Schedule.recurs(config.maxRetries));
385
- return capped.pipe(Schedule.map(([d]) => d));
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)));
386
503
  };
387
504
  var FetchHttpClient = (retryConfig = defaultRetryConfig) => ({
388
505
  retryConfig,
@@ -470,58 +587,42 @@ var FetchHttpClient = (retryConfig = defaultRetryConfig) => ({
470
587
  });
471
588
  var HttpClientLive = Layer2.succeed(HttpClient, FetchHttpClient());
472
589
  var makeHttpClientLayer = (retryConfig) => Layer2.succeed(HttpClient, FetchHttpClient({ ...defaultRetryConfig, ...retryConfig }));
473
- // src/pipes/Embed.ts
474
- var exports_Embed = {};
475
- __export(exports_Embed, {
476
- setVideo: () => setVideo,
477
- setURL: () => setURL,
478
- setTitle: () => setTitle,
479
- setTimestamp: () => setTimestamp,
480
- setThumbnail: () => setThumbnail,
481
- setProvider: () => setProvider,
482
- setImage: () => setImage,
483
- setFooter: () => setFooter,
484
- setFields: () => setFields,
485
- setDescription: () => setDescription,
486
- setColor: () => setColor,
487
- setAuthor: () => setAuthor,
488
- make: () => make,
489
- buildDirect: () => buildDirect,
490
- build: () => build,
491
- addFieldImpl: () => addFieldImpl,
492
- addFieldDirect: () => addFieldDirect,
493
- addField: () => addField
590
+ var makeTestHttpClient = (resolver, retryConfig = defaultRetryConfig) => Layer2.succeed(HttpClient, {
591
+ request: resolver,
592
+ retryConfig
494
593
  });
495
- import { Effect as Effect3, pipe, Schema as Schema6 } from "effect";
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";
496
607
 
497
608
  // src/schemas/Common.ts
498
609
  import { Schema as Schema2 } from "effect";
499
- var maxLength = (max) => Schema2.String.pipe(Schema2.maxLength(max));
500
- var positiveInt = Schema2.Number.pipe(Schema2.int(), Schema2.positive());
501
- var UrlString = Schema2.String.pipe(Schema2.filter((s) => {
610
+ var maxLength = (max) => Schema2.String.check(Schema2.isMaxLength(max));
611
+ var positiveInt = Schema2.Number.check(Schema2.isInt(), Schema2.isGreaterThan(0));
612
+ var UrlString = Schema2.String.pipe(Schema2.check(Schema2.makeFilter((value) => {
502
613
  try {
503
- new URL(s);
614
+ new URL(value);
504
615
  return true;
505
616
  } catch {
506
- return false;
617
+ return "Invalid URL format";
507
618
  }
508
- }, { message: () => "Invalid URL format" }), Schema2.annotations({
509
- arbitrary: () => (fc) => fc.webUrl()
510
- }));
511
- var ISO8601Timestamp = Schema2.String.pipe(Schema2.filter((s) => {
619
+ })));
620
+ var ISO8601Timestamp = Schema2.String.pipe(Schema2.check(Schema2.makeFilter((value) => {
512
621
  const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
513
- return iso8601Regex.test(s);
514
- }, { message: () => "Invalid ISO8601 timestamp format" }), Schema2.annotations({
515
- arbitrary: () => (fc) => fc.date().map((d) => d.toISOString())
516
- }));
517
- var ColorCode = Schema2.Number.pipe(Schema2.int(), Schema2.greaterThanOrEqualTo(0), Schema2.lessThanOrEqualTo(16777215));
518
- var Uint8ArraySchema = Schema2.instanceOf(Uint8Array).pipe(Schema2.annotations({
519
- arbitrary: () => (fc) => fc.uint8Array().map((u8) => {
520
- const ab = new ArrayBuffer(u8.byteLength);
521
- new Uint8Array(ab).set(u8);
522
- return new Uint8Array(ab);
523
- })
524
- }));
622
+ return iso8601Regex.test(value) ? true : "Invalid ISO8601 timestamp format";
623
+ })));
624
+ var ColorCode = Schema2.Number.check(Schema2.isInt(), Schema2.isGreaterThanOrEqualTo(0), Schema2.isLessThanOrEqualTo(16777215));
625
+ var Uint8ArraySchema = Schema2.instanceOf(Uint8Array);
525
626
 
526
627
  // src/schemas/Embed.ts
527
628
  import { Schema as Schema4 } from "effect";
@@ -531,222 +632,417 @@ import { Schema as Schema3 } from "effect";
531
632
  class Field extends Schema3.Class("Field")({
532
633
  name: maxLength(256),
533
634
  value: maxLength(1024),
534
- inline: Schema3.optional(Schema3.Boolean)
635
+ inline: Schema3.optionalKey(Schema3.Boolean)
535
636
  }) {
536
637
  }
537
- var FieldArray = Schema3.Array(Field).pipe(Schema3.maxItems(25));
638
+ var FieldArray = Schema3.Array(Field).pipe(Schema3.check(Schema3.isMaxLength(25)));
538
639
 
539
640
  // src/schemas/Embed.ts
540
641
  class Attachment extends Schema4.Class("Attachment")({
541
642
  url: Schema4.String,
542
- proxyUrl: Schema4.optional(Schema4.String),
543
- filename: Schema4.optional(Schema4.String),
544
- size: Schema4.optional(positiveInt),
545
- height: Schema4.optional(positiveInt),
546
- width: Schema4.optional(positiveInt)
643
+ proxyUrl: Schema4.optionalKey(Schema4.String),
644
+ filename: Schema4.optionalKey(Schema4.String),
645
+ size: Schema4.optionalKey(positiveInt),
646
+ height: Schema4.optionalKey(positiveInt),
647
+ width: Schema4.optionalKey(positiveInt)
547
648
  }) {
548
649
  }
549
- var UrlOrAttachment = Schema4.Union(UrlString, Attachment);
650
+ var UrlOrAttachment = Schema4.Union([UrlString, Attachment]);
550
651
 
551
652
  class Author extends Schema4.Class("Author")({
552
- name: Schema4.optional(maxLength(256)),
553
- url: Schema4.optional(UrlString),
554
- icon_url: Schema4.optional(UrlOrAttachment),
555
- proxy_icon_url: Schema4.optional(Schema4.String)
653
+ name: Schema4.optionalKey(maxLength(256)),
654
+ url: Schema4.optionalKey(UrlString),
655
+ icon_url: Schema4.optionalKey(UrlOrAttachment),
656
+ proxy_icon_url: Schema4.optionalKey(Schema4.String)
556
657
  }) {
557
658
  }
558
659
 
559
660
  class Footer extends Schema4.Class("Footer")({
560
661
  text: maxLength(2048),
561
- icon_url: Schema4.optional(UrlOrAttachment),
562
- proxy_icon_url: Schema4.optional(Schema4.String)
662
+ icon_url: Schema4.optionalKey(UrlOrAttachment),
663
+ proxy_icon_url: Schema4.optionalKey(Schema4.String)
563
664
  }) {
564
665
  }
565
666
 
566
667
  class Image extends Schema4.Class("Image")({
567
668
  url: UrlOrAttachment,
568
- proxy_url: Schema4.optional(Schema4.String),
569
- height: Schema4.optional(positiveInt),
570
- width: Schema4.optional(positiveInt)
669
+ proxy_url: Schema4.optionalKey(Schema4.String),
670
+ height: Schema4.optionalKey(positiveInt),
671
+ width: Schema4.optionalKey(positiveInt)
571
672
  }) {
572
673
  }
573
674
 
574
675
  class Thumbnail extends Schema4.Class("Thumbnail")({
575
676
  url: UrlOrAttachment,
576
- proxy_url: Schema4.optional(Schema4.String),
577
- height: Schema4.optional(positiveInt),
578
- width: Schema4.optional(positiveInt)
677
+ proxy_url: Schema4.optionalKey(Schema4.String),
678
+ height: Schema4.optionalKey(positiveInt),
679
+ width: Schema4.optionalKey(positiveInt)
579
680
  }) {
580
681
  }
581
682
 
582
683
  class Video extends Schema4.Class("Video")({
583
- url: Schema4.optional(UrlString),
584
- proxy_url: Schema4.optional(Schema4.String),
585
- height: Schema4.optional(positiveInt),
586
- width: Schema4.optional(positiveInt)
684
+ url: Schema4.optionalKey(UrlString),
685
+ proxy_url: Schema4.optionalKey(Schema4.String),
686
+ height: Schema4.optionalKey(positiveInt),
687
+ width: Schema4.optionalKey(positiveInt)
587
688
  }) {
588
689
  }
589
690
 
590
691
  class Provider extends Schema4.Class("Provider")({
591
- name: Schema4.optional(Schema4.String),
592
- url: Schema4.optional(UrlString)
692
+ name: Schema4.optionalKey(Schema4.String),
693
+ url: Schema4.optionalKey(UrlString)
593
694
  }) {
594
695
  }
595
696
 
596
697
  class Embed extends Schema4.Class("Embed")({
597
- title: Schema4.optional(maxLength(256)),
598
- type: Schema4.optional(Schema4.Literal("rich")),
599
- url: Schema4.optional(UrlString),
600
- description: Schema4.optional(maxLength(4096)),
601
- timestamp: Schema4.optional(ISO8601Timestamp),
602
- color: Schema4.optional(ColorCode),
603
- footer: Schema4.optional(Footer),
604
- image: Schema4.optional(Image),
605
- thumbnail: Schema4.optional(Thumbnail),
606
- video: Schema4.optional(Video),
607
- provider: Schema4.optional(Provider),
608
- author: Schema4.optional(Author),
609
- fields: Schema4.optional(FieldArray)
698
+ title: Schema4.optionalKey(maxLength(256)),
699
+ type: Schema4.optionalKey(Schema4.Literal("rich")),
700
+ url: Schema4.optionalKey(UrlString),
701
+ description: Schema4.optionalKey(maxLength(4096)),
702
+ timestamp: Schema4.optionalKey(ISO8601Timestamp),
703
+ color: Schema4.optionalKey(ColorCode),
704
+ footer: Schema4.optionalKey(Footer),
705
+ image: Schema4.optionalKey(Image),
706
+ thumbnail: Schema4.optionalKey(Thumbnail),
707
+ video: Schema4.optionalKey(Video),
708
+ provider: Schema4.optionalKey(Provider),
709
+ author: Schema4.optionalKey(Author),
710
+ fields: Schema4.optionalKey(FieldArray)
610
711
  }) {
611
712
  }
612
- var EmbedArray = Schema4.Array(Embed).pipe(Schema4.maxItems(10));
713
+ var EmbedArray = Schema4.Array(Embed).pipe(Schema4.check(Schema4.isMaxLength(10)));
613
714
 
614
715
  // src/schemas/Webhook.ts
615
716
  import { Schema as Schema5 } from "effect";
616
- var WebhookFile = Schema5.Union(Schema5.String, Attachment, Schema5.Struct({
617
- name: Schema5.String,
618
- data: Schema5.Union(Schema5.String, Uint8ArraySchema)
619
- }));
717
+ var WebhookFile = Schema5.Union([
718
+ Schema5.String,
719
+ Attachment,
720
+ Schema5.Struct({
721
+ name: Schema5.String,
722
+ data: Schema5.Union([Schema5.String, Uint8ArraySchema])
723
+ })
724
+ ]);
620
725
 
621
726
  class Webhook extends Schema5.Class("Webhook")({
622
- username: Schema5.optional(maxLength(80)),
623
- avatar_url: Schema5.optional(UrlString),
624
- tts: Schema5.optional(Schema5.Boolean),
625
- content: Schema5.optional(maxLength(2000)),
626
- file: Schema5.optional(WebhookFile),
627
- embeds: Schema5.optional(EmbedArray)
727
+ username: Schema5.optionalKey(maxLength(80)),
728
+ avatar_url: Schema5.optionalKey(UrlString),
729
+ tts: Schema5.optionalKey(Schema5.Boolean),
730
+ content: Schema5.optionalKey(maxLength(2000)),
731
+ file: Schema5.optionalKey(WebhookFile),
732
+ embeds: Schema5.optionalKey(EmbedArray)
628
733
  }) {
629
734
  }
630
735
 
631
736
  class WebhookParameter extends Schema5.Class("WebhookParameter")({
632
- name: Schema5.optional(maxLength(80)),
633
- avatar: Schema5.optional(UrlString),
634
- channel_id: Schema5.optional(Schema5.String)
737
+ name: Schema5.optionalKey(maxLength(80)),
738
+ avatar: Schema5.optionalKey(UrlString),
739
+ channel_id: Schema5.optionalKey(Schema5.String)
635
740
  }) {
636
741
  }
637
742
 
638
743
  class WebhookResponse extends Schema5.Class("WebhookResponse")({
639
744
  id: Schema5.String,
640
745
  type: Schema5.Number,
641
- guild_id: Schema5.optional(Schema5.String),
746
+ guild_id: Schema5.optionalKey(Schema5.String),
642
747
  channel_id: Schema5.String,
643
- user: Schema5.optional(Schema5.Struct({
748
+ user: Schema5.optionalKey(Schema5.Struct({
644
749
  id: Schema5.String,
645
750
  username: Schema5.String,
646
- avatar: Schema5.optional(Schema5.String),
751
+ avatar: Schema5.optionalKey(Schema5.String),
647
752
  discriminator: Schema5.String,
648
- public_flags: Schema5.optional(Schema5.Number)
753
+ public_flags: Schema5.optionalKey(Schema5.Number)
649
754
  })),
650
- name: Schema5.optional(Schema5.String),
651
- avatar: Schema5.optional(Schema5.String),
652
- token: Schema5.optional(Schema5.String),
653
- application_id: Schema5.optional(Schema5.String),
654
- source_guild: Schema5.optional(Schema5.Struct({
755
+ name: Schema5.optionalKey(Schema5.String),
756
+ avatar: Schema5.optionalKey(Schema5.String),
757
+ token: Schema5.optionalKey(Schema5.String),
758
+ application_id: Schema5.optionalKey(Schema5.String),
759
+ source_guild: Schema5.optionalKey(Schema5.Struct({
655
760
  id: Schema5.String,
656
761
  name: Schema5.String,
657
- icon: Schema5.optional(Schema5.String)
762
+ icon: Schema5.optionalKey(Schema5.String)
658
763
  })),
659
- source_channel: Schema5.optional(Schema5.Struct({
764
+ source_channel: Schema5.optionalKey(Schema5.Struct({
660
765
  id: Schema5.String,
661
766
  name: Schema5.String
662
767
  })),
663
- url: Schema5.optional(UrlString)
768
+ url: Schema5.optionalKey(UrlString)
664
769
  }) {
665
770
  }
666
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));
667
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";
668
881
  var createDraft = () => ({});
669
- var setTitleImpl = (title) => (d) => pipe(Schema6.decodeUnknown(maxLength(256))(title), Effect3.mapError(() => ValidationError.fromIssue("title", "Title must be 256 characters or less", {
882
+ var setTitleImpl = (title) => (d) => pipe2(Schema7.decodeUnknownEffect(maxLength(256))(title), Effect4.mapError(() => ValidationError.fromIssue("title", "Title must be 256 characters or less", {
670
883
  constraint: "MaxLength",
671
884
  expected: "256 characters or less",
672
885
  actual: title.length
673
- })), Effect3.as({ ...d, title }));
674
- var setURLImpl = (url) => (d) => pipe(Schema6.decodeUnknown(UrlString)(url), Effect3.mapError(() => ValidationError.fromIssue("url", "Invalid URL", {
886
+ })), Effect4.as({ ...d, title }));
887
+ var setURLImpl = (url) => (d) => pipe2(Schema7.decodeUnknownEffect(UrlString)(url), Effect4.mapError(() => ValidationError.fromIssue("url", "Invalid URL", {
675
888
  constraint: "URL",
676
889
  expected: "valid URL",
677
890
  actual: url
678
- })), Effect3.as({ ...d, url }));
679
- var setDescriptionImpl = (description) => (d) => pipe(Schema6.decodeUnknown(maxLength(4096))(description), Effect3.mapError(() => ValidationError.fromIssue("description", "Description must be 4096 characters or less", {
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", {
680
893
  constraint: "MaxLength",
681
894
  expected: "4096 characters or less",
682
895
  actual: description.length
683
- })), Effect3.as({ ...d, description }));
684
- var setTimestampImpl = (date) => (d) => Effect3.succeed({ ...d, timestamp: (date ?? new Date).toISOString() });
896
+ })), Effect4.as({ ...d, description }));
897
+ var setTimestampImpl = (date) => (d) => Effect4.succeed({ ...d, timestamp: (date ?? new Date).toISOString() });
685
898
  var setColorImpl = (color) => (d) => {
686
899
  const colorValue = typeof color === "string" ? parseInt(color.replace("#", ""), 16) : color;
687
- return pipe(Schema6.decodeUnknown(ColorCode)(colorValue), Effect3.mapError(() => ValidationError.fromIssue("color", "Invalid color code (must be 0-16777215)", {
900
+ return pipe2(Schema7.decodeUnknownEffect(ColorCode)(colorValue), Effect4.mapError(() => ValidationError.fromIssue("color", "Invalid color code (must be 0-16777215)", {
688
901
  constraint: "Range",
689
902
  expected: "0-16777215",
690
903
  actual: colorValue
691
- })), Effect3.as({ ...d, color: colorValue }));
904
+ })), Effect4.as({ ...d, color: colorValue }));
692
905
  };
693
906
  var setFooterImpl = (footerOrText, icon_url, proxy_icon_url) => (d) => {
694
- const footer = typeof footerOrText === "string" ? { text: footerOrText, icon_url, proxy_icon_url } : footerOrText;
695
- return pipe(Schema6.decodeUnknown(Footer)(footer), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "footer", value: footer })), Effect3.as({ ...d, footer }));
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 }));
696
920
  };
697
921
  var setImageImpl = (imageOrUrl, height, width) => (d) => {
698
- const image = typeof imageOrUrl === "string" ? { url: imageOrUrl, height, width } : imageOrUrl;
699
- return pipe(Schema6.decodeUnknown(Image)(image), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "image", value: image })), Effect3.as({ ...d, image }));
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 }));
700
936
  };
701
937
  var setThumbnailImpl = (thumbnailOrUrl, height, width) => (d) => {
702
- const thumbnail = typeof thumbnailOrUrl === "string" ? { url: thumbnailOrUrl, height, width } : thumbnailOrUrl;
703
- return pipe(Schema6.decodeUnknown(Thumbnail)(thumbnail), Effect3.mapError((e) => ValidationError.fromParseError(e, {
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, {
704
952
  field: "thumbnail",
705
953
  value: thumbnail
706
- })), Effect3.as({ ...d, thumbnail }));
954
+ })), Effect4.as({ ...d, thumbnail }));
707
955
  };
708
956
  var setVideoImpl = (videoOrUrl, height, width) => (d) => {
709
- const video = typeof videoOrUrl === "string" ? { url: videoOrUrl, height, width } : videoOrUrl;
710
- return pipe(Schema6.decodeUnknown(Video)(video), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "video", value: video })), Effect3.as({ ...d, video }));
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 }));
711
971
  };
712
972
  var setProviderImpl = (providerOrName, url) => (d) => {
713
- const provider = typeof providerOrName === "string" ? { name: providerOrName, url } : providerOrName;
714
- return pipe(Schema6.decodeUnknown(Provider)(provider), Effect3.mapError((e) => ValidationError.fromParseError(e, {
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, {
715
984
  field: "provider",
716
985
  value: provider
717
- })), Effect3.as({ ...d, provider }));
986
+ })), Effect4.as({ ...d, provider }));
718
987
  };
719
988
  var setAuthorImpl = (authorOrName, url, icon_url) => (d) => {
720
- const author = typeof authorOrName === "string" ? { name: authorOrName, url, icon_url } : authorOrName;
721
- return pipe(Schema6.decodeUnknown(Author)(author), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "author", value: author })), Effect3.as({ ...d, author }));
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 }));
722
1003
  };
723
1004
  var addFieldImpl = (fieldOrName, value, inline = false) => (d) => {
724
1005
  const currentFields = d.fields || [];
725
1006
  if (currentFields.length >= 25) {
726
- return Effect3.fail(ValidationError.fromIssue("fields", "Cannot add more than 25 fields", {
1007
+ return Effect4.fail(ValidationError.fromIssue("fields", "Cannot add more than 25 fields", {
727
1008
  constraint: "MaxItems",
728
1009
  expected: "25 fields or less",
729
1010
  actual: currentFields.length + 1
730
1011
  }));
731
1012
  }
732
- const field = typeof fieldOrName === "string" ? { name: fieldOrName, value: value ?? "", inline } : fieldOrName;
733
- return pipe(Schema6.decodeUnknown(Field)(field), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "fields", value: field })), Effect3.map(() => ({ ...d, fields: [...currentFields, field] })));
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] })));
734
1022
  };
735
1023
  var setFieldsImpl = (fields) => (d) => {
736
1024
  if (fields.length > 25) {
737
- return Effect3.fail(ValidationError.fromIssue("fields", "Cannot have more than 25 fields", {
1025
+ return Effect4.fail(ValidationError.fromIssue("fields", "Cannot have more than 25 fields", {
738
1026
  constraint: "MaxItems",
739
1027
  expected: "25 fields or less",
740
1028
  actual: fields.length
741
1029
  }));
742
1030
  }
743
- return pipe(Effect3.forEach(fields, (f) => Schema6.decodeUnknown(Field)(f)), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "fields", value: fields })), Effect3.map(() => ({ ...d, fields: [...fields] })));
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 })));
744
1040
  };
745
- var buildImpl = (d) => pipe(Schema6.decodeUnknown(Embed)({ ...d, type: "rich" }), Effect3.mapError((e) => ValidationError.fromParseError(e, { field: "embed", value: d })));
1041
+ var buildImpl = (d) => pipe2(Schema7.decodeUnknownEffect(Embed)(stripUndefined({ ...d, type: "rich" })), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "embed", value: d })));
746
1042
  var buildDirect = buildImpl;
747
1043
  var addFieldDirect = addFieldImpl;
748
- var make = Effect3.succeed(createDraft());
749
- var chain = (f) => (self) => pipe(self, Effect3.flatMap(f));
1044
+ var make = Effect4.succeed(createDraft());
1045
+ var chain = (f) => (self) => pipe2(self, Effect4.flatMap(f));
750
1046
  var setTitle = (title) => chain(setTitleImpl(title));
751
1047
  var setURL = (url) => chain(setURLImpl(url));
752
1048
  var setDescription = (description) => chain(setDescriptionImpl(description));
@@ -760,10 +1056,11 @@ var setProvider = (provider, url) => chain(setProviderImpl(provider, url));
760
1056
  var setAuthor = (author, url, icon_url) => chain(setAuthorImpl(author, url, icon_url));
761
1057
  var addField = (field, value, inline = false) => chain(addFieldImpl(field, value, inline));
762
1058
  var setFields = (fields) => chain(setFieldsImpl(fields));
763
- var build = (self) => pipe(self, Effect3.flatMap(buildImpl));
1059
+ var build = (self) => pipe2(self, Effect4.flatMap(buildImpl));
764
1060
  // src/pipes/Webhook.ts
765
1061
  var exports_Webhook = {};
766
1062
  __export(exports_Webhook, {
1063
+ validate: () => validate,
767
1064
  setUsername: () => setUsername,
768
1065
  setTTS: () => setTTS,
769
1066
  setFile: () => setFile,
@@ -777,123 +1074,52 @@ __export(exports_Webhook, {
777
1074
  addEmbedDirect: () => addEmbedDirect,
778
1075
  addEmbed: () => addEmbed
779
1076
  });
780
- import { Effect as Effect4, pipe as pipe2, Schema as Schema7 } from "effect";
1077
+ import { Effect as Effect5, pipe as pipe3, Schema as Schema8 } from "effect";
781
1078
  var createDraft2 = () => ({});
782
- var setUsernameImpl = (username) => (d) => pipe2(Schema7.decodeUnknown(maxLength(80))(username), Effect4.mapError(() => ValidationError.fromIssue("username", "Username must be 80 characters or less", {
1079
+ var setUsernameImpl = (username) => (d) => pipe3(Schema8.decodeUnknownEffect(maxLength(80))(username), Effect5.mapError(() => ValidationError.fromIssue("username", "Username must be 80 characters or less", {
783
1080
  constraint: "MaxLength",
784
1081
  expected: "80 characters or less",
785
1082
  actual: username.length
786
- })), Effect4.as({ ...d, username }));
787
- var setAvatarUrlImpl = (avatar_url) => (d) => pipe2(Schema7.decodeUnknown(UrlString)(avatar_url), Effect4.mapError(() => ValidationError.fromIssue("avatar_url", "Invalid avatar URL", {
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", {
788
1085
  constraint: "URL",
789
1086
  expected: "valid URL",
790
1087
  actual: avatar_url
791
- })), Effect4.as({ ...d, avatar_url }));
792
- var setContentImpl = (content) => (d) => pipe2(Schema7.decodeUnknown(maxLength(2000))(content), Effect4.mapError(() => ValidationError.fromIssue("content", "Content must be 2000 characters or less", {
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", {
793
1090
  constraint: "MaxLength",
794
1091
  expected: "2000 characters or less",
795
1092
  actual: content.length
796
- })), Effect4.as({ ...d, content }));
797
- var setTTSImpl = (tts) => (d) => Effect4.succeed({ ...d, tts });
1093
+ })), Effect5.as({ ...d, content }));
1094
+ var setTTSImpl = (tts) => (d) => Effect5.succeed({ ...d, tts });
798
1095
  var addEmbedImpl = (embed) => (d) => {
799
1096
  const current = d.embeds || [];
800
1097
  const next = [...current, embed];
801
- return pipe2(Schema7.decodeUnknown(EmbedArray)(next), Effect4.mapError(() => ValidationError.fromIssue("embeds", "Cannot have more than 10 embeds", {
1098
+ return pipe3(Schema8.decodeUnknownEffect(EmbedArray)(stripUndefined(next)), Effect5.mapError(() => ValidationError.fromIssue("embeds", "Cannot have more than 10 embeds", {
802
1099
  constraint: "MaxItems",
803
1100
  expected: "10 embeds or less",
804
1101
  actual: next.length
805
- })), Effect4.as({ ...d, embeds: next }));
1102
+ })), Effect5.map((decoded) => ({ ...d, embeds: decoded })));
806
1103
  };
807
- var setFileImpl = (file) => (d) => Effect4.succeed({ ...d, file });
808
- var buildImpl2 = (d) => pipe2(Schema7.decodeUnknown(Webhook)(d), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "webhook", value: d })));
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 })));
809
1106
  var buildDirect2 = buildImpl2;
810
1107
  var addEmbedDirect = addEmbedImpl;
811
- var modifyParams = (p) => pipe2(Schema7.decodeUnknown(WebhookParameter)(p), Effect4.mapError((e) => ValidationError.fromParseError(e, { field: "params", value: p })));
812
- var make2 = Effect4.succeed(createDraft2());
813
- var chain2 = (f) => (self) => pipe2(self, Effect4.flatMap(f));
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));
814
1112
  var setUsername = (username) => chain2(setUsernameImpl(username));
815
1113
  var setAvatarUrl = (url) => chain2(setAvatarUrlImpl(url));
816
1114
  var setContent = (content) => chain2(setContentImpl(content));
817
1115
  var setTTS = (tts) => chain2(setTTSImpl(tts));
818
1116
  var addEmbed = (embed) => chain2(addEmbedImpl(embed));
819
1117
  var setFile = (file) => chain2(setFileImpl(file));
820
- var build2 = (self) => pipe2(self, Effect4.flatMap(buildImpl2));
821
- // src/services/WebhookService.ts
822
- import {
823
- Context as Context3,
824
- Duration as Duration2,
825
- Effect as Effect5,
826
- Layer as Layer3,
827
- pipe as pipe3,
828
- Schedule as Schedule2,
829
- Schema as Schema8
830
- } from "effect";
831
- class WebhookService extends Context3.Tag("WebhookService")() {
832
- }
833
- var makeWebhookService = Effect5.gen(function* () {
834
- const config = yield* Config;
835
- const httpClient = yield* HttpClient;
836
- const webhookUrl = config.webhook.webhookUrl;
837
- const retrySchedule = createRetrySchedule({
838
- maxRetries: config.webhook.maxRetries ?? httpClient.retryConfig.maxRetries,
839
- baseDelay: Duration2.millis(config.webhook.baseDelayMs ?? Duration2.toMillis(httpClient.retryConfig.baseDelay)),
840
- maxDelay: Duration2.millis(config.webhook.maxDelayMs ?? Duration2.toMillis(httpClient.retryConfig.maxDelay)),
841
- jitter: config.webhook.enableJitter ?? httpClient.retryConfig.jitter
842
- });
843
- const sendWebhook = (webhook) => pipe3(Schema8.decodeUnknown(Webhook)(webhook), Effect5.mapError((e) => ValidationError.fromParseError(e, { field: "webhook", value: webhook })), Effect5.flatMap((validatedWebhook) => httpClient.request({
844
- method: "POST",
845
- url: webhookUrl,
846
- body: validatedWebhook
847
- }).pipe(Effect5.retry(retrySchedule.pipe(Schedule2.whileInput((error) => {
848
- if (error instanceof NetworkError)
849
- return true;
850
- if (error instanceof RateLimitError)
851
- return true;
852
- if (error instanceof HttpError) {
853
- const status = error.response?.status;
854
- return typeof status === "number" && status >= 500;
855
- }
856
- return false;
857
- }))))), Effect5.map((response) => response.status === 204 && response.text === ""));
858
- const modifyWebhook = (params) => pipe3(Schema8.decodeUnknown(WebhookParameter)(params), Effect5.mapError((e) => ValidationError.fromParseError(e, { field: "params", value: params })), Effect5.flatMap((validatedParams) => httpClient.request({
859
- method: "PATCH",
860
- url: webhookUrl,
861
- body: validatedParams
862
- })), Effect5.flatMap((response) => Schema8.decodeUnknown(WebhookResponse)(response.body).pipe(Effect5.mapError(() => new WebhookError({
863
- message: "Invalid webhook response",
864
- cause: response.body
865
- })))));
866
- const getWebhook = () => pipe3(httpClient.request({
867
- method: "GET",
868
- url: webhookUrl
869
- }), Effect5.flatMap((response) => Schema8.decodeUnknown(WebhookResponse)(response.body).pipe(Effect5.mapError(() => new WebhookError({
870
- message: "Invalid webhook response",
871
- cause: response.body
872
- })))));
873
- const validateWebhook = () => pipe3(httpClient.request({
874
- method: "GET",
875
- url: webhookUrl
876
- }), Effect5.as(true), Effect5.catchAll(() => Effect5.succeed(false)));
877
- const deleteWebhook = () => pipe3(httpClient.request({
878
- method: "DELETE",
879
- url: webhookUrl
880
- }), Effect5.map((response) => response.status === 204));
881
- return {
882
- sendWebhook,
883
- modifyWebhook,
884
- getWebhook,
885
- deleteWebhook,
886
- validateWebhook
887
- };
888
- });
889
- var WebhookServiceLive = Layer3.effect(WebhookService, makeWebhookService);
890
- var sendWebhook = (webhook) => Effect5.flatMap(WebhookService, (service) => service.sendWebhook(webhook));
891
- var modifyWebhook = (params) => Effect5.flatMap(WebhookService, (service) => service.modifyWebhook(params));
892
- var getWebhook = () => Effect5.flatMap(WebhookService, (service) => service.getWebhook());
893
- var deleteWebhook = () => Effect5.flatMap(WebhookService, (service) => service.deleteWebhook());
894
- var validateWebhook = () => Effect5.flatMap(WebhookService, (service) => service.validateWebhook());
1118
+ var build2 = (self) => pipe3(self, Effect5.flatMap(buildImpl2));
895
1119
  export {
1120
+ webhookErrorToLogObject,
896
1121
  validateWebhook,
1122
+ sendWebhookRaw,
897
1123
  sendWebhook,
898
1124
  positiveInt,
899
1125
  parseWebhookUrl,
@@ -902,11 +1128,14 @@ export {
902
1128
  modifyWebhook,
903
1129
  maxLength,
904
1130
  makeWebhookService,
1131
+ makeTestHttpClient,
905
1132
  makeIssue,
906
1133
  makeHttpClientLayer,
1134
+ makeDefaultLayer,
907
1135
  makeConfigLayer,
908
1136
  makeConfig,
909
1137
  getWebhook,
1138
+ formatWebhookError,
910
1139
  deleteWebhook,
911
1140
  defaultRetryConfig,
912
1141
  createRetrySchedule,
@@ -948,5 +1177,5 @@ export {
948
1177
  Attachment
949
1178
  };
950
1179
 
951
- //# debugId=8ED6FFCE1ADA022964756E2164756E21
1180
+ //# debugId=484262147C37081864756E2164756E21
952
1181
  //# sourceMappingURL=index.js.map