@teleporthq/teleport-plugin-next-workflows 0.44.8 → 0.44.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/__tests__/_helpers/load-server-runtime.ts +6 -2
- package/__tests__/_helpers/run-account-delete-route.ts +140 -0
- package/__tests__/account-delete-current-password.test.ts +73 -0
- package/__tests__/account-delete-password-check.test.ts +113 -0
- package/__tests__/account-delete-visitor-details.test.ts +206 -0
- package/__tests__/ai-error-contract.test.ts +10 -0
- package/__tests__/auth-password-change-ends-sessions.test.ts +178 -0
- package/__tests__/continue-on-error.test.ts +27 -0
- package/__tests__/data-api-invalid-body.test.ts +110 -0
- package/__tests__/data-node-not-awaited.test.ts +7 -6
- package/__tests__/email-node-recipients.test.ts +288 -0
- package/__tests__/invoice-payment-line.test.ts +68 -0
- package/__tests__/invoice-tender.test.ts +116 -0
- package/__tests__/pending-work-after-reply.test.ts +294 -0
- package/__tests__/runtime-condition-operands.test.ts +86 -0
- package/__tests__/sent-email-log-module.test.ts +54 -0
- package/__tests__/webhook-delivery-lease.test.ts +68 -6
- package/dist/cjs/account-delete-route-generator.d.ts +7 -2
- package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
- package/dist/cjs/account-delete-route-generator.js +90 -43
- package/dist/cjs/account-delete-route-generator.js.map +1 -1
- package/dist/cjs/api-route-generator.d.ts.map +1 -1
- package/dist/cjs/api-route-generator.js +12 -13
- package/dist/cjs/api-route-generator.js.map +1 -1
- package/dist/cjs/auth-generator.d.ts.map +1 -1
- package/dist/cjs/auth-generator.js +34 -1
- package/dist/cjs/auth-generator.js.map +1 -1
- package/dist/cjs/await-result.d.ts +13 -5
- package/dist/cjs/await-result.d.ts.map +1 -1
- package/dist/cjs/await-result.js +19 -8
- package/dist/cjs/await-result.js.map +1 -1
- package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
- package/dist/cjs/data-api-route-generator.js +9 -1
- package/dist/cjs/data-api-route-generator.js.map +1 -1
- package/dist/cjs/executor-generator.d.ts.map +1 -1
- package/dist/cjs/executor-generator.js +22 -7
- package/dist/cjs/executor-generator.js.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/cjs/invoice/invoice-html-code.js +96 -5
- package/dist/cjs/invoice/invoice-html-code.js.map +1 -1
- package/dist/cjs/nodes/account/account-delete-current.d.ts.map +1 -1
- package/dist/cjs/nodes/account/account-delete-current.js +4 -1
- package/dist/cjs/nodes/account/account-delete-current.js.map +1 -1
- package/dist/cjs/nodes/email/email-mailersend.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-mailersend.js +55 -48
- package/dist/cjs/nodes/email/email-mailersend.js.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-mailgun.js +44 -32
- package/dist/cjs/nodes/email/email-mailgun.js.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-postmark.js +44 -31
- package/dist/cjs/nodes/email/email-postmark.js.map +1 -1
- package/dist/cjs/nodes/email/email-recipients.d.ts +28 -0
- package/dist/cjs/nodes/email/email-recipients.d.ts.map +1 -0
- package/dist/cjs/nodes/email/email-recipients.js +74 -0
- package/dist/cjs/nodes/email/email-recipients.js.map +1 -0
- package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-resend.js +42 -28
- package/dist/cjs/nodes/email/email-resend.js.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/cjs/nodes/email/email-sendgrid.js +56 -41
- package/dist/cjs/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/cjs/sent-email-log/sent-email-log-code.d.ts.map +1 -1
- package/dist/cjs/sent-email-log/sent-email-log-code.js +17 -1
- package/dist/cjs/sent-email-log/sent-email-log-code.js.map +1 -1
- package/dist/cjs/server-runtime-code.d.ts.map +1 -1
- package/dist/cjs/server-runtime-code.js +21 -0
- package/dist/cjs/server-runtime-code.js.map +1 -1
- package/dist/cjs/transactional-email-code.d.ts +7 -0
- package/dist/cjs/transactional-email-code.d.ts.map +1 -1
- package/dist/cjs/transactional-email-code.js +5 -5
- package/dist/cjs/transactional-email-code.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/vercel-functions.d.ts +34 -0
- package/dist/cjs/vercel-functions.d.ts.map +1 -0
- package/dist/cjs/vercel-functions.js +62 -0
- package/dist/cjs/vercel-functions.js.map +1 -0
- package/dist/cjs/webhook-delivery-lease/webhook-delivery-lease-code.d.ts +6 -0
- package/dist/cjs/webhook-delivery-lease/webhook-delivery-lease-code.d.ts.map +1 -1
- package/dist/cjs/webhook-delivery-lease/webhook-delivery-lease-code.js +26 -11
- package/dist/cjs/webhook-delivery-lease/webhook-delivery-lease-code.js.map +1 -1
- package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
- package/dist/cjs/workflow-project-plugin.js +3 -0
- package/dist/cjs/workflow-project-plugin.js.map +1 -1
- package/dist/esm/account-delete-route-generator.d.ts +7 -2
- package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
- package/dist/esm/account-delete-route-generator.js +90 -43
- package/dist/esm/account-delete-route-generator.js.map +1 -1
- package/dist/esm/api-route-generator.d.ts.map +1 -1
- package/dist/esm/api-route-generator.js +12 -13
- package/dist/esm/api-route-generator.js.map +1 -1
- package/dist/esm/auth-generator.d.ts.map +1 -1
- package/dist/esm/auth-generator.js +34 -1
- package/dist/esm/auth-generator.js.map +1 -1
- package/dist/esm/await-result.d.ts +13 -5
- package/dist/esm/await-result.d.ts.map +1 -1
- package/dist/esm/await-result.js +18 -7
- package/dist/esm/await-result.js.map +1 -1
- package/dist/esm/data-api-route-generator.d.ts.map +1 -1
- package/dist/esm/data-api-route-generator.js +9 -1
- package/dist/esm/data-api-route-generator.js.map +1 -1
- package/dist/esm/executor-generator.d.ts.map +1 -1
- package/dist/esm/executor-generator.js +22 -7
- package/dist/esm/executor-generator.js.map +1 -1
- package/dist/esm/invoice/invoice-html-code.d.ts.map +1 -1
- package/dist/esm/invoice/invoice-html-code.js +96 -5
- package/dist/esm/invoice/invoice-html-code.js.map +1 -1
- package/dist/esm/nodes/account/account-delete-current.d.ts.map +1 -1
- package/dist/esm/nodes/account/account-delete-current.js +4 -1
- package/dist/esm/nodes/account/account-delete-current.js.map +1 -1
- package/dist/esm/nodes/email/email-mailersend.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-mailersend.js +55 -48
- package/dist/esm/nodes/email/email-mailersend.js.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-mailgun.js +44 -32
- package/dist/esm/nodes/email/email-mailgun.js.map +1 -1
- package/dist/esm/nodes/email/email-postmark.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-postmark.js +44 -31
- package/dist/esm/nodes/email/email-postmark.js.map +1 -1
- package/dist/esm/nodes/email/email-recipients.d.ts +28 -0
- package/dist/esm/nodes/email/email-recipients.d.ts.map +1 -0
- package/dist/esm/nodes/email/email-recipients.js +71 -0
- package/dist/esm/nodes/email/email-recipients.js.map +1 -0
- package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-resend.js +42 -28
- package/dist/esm/nodes/email/email-resend.js.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.d.ts.map +1 -1
- package/dist/esm/nodes/email/email-sendgrid.js +56 -41
- package/dist/esm/nodes/email/email-sendgrid.js.map +1 -1
- package/dist/esm/sent-email-log/sent-email-log-code.d.ts.map +1 -1
- package/dist/esm/sent-email-log/sent-email-log-code.js +17 -1
- package/dist/esm/sent-email-log/sent-email-log-code.js.map +1 -1
- package/dist/esm/server-runtime-code.d.ts.map +1 -1
- package/dist/esm/server-runtime-code.js +21 -0
- package/dist/esm/server-runtime-code.js.map +1 -1
- package/dist/esm/transactional-email-code.d.ts +7 -0
- package/dist/esm/transactional-email-code.d.ts.map +1 -1
- package/dist/esm/transactional-email-code.js +5 -5
- package/dist/esm/transactional-email-code.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/vercel-functions.d.ts +34 -0
- package/dist/esm/vercel-functions.d.ts.map +1 -0
- package/dist/esm/vercel-functions.js +57 -0
- package/dist/esm/vercel-functions.js.map +1 -0
- package/dist/esm/webhook-delivery-lease/webhook-delivery-lease-code.d.ts +6 -0
- package/dist/esm/webhook-delivery-lease/webhook-delivery-lease-code.d.ts.map +1 -1
- package/dist/esm/webhook-delivery-lease/webhook-delivery-lease-code.js +26 -11
- package/dist/esm/webhook-delivery-lease/webhook-delivery-lease-code.js.map +1 -1
- package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
- package/dist/esm/workflow-project-plugin.js +3 -0
- package/dist/esm/workflow-project-plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/account-delete-route-generator.ts +97 -45
- package/src/api-route-generator.ts +12 -13
- package/src/auth-generator.ts +34 -1
- package/src/await-result.ts +19 -7
- package/src/data-api-route-generator.ts +9 -1
- package/src/executor-generator.ts +22 -7
- package/src/invoice/invoice-html-code.ts +96 -5
- package/src/nodes/account/account-delete-current.ts +4 -1
- package/src/nodes/email/email-mailersend.ts +72 -55
- package/src/nodes/email/email-mailgun.ts +51 -36
- package/src/nodes/email/email-postmark.ts +51 -35
- package/src/nodes/email/email-recipients.ts +86 -0
- package/src/nodes/email/email-resend.ts +49 -31
- package/src/nodes/email/email-sendgrid.ts +63 -44
- package/src/sent-email-log/sent-email-log-code.ts +17 -1
- package/src/server-runtime-code.ts +22 -0
- package/src/transactional-email-code.ts +12 -5
- package/src/vercel-functions.ts +67 -0
- package/src/webhook-delivery-lease/webhook-delivery-lease-code.ts +26 -11
- package/src/workflow-project-plugin.ts +3 -0
|
@@ -1,69 +1,84 @@
|
|
|
1
1
|
import { EmailSender } from '@teleporthq/teleport-shared'
|
|
2
2
|
import { NodeHandlerGenerator, handlerToString } from '../types'
|
|
3
3
|
import { assertHandlerHasNoModuleRefs } from '../analytics/commerce-tracking'
|
|
4
|
+
import { EMAIL_RECIPIENTS_HELPER_SOURCE, EmailDeliveriesPlan } from './email-recipients'
|
|
4
5
|
|
|
5
6
|
// Ambient, not imported: a serialized handler has no module scope, so
|
|
6
|
-
// generateHandler appends the
|
|
7
|
+
// generateHandler appends the helpers' source (EmailSender from teleport-shared,
|
|
8
|
+
// the recipients helper beside this file).
|
|
7
9
|
declare function __emailSender(sender: EmailSender.EmailSenderInput): EmailSender.EmailSenderParts
|
|
10
|
+
declare function __emailRecipients(to: unknown): unknown[]
|
|
11
|
+
declare function __emailDeliveries(plan: EmailDeliveriesPlan): Promise<unknown>
|
|
8
12
|
|
|
9
13
|
async function email_sendgrid(config: any, context: Record<string, unknown>) {
|
|
10
14
|
const apiKey = config.apiKey
|
|
11
15
|
const from = config.from
|
|
12
16
|
const fromName = config.fromName
|
|
13
|
-
const to = config.to
|
|
14
17
|
const subject = config.subject
|
|
15
18
|
const body = config.body
|
|
16
19
|
const replyTo = config.replyTo
|
|
17
20
|
const cc = config.cc
|
|
18
21
|
const bcc = config.bcc
|
|
22
|
+
const recipients = __emailRecipients(config.to)
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
async function sendTo(to: unknown) {
|
|
25
|
+
try {
|
|
26
|
+
const __nodeRequire =
|
|
27
|
+
typeof __non_webpack_require__ !== 'undefined' ? __non_webpack_require__ : require
|
|
28
|
+
const MailService = __nodeRequire('@sendgrid/mail').MailService
|
|
29
|
+
const sgMail = new MailService()
|
|
30
|
+
sgMail.setApiKey(apiKey)
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// SendGrid reads a "Name <email>" string's name verbatim, quotes included,
|
|
33
|
+
// so a named sender goes as its two parts.
|
|
34
|
+
const sender = __emailSender({ from, name: fromName })
|
|
35
|
+
const msg: Record<string, any> = {
|
|
36
|
+
to: [to],
|
|
37
|
+
from: sender.name ? { email: sender.email, name: sender.name } : sender.email,
|
|
38
|
+
subject,
|
|
39
|
+
html: body,
|
|
40
|
+
}
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (replyTo) {
|
|
43
|
+
msg.replyTo = replyTo
|
|
44
|
+
}
|
|
45
|
+
if (cc) {
|
|
46
|
+
msg.cc = Array.isArray(cc) ? cc : [cc]
|
|
47
|
+
}
|
|
48
|
+
if (bcc) {
|
|
49
|
+
msg.bcc = Array.isArray(bcc) ? bcc : [bcc]
|
|
50
|
+
}
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const response = await sgMail.send(msg)
|
|
53
|
+
const statusCode = response && response[0] && response[0].statusCode
|
|
54
|
+
const messageId =
|
|
55
|
+
(response && response[0] && response[0].headers && response[0].headers['x-message-id']) ||
|
|
56
|
+
''
|
|
51
57
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
return { messageId, success: statusCode === 202 || statusCode === 200 }
|
|
59
|
+
} catch (err: unknown) {
|
|
60
|
+
const error = err as any
|
|
61
|
+
let message = 'Failed to send email'
|
|
62
|
+
if (error.response && error.response.body && error.response.body.errors) {
|
|
63
|
+
message = error.response.body.errors
|
|
64
|
+
.map(function (e: any) {
|
|
65
|
+
return e.message
|
|
66
|
+
})
|
|
67
|
+
.join(', ')
|
|
68
|
+
} else if (error.message) {
|
|
69
|
+
message = error.message
|
|
70
|
+
}
|
|
71
|
+
return { messageId: '', success: false, error: message }
|
|
64
72
|
}
|
|
65
|
-
return { messageId: '', success: false, error: message }
|
|
66
73
|
}
|
|
74
|
+
|
|
75
|
+
if (recipients.length === 0) {
|
|
76
|
+
return { messageId: '', success: false, error: 'No recipient' }
|
|
77
|
+
}
|
|
78
|
+
if (recipients.length === 1) {
|
|
79
|
+
return sendTo(recipients[0])
|
|
80
|
+
}
|
|
81
|
+
return __emailDeliveries({ recipients, send: sendTo, idKey: 'messageId' })
|
|
67
82
|
}
|
|
68
83
|
export const emailSendgrid: NodeHandlerGenerator = {
|
|
69
84
|
nodeType: 'email-sendgrid',
|
|
@@ -73,7 +88,11 @@ export const emailSendgrid: NodeHandlerGenerator = {
|
|
|
73
88
|
},
|
|
74
89
|
generateHandler(): string {
|
|
75
90
|
return assertHandlerHasNoModuleRefs(
|
|
76
|
-
handlerToString(email_sendgrid) +
|
|
91
|
+
handlerToString(email_sendgrid) +
|
|
92
|
+
'\n' +
|
|
93
|
+
EmailSender.generateEmailSenderHelperCode() +
|
|
94
|
+
'\n' +
|
|
95
|
+
EMAIL_RECIPIENTS_HELPER_SOURCE,
|
|
77
96
|
'email-sendgrid'
|
|
78
97
|
)
|
|
79
98
|
},
|
|
@@ -348,6 +348,19 @@ function __entryFromNode(nodeType, config, result, error) {
|
|
|
348
348
|
};
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
// A node that reached several recipients one provider call each reports a
|
|
352
|
+
// delivery per recipient; the ledger then says who got the email and who was
|
|
353
|
+
// refused, one row per address. Any other result is one row for the node run.
|
|
354
|
+
function __entriesFromNode(nodeType, config, result) {
|
|
355
|
+
var deliveries = result && Array.isArray(result.deliveries) ? result.deliveries : [];
|
|
356
|
+
if (deliveries.length === 0) return [__entryFromNode(nodeType, config, result, null)];
|
|
357
|
+
return deliveries.map(function (delivery) {
|
|
358
|
+
var entry = __entryFromNode(nodeType, config, delivery, null);
|
|
359
|
+
entry.to = delivery.to;
|
|
360
|
+
return entry;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
351
364
|
/**
|
|
352
365
|
* Wraps a server node handler so every send is recorded. The record is
|
|
353
366
|
* registered on the workflow context's fire-and-forget queue — the same one
|
|
@@ -368,7 +381,10 @@ function wrapEmailNodeHandler(nodeType, handler) {
|
|
|
368
381
|
__register(context, recordSentEmail(__entryFromNode(nodeType, config, null, err)));
|
|
369
382
|
throw err;
|
|
370
383
|
}
|
|
371
|
-
|
|
384
|
+
var entries = __entriesFromNode(nodeType, config, result);
|
|
385
|
+
for (var i = 0; i < entries.length; i++) {
|
|
386
|
+
__register(context, recordSentEmail(entries[i]));
|
|
387
|
+
}
|
|
372
388
|
return result;
|
|
373
389
|
};
|
|
374
390
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { generateVercelFunctionsLoaderCode } from './vercel-functions'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* `utils/workflows/server-runtime.js`: what every server route loads. It is the
|
|
3
5
|
* shared runtime utilities plus the parts only a server may run — the signed
|
|
@@ -42,6 +44,8 @@ export const generateServerRuntimeCode = (): string => `'use strict';
|
|
|
42
44
|
var crypto = require('crypto');
|
|
43
45
|
var utils = require('./runtime-utils');
|
|
44
46
|
|
|
47
|
+
${generateVercelFunctionsLoaderCode()}
|
|
48
|
+
|
|
45
49
|
var __stepKeyCache = { secret: null, key: null };
|
|
46
50
|
|
|
47
51
|
function stepKey() {
|
|
@@ -394,7 +398,25 @@ function trustedBaseUrl(req) {
|
|
|
394
398
|
return origin;
|
|
395
399
|
}
|
|
396
400
|
|
|
401
|
+
// The work a segment's not-awaited steps left running (an invoice being
|
|
402
|
+
// rendered and mailed) must still finish, but the visitor need not wait for
|
|
403
|
+
// it: on Vercel it is handed to waitUntil, which keeps the function running
|
|
404
|
+
// after the reply. Anywhere else the route awaits it before replying, since a
|
|
405
|
+
// function frozen after its reply would drop it.
|
|
406
|
+
function handOffPendingNodePromises(context) {
|
|
407
|
+
var queue = context && context.__pendingNodePromises;
|
|
408
|
+
if (!queue || queue.length === 0) return Promise.resolve();
|
|
409
|
+
// Drained through the queue itself: the route deletes it from the context
|
|
410
|
+
// before replying, and a parallel branch still running pushes onto it.
|
|
411
|
+
var drained = utils.settlePendingNodePromises({ __pendingNodePromises: queue });
|
|
412
|
+
var waitUntil = __vercelWaitUntil();
|
|
413
|
+
if (!waitUntil) return drained;
|
|
414
|
+
waitUntil(drained);
|
|
415
|
+
return Promise.resolve();
|
|
416
|
+
}
|
|
417
|
+
|
|
397
418
|
module.exports = Object.assign({}, utils, {
|
|
419
|
+
handOffPendingNodePromises: handOffPendingNodePromises,
|
|
398
420
|
claimSegmentContext: claimSegmentContext,
|
|
399
421
|
segmentReply: segmentReply,
|
|
400
422
|
internalRequestHeaders: internalRequestHeaders,
|
|
@@ -38,10 +38,17 @@ export interface TransactionalEmailLedger {
|
|
|
38
38
|
source: string
|
|
39
39
|
sourceRef: string
|
|
40
40
|
relativePrefix: string
|
|
41
|
+
/**
|
|
42
|
+
* Records the send without the recipient's details: `msg.recordedTo` in
|
|
43
|
+
* place of the address, and neither the body nor the values it was filled
|
|
44
|
+
* with. For an email to someone whose details the route is removing (the
|
|
45
|
+
* farewell after an account deletion).
|
|
46
|
+
*/
|
|
47
|
+
withoutRecipientDetails?: boolean
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
// Provider-specific `__sendProviderEmail({ from, fromName?, to,
|
|
44
|
-
// apiKey, tokenValues?, userId? })`, preceded by the `__emailSender` helper that
|
|
50
|
+
// Provider-specific `__sendProviderEmail({ from, fromName?, to, recordedTo?,
|
|
51
|
+
// subject, html, apiKey, tokenValues?, userId? })`, preceded by the `__emailSender` helper that
|
|
45
52
|
// names the sender. No attachment (unlike the invoice sender). When no provider
|
|
46
53
|
// is configured the function is a no-op so the route stays self-contained and
|
|
47
54
|
// never throws. With `ledger`, the provider function is emitted as
|
|
@@ -77,12 +84,12 @@ async function __sendProviderEmail(msg) {
|
|
|
77
84
|
__sentEmailLog.recordSentEmail({
|
|
78
85
|
emailType: ${JSON.stringify(ledger.emailType)},
|
|
79
86
|
audience: 'customer',
|
|
80
|
-
to: msg.to,
|
|
87
|
+
to: ${ledger.withoutRecipientDetails ? 'msg.recordedTo' : 'msg.to'},
|
|
81
88
|
from: sender.email,
|
|
82
89
|
fromName: sender.name,
|
|
83
90
|
subject: msg.subject,
|
|
84
|
-
html: msg.html,
|
|
85
|
-
payload: msg.tokenValues,
|
|
91
|
+
html: ${ledger.withoutRecipientDetails ? "''" : 'msg.html'},
|
|
92
|
+
payload: ${ledger.withoutRecipientDetails ? 'null' : 'msg.tokenValues'},
|
|
86
93
|
provider: ${JSON.stringify(provider)},
|
|
87
94
|
status: failure ? 'failed' : 'sent',
|
|
88
95
|
error: failure ? (failure.message || String(failure)) : null,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@vercel/functions` in a generated project: `waitUntil`, which keeps a
|
|
3
|
+
* Vercel function running after its reply, and `attachDatabasePool`, which
|
|
4
|
+
* closes a pool's idle connections before the function is suspended.
|
|
5
|
+
*
|
|
6
|
+
* The generated code loads it inside a try, so a project that does not have
|
|
7
|
+
* it installed (or a bundler that cannot find it) keeps working without
|
|
8
|
+
* either. A project whose emitted files require it declares it (see
|
|
9
|
+
* `declareVercelFunctionsWhereRequired`).
|
|
10
|
+
*/
|
|
11
|
+
export const VERCEL_FUNCTIONS_PACKAGE = '@vercel/functions'
|
|
12
|
+
|
|
13
|
+
// The 2.x line still supports Node 18, which the Next project template allows;
|
|
14
|
+
// 3.x requires Node 20. attachDatabasePool is stable from 2.2.8.
|
|
15
|
+
export const VERCEL_FUNCTIONS_VERSION = '^2.2.13'
|
|
16
|
+
|
|
17
|
+
const VERCEL_FUNCTIONS_REQUIRE = `require('${VERCEL_FUNCTIONS_PACKAGE}')`
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `__vercelFunctions()` (the module, or null) and `__vercelWaitUntil()` (the
|
|
21
|
+
* module's waitUntil while this code runs inside a Vercel request, else null).
|
|
22
|
+
*
|
|
23
|
+
* waitUntil does nothing outside a Vercel request (a local `next dev`, another
|
|
24
|
+
* host): it reads the request context Vercel installs under
|
|
25
|
+
* `Symbol.for('@vercel/request-context')` and silently drops the promise when
|
|
26
|
+
* there is none. The same check here lets a caller await the work instead.
|
|
27
|
+
*/
|
|
28
|
+
export const generateVercelFunctionsLoaderCode = (): string => `var __vercelFunctionsModule;
|
|
29
|
+
function __vercelFunctions() {
|
|
30
|
+
if (__vercelFunctionsModule === undefined) {
|
|
31
|
+
try { __vercelFunctionsModule = ${VERCEL_FUNCTIONS_REQUIRE}; } catch (_e) { __vercelFunctionsModule = null; }
|
|
32
|
+
}
|
|
33
|
+
return __vercelFunctionsModule;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function __vercelWaitUntil() {
|
|
37
|
+
var store = globalThis[Symbol.for('@vercel/request-context')];
|
|
38
|
+
var requestContext = store && typeof store.get === 'function' ? store.get() : null;
|
|
39
|
+
if (!requestContext || typeof requestContext.waitUntil !== 'function') return null;
|
|
40
|
+
var vercel = __vercelFunctions();
|
|
41
|
+
return vercel && typeof vercel.waitUntil === 'function' ? vercel.waitUntil : null;
|
|
42
|
+
}`
|
|
43
|
+
|
|
44
|
+
interface GeneratedFileRecord {
|
|
45
|
+
files?: Array<{ content?: unknown }>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Declares `@vercel/functions` in the project's dependencies when any emitted
|
|
50
|
+
* file requires it, keeping a version the project already declares.
|
|
51
|
+
*/
|
|
52
|
+
export const declareVercelFunctionsWhereRequired = (
|
|
53
|
+
files: Map<string, GeneratedFileRecord>,
|
|
54
|
+
dependencies: Record<string, string>
|
|
55
|
+
): void => {
|
|
56
|
+
if (dependencies[VERCEL_FUNCTIONS_PACKAGE]) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
for (const record of files.values()) {
|
|
60
|
+
for (const file of record.files || []) {
|
|
61
|
+
if (typeof file.content === 'string' && file.content.includes(VERCEL_FUNCTIONS_REQUIRE)) {
|
|
62
|
+
dependencies[VERCEL_FUNCTIONS_PACKAGE] = VERCEL_FUNCTIONS_VERSION
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -22,6 +22,12 @@ import {
|
|
|
22
22
|
* released before the route replies; one left by a run that died expires
|
|
23
23
|
* after a lease longer than any route may run.
|
|
24
24
|
*
|
|
25
|
+
* The table is used when it exists (the editor creates it with the payment
|
|
26
|
+
* tables) and created only when it does not: a site's database role may read
|
|
27
|
+
* and write its tables without being allowed to create one, and
|
|
28
|
+
* CREATE TABLE IF NOT EXISTS asks for that permission even when the table is
|
|
29
|
+
* there.
|
|
30
|
+
*
|
|
25
31
|
* Contract: `claimWebhookDelivery` never throws. Without Postgres, a driver,
|
|
26
32
|
* the table or the privilege to create it, it claims nothing and lets the
|
|
27
33
|
* delivery run — as every delivery ran before the lease existed.
|
|
@@ -48,6 +54,7 @@ ${generateLazyPgClientCode()}
|
|
|
48
54
|
var TABLE_NAME = '${WEBHOOK_DELIVERY_LEASE_TABLE}';
|
|
49
55
|
var LEASE_SECONDS = ${WEBHOOK_DELIVERY_LEASE_SECONDS};
|
|
50
56
|
var QUERY_TIMEOUT_MS = 5000;
|
|
57
|
+
var TABLE_EXISTS_SQL = 'SELECT to_regclass($1) IS NOT NULL AS present';
|
|
51
58
|
var CREATE_SQL = 'CREATE TABLE IF NOT EXISTS "' + TABLE_NAME + '" (' +
|
|
52
59
|
'delivery_key TEXT PRIMARY KEY, claim_token TEXT NOT NULL, ' +
|
|
53
60
|
'claimed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), attempts INTEGER NOT NULL DEFAULT 1)';
|
|
@@ -80,20 +87,28 @@ function __withTimeout(promise, ms) {
|
|
|
80
87
|
return Promise.race([promise, timeout]).then(function (v) { clearTimeout(timer); return v; }, function (err) { clearTimeout(timer); throw err; });
|
|
81
88
|
}
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
// Runs \`work(query)\` on one connection, closed afterwards.
|
|
91
|
+
async function __run(work) {
|
|
84
92
|
var client = getClient();
|
|
85
93
|
await __withTimeout(client.connect(), QUERY_TIMEOUT_MS);
|
|
86
94
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
return result;
|
|
95
|
+
return await work(function (sql, values) {
|
|
96
|
+
return __withTimeout(client.query(sql, values || []), QUERY_TIMEOUT_MS);
|
|
97
|
+
});
|
|
92
98
|
} finally {
|
|
93
99
|
try { await client.end(); } catch (_e) {}
|
|
94
100
|
}
|
|
95
101
|
}
|
|
96
102
|
|
|
103
|
+
async function __ensureTable(query) {
|
|
104
|
+
if (__tableReady) return;
|
|
105
|
+
var found = await query(TABLE_EXISTS_SQL, [TABLE_NAME]);
|
|
106
|
+
if (!found || !Array.isArray(found.rows) || !found.rows[0] || !found.rows[0].present) {
|
|
107
|
+
await query(CREATE_SQL);
|
|
108
|
+
}
|
|
109
|
+
__tableReady = true;
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
function __stableJson(value) {
|
|
98
113
|
try { return JSON.stringify(value === undefined ? null : value) || ''; } catch (_e) { return ''; }
|
|
99
114
|
}
|
|
@@ -120,12 +135,12 @@ async function claimWebhookDelivery(scope, rawBody, verifiedBody) {
|
|
|
120
135
|
.update(__stableJson(verifiedBody))
|
|
121
136
|
.digest('hex');
|
|
122
137
|
var token = crypto.randomBytes(16).toString('hex');
|
|
123
|
-
var statements = __tableReady ? [] : [{ sql: CREATE_SQL, values: [] }];
|
|
124
|
-
statements.push({ sql: CLAIM_SQL, values: [key, token, LEASE_SECONDS] });
|
|
125
138
|
var result;
|
|
126
139
|
try {
|
|
127
|
-
result = await __run(
|
|
128
|
-
|
|
140
|
+
result = await __run(async function (query) {
|
|
141
|
+
await __ensureTable(query);
|
|
142
|
+
return query(CLAIM_SQL, [key, token, LEASE_SECONDS]);
|
|
143
|
+
});
|
|
129
144
|
} catch (err) {
|
|
130
145
|
var code = err && err.code;
|
|
131
146
|
if (code === '42501') { __disable('no privilege to create or write table ' + TABLE_NAME); }
|
|
@@ -138,7 +153,7 @@ async function claimWebhookDelivery(scope, rawBody, verifiedBody) {
|
|
|
138
153
|
return {
|
|
139
154
|
claimed: true,
|
|
140
155
|
release: function () {
|
|
141
|
-
return __run(
|
|
156
|
+
return __run(function (query) { return query(RELEASE_SQL, [key, token]); }).then(
|
|
142
157
|
function () {},
|
|
143
158
|
function (err) { console.error('[webhook-delivery-lease] release failed: ' + (err && err.message ? err.message : String(err))); }
|
|
144
159
|
);
|
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
import { generateInvoiceFiles, resolveInvoiceDataSource } from './invoice'
|
|
73
73
|
import { ensureSentEmailLogModule, hasEmailSendingNodeType } from './sent-email-log'
|
|
74
74
|
import { ensureWebhookDeliveryLeaseModule } from './webhook-delivery-lease'
|
|
75
|
+
import { declareVercelFunctionsWhereRequired } from './vercel-functions'
|
|
75
76
|
import { ensureEmailLocaleModule, resolveEmailLocaleConfig } from './email-locale'
|
|
76
77
|
import { workflowUtilsImportLine, workflowUtilsRequireLine } from './workflow-utils-alias'
|
|
77
78
|
import { generateWebhookFiles } from './webhook-generator'
|
|
@@ -385,6 +386,7 @@ export class NextWorkflowProjectPlugin implements ProjectPlugin {
|
|
|
385
386
|
if (hasAuthentication) {
|
|
386
387
|
this.injectSessionProviderIntoApp(structure)
|
|
387
388
|
}
|
|
389
|
+
declareVercelFunctionsWhereRequired(files, dependencies)
|
|
388
390
|
return structure
|
|
389
391
|
}
|
|
390
392
|
|
|
@@ -974,6 +976,7 @@ export class NextWorkflowProjectPlugin implements ProjectPlugin {
|
|
|
974
976
|
this.injectSessionProviderIntoApp(structure)
|
|
975
977
|
}
|
|
976
978
|
|
|
979
|
+
declareVercelFunctionsWhereRequired(files, dependencies)
|
|
977
980
|
return structure
|
|
978
981
|
}
|
|
979
982
|
|