@spfn/notification 0.1.0-beta.2 → 0.1.0-beta.3
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/server.d.ts +7 -0
- package/dist/server.js +19 -5
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/dist/server.d.ts
CHANGED
|
@@ -736,6 +736,12 @@ interface ErrorSlackOptions {
|
|
|
736
736
|
* @default 500
|
|
737
737
|
*/
|
|
738
738
|
minStatusCode?: number;
|
|
739
|
+
/**
|
|
740
|
+
* Throttle window in milliseconds.
|
|
741
|
+
* Duplicate errors (same name + statusCode + path) within this window are suppressed.
|
|
742
|
+
* @default 60_000
|
|
743
|
+
*/
|
|
744
|
+
throttleMs?: number;
|
|
739
745
|
/**
|
|
740
746
|
* Webhook URL override (defaults to env/config)
|
|
741
747
|
*/
|
|
@@ -752,6 +758,7 @@ interface ErrorSlackOptions {
|
|
|
752
758
|
* Create an onError callback that sends Slack notifications
|
|
753
759
|
*
|
|
754
760
|
* Returns a function matching ErrorHandler's onError signature.
|
|
761
|
+
* Duplicate errors (same name + statusCode + path) within `throttleMs` are suppressed.
|
|
755
762
|
*/
|
|
756
763
|
declare function createErrorSlackNotifier(options?: ErrorSlackOptions): (err: Error, ctx: ErrorContext) => Promise<void>;
|
|
757
764
|
|
package/dist/server.js
CHANGED
|
@@ -3900,7 +3900,11 @@ function shortStack(err, maxLines = 3) {
|
|
|
3900
3900
|
const lines = err.stack.split("\n").slice(1);
|
|
3901
3901
|
return lines.slice(0, maxLines).map((line) => line.trim()).join("\n");
|
|
3902
3902
|
}
|
|
3903
|
-
|
|
3903
|
+
var throttleMap = /* @__PURE__ */ new Map();
|
|
3904
|
+
function throttleKey(err, ctx) {
|
|
3905
|
+
return `${err.name}:${ctx.statusCode}:${ctx.path}`;
|
|
3906
|
+
}
|
|
3907
|
+
function defaultFormat(err, ctx, suppressed = 0) {
|
|
3904
3908
|
const emoji = ctx.statusCode >= 500 ? ":rotating_light:" : ":warning:";
|
|
3905
3909
|
const title = `${emoji} *${err.name || "Error"}* \u2014 ${ctx.statusCode}`;
|
|
3906
3910
|
const fields = [
|
|
@@ -3929,11 +3933,12 @@ ${ctx.requestId ?? "(none)"}` }
|
|
|
3929
3933
|
type: "section",
|
|
3930
3934
|
fields
|
|
3931
3935
|
},
|
|
3932
|
-
// Timestamp
|
|
3936
|
+
// Timestamp + suppressed count
|
|
3933
3937
|
{
|
|
3934
3938
|
type: "context",
|
|
3935
3939
|
elements: [
|
|
3936
|
-
{ type: "mrkdwn", text: `*Time:* ${ctx.timestamp}` }
|
|
3940
|
+
{ type: "mrkdwn", text: `*Time:* ${ctx.timestamp}` },
|
|
3941
|
+
...suppressed > 0 ? [{ type: "mrkdwn", text: `_+${suppressed} suppressed since last notification_` }] : []
|
|
3937
3942
|
]
|
|
3938
3943
|
},
|
|
3939
3944
|
{ type: "divider" },
|
|
@@ -3960,12 +3965,21 @@ ${ctx.requestId ?? "(none)"}` }
|
|
|
3960
3965
|
return { text: title, blocks };
|
|
3961
3966
|
}
|
|
3962
3967
|
function createErrorSlackNotifier(options = {}) {
|
|
3963
|
-
const { minStatusCode = 500 } = options;
|
|
3968
|
+
const { minStatusCode = 500, throttleMs = 6e4 } = options;
|
|
3964
3969
|
return async (err, ctx) => {
|
|
3965
3970
|
if (ctx.statusCode < minStatusCode) {
|
|
3966
3971
|
return;
|
|
3967
3972
|
}
|
|
3968
|
-
const
|
|
3973
|
+
const key = throttleKey(err, ctx);
|
|
3974
|
+
const now = Date.now();
|
|
3975
|
+
const entry = throttleMap.get(key);
|
|
3976
|
+
if (entry && now - entry.lastSent < throttleMs) {
|
|
3977
|
+
entry.suppressed++;
|
|
3978
|
+
return;
|
|
3979
|
+
}
|
|
3980
|
+
const suppressed = entry?.suppressed ?? 0;
|
|
3981
|
+
throttleMap.set(key, { lastSent: now, suppressed: 0 });
|
|
3982
|
+
const message = options.formatMessage?.(err, ctx) ?? defaultFormat(err, ctx, suppressed);
|
|
3969
3983
|
await sendSlack({
|
|
3970
3984
|
...message,
|
|
3971
3985
|
webhookUrl: options.webhookUrl
|