@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 +96 -22
- package/dist/index.d.cts +55 -2
- package/dist/index.d.ts +55 -2
- package/dist/index.js +91 -19
- package/package.json +4 -4
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
1923
|
-
const sanitizeCallbacks = (0,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
"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": "^
|
|
52
|
-
"@gravito/signal": "^
|
|
53
|
-
"@gravito/radiance": "^
|
|
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",
|