@gravito/flare 4.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -200,6 +200,8 @@ __export(index_exports, {
200
200
  AbortError: () => AbortError,
201
201
  BroadcastChannel: () => BroadcastChannel,
202
202
  DatabaseChannel: () => DatabaseChannel,
203
+ FlareError: () => FlareError,
204
+ FlareErrorCodes: () => FlareErrorCodes,
203
205
  LazyNotification: () => LazyNotification,
204
206
  MailChannel: () => MailChannel,
205
207
  MemoryStore: () => MemoryStore,
@@ -225,6 +227,37 @@ __export(index_exports, {
225
227
  });
226
228
  module.exports = __toCommonJS(index_exports);
227
229
 
230
+ // src/errors/FlareError.ts
231
+ var import_core = require("@gravito/core");
232
+ var FlareError = class extends import_core.InfrastructureException {
233
+ /**
234
+ * Creates a new FlareError instance.
235
+ *
236
+ * @param code - The error code.
237
+ * @param message - The error message.
238
+ * @param options - Optional infrastructure exception options.
239
+ */
240
+ constructor(code, message, options = {}) {
241
+ const status = options.retryable ? 503 : 500;
242
+ super(status, code, { message, ...options });
243
+ this.name = "FlareError";
244
+ Object.setPrototypeOf(this, new.target.prototype);
245
+ }
246
+ };
247
+
248
+ // src/errors/codes.ts
249
+ var FlareErrorCodes = {
250
+ RATE_LIMIT_EXCEEDED: "flare.rate_limit_exceeded",
251
+ SERIALIZATION_FAILED: "flare.serialization_failed",
252
+ TEMPLATE_NOT_DEFINED: "flare.template_not_defined",
253
+ NOTIFIABLE_MISSING_EMAIL: "flare.notifiable_missing_email",
254
+ INVALID_CONFIG: "flare.invalid_config",
255
+ NOTIFICATION_METHOD_NOT_IMPLEMENTED: "flare.notification_method_not_implemented",
256
+ UNSUPPORTED_PROVIDER: "flare.unsupported_provider",
257
+ CREDENTIALS_MISSING: "flare.credentials_missing",
258
+ SEND_FAILED: "flare.send_failed"
259
+ };
260
+
228
261
  // src/channels/TimeoutChannel.ts
229
262
  var TimeoutError = class extends Error {
230
263
  constructor(message) {
@@ -322,7 +355,10 @@ var BroadcastChannel = class {
322
355
  const innerChannel = {
323
356
  send: async (notification, notifiable, _options) => {
324
357
  if (!notification.toBroadcast) {
325
- throw new Error("Notification does not implement toBroadcast method");
358
+ throw new FlareError(
359
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
360
+ "Notification does not implement toBroadcast method"
361
+ );
326
362
  }
327
363
  const broadcastNotification = notification.toBroadcast(notifiable);
328
364
  const notifiableId = notifiable.getNotifiableId();
@@ -356,7 +392,10 @@ var DatabaseChannel = class {
356
392
  const innerChannel = {
357
393
  send: async (notification, notifiable, _options) => {
358
394
  if (!notification.toDatabase) {
359
- throw new Error("Notification does not implement toDatabase method");
395
+ throw new FlareError(
396
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
397
+ "Notification does not implement toDatabase method"
398
+ );
360
399
  }
361
400
  const dbNotification = notification.toDatabase(notifiable);
362
401
  await this.dbService.insertNotification({
@@ -388,7 +427,10 @@ var MailChannel = class {
388
427
  const innerChannel = {
389
428
  send: async (notification, notifiable, _options) => {
390
429
  if (!notification.toMail) {
391
- throw new Error("Notification does not implement toMail method");
430
+ throw new FlareError(
431
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
432
+ "Notification does not implement toMail method"
433
+ );
392
434
  }
393
435
  const message = notification.toMail(notifiable);
394
436
  await this.mailService.send(message);
@@ -414,7 +456,10 @@ var SlackChannel = class {
414
456
  const innerChannel = {
415
457
  send: async (notification, notifiable, options) => {
416
458
  if (!notification.toSlack) {
417
- throw new Error("Notification does not implement toSlack method");
459
+ throw new FlareError(
460
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
461
+ "Notification does not implement toSlack method"
462
+ );
418
463
  }
419
464
  const slackMessage = notification.toSlack(notifiable);
420
465
  const response = await fetch(this.config.webhookUrl, {
@@ -433,7 +478,10 @@ var SlackChannel = class {
433
478
  // Pass AbortSignal to fetch
434
479
  });
435
480
  if (!response.ok) {
436
- throw new Error(`Failed to send Slack notification: ${response.statusText}`);
481
+ throw new FlareError(
482
+ FlareErrorCodes.SEND_FAILED,
483
+ `Failed to send Slack notification: ${response.statusText}`
484
+ );
437
485
  }
438
486
  }
439
487
  };
@@ -457,7 +505,10 @@ var SmsChannel = class {
457
505
  const innerChannel = {
458
506
  send: async (notification, notifiable, options) => {
459
507
  if (!notification.toSms) {
460
- throw new Error("Notification does not implement toSms method");
508
+ throw new FlareError(
509
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
510
+ "Notification does not implement toSms method"
511
+ );
461
512
  }
462
513
  const smsMessage = notification.toSms(notifiable);
463
514
  switch (this.config.provider) {
@@ -468,7 +519,10 @@ var SmsChannel = class {
468
519
  await this.sendViaAwsSns(smsMessage, options?.signal);
469
520
  break;
470
521
  default:
471
- throw new Error(`Unsupported SMS provider: ${this.config.provider}`);
522
+ throw new FlareError(
523
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
524
+ `Unsupported SMS provider: ${this.config.provider}`
525
+ );
472
526
  }
473
527
  }
474
528
  };
@@ -487,7 +541,10 @@ var SmsChannel = class {
487
541
  */
488
542
  async sendViaTwilio(message, signal) {
489
543
  if (!this.config.apiKey || !this.config.apiSecret) {
490
- throw new Error("Twilio API key and secret are required");
544
+ throw new FlareError(
545
+ FlareErrorCodes.CREDENTIALS_MISSING,
546
+ "Twilio API key and secret are required"
547
+ );
491
548
  }
492
549
  const accountSid = this.config.apiKey;
493
550
  const authToken = this.config.apiSecret;
@@ -510,7 +567,7 @@ var SmsChannel = class {
510
567
  );
511
568
  if (!response.ok) {
512
569
  const error = await response.text();
513
- throw new Error(`Failed to send SMS via Twilio: ${error}`);
570
+ throw new FlareError(FlareErrorCodes.SEND_FAILED, `Failed to send SMS via Twilio: ${error}`);
514
571
  }
515
572
  }
516
573
  /**
@@ -524,7 +581,8 @@ var SmsChannel = class {
524
581
  SNSClient = awsSns.SNSClient;
525
582
  PublishCommand = awsSns.PublishCommand;
526
583
  } catch {
527
- throw new Error(
584
+ throw new FlareError(
585
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
528
586
  "AWS SNS SMS requires @aws-sdk/client-sns. Install it with: bun add @aws-sdk/client-sns"
529
587
  );
530
588
  }
@@ -555,7 +613,10 @@ var SmsChannel = class {
555
613
  await client.send(command, { abortSignal: signal });
556
614
  } catch (error) {
557
615
  const err = error instanceof Error ? error : new Error(String(error));
558
- throw new Error(`Failed to send SMS via AWS SNS: ${err.message}`);
616
+ throw new FlareError(
617
+ FlareErrorCodes.SEND_FAILED,
618
+ `Failed to send SMS via AWS SNS: ${err.message}`
619
+ );
559
620
  }
560
621
  }
561
622
  };
@@ -907,7 +968,8 @@ var RateLimitMiddleware = class {
907
968
  if (bucket) {
908
969
  const allowed = bucket.tryConsume();
909
970
  if (!allowed) {
910
- throw new Error(
971
+ throw new FlareError(
972
+ FlareErrorCodes.RATE_LIMIT_EXCEEDED,
911
973
  `Rate limit exceeded for channel '${channel}' (${window}ly limit). Please try again later.`
912
974
  );
913
975
  }
@@ -1210,7 +1272,8 @@ function checkSerializable(obj, path = "") {
1210
1272
  function assertSerializable(obj) {
1211
1273
  const result = checkSerializable(obj);
1212
1274
  if (!result.serializable) {
1213
- throw new Error(
1275
+ throw new FlareError(
1276
+ FlareErrorCodes.SERIALIZATION_FAILED,
1214
1277
  `\u7269\u4EF6\u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u5C6C\u6027:
1215
1278
  \u554F\u984C\u8DEF\u5F91: ${result.problematicPaths.join(", ")}
1216
1279
  \u8A73\u7D30\u8CC7\u8A0A:
@@ -1729,24 +1792,30 @@ var OrbitFlare = class _OrbitFlare {
1729
1792
  if (options.enableSlack) {
1730
1793
  const slack = options.channels?.slack;
1731
1794
  if (!slack?.webhookUrl) {
1732
- throw new Error(
1795
+ throw new FlareError(
1796
+ FlareErrorCodes.INVALID_CONFIG,
1733
1797
  "[OrbitFlare] Slack channel enabled but webhookUrl not provided. Configure channels.slack.webhookUrl or set enableSlack to false."
1734
1798
  );
1735
1799
  }
1736
1800
  if (!this.isValidUrl(slack.webhookUrl)) {
1737
- throw new Error(`[OrbitFlare] Invalid Slack webhook URL: ${slack.webhookUrl}`);
1801
+ throw new FlareError(
1802
+ FlareErrorCodes.INVALID_CONFIG,
1803
+ `[OrbitFlare] Invalid Slack webhook URL: ${slack.webhookUrl}`
1804
+ );
1738
1805
  }
1739
1806
  }
1740
1807
  if (options.enableSms) {
1741
1808
  const sms = options.channels?.sms;
1742
1809
  if (!sms?.provider) {
1743
- throw new Error(
1810
+ throw new FlareError(
1811
+ FlareErrorCodes.INVALID_CONFIG,
1744
1812
  "[OrbitFlare] SMS channel enabled but provider not specified. Configure channels.sms.provider or set enableSms to false."
1745
1813
  );
1746
1814
  }
1747
1815
  const supportedProviders = ["twilio", "aws-sns"];
1748
1816
  if (!supportedProviders.includes(sms.provider)) {
1749
- throw new Error(
1817
+ throw new FlareError(
1818
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
1750
1819
  `[OrbitFlare] Unsupported SMS provider: ${sms.provider}. Supported providers: ${supportedProviders.join(", ")}`
1751
1820
  );
1752
1821
  }
@@ -1869,7 +1938,7 @@ var OrbitFlare = class _OrbitFlare {
1869
1938
  };
1870
1939
 
1871
1940
  // src/templates/NotificationTemplate.ts
1872
- var import_core = require("@gravito/core");
1941
+ var import_core2 = require("@gravito/core");
1873
1942
  var TemplatedNotification = class extends Notification {
1874
1943
  data = {};
1875
1944
  with(data) {
@@ -1889,7 +1958,7 @@ var TemplatedNotification = class extends Notification {
1889
1958
  // Auto-implement toSlack
1890
1959
  toSlack(_notifiable) {
1891
1960
  if (!this.slackTemplate) {
1892
- throw new Error("slackTemplate not defined");
1961
+ throw new FlareError(FlareErrorCodes.TEMPLATE_NOT_DEFINED, "slackTemplate not defined");
1893
1962
  }
1894
1963
  const template = this.slackTemplate();
1895
1964
  return {
@@ -1919,8 +1988,8 @@ var TemplatedNotification = class extends Notification {
1919
1988
  return "";
1920
1989
  }
1921
1990
  const interpolated = this.interpolate(template);
1922
- const md = (0, import_core.getMarkdownAdapter)();
1923
- const sanitizeCallbacks = (0, import_core.createHtmlRenderCallbacks)({
1991
+ const md = (0, import_core2.getMarkdownAdapter)();
1992
+ const sanitizeCallbacks = (0, import_core2.createHtmlRenderCallbacks)({
1924
1993
  html: (rawHtml) => sanitizeHtml(rawHtml)
1925
1994
  });
1926
1995
  return md.render(interpolated, sanitizeCallbacks);
@@ -1932,7 +2001,10 @@ var TemplatedNotification = class extends Notification {
1932
2001
  if ("email" in notifiable && typeof notifiable.email === "string") {
1933
2002
  return notifiable.email;
1934
2003
  }
1935
- throw new Error("Notifiable does not have an email property");
2004
+ throw new FlareError(
2005
+ FlareErrorCodes.NOTIFIABLE_MISSING_EMAIL,
2006
+ "Notifiable does not have an email property"
2007
+ );
1936
2008
  }
1937
2009
  };
1938
2010
  var DANGEROUS_TAGS = /^<\/?(?:script|iframe|object|embed|form|input|textarea|button|select|style|link|meta|base)\b[^>]*>$/i;
@@ -2004,6 +2076,8 @@ var LazyNotification = class extends Notification {
2004
2076
  AbortError,
2005
2077
  BroadcastChannel,
2006
2078
  DatabaseChannel,
2079
+ FlareError,
2080
+ FlareErrorCodes,
2007
2081
  LazyNotification,
2008
2082
  MailChannel,
2009
2083
  MemoryStore,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { PlanetCore, GravitoOrbit } from '@gravito/core';
1
+ import { InfrastructureException, InfrastructureExceptionOptions, PlanetCore, GravitoOrbit } from '@gravito/core';
2
2
 
3
3
  /**
4
4
  * Notification system type definitions.
@@ -404,6 +404,59 @@ declare class BroadcastChannel implements NotificationChannel {
404
404
  send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise<void>;
405
405
  }
406
406
 
407
+ /**
408
+ * @fileoverview Flare error codes
409
+ *
410
+ * Namespaced error codes for the Flare notification module.
411
+ *
412
+ * @module @gravito/flare/errors
413
+ */
414
+ /**
415
+ * Error codes for Flare module operations.
416
+ * Follows dot-separated namespace convention.
417
+ */
418
+ declare const FlareErrorCodes: {
419
+ readonly RATE_LIMIT_EXCEEDED: "flare.rate_limit_exceeded";
420
+ readonly SERIALIZATION_FAILED: "flare.serialization_failed";
421
+ readonly TEMPLATE_NOT_DEFINED: "flare.template_not_defined";
422
+ readonly NOTIFIABLE_MISSING_EMAIL: "flare.notifiable_missing_email";
423
+ readonly INVALID_CONFIG: "flare.invalid_config";
424
+ readonly NOTIFICATION_METHOD_NOT_IMPLEMENTED: "flare.notification_method_not_implemented";
425
+ readonly UNSUPPORTED_PROVIDER: "flare.unsupported_provider";
426
+ readonly CREDENTIALS_MISSING: "flare.credentials_missing";
427
+ readonly SEND_FAILED: "flare.send_failed";
428
+ };
429
+ type FlareErrorCode = (typeof FlareErrorCodes)[keyof typeof FlareErrorCodes];
430
+
431
+ /**
432
+ * @fileoverview Flare error types
433
+ *
434
+ * @module @gravito/flare/errors
435
+ */
436
+
437
+ /**
438
+ * Base error class for Flare module.
439
+ *
440
+ * Provides structured error handling with error codes and messages.
441
+ * Extends InfrastructureException for unified error handling across Gravito.
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * throw new FlareError('flare.send_failed', 'Failed to send notification')
446
+ * ```
447
+ * @public
448
+ */
449
+ declare class FlareError extends InfrastructureException {
450
+ /**
451
+ * Creates a new FlareError instance.
452
+ *
453
+ * @param code - The error code.
454
+ * @param message - The error message.
455
+ * @param options - Optional infrastructure exception options.
456
+ */
457
+ constructor(code: FlareErrorCode, message: string, options?: InfrastructureExceptionOptions);
458
+ }
459
+
407
460
  /**
408
461
  * Database channel 配置選項。
409
462
  */
@@ -1882,4 +1935,4 @@ declare class TokenBucket {
1882
1935
  private refill;
1883
1936
  }
1884
1937
 
1885
- export { AbortError, type BatchResult, BroadcastChannel, type BroadcastNotification, type CacheStore, type ChannelFailurePayload, type ChannelHookPayload, type ChannelMiddleware, type ChannelRateLimitConfig, type ChannelSuccessPayload, DatabaseChannel, type DatabaseNotification, type FlareHookEvent, type FlareHookPayloads, type FlareHooks, type HookEmitter, LazyNotification, MailChannel, type MailChannelConfig, type MailMessage, type MailTemplate, MemoryStore, type MetricsSummary, MiddlewarePriority, type MiddlewarePriorityValue, type Notifiable, Notification, type NotificationBatchCompletePayload, type NotificationBatchStartPayload, type NotificationChannel, type NotificationCompletePayload, type NotificationHookPayload, NotificationManager, type NotificationMetric, NotificationMetricsCollector, type NotificationPreference, type NotificationResult, OrbitFlare, type OrbitFlareOptions, PreferenceMiddleware, type RateLimitConfig, RateLimitMiddleware, type RetryConfig, type SendOptions, type SendResult, type SerializationCheckResult, type ShouldQueue, type ShouldRetry, SlackChannel, type SlackChannelConfig, type SlackMessage, type SlackTemplate, SmsChannel, type SmsChannelConfig, type SmsMessage, type TemplateData, TemplatedNotification, TimeoutChannel, type TimeoutConfig, TimeoutError, TokenBucket, assertSerializable, checkSerializable, createHookEmitter, deepDeserialize, deepSerialize, toPrometheusFormat };
1938
+ export { AbortError, type BatchResult, BroadcastChannel, type BroadcastNotification, type CacheStore, type ChannelFailurePayload, type ChannelHookPayload, type ChannelMiddleware, type ChannelRateLimitConfig, type ChannelSuccessPayload, DatabaseChannel, type DatabaseNotification, FlareError, type FlareErrorCode, FlareErrorCodes, type FlareHookEvent, type FlareHookPayloads, type FlareHooks, type HookEmitter, LazyNotification, MailChannel, type MailChannelConfig, type MailMessage, type MailTemplate, MemoryStore, type MetricsSummary, MiddlewarePriority, type MiddlewarePriorityValue, type Notifiable, Notification, type NotificationBatchCompletePayload, type NotificationBatchStartPayload, type NotificationChannel, type NotificationCompletePayload, type NotificationHookPayload, NotificationManager, type NotificationMetric, NotificationMetricsCollector, type NotificationPreference, type NotificationResult, OrbitFlare, type OrbitFlareOptions, PreferenceMiddleware, type RateLimitConfig, RateLimitMiddleware, type RetryConfig, type SendOptions, type SendResult, type SerializationCheckResult, type ShouldQueue, type ShouldRetry, SlackChannel, type SlackChannelConfig, type SlackMessage, type SlackTemplate, SmsChannel, type SmsChannelConfig, type SmsMessage, type TemplateData, TemplatedNotification, TimeoutChannel, type TimeoutConfig, TimeoutError, TokenBucket, assertSerializable, checkSerializable, createHookEmitter, deepDeserialize, deepSerialize, toPrometheusFormat };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { PlanetCore, GravitoOrbit } from '@gravito/core';
1
+ import { InfrastructureException, InfrastructureExceptionOptions, PlanetCore, GravitoOrbit } from '@gravito/core';
2
2
 
3
3
  /**
4
4
  * Notification system type definitions.
@@ -404,6 +404,59 @@ declare class BroadcastChannel implements NotificationChannel {
404
404
  send(notification: Notification, notifiable: Notifiable, options?: AbortableSendOptions): Promise<void>;
405
405
  }
406
406
 
407
+ /**
408
+ * @fileoverview Flare error codes
409
+ *
410
+ * Namespaced error codes for the Flare notification module.
411
+ *
412
+ * @module @gravito/flare/errors
413
+ */
414
+ /**
415
+ * Error codes for Flare module operations.
416
+ * Follows dot-separated namespace convention.
417
+ */
418
+ declare const FlareErrorCodes: {
419
+ readonly RATE_LIMIT_EXCEEDED: "flare.rate_limit_exceeded";
420
+ readonly SERIALIZATION_FAILED: "flare.serialization_failed";
421
+ readonly TEMPLATE_NOT_DEFINED: "flare.template_not_defined";
422
+ readonly NOTIFIABLE_MISSING_EMAIL: "flare.notifiable_missing_email";
423
+ readonly INVALID_CONFIG: "flare.invalid_config";
424
+ readonly NOTIFICATION_METHOD_NOT_IMPLEMENTED: "flare.notification_method_not_implemented";
425
+ readonly UNSUPPORTED_PROVIDER: "flare.unsupported_provider";
426
+ readonly CREDENTIALS_MISSING: "flare.credentials_missing";
427
+ readonly SEND_FAILED: "flare.send_failed";
428
+ };
429
+ type FlareErrorCode = (typeof FlareErrorCodes)[keyof typeof FlareErrorCodes];
430
+
431
+ /**
432
+ * @fileoverview Flare error types
433
+ *
434
+ * @module @gravito/flare/errors
435
+ */
436
+
437
+ /**
438
+ * Base error class for Flare module.
439
+ *
440
+ * Provides structured error handling with error codes and messages.
441
+ * Extends InfrastructureException for unified error handling across Gravito.
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * throw new FlareError('flare.send_failed', 'Failed to send notification')
446
+ * ```
447
+ * @public
448
+ */
449
+ declare class FlareError extends InfrastructureException {
450
+ /**
451
+ * Creates a new FlareError instance.
452
+ *
453
+ * @param code - The error code.
454
+ * @param message - The error message.
455
+ * @param options - Optional infrastructure exception options.
456
+ */
457
+ constructor(code: FlareErrorCode, message: string, options?: InfrastructureExceptionOptions);
458
+ }
459
+
407
460
  /**
408
461
  * Database channel 配置選項。
409
462
  */
@@ -1882,4 +1935,4 @@ declare class TokenBucket {
1882
1935
  private refill;
1883
1936
  }
1884
1937
 
1885
- export { AbortError, type BatchResult, BroadcastChannel, type BroadcastNotification, type CacheStore, type ChannelFailurePayload, type ChannelHookPayload, type ChannelMiddleware, type ChannelRateLimitConfig, type ChannelSuccessPayload, DatabaseChannel, type DatabaseNotification, type FlareHookEvent, type FlareHookPayloads, type FlareHooks, type HookEmitter, LazyNotification, MailChannel, type MailChannelConfig, type MailMessage, type MailTemplate, MemoryStore, type MetricsSummary, MiddlewarePriority, type MiddlewarePriorityValue, type Notifiable, Notification, type NotificationBatchCompletePayload, type NotificationBatchStartPayload, type NotificationChannel, type NotificationCompletePayload, type NotificationHookPayload, NotificationManager, type NotificationMetric, NotificationMetricsCollector, type NotificationPreference, type NotificationResult, OrbitFlare, type OrbitFlareOptions, PreferenceMiddleware, type RateLimitConfig, RateLimitMiddleware, type RetryConfig, type SendOptions, type SendResult, type SerializationCheckResult, type ShouldQueue, type ShouldRetry, SlackChannel, type SlackChannelConfig, type SlackMessage, type SlackTemplate, SmsChannel, type SmsChannelConfig, type SmsMessage, type TemplateData, TemplatedNotification, TimeoutChannel, type TimeoutConfig, TimeoutError, TokenBucket, assertSerializable, checkSerializable, createHookEmitter, deepDeserialize, deepSerialize, toPrometheusFormat };
1938
+ export { AbortError, type BatchResult, BroadcastChannel, type BroadcastNotification, type CacheStore, type ChannelFailurePayload, type ChannelHookPayload, type ChannelMiddleware, type ChannelRateLimitConfig, type ChannelSuccessPayload, DatabaseChannel, type DatabaseNotification, FlareError, type FlareErrorCode, FlareErrorCodes, type FlareHookEvent, type FlareHookPayloads, type FlareHooks, type HookEmitter, LazyNotification, MailChannel, type MailChannelConfig, type MailMessage, type MailTemplate, MemoryStore, type MetricsSummary, MiddlewarePriority, type MiddlewarePriorityValue, type Notifiable, Notification, type NotificationBatchCompletePayload, type NotificationBatchStartPayload, type NotificationChannel, type NotificationCompletePayload, type NotificationHookPayload, NotificationManager, type NotificationMetric, NotificationMetricsCollector, type NotificationPreference, type NotificationResult, OrbitFlare, type OrbitFlareOptions, PreferenceMiddleware, type RateLimitConfig, RateLimitMiddleware, type RetryConfig, type SendOptions, type SendResult, type SerializationCheckResult, type ShouldQueue, type ShouldRetry, SlackChannel, type SlackChannelConfig, type SlackMessage, type SlackTemplate, SmsChannel, type SmsChannelConfig, type SmsMessage, type TemplateData, TemplatedNotification, TimeoutChannel, type TimeoutConfig, TimeoutError, TokenBucket, assertSerializable, checkSerializable, createHookEmitter, deepDeserialize, deepSerialize, toPrometheusFormat };
package/dist/index.js CHANGED
@@ -183,6 +183,37 @@ var init_PreferenceMiddleware = __esm({
183
183
  }
184
184
  });
185
185
 
186
+ // src/errors/FlareError.ts
187
+ import { InfrastructureException } from "@gravito/core";
188
+ var FlareError = class extends InfrastructureException {
189
+ /**
190
+ * Creates a new FlareError instance.
191
+ *
192
+ * @param code - The error code.
193
+ * @param message - The error message.
194
+ * @param options - Optional infrastructure exception options.
195
+ */
196
+ constructor(code, message, options = {}) {
197
+ const status = options.retryable ? 503 : 500;
198
+ super(status, code, { message, ...options });
199
+ this.name = "FlareError";
200
+ Object.setPrototypeOf(this, new.target.prototype);
201
+ }
202
+ };
203
+
204
+ // src/errors/codes.ts
205
+ var FlareErrorCodes = {
206
+ RATE_LIMIT_EXCEEDED: "flare.rate_limit_exceeded",
207
+ SERIALIZATION_FAILED: "flare.serialization_failed",
208
+ TEMPLATE_NOT_DEFINED: "flare.template_not_defined",
209
+ NOTIFIABLE_MISSING_EMAIL: "flare.notifiable_missing_email",
210
+ INVALID_CONFIG: "flare.invalid_config",
211
+ NOTIFICATION_METHOD_NOT_IMPLEMENTED: "flare.notification_method_not_implemented",
212
+ UNSUPPORTED_PROVIDER: "flare.unsupported_provider",
213
+ CREDENTIALS_MISSING: "flare.credentials_missing",
214
+ SEND_FAILED: "flare.send_failed"
215
+ };
216
+
186
217
  // src/channels/TimeoutChannel.ts
187
218
  var TimeoutError = class extends Error {
188
219
  constructor(message) {
@@ -280,7 +311,10 @@ var BroadcastChannel = class {
280
311
  const innerChannel = {
281
312
  send: async (notification, notifiable, _options) => {
282
313
  if (!notification.toBroadcast) {
283
- throw new Error("Notification does not implement toBroadcast method");
314
+ throw new FlareError(
315
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
316
+ "Notification does not implement toBroadcast method"
317
+ );
284
318
  }
285
319
  const broadcastNotification = notification.toBroadcast(notifiable);
286
320
  const notifiableId = notifiable.getNotifiableId();
@@ -314,7 +348,10 @@ var DatabaseChannel = class {
314
348
  const innerChannel = {
315
349
  send: async (notification, notifiable, _options) => {
316
350
  if (!notification.toDatabase) {
317
- throw new Error("Notification does not implement toDatabase method");
351
+ throw new FlareError(
352
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
353
+ "Notification does not implement toDatabase method"
354
+ );
318
355
  }
319
356
  const dbNotification = notification.toDatabase(notifiable);
320
357
  await this.dbService.insertNotification({
@@ -346,7 +383,10 @@ var MailChannel = class {
346
383
  const innerChannel = {
347
384
  send: async (notification, notifiable, _options) => {
348
385
  if (!notification.toMail) {
349
- throw new Error("Notification does not implement toMail method");
386
+ throw new FlareError(
387
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
388
+ "Notification does not implement toMail method"
389
+ );
350
390
  }
351
391
  const message = notification.toMail(notifiable);
352
392
  await this.mailService.send(message);
@@ -372,7 +412,10 @@ var SlackChannel = class {
372
412
  const innerChannel = {
373
413
  send: async (notification, notifiable, options) => {
374
414
  if (!notification.toSlack) {
375
- throw new Error("Notification does not implement toSlack method");
415
+ throw new FlareError(
416
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
417
+ "Notification does not implement toSlack method"
418
+ );
376
419
  }
377
420
  const slackMessage = notification.toSlack(notifiable);
378
421
  const response = await fetch(this.config.webhookUrl, {
@@ -391,7 +434,10 @@ var SlackChannel = class {
391
434
  // Pass AbortSignal to fetch
392
435
  });
393
436
  if (!response.ok) {
394
- throw new Error(`Failed to send Slack notification: ${response.statusText}`);
437
+ throw new FlareError(
438
+ FlareErrorCodes.SEND_FAILED,
439
+ `Failed to send Slack notification: ${response.statusText}`
440
+ );
395
441
  }
396
442
  }
397
443
  };
@@ -415,7 +461,10 @@ var SmsChannel = class {
415
461
  const innerChannel = {
416
462
  send: async (notification, notifiable, options) => {
417
463
  if (!notification.toSms) {
418
- throw new Error("Notification does not implement toSms method");
464
+ throw new FlareError(
465
+ FlareErrorCodes.NOTIFICATION_METHOD_NOT_IMPLEMENTED,
466
+ "Notification does not implement toSms method"
467
+ );
419
468
  }
420
469
  const smsMessage = notification.toSms(notifiable);
421
470
  switch (this.config.provider) {
@@ -426,7 +475,10 @@ var SmsChannel = class {
426
475
  await this.sendViaAwsSns(smsMessage, options?.signal);
427
476
  break;
428
477
  default:
429
- throw new Error(`Unsupported SMS provider: ${this.config.provider}`);
478
+ throw new FlareError(
479
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
480
+ `Unsupported SMS provider: ${this.config.provider}`
481
+ );
430
482
  }
431
483
  }
432
484
  };
@@ -445,7 +497,10 @@ var SmsChannel = class {
445
497
  */
446
498
  async sendViaTwilio(message, signal) {
447
499
  if (!this.config.apiKey || !this.config.apiSecret) {
448
- throw new Error("Twilio API key and secret are required");
500
+ throw new FlareError(
501
+ FlareErrorCodes.CREDENTIALS_MISSING,
502
+ "Twilio API key and secret are required"
503
+ );
449
504
  }
450
505
  const accountSid = this.config.apiKey;
451
506
  const authToken = this.config.apiSecret;
@@ -468,7 +523,7 @@ var SmsChannel = class {
468
523
  );
469
524
  if (!response.ok) {
470
525
  const error = await response.text();
471
- throw new Error(`Failed to send SMS via Twilio: ${error}`);
526
+ throw new FlareError(FlareErrorCodes.SEND_FAILED, `Failed to send SMS via Twilio: ${error}`);
472
527
  }
473
528
  }
474
529
  /**
@@ -482,7 +537,8 @@ var SmsChannel = class {
482
537
  SNSClient = awsSns.SNSClient;
483
538
  PublishCommand = awsSns.PublishCommand;
484
539
  } catch {
485
- throw new Error(
540
+ throw new FlareError(
541
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
486
542
  "AWS SNS SMS requires @aws-sdk/client-sns. Install it with: bun add @aws-sdk/client-sns"
487
543
  );
488
544
  }
@@ -513,7 +569,10 @@ var SmsChannel = class {
513
569
  await client.send(command, { abortSignal: signal });
514
570
  } catch (error) {
515
571
  const err = error instanceof Error ? error : new Error(String(error));
516
- throw new Error(`Failed to send SMS via AWS SNS: ${err.message}`);
572
+ throw new FlareError(
573
+ FlareErrorCodes.SEND_FAILED,
574
+ `Failed to send SMS via AWS SNS: ${err.message}`
575
+ );
517
576
  }
518
577
  }
519
578
  };
@@ -865,7 +924,8 @@ var RateLimitMiddleware = class {
865
924
  if (bucket) {
866
925
  const allowed = bucket.tryConsume();
867
926
  if (!allowed) {
868
- throw new Error(
927
+ throw new FlareError(
928
+ FlareErrorCodes.RATE_LIMIT_EXCEEDED,
869
929
  `Rate limit exceeded for channel '${channel}' (${window}ly limit). Please try again later.`
870
930
  );
871
931
  }
@@ -1168,7 +1228,8 @@ function checkSerializable(obj, path = "") {
1168
1228
  function assertSerializable(obj) {
1169
1229
  const result = checkSerializable(obj);
1170
1230
  if (!result.serializable) {
1171
- throw new Error(
1231
+ throw new FlareError(
1232
+ FlareErrorCodes.SERIALIZATION_FAILED,
1172
1233
  `\u7269\u4EF6\u5305\u542B\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u5C6C\u6027:
1173
1234
  \u554F\u984C\u8DEF\u5F91: ${result.problematicPaths.join(", ")}
1174
1235
  \u8A73\u7D30\u8CC7\u8A0A:
@@ -1687,24 +1748,30 @@ var OrbitFlare = class _OrbitFlare {
1687
1748
  if (options.enableSlack) {
1688
1749
  const slack = options.channels?.slack;
1689
1750
  if (!slack?.webhookUrl) {
1690
- throw new Error(
1751
+ throw new FlareError(
1752
+ FlareErrorCodes.INVALID_CONFIG,
1691
1753
  "[OrbitFlare] Slack channel enabled but webhookUrl not provided. Configure channels.slack.webhookUrl or set enableSlack to false."
1692
1754
  );
1693
1755
  }
1694
1756
  if (!this.isValidUrl(slack.webhookUrl)) {
1695
- throw new Error(`[OrbitFlare] Invalid Slack webhook URL: ${slack.webhookUrl}`);
1757
+ throw new FlareError(
1758
+ FlareErrorCodes.INVALID_CONFIG,
1759
+ `[OrbitFlare] Invalid Slack webhook URL: ${slack.webhookUrl}`
1760
+ );
1696
1761
  }
1697
1762
  }
1698
1763
  if (options.enableSms) {
1699
1764
  const sms = options.channels?.sms;
1700
1765
  if (!sms?.provider) {
1701
- throw new Error(
1766
+ throw new FlareError(
1767
+ FlareErrorCodes.INVALID_CONFIG,
1702
1768
  "[OrbitFlare] SMS channel enabled but provider not specified. Configure channels.sms.provider or set enableSms to false."
1703
1769
  );
1704
1770
  }
1705
1771
  const supportedProviders = ["twilio", "aws-sns"];
1706
1772
  if (!supportedProviders.includes(sms.provider)) {
1707
- throw new Error(
1773
+ throw new FlareError(
1774
+ FlareErrorCodes.UNSUPPORTED_PROVIDER,
1708
1775
  `[OrbitFlare] Unsupported SMS provider: ${sms.provider}. Supported providers: ${supportedProviders.join(", ")}`
1709
1776
  );
1710
1777
  }
@@ -1847,7 +1914,7 @@ var TemplatedNotification = class extends Notification {
1847
1914
  // Auto-implement toSlack
1848
1915
  toSlack(_notifiable) {
1849
1916
  if (!this.slackTemplate) {
1850
- throw new Error("slackTemplate not defined");
1917
+ throw new FlareError(FlareErrorCodes.TEMPLATE_NOT_DEFINED, "slackTemplate not defined");
1851
1918
  }
1852
1919
  const template = this.slackTemplate();
1853
1920
  return {
@@ -1890,7 +1957,10 @@ var TemplatedNotification = class extends Notification {
1890
1957
  if ("email" in notifiable && typeof notifiable.email === "string") {
1891
1958
  return notifiable.email;
1892
1959
  }
1893
- throw new Error("Notifiable does not have an email property");
1960
+ throw new FlareError(
1961
+ FlareErrorCodes.NOTIFIABLE_MISSING_EMAIL,
1962
+ "Notifiable does not have an email property"
1963
+ );
1894
1964
  }
1895
1965
  };
1896
1966
  var DANGEROUS_TAGS = /^<\/?(?:script|iframe|object|embed|form|input|textarea|button|select|style|link|meta|base)\b[^>]*>$/i;
@@ -1961,6 +2031,8 @@ export {
1961
2031
  AbortError,
1962
2032
  BroadcastChannel,
1963
2033
  DatabaseChannel,
2034
+ FlareError,
2035
+ FlareErrorCodes,
1964
2036
  LazyNotification,
1965
2037
  MailChannel,
1966
2038
  MemoryStore,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gravito/flare",
3
3
  "sideEffects": false,
4
- "version": "4.0.2",
4
+ "version": "5.0.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -48,9 +48,9 @@
48
48
  "@aws-sdk/client-sns": "^3.734.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "@gravito/stream": "^2.1.0",
52
- "@gravito/signal": "^3.1.0",
53
- "@gravito/radiance": "^1.0.4"
51
+ "@gravito/stream": "^3.0.0",
52
+ "@gravito/signal": "^4.0.0",
53
+ "@gravito/radiance": "^2.0.0"
54
54
  },
55
55
  "devDependencies": {
56
56
  "bun-types": "latest",