@spfn/notification 0.1.0-beta.1 → 0.1.0-beta.10
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/README.md +33 -1
- package/dist/config/index.d.ts +14 -0
- package/dist/{index-D_XTwHmO.d.ts → index-BAe1ZBYQ.d.ts} +45 -1
- package/dist/index.d.ts +1 -1
- package/dist/server.d.ts +102 -5
- package/dist/server.js +294 -4
- package/dist/server.js.map +1 -1
- package/migrations/0000_acoustic_bromley.sql +32 -0
- package/migrations/meta/0000_snapshot.json +278 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +12 -2
package/dist/server.js
CHANGED
|
@@ -95,6 +95,8 @@ function isHistoryEnabled() {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
// src/channels/email/providers/aws-ses.ts
|
|
98
|
+
import { logger } from "@spfn/core/logger";
|
|
99
|
+
var log = logger.child("@spfn/notification:ses");
|
|
98
100
|
var sesClient = null;
|
|
99
101
|
async function getSESClient() {
|
|
100
102
|
if (sesClient) {
|
|
@@ -109,6 +111,7 @@ async function getSESClient() {
|
|
|
109
111
|
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
|
|
110
112
|
} : void 0
|
|
111
113
|
});
|
|
114
|
+
log.debug("SES client created", { region: env.AWS_REGION });
|
|
112
115
|
return sesClient;
|
|
113
116
|
} catch {
|
|
114
117
|
throw new Error(
|
|
@@ -156,6 +159,7 @@ var awsSesProvider = {
|
|
|
156
159
|
};
|
|
157
160
|
} catch (error) {
|
|
158
161
|
const err = error;
|
|
162
|
+
log.error("SES send failed", err, { to: params.to, from: params.from });
|
|
159
163
|
return {
|
|
160
164
|
success: false,
|
|
161
165
|
error: err.message
|
|
@@ -630,6 +634,8 @@ async function findScheduledNotifications(options = {}) {
|
|
|
630
634
|
}
|
|
631
635
|
|
|
632
636
|
// src/channels/email/index.ts
|
|
637
|
+
import { logger as logger2 } from "@spfn/core/logger";
|
|
638
|
+
var log2 = logger2.child("@spfn/notification:email");
|
|
633
639
|
var providers = {
|
|
634
640
|
"aws-ses": awsSesProvider
|
|
635
641
|
};
|
|
@@ -651,6 +657,7 @@ async function sendEmail(params) {
|
|
|
651
657
|
let html = params.html;
|
|
652
658
|
if (params.template) {
|
|
653
659
|
if (!hasTemplate(params.template)) {
|
|
660
|
+
log2.warn(`Template not found: ${params.template}`);
|
|
654
661
|
return {
|
|
655
662
|
success: false,
|
|
656
663
|
error: `Template not found: ${params.template}`
|
|
@@ -664,12 +671,14 @@ async function sendEmail(params) {
|
|
|
664
671
|
}
|
|
665
672
|
}
|
|
666
673
|
if (!subject) {
|
|
674
|
+
log2.warn("Email subject is required", { to: recipients });
|
|
667
675
|
return {
|
|
668
676
|
success: false,
|
|
669
677
|
error: "Email subject is required"
|
|
670
678
|
};
|
|
671
679
|
}
|
|
672
680
|
if (!text2 && !html) {
|
|
681
|
+
log2.warn("Email content (text or html) is required", { to: recipients, subject });
|
|
673
682
|
return {
|
|
674
683
|
success: false,
|
|
675
684
|
error: "Email content (text or html) is required"
|
|
@@ -697,10 +706,16 @@ async function sendEmail(params) {
|
|
|
697
706
|
providerName: provider.name
|
|
698
707
|
});
|
|
699
708
|
historyId = record.id;
|
|
700
|
-
} catch {
|
|
709
|
+
} catch (error) {
|
|
710
|
+
log2.warn("Failed to create notification history record", error);
|
|
701
711
|
}
|
|
702
712
|
}
|
|
703
713
|
const result = await provider.send(internalParams);
|
|
714
|
+
if (result.success) {
|
|
715
|
+
log2.info("Email sent", { to: recipients, subject, messageId: result.messageId });
|
|
716
|
+
} else {
|
|
717
|
+
log2.error("Email send failed", { to: recipients, subject, error: result.error });
|
|
718
|
+
}
|
|
704
719
|
if (historyId && isHistoryEnabled()) {
|
|
705
720
|
try {
|
|
706
721
|
if (result.success) {
|
|
@@ -708,7 +723,8 @@ async function sendEmail(params) {
|
|
|
708
723
|
} else {
|
|
709
724
|
await markNotificationFailed(historyId, result.error || "Unknown error");
|
|
710
725
|
}
|
|
711
|
-
} catch {
|
|
726
|
+
} catch (error) {
|
|
727
|
+
log2.warn("Failed to update notification history record", error);
|
|
712
728
|
}
|
|
713
729
|
}
|
|
714
730
|
return result;
|
|
@@ -730,6 +746,8 @@ async function sendEmailBulk(items) {
|
|
|
730
746
|
}
|
|
731
747
|
|
|
732
748
|
// src/channels/sms/providers/aws-sns.ts
|
|
749
|
+
import { logger as logger3 } from "@spfn/core/logger";
|
|
750
|
+
var log3 = logger3.child("@spfn/notification:sns");
|
|
733
751
|
var snsClient = null;
|
|
734
752
|
async function getSNSClient() {
|
|
735
753
|
if (snsClient) {
|
|
@@ -744,6 +762,7 @@ async function getSNSClient() {
|
|
|
744
762
|
secretAccessKey: env.AWS_SECRET_ACCESS_KEY
|
|
745
763
|
} : void 0
|
|
746
764
|
});
|
|
765
|
+
log3.debug("SNS client created", { region: env.AWS_REGION });
|
|
747
766
|
return snsClient;
|
|
748
767
|
} catch {
|
|
749
768
|
throw new Error(
|
|
@@ -774,6 +793,7 @@ var awsSnsProvider = {
|
|
|
774
793
|
};
|
|
775
794
|
} catch (error) {
|
|
776
795
|
const err = error;
|
|
796
|
+
log3.error("SNS send failed", err, { to: params.to });
|
|
777
797
|
return {
|
|
778
798
|
success: false,
|
|
779
799
|
error: err.message
|
|
@@ -796,6 +816,8 @@ function normalizePhoneNumber(phone, defaultCountryCode) {
|
|
|
796
816
|
}
|
|
797
817
|
|
|
798
818
|
// src/channels/sms/index.ts
|
|
819
|
+
import { logger as logger4 } from "@spfn/core/logger";
|
|
820
|
+
var log4 = logger4.child("@spfn/notification:sms");
|
|
799
821
|
var providers2 = {
|
|
800
822
|
"aws-sns": awsSnsProvider
|
|
801
823
|
};
|
|
@@ -815,6 +837,7 @@ async function sendSMS(params) {
|
|
|
815
837
|
let message = params.message;
|
|
816
838
|
if (params.template) {
|
|
817
839
|
if (!hasTemplate(params.template)) {
|
|
840
|
+
log4.warn(`Template not found: ${params.template}`);
|
|
818
841
|
return {
|
|
819
842
|
success: false,
|
|
820
843
|
error: `Template not found: ${params.template}`
|
|
@@ -826,6 +849,7 @@ async function sendSMS(params) {
|
|
|
826
849
|
}
|
|
827
850
|
}
|
|
828
851
|
if (!message) {
|
|
852
|
+
log4.warn("SMS message is required", { to: recipients });
|
|
829
853
|
return {
|
|
830
854
|
success: false,
|
|
831
855
|
error: "SMS message is required"
|
|
@@ -851,10 +875,16 @@ async function sendSMS(params) {
|
|
|
851
875
|
providerName: provider.name
|
|
852
876
|
});
|
|
853
877
|
historyId = record.id;
|
|
854
|
-
} catch {
|
|
878
|
+
} catch (error) {
|
|
879
|
+
log4.warn("Failed to create notification history record", error);
|
|
855
880
|
}
|
|
856
881
|
}
|
|
857
882
|
const result = await provider.send(internalParams);
|
|
883
|
+
if (result.success) {
|
|
884
|
+
log4.info("SMS sent", { to: normalizedPhone, messageId: result.messageId });
|
|
885
|
+
} else {
|
|
886
|
+
log4.error("SMS send failed", { to: normalizedPhone, error: result.error });
|
|
887
|
+
}
|
|
858
888
|
if (historyId && isHistoryEnabled()) {
|
|
859
889
|
try {
|
|
860
890
|
if (result.success) {
|
|
@@ -862,7 +892,8 @@ async function sendSMS(params) {
|
|
|
862
892
|
} else {
|
|
863
893
|
await markNotificationFailed(historyId, result.error || "Unknown error");
|
|
864
894
|
}
|
|
865
|
-
} catch {
|
|
895
|
+
} catch (error) {
|
|
896
|
+
log4.warn("Failed to update notification history record", error);
|
|
866
897
|
}
|
|
867
898
|
}
|
|
868
899
|
results.push(result);
|
|
@@ -892,6 +923,140 @@ async function sendSMSBulk(items) {
|
|
|
892
923
|
return { results, successCount, failureCount };
|
|
893
924
|
}
|
|
894
925
|
|
|
926
|
+
// src/channels/slack/providers/webhook.ts
|
|
927
|
+
import { logger as logger5 } from "@spfn/core/logger";
|
|
928
|
+
var log5 = logger5.child("@spfn/notification:slack-webhook");
|
|
929
|
+
var webhookProvider = {
|
|
930
|
+
name: "webhook",
|
|
931
|
+
async send(params) {
|
|
932
|
+
try {
|
|
933
|
+
const res = await fetch(params.webhookUrl, {
|
|
934
|
+
method: "POST",
|
|
935
|
+
headers: { "Content-Type": "application/json" },
|
|
936
|
+
body: JSON.stringify({
|
|
937
|
+
text: params.text,
|
|
938
|
+
blocks: params.blocks
|
|
939
|
+
})
|
|
940
|
+
});
|
|
941
|
+
return {
|
|
942
|
+
success: res.ok,
|
|
943
|
+
error: res.ok ? void 0 : await res.text()
|
|
944
|
+
};
|
|
945
|
+
} catch (error) {
|
|
946
|
+
const err = error;
|
|
947
|
+
log5.error("Webhook request failed", err);
|
|
948
|
+
return {
|
|
949
|
+
success: false,
|
|
950
|
+
error: err.message
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// src/channels/slack/index.ts
|
|
957
|
+
import { logger as logger6 } from "@spfn/core/logger";
|
|
958
|
+
var log6 = logger6.child("@spfn/notification:slack");
|
|
959
|
+
var providers3 = {
|
|
960
|
+
"webhook": webhookProvider
|
|
961
|
+
};
|
|
962
|
+
function registerSlackProvider(provider) {
|
|
963
|
+
providers3[provider.name] = provider;
|
|
964
|
+
}
|
|
965
|
+
function getProvider3() {
|
|
966
|
+
return providers3["webhook"];
|
|
967
|
+
}
|
|
968
|
+
function resolveWebhookUrl(params) {
|
|
969
|
+
return params.webhookUrl || env.SPFN_NOTIFICATION_SLACK_WEBHOOK_URL;
|
|
970
|
+
}
|
|
971
|
+
async function sendSlack(params) {
|
|
972
|
+
const webhookUrl = resolveWebhookUrl(params);
|
|
973
|
+
if (!webhookUrl) {
|
|
974
|
+
log6.warn("Slack webhook URL is required");
|
|
975
|
+
return {
|
|
976
|
+
success: false,
|
|
977
|
+
error: "Slack webhook URL is required. Set SPFN_NOTIFICATION_SLACK_WEBHOOK_URL or pass webhookUrl."
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
let text2 = params.text;
|
|
981
|
+
let blocks = params.blocks;
|
|
982
|
+
if (params.template) {
|
|
983
|
+
if (!hasTemplate(params.template)) {
|
|
984
|
+
log6.warn(`Template not found: ${params.template}`);
|
|
985
|
+
return {
|
|
986
|
+
success: false,
|
|
987
|
+
error: `Template not found: ${params.template}`
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
const rendered = renderTemplate(params.template, params.data || {}, "slack");
|
|
991
|
+
if (rendered.slack) {
|
|
992
|
+
text2 = rendered.slack.text;
|
|
993
|
+
blocks = rendered.slack.blocks;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (!text2 && !blocks) {
|
|
997
|
+
log6.warn("Slack message requires text or blocks");
|
|
998
|
+
return {
|
|
999
|
+
success: false,
|
|
1000
|
+
error: "Slack message requires text or blocks"
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
const internalParams = {
|
|
1004
|
+
webhookUrl,
|
|
1005
|
+
text: text2,
|
|
1006
|
+
blocks
|
|
1007
|
+
};
|
|
1008
|
+
const provider = getProvider3();
|
|
1009
|
+
let historyId;
|
|
1010
|
+
if (isHistoryEnabled()) {
|
|
1011
|
+
try {
|
|
1012
|
+
const record = await createNotificationRecord({
|
|
1013
|
+
channel: "slack",
|
|
1014
|
+
recipient: webhookUrl,
|
|
1015
|
+
templateName: params.template,
|
|
1016
|
+
templateData: params.data,
|
|
1017
|
+
content: text2,
|
|
1018
|
+
providerName: provider.name
|
|
1019
|
+
});
|
|
1020
|
+
historyId = record.id;
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
log6.warn("Failed to create notification history record", error);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
const result = await provider.send(internalParams);
|
|
1026
|
+
if (result.success) {
|
|
1027
|
+
log6.info("Slack message sent");
|
|
1028
|
+
} else {
|
|
1029
|
+
log6.error("Slack send failed", { error: result.error });
|
|
1030
|
+
}
|
|
1031
|
+
if (historyId && isHistoryEnabled()) {
|
|
1032
|
+
try {
|
|
1033
|
+
if (result.success) {
|
|
1034
|
+
await markNotificationSent(historyId, result.messageId);
|
|
1035
|
+
} else {
|
|
1036
|
+
await markNotificationFailed(historyId, result.error || "Unknown error");
|
|
1037
|
+
}
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
log6.warn("Failed to update notification history record", error);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return result;
|
|
1043
|
+
}
|
|
1044
|
+
async function sendSlackBulk(items) {
|
|
1045
|
+
const results = [];
|
|
1046
|
+
let successCount = 0;
|
|
1047
|
+
let failureCount = 0;
|
|
1048
|
+
for (const item of items) {
|
|
1049
|
+
const result = await sendSlack(item);
|
|
1050
|
+
results.push(result);
|
|
1051
|
+
if (result.success) {
|
|
1052
|
+
successCount++;
|
|
1053
|
+
} else {
|
|
1054
|
+
failureCount++;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
return { results, successCount, failureCount };
|
|
1058
|
+
}
|
|
1059
|
+
|
|
895
1060
|
// src/jobs/send-scheduled-email.ts
|
|
896
1061
|
import { job } from "@spfn/core/job";
|
|
897
1062
|
|
|
@@ -3759,6 +3924,127 @@ var notificationJobRouter = defineJobRouter({
|
|
|
3759
3924
|
sendScheduledSms: sendScheduledSmsJob
|
|
3760
3925
|
});
|
|
3761
3926
|
|
|
3927
|
+
// src/integrations/error-slack.ts
|
|
3928
|
+
import { hostname } from "os";
|
|
3929
|
+
function formatHeaders(headers) {
|
|
3930
|
+
const entries = Object.entries(headers);
|
|
3931
|
+
if (entries.length === 0) {
|
|
3932
|
+
return "(none)";
|
|
3933
|
+
}
|
|
3934
|
+
return entries.map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
3935
|
+
}
|
|
3936
|
+
function formatQuery(query) {
|
|
3937
|
+
const entries = Object.entries(query);
|
|
3938
|
+
if (entries.length === 0) {
|
|
3939
|
+
return "(none)";
|
|
3940
|
+
}
|
|
3941
|
+
return entries.map(([k, v]) => `${k}=${v}`).join("\n");
|
|
3942
|
+
}
|
|
3943
|
+
function shortStack(err, maxLines = 3) {
|
|
3944
|
+
if (!err.stack) {
|
|
3945
|
+
return "(no stack)";
|
|
3946
|
+
}
|
|
3947
|
+
const lines = err.stack.split("\n").slice(1);
|
|
3948
|
+
return lines.slice(0, maxLines).map((line) => line.trim()).join("\n");
|
|
3949
|
+
}
|
|
3950
|
+
var throttleMap = /* @__PURE__ */ new Map();
|
|
3951
|
+
function throttleKey(err, ctx) {
|
|
3952
|
+
return `${err.name}:${ctx.statusCode}:${ctx.path}`;
|
|
3953
|
+
}
|
|
3954
|
+
function getEnvLabel() {
|
|
3955
|
+
const env2 = process.env.NODE_ENV || "unknown";
|
|
3956
|
+
const host = hostname();
|
|
3957
|
+
const dbUrl = process.env.DATABASE_URL || "";
|
|
3958
|
+
const dbName = dbUrl.match(/\/([^/?]+)(\?|$)/)?.[1] || "(unknown)";
|
|
3959
|
+
return `${env2} | ${host} | db:${dbName}`;
|
|
3960
|
+
}
|
|
3961
|
+
function defaultFormat(err, ctx, suppressed = 0) {
|
|
3962
|
+
const envLabel = getEnvLabel();
|
|
3963
|
+
const emoji = ctx.statusCode >= 500 ? ":rotating_light:" : ":warning:";
|
|
3964
|
+
const title = `${emoji} *${err.name || "Error"}* \u2014 ${ctx.statusCode} [${envLabel}]`;
|
|
3965
|
+
const fields = [
|
|
3966
|
+
{ type: "mrkdwn", text: `*Method*
|
|
3967
|
+
${ctx.method}` },
|
|
3968
|
+
{ type: "mrkdwn", text: `*Path*
|
|
3969
|
+
${ctx.path}` },
|
|
3970
|
+
{ type: "mrkdwn", text: `*User*
|
|
3971
|
+
${ctx.userId ?? "(anonymous)"}` },
|
|
3972
|
+
{ type: "mrkdwn", text: `*Request ID*
|
|
3973
|
+
${ctx.requestId ?? "(none)"}` }
|
|
3974
|
+
];
|
|
3975
|
+
const blocks = [
|
|
3976
|
+
// Title
|
|
3977
|
+
{
|
|
3978
|
+
type: "header",
|
|
3979
|
+
text: { type: "plain_text", text: `${err.name || "Error"} \u2014 ${ctx.statusCode}`, emoji: true }
|
|
3980
|
+
},
|
|
3981
|
+
// Error message
|
|
3982
|
+
{
|
|
3983
|
+
type: "section",
|
|
3984
|
+
text: { type: "mrkdwn", text: `> ${err.message}` }
|
|
3985
|
+
},
|
|
3986
|
+
// Fields: method, path, user, requestId
|
|
3987
|
+
{
|
|
3988
|
+
type: "section",
|
|
3989
|
+
fields
|
|
3990
|
+
},
|
|
3991
|
+
// Timestamp + suppressed count
|
|
3992
|
+
{
|
|
3993
|
+
type: "context",
|
|
3994
|
+
elements: [
|
|
3995
|
+
{ type: "mrkdwn", text: `*Time:* ${ctx.timestamp}` },
|
|
3996
|
+
...suppressed > 0 ? [{ type: "mrkdwn", text: `_+${suppressed} suppressed since last notification_` }] : []
|
|
3997
|
+
]
|
|
3998
|
+
},
|
|
3999
|
+
{ type: "divider" },
|
|
4000
|
+
// Headers
|
|
4001
|
+
{
|
|
4002
|
+
type: "section",
|
|
4003
|
+
text: { type: "mrkdwn", text: `*Request Headers*
|
|
4004
|
+
\`\`\`${formatHeaders(ctx.request.headers)}\`\`\`` }
|
|
4005
|
+
},
|
|
4006
|
+
// Query
|
|
4007
|
+
{
|
|
4008
|
+
type: "section",
|
|
4009
|
+
text: { type: "mrkdwn", text: `*Query Params*
|
|
4010
|
+
\`\`\`${formatQuery(ctx.request.query)}\`\`\`` }
|
|
4011
|
+
},
|
|
4012
|
+
{ type: "divider" },
|
|
4013
|
+
// Stack trace
|
|
4014
|
+
{
|
|
4015
|
+
type: "section",
|
|
4016
|
+
text: { type: "mrkdwn", text: `*Stack Trace*
|
|
4017
|
+
\`\`\`${shortStack(err)}\`\`\`` }
|
|
4018
|
+
}
|
|
4019
|
+
];
|
|
4020
|
+
return { text: title, blocks };
|
|
4021
|
+
}
|
|
4022
|
+
function createErrorSlackNotifier(options = {}) {
|
|
4023
|
+
console.warn(
|
|
4024
|
+
"[@spfn/notification] createErrorSlackNotifier() is deprecated. Use createMonitorErrorHandler() from @spfn/monitor/server instead."
|
|
4025
|
+
);
|
|
4026
|
+
const { minStatusCode = 500, throttleMs = 6e4 } = options;
|
|
4027
|
+
return async (err, ctx) => {
|
|
4028
|
+
if (ctx.statusCode < minStatusCode) {
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
const key = throttleKey(err, ctx);
|
|
4032
|
+
const now = Date.now();
|
|
4033
|
+
const entry = throttleMap.get(key);
|
|
4034
|
+
if (entry && now - entry.lastSent < throttleMs) {
|
|
4035
|
+
entry.suppressed++;
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
const suppressed = entry?.suppressed ?? 0;
|
|
4039
|
+
throttleMap.set(key, { lastSent: now, suppressed: 0 });
|
|
4040
|
+
const message = options.formatMessage?.(err, ctx) ?? defaultFormat(err, ctx, suppressed);
|
|
4041
|
+
await sendSlack({
|
|
4042
|
+
...message,
|
|
4043
|
+
webhookUrl: options.webhookUrl
|
|
4044
|
+
});
|
|
4045
|
+
};
|
|
4046
|
+
}
|
|
4047
|
+
|
|
3762
4048
|
// src/server.ts
|
|
3763
4049
|
registerBuiltinTemplates();
|
|
3764
4050
|
export {
|
|
@@ -3769,6 +4055,7 @@ export {
|
|
|
3769
4055
|
cancelScheduledNotification,
|
|
3770
4056
|
configureNotification,
|
|
3771
4057
|
countNotifications,
|
|
4058
|
+
createErrorSlackNotifier,
|
|
3772
4059
|
createNotificationRecord,
|
|
3773
4060
|
createScheduledNotification,
|
|
3774
4061
|
findNotificationByJobId,
|
|
@@ -3792,6 +4079,7 @@ export {
|
|
|
3792
4079
|
registerEmailProvider,
|
|
3793
4080
|
registerFilter,
|
|
3794
4081
|
registerSMSProvider,
|
|
4082
|
+
registerSlackProvider,
|
|
3795
4083
|
registerTemplate,
|
|
3796
4084
|
renderTemplate,
|
|
3797
4085
|
scheduleEmail,
|
|
@@ -3802,6 +4090,8 @@ export {
|
|
|
3802
4090
|
sendSMSBulk,
|
|
3803
4091
|
sendScheduledEmailJob,
|
|
3804
4092
|
sendScheduledSmsJob,
|
|
4093
|
+
sendSlack,
|
|
4094
|
+
sendSlackBulk,
|
|
3805
4095
|
updateNotificationJobId
|
|
3806
4096
|
};
|
|
3807
4097
|
//# sourceMappingURL=server.js.map
|