@teleporthq/teleport-plugin-next-workflows 0.44.11 → 0.45.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.
Files changed (976) hide show
  1. package/__tests__/_helpers/fake-fetch.ts +75 -0
  2. package/__tests__/_helpers/gui-place-order-statements.ts +17 -0
  3. package/__tests__/_helpers/inserted-lines.ts +22 -0
  4. package/__tests__/_helpers/invoice-store.ts +10 -1
  5. package/__tests__/_helpers/load-security-runtime.ts +45 -0
  6. package/__tests__/_helpers/load-server-runtime.ts +26 -3
  7. package/__tests__/_helpers/run-account-delete-route.ts +3 -1
  8. package/__tests__/_helpers/run-generated-middleware.ts +7 -2
  9. package/__tests__/account-delete-owned-rows.test.ts +3 -1
  10. package/__tests__/adopted-element-trigger-code.test.ts +128 -0
  11. package/__tests__/analytics-track-outcome.test.ts +145 -0
  12. package/__tests__/auth-password-change-ends-sessions.test.ts +8 -1
  13. package/__tests__/auth-password-security.test.ts +2275 -0
  14. package/__tests__/auth-session-cost.test.ts +31 -0
  15. package/__tests__/auth-signup-route.test.ts +960 -0
  16. package/__tests__/authored-code-every-path.test.ts +247 -0
  17. package/__tests__/automations-bus.test.ts +3820 -0
  18. package/__tests__/automations-catalog-drift.test.ts +97 -0
  19. package/__tests__/booking-calendar-feed-route.test.ts +20 -3
  20. package/__tests__/cart-price-order.test.ts +336 -1
  21. package/__tests__/code-lookup-rate-limit-route.test.ts +189 -0
  22. package/__tests__/cost-bearing-node-types.test.ts +119 -0
  23. package/__tests__/credential-columns-reply.test.ts +9 -0
  24. package/__tests__/credential-result-reply.test.ts +19 -5
  25. package/__tests__/cron-route-loop-templateparams.test.ts +6 -3
  26. package/__tests__/custom-js-authored-code.test.ts +304 -0
  27. package/__tests__/custom-node-custom-code-gate.test.ts +136 -0
  28. package/__tests__/custom-node-step-handlers.test.ts +251 -0
  29. package/__tests__/custom-node-step-reach.test.ts +63 -0
  30. package/__tests__/data-api-anon-user-id-fallback.test.ts +7 -2
  31. package/__tests__/data-api-browser-reads.test.ts +7 -0
  32. package/__tests__/data-api-business-events.test.ts +403 -0
  33. package/__tests__/data-api-invalid-body.test.ts +7 -0
  34. package/__tests__/data-api-low-stock-autofire.test.ts +30 -4
  35. package/__tests__/data-api-optional-feature-columns.test.ts +44 -0
  36. package/__tests__/data-api-owner-scope-credentials.test.ts +1324 -0
  37. package/__tests__/data-api-protected-tables.test.ts +48 -15
  38. package/__tests__/data-api-retired-table-and-account-fields.test.ts +208 -0
  39. package/__tests__/email-locale-senders.test.ts +5 -3
  40. package/__tests__/expression-injection.test.ts +343 -0
  41. package/__tests__/fixtures/admin-file-workflows.json +242 -0
  42. package/__tests__/form-confirmation-email-template.test.ts +44 -0
  43. package/__tests__/internal-request-headers.test.ts +25 -0
  44. package/__tests__/invoice-pdf-service-project-header.test.ts +120 -0
  45. package/__tests__/invoice-self-call-origin.test.ts +57 -1
  46. package/__tests__/legacy-webhook-self-calls.test.ts +86 -1
  47. package/__tests__/middleware-home-route-protection.test.ts +3 -1
  48. package/__tests__/middleware-subscriber-access.test.ts +1 -1
  49. package/__tests__/newsletter-admin-route.test.ts +649 -0
  50. package/__tests__/newsletter-plugin.test.ts +1248 -0
  51. package/__tests__/outbound-nodes-security.test.ts +1929 -0
  52. package/__tests__/page-access-guard.test.ts +217 -0
  53. package/__tests__/page-access-route-table.test.ts +231 -0
  54. package/__tests__/payment-drivers-module.test.ts +23 -0
  55. package/__tests__/payment-legacy-webhook-routes.test.ts +100 -5
  56. package/__tests__/payment-webhook-route.test.ts +4 -1
  57. package/__tests__/paypal-webhook-signature-verification.test.ts +44 -0
  58. package/__tests__/place-order-guards.test.ts +506 -0
  59. package/__tests__/public-step-guards.test.ts +310 -0
  60. package/__tests__/publish-refusals.test.ts +505 -0
  61. package/__tests__/realtime-identity-routes.test.ts +492 -0
  62. package/__tests__/record-event-writer-server-only.test.ts +975 -0
  63. package/__tests__/resolve-config-template-params.test.ts +3 -3
  64. package/__tests__/runtime-storage-upload-access.test.ts +280 -17
  65. package/__tests__/security-integration-env-names.test.ts +74 -0
  66. package/__tests__/security-lists-parity.test.ts +245 -0
  67. package/__tests__/security-runtime-shadow.test.ts +224 -1
  68. package/__tests__/security-runtime.test.ts +893 -0
  69. package/__tests__/segment-rate-limit-error-branch.test.ts +284 -0
  70. package/__tests__/segment-route-abuse-gate.test.ts +309 -0
  71. package/__tests__/server-job-hops-real-data-api.test.ts +669 -0
  72. package/__tests__/server-session-identity.test.ts +522 -0
  73. package/__tests__/server-step-chain.test.ts +222 -0
  74. package/__tests__/server-step-signing.test.ts +107 -2
  75. package/__tests__/session-cookie-resolver.test.ts +62 -106
  76. package/__tests__/signup-event.test.ts +649 -0
  77. package/__tests__/signup-reply-identical.test.ts +144 -0
  78. package/__tests__/slack-message-escaping.test.ts +123 -0
  79. package/__tests__/visitor-step-limits.test.ts +318 -0
  80. package/__tests__/webhook-delivery-lease.test.ts +4 -1
  81. package/__tests__/webpack-safe-handlers.test.ts +57 -8
  82. package/__tests__/workflow-auth-guard.test.ts +9 -1
  83. package/dist/cjs/account-delete-route-generator.d.ts +1 -0
  84. package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
  85. package/dist/cjs/account-delete-route-generator.js +18 -3
  86. package/dist/cjs/account-delete-route-generator.js.map +1 -1
  87. package/dist/cjs/api-route-generator.d.ts +52 -3
  88. package/dist/cjs/api-route-generator.d.ts.map +1 -1
  89. package/dist/cjs/api-route-generator.js +600 -145
  90. package/dist/cjs/api-route-generator.js.map +1 -1
  91. package/dist/cjs/auth-generator.d.ts +126 -3
  92. package/dist/cjs/auth-generator.d.ts.map +1 -1
  93. package/dist/cjs/auth-generator.js +1353 -139
  94. package/dist/cjs/auth-generator.js.map +1 -1
  95. package/dist/cjs/automations/activity-kicks.d.ts +12 -0
  96. package/dist/cjs/automations/activity-kicks.d.ts.map +1 -0
  97. package/dist/cjs/automations/activity-kicks.js +123 -0
  98. package/dist/cjs/automations/activity-kicks.js.map +1 -0
  99. package/dist/cjs/automations/app-events-source.d.ts +33 -0
  100. package/dist/cjs/automations/app-events-source.d.ts.map +1 -0
  101. package/dist/cjs/automations/app-events-source.js +109 -0
  102. package/dist/cjs/automations/app-events-source.js.map +1 -0
  103. package/dist/cjs/automations/automations-project-plugin.d.ts +21 -0
  104. package/dist/cjs/automations/automations-project-plugin.d.ts.map +1 -0
  105. package/dist/cjs/automations/automations-project-plugin.js +102 -0
  106. package/dist/cjs/automations/automations-project-plugin.js.map +1 -0
  107. package/dist/cjs/automations/automations-scope.d.ts +104 -0
  108. package/dist/cjs/automations/automations-scope.d.ts.map +1 -0
  109. package/dist/cjs/automations/automations-scope.js +176 -0
  110. package/dist/cjs/automations/automations-scope.js.map +1 -0
  111. package/dist/cjs/automations/dispatcher-source.d.ts +72 -0
  112. package/dist/cjs/automations/dispatcher-source.d.ts.map +1 -0
  113. package/dist/cjs/automations/dispatcher-source.js +1008 -0
  114. package/dist/cjs/automations/dispatcher-source.js.map +1 -0
  115. package/dist/cjs/automations/event-bus-source.d.ts +44 -0
  116. package/dist/cjs/automations/event-bus-source.d.ts.map +1 -0
  117. package/dist/cjs/automations/event-bus-source.js +427 -0
  118. package/dist/cjs/automations/event-bus-source.js.map +1 -0
  119. package/dist/cjs/automations/handler-module-generator.d.ts +25 -0
  120. package/dist/cjs/automations/handler-module-generator.d.ts.map +1 -0
  121. package/dist/cjs/automations/handler-module-generator.js +78 -0
  122. package/dist/cjs/automations/handler-module-generator.js.map +1 -0
  123. package/dist/cjs/automations/hydrators-source.d.ts +33 -0
  124. package/dist/cjs/automations/hydrators-source.d.ts.map +1 -0
  125. package/dist/cjs/automations/hydrators-source.js +571 -0
  126. package/dist/cjs/automations/hydrators-source.js.map +1 -0
  127. package/dist/cjs/booking-calendar-feed-route-generator.d.ts.map +1 -1
  128. package/dist/cjs/booking-calendar-feed-route-generator.js +8 -0
  129. package/dist/cjs/booking-calendar-feed-route-generator.js.map +1 -1
  130. package/dist/cjs/configured-nextauth-url.d.ts +17 -0
  131. package/dist/cjs/configured-nextauth-url.d.ts.map +1 -0
  132. package/dist/cjs/configured-nextauth-url.js +20 -0
  133. package/dist/cjs/configured-nextauth-url.js.map +1 -0
  134. package/dist/cjs/cron-request-verification.d.ts +22 -1
  135. package/dist/cjs/cron-request-verification.d.ts.map +1 -1
  136. package/dist/cjs/cron-request-verification.js +50 -6
  137. package/dist/cjs/cron-request-verification.js.map +1 -1
  138. package/dist/cjs/data-api-route-generator.d.ts +19 -0
  139. package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
  140. package/dist/cjs/data-api-route-generator.js +711 -43
  141. package/dist/cjs/data-api-route-generator.js.map +1 -1
  142. package/dist/cjs/ecommerce/parameterized-stock-chain.d.ts +3 -1
  143. package/dist/cjs/ecommerce/parameterized-stock-chain.d.ts.map +1 -1
  144. package/dist/cjs/ecommerce/parameterized-stock-chain.js +45 -5
  145. package/dist/cjs/ecommerce/parameterized-stock-chain.js.map +1 -1
  146. package/dist/cjs/ecommerce/place-order-guards.d.ts +126 -0
  147. package/dist/cjs/ecommerce/place-order-guards.d.ts.map +1 -0
  148. package/dist/cjs/ecommerce/place-order-guards.js +670 -0
  149. package/dist/cjs/ecommerce/place-order-guards.js.map +1 -0
  150. package/dist/cjs/ecommerce-customhandler-rewriter.d.ts +2 -0
  151. package/dist/cjs/ecommerce-customhandler-rewriter.d.ts.map +1 -1
  152. package/dist/cjs/ecommerce-customhandler-rewriter.js +38 -2
  153. package/dist/cjs/ecommerce-customhandler-rewriter.js.map +1 -1
  154. package/dist/cjs/executor-generator.d.ts.map +1 -1
  155. package/dist/cjs/executor-generator.js +368 -60
  156. package/dist/cjs/executor-generator.js.map +1 -1
  157. package/dist/cjs/graph-utils.d.ts +9 -0
  158. package/dist/cjs/graph-utils.d.ts.map +1 -1
  159. package/dist/cjs/graph-utils.js +32 -1
  160. package/dist/cjs/graph-utils.js.map +1 -1
  161. package/dist/cjs/index.d.ts +20 -1
  162. package/dist/cjs/index.d.ts.map +1 -1
  163. package/dist/cjs/index.js +72 -2
  164. package/dist/cjs/index.js.map +1 -1
  165. package/dist/cjs/invoice/api-routes-code.d.ts +9 -3
  166. package/dist/cjs/invoice/api-routes-code.d.ts.map +1 -1
  167. package/dist/cjs/invoice/api-routes-code.js +9 -7
  168. package/dist/cjs/invoice/api-routes-code.js.map +1 -1
  169. package/dist/cjs/invoice/index.d.ts.map +1 -1
  170. package/dist/cjs/invoice/index.js +12 -7
  171. package/dist/cjs/invoice/index.js.map +1 -1
  172. package/dist/cjs/invoice/invoice-assembly-code.d.ts.map +1 -1
  173. package/dist/cjs/invoice/invoice-assembly-code.js +8 -3
  174. package/dist/cjs/invoice/invoice-assembly-code.js.map +1 -1
  175. package/dist/cjs/invoice/pdf-generator-code.d.ts.map +1 -1
  176. package/dist/cjs/invoice/pdf-generator-code.js +2 -1
  177. package/dist/cjs/invoice/pdf-generator-code.js.map +1 -1
  178. package/dist/cjs/invoice/pdf-service-client-code.d.ts.map +1 -1
  179. package/dist/cjs/invoice/pdf-service-client-code.js +15 -7
  180. package/dist/cjs/invoice/pdf-service-client-code.js.map +1 -1
  181. package/dist/cjs/newsletter/admin-route-code.d.ts +60 -0
  182. package/dist/cjs/newsletter/admin-route-code.d.ts.map +1 -0
  183. package/dist/cjs/newsletter/admin-route-code.js +300 -0
  184. package/dist/cjs/newsletter/admin-route-code.js.map +1 -0
  185. package/dist/cjs/newsletter/helper-code.d.ts +109 -0
  186. package/dist/cjs/newsletter/helper-code.d.ts.map +1 -0
  187. package/dist/cjs/newsletter/helper-code.js +662 -0
  188. package/dist/cjs/newsletter/helper-code.js.map +1 -0
  189. package/dist/cjs/newsletter/newsletter-scope.d.ts +65 -0
  190. package/dist/cjs/newsletter/newsletter-scope.d.ts.map +1 -0
  191. package/dist/cjs/newsletter/newsletter-scope.js +103 -0
  192. package/dist/cjs/newsletter/newsletter-scope.js.map +1 -0
  193. package/dist/cjs/newsletter/project-plugin.d.ts +18 -0
  194. package/dist/cjs/newsletter/project-plugin.d.ts.map +1 -0
  195. package/dist/cjs/newsletter/project-plugin.js +97 -0
  196. package/dist/cjs/newsletter/project-plugin.js.map +1 -0
  197. package/dist/cjs/newsletter/routes-code.d.ts +6 -0
  198. package/dist/cjs/newsletter/routes-code.d.ts.map +1 -0
  199. package/dist/cjs/newsletter/routes-code.js +181 -0
  200. package/dist/cjs/newsletter/routes-code.js.map +1 -0
  201. package/dist/cjs/nodes/account/account-compare-passwords.d.ts.map +1 -1
  202. package/dist/cjs/nodes/account/account-compare-passwords.js +16 -6
  203. package/dist/cjs/nodes/account/account-compare-passwords.js.map +1 -1
  204. package/dist/cjs/nodes/account/account-delete-current.js +1 -1
  205. package/dist/cjs/nodes/account/account-get-current.js +1 -1
  206. package/dist/cjs/nodes/account/account-hash-password.d.ts.map +1 -1
  207. package/dist/cjs/nodes/account/account-hash-password.js +15 -3
  208. package/dist/cjs/nodes/account/account-hash-password.js.map +1 -1
  209. package/dist/cjs/nodes/account/account-login.d.ts.map +1 -1
  210. package/dist/cjs/nodes/account/account-login.js +23 -6
  211. package/dist/cjs/nodes/account/account-login.js.map +1 -1
  212. package/dist/cjs/nodes/account/account-logout.js +1 -1
  213. package/dist/cjs/nodes/account/account-refresh-session.js +1 -1
  214. package/dist/cjs/nodes/account/account-request-password-reset.d.ts +3 -0
  215. package/dist/cjs/nodes/account/account-request-password-reset.d.ts.map +1 -0
  216. package/dist/cjs/nodes/account/account-request-password-reset.js +46 -0
  217. package/dist/cjs/nodes/account/account-request-password-reset.js.map +1 -0
  218. package/dist/cjs/nodes/account/account-reset-password.d.ts +3 -0
  219. package/dist/cjs/nodes/account/account-reset-password.d.ts.map +1 -0
  220. package/dist/cjs/nodes/account/account-reset-password.js +44 -0
  221. package/dist/cjs/nodes/account/account-reset-password.js.map +1 -0
  222. package/dist/cjs/nodes/account/account-signup.d.ts.map +1 -1
  223. package/dist/cjs/nodes/account/account-signup.js +82 -19
  224. package/dist/cjs/nodes/account/account-signup.js.map +1 -1
  225. package/dist/cjs/nodes/account/account-social-login.js +1 -1
  226. package/dist/cjs/nodes/ai/ai-custom-prompt.d.ts.map +1 -1
  227. package/dist/cjs/nodes/ai/ai-custom-prompt.js +20 -5
  228. package/dist/cjs/nodes/ai/ai-custom-prompt.js.map +1 -1
  229. package/dist/cjs/nodes/ai/ai-detect-language.d.ts.map +1 -1
  230. package/dist/cjs/nodes/ai/ai-detect-language.js +20 -5
  231. package/dist/cjs/nodes/ai/ai-detect-language.js.map +1 -1
  232. package/dist/cjs/nodes/ai/ai-generate-text-embedding.d.ts.map +1 -1
  233. package/dist/cjs/nodes/ai/ai-generate-text-embedding.js +10 -2
  234. package/dist/cjs/nodes/ai/ai-generate-text-embedding.js.map +1 -1
  235. package/dist/cjs/nodes/ai/ai-provider-utils-sources.generated.js +2 -2
  236. package/dist/cjs/nodes/ai/ai-provider-utils-sources.generated.js.map +1 -1
  237. package/dist/cjs/nodes/ai/ai-provider-utils.d.ts +31 -2
  238. package/dist/cjs/nodes/ai/ai-provider-utils.d.ts.map +1 -1
  239. package/dist/cjs/nodes/ai/ai-provider-utils.js +125 -11
  240. package/dist/cjs/nodes/ai/ai-provider-utils.js.map +1 -1
  241. package/dist/cjs/nodes/ai/ai-select-database-data.d.ts.map +1 -1
  242. package/dist/cjs/nodes/ai/ai-select-database-data.js +6 -2
  243. package/dist/cjs/nodes/ai/ai-select-database-data.js.map +1 -1
  244. package/dist/cjs/nodes/ai/ai-sentiment-analysis.d.ts.map +1 -1
  245. package/dist/cjs/nodes/ai/ai-sentiment-analysis.js +20 -5
  246. package/dist/cjs/nodes/ai/ai-sentiment-analysis.js.map +1 -1
  247. package/dist/cjs/nodes/ai/ai-summarization.d.ts.map +1 -1
  248. package/dist/cjs/nodes/ai/ai-summarization.js +20 -5
  249. package/dist/cjs/nodes/ai/ai-summarization.js.map +1 -1
  250. package/dist/cjs/nodes/ai/ai-text-classifier.d.ts.map +1 -1
  251. package/dist/cjs/nodes/ai/ai-text-classifier.js +20 -5
  252. package/dist/cjs/nodes/ai/ai-text-classifier.js.map +1 -1
  253. package/dist/cjs/nodes/ai/ai-text-transform.d.ts.map +1 -1
  254. package/dist/cjs/nodes/ai/ai-text-transform.js +20 -5
  255. package/dist/cjs/nodes/ai/ai-text-transform.js.map +1 -1
  256. package/dist/cjs/nodes/analytics/analytics-track-outcome.d.ts +3 -0
  257. package/dist/cjs/nodes/analytics/analytics-track-outcome.d.ts.map +1 -0
  258. package/dist/cjs/nodes/analytics/analytics-track-outcome.js +110 -0
  259. package/dist/cjs/nodes/analytics/analytics-track-outcome.js.map +1 -0
  260. package/dist/cjs/nodes/cart/cart-price-order.d.ts.map +1 -1
  261. package/dist/cjs/nodes/cart/cart-price-order.js +161 -2
  262. package/dist/cjs/nodes/cart/cart-price-order.js.map +1 -1
  263. package/dist/cjs/nodes/cost-bearing-node-types.d.ts +4 -0
  264. package/dist/cjs/nodes/cost-bearing-node-types.d.ts.map +1 -0
  265. package/dist/cjs/nodes/cost-bearing-node-types.js +43 -0
  266. package/dist/cjs/nodes/cost-bearing-node-types.js.map +1 -0
  267. package/dist/cjs/nodes/data/data-select.d.ts.map +1 -1
  268. package/dist/cjs/nodes/data/data-select.js +12 -2
  269. package/dist/cjs/nodes/data/data-select.js.map +1 -1
  270. package/dist/cjs/nodes/email/email-mailersend.d.ts.map +1 -1
  271. package/dist/cjs/nodes/email/email-mailersend.js +15 -11
  272. package/dist/cjs/nodes/email/email-mailersend.js.map +1 -1
  273. package/dist/cjs/nodes/email/email-mailgun.d.ts.map +1 -1
  274. package/dist/cjs/nodes/email/email-mailgun.js +15 -15
  275. package/dist/cjs/nodes/email/email-mailgun.js.map +1 -1
  276. package/dist/cjs/nodes/email/email-postmark.d.ts.map +1 -1
  277. package/dist/cjs/nodes/email/email-postmark.js +15 -15
  278. package/dist/cjs/nodes/email/email-postmark.js.map +1 -1
  279. package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
  280. package/dist/cjs/nodes/email/email-resend.js +15 -15
  281. package/dist/cjs/nodes/email/email-resend.js.map +1 -1
  282. package/dist/cjs/nodes/email/email-sendgrid.d.ts.map +1 -1
  283. package/dist/cjs/nodes/email/email-sendgrid.js +15 -15
  284. package/dist/cjs/nodes/email/email-sendgrid.js.map +1 -1
  285. package/dist/cjs/nodes/email/html-escape-code.d.ts +3 -0
  286. package/dist/cjs/nodes/email/html-escape-code.d.ts.map +1 -0
  287. package/dist/cjs/nodes/email/html-escape-code.js +23 -0
  288. package/dist/cjs/nodes/email/html-escape-code.js.map +1 -0
  289. package/dist/cjs/nodes/email/message-trust.d.ts +40 -0
  290. package/dist/cjs/nodes/email/message-trust.d.ts.map +1 -0
  291. package/dist/cjs/nodes/email/message-trust.js +167 -0
  292. package/dist/cjs/nodes/email/message-trust.js.map +1 -0
  293. package/dist/cjs/nodes/email/outbound-message-guard.d.ts +113 -0
  294. package/dist/cjs/nodes/email/outbound-message-guard.d.ts.map +1 -0
  295. package/dist/cjs/nodes/email/outbound-message-guard.js +460 -0
  296. package/dist/cjs/nodes/email/outbound-message-guard.js.map +1 -0
  297. package/dist/cjs/nodes/email/trusted-html-code.d.ts +22 -0
  298. package/dist/cjs/nodes/email/trusted-html-code.d.ts.map +1 -0
  299. package/dist/cjs/nodes/email/trusted-html-code.js +52 -0
  300. package/dist/cjs/nodes/email/trusted-html-code.js.map +1 -0
  301. package/dist/cjs/nodes/general/general-custom-js.d.ts.map +1 -1
  302. package/dist/cjs/nodes/general/general-custom-js.js +44 -7
  303. package/dist/cjs/nodes/general/general-custom-js.js.map +1 -1
  304. package/dist/cjs/nodes/general/general-http-request.d.ts.map +1 -1
  305. package/dist/cjs/nodes/general/general-http-request.js +431 -45
  306. package/dist/cjs/nodes/general/general-http-request.js.map +1 -1
  307. package/dist/cjs/nodes/general/general-rate-limiter.d.ts.map +1 -1
  308. package/dist/cjs/nodes/general/general-rate-limiter.js +46 -78
  309. package/dist/cjs/nodes/general/general-rate-limiter.js.map +1 -1
  310. package/dist/cjs/nodes/general/general-wait.d.ts +3 -0
  311. package/dist/cjs/nodes/general/general-wait.d.ts.map +1 -0
  312. package/dist/cjs/nodes/general/general-wait.js +81 -0
  313. package/dist/cjs/nodes/general/general-wait.js.map +1 -0
  314. package/dist/cjs/nodes/general/visitor-request.d.ts +44 -0
  315. package/dist/cjs/nodes/general/visitor-request.d.ts.map +1 -0
  316. package/dist/cjs/nodes/general/visitor-request.js +72 -0
  317. package/dist/cjs/nodes/general/visitor-request.js.map +1 -0
  318. package/dist/cjs/nodes/index.d.ts.map +1 -1
  319. package/dist/cjs/nodes/index.js +7 -1
  320. package/dist/cjs/nodes/index.js.map +1 -1
  321. package/dist/cjs/nodes/integrations/integration-slack.d.ts.map +1 -1
  322. package/dist/cjs/nodes/integrations/integration-slack.js +17 -6
  323. package/dist/cjs/nodes/integrations/integration-slack.js.map +1 -1
  324. package/dist/cjs/nodes/newsletter/newsletter-confirm.d.ts +3 -0
  325. package/dist/cjs/nodes/newsletter/newsletter-confirm.d.ts.map +1 -0
  326. package/dist/cjs/nodes/newsletter/newsletter-confirm.js +41 -0
  327. package/dist/cjs/nodes/newsletter/newsletter-confirm.js.map +1 -0
  328. package/dist/cjs/nodes/newsletter/newsletter-subscribe.d.ts +7 -0
  329. package/dist/cjs/nodes/newsletter/newsletter-subscribe.d.ts.map +1 -0
  330. package/dist/cjs/nodes/newsletter/newsletter-subscribe.js +69 -0
  331. package/dist/cjs/nodes/newsletter/newsletter-subscribe.js.map +1 -0
  332. package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.d.ts +3 -0
  333. package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.d.ts.map +1 -0
  334. package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.js +42 -0
  335. package/dist/cjs/nodes/newsletter/newsletter-unsubscribe.js.map +1 -0
  336. package/dist/cjs/nodes/realtime/realtime-join-channel.d.ts.map +1 -1
  337. package/dist/cjs/nodes/realtime/realtime-join-channel.js +5 -4
  338. package/dist/cjs/nodes/realtime/realtime-join-channel.js.map +1 -1
  339. package/dist/cjs/nodes/realtime/realtime-send-channel-event.d.ts.map +1 -1
  340. package/dist/cjs/nodes/realtime/realtime-send-channel-event.js +11 -2
  341. package/dist/cjs/nodes/realtime/realtime-send-channel-event.js.map +1 -1
  342. package/dist/cjs/nodes/realtime/realtime-send-channel-message.d.ts.map +1 -1
  343. package/dist/cjs/nodes/realtime/realtime-send-channel-message.js +11 -2
  344. package/dist/cjs/nodes/realtime/realtime-send-channel-message.js.map +1 -1
  345. package/dist/cjs/nodes/sms/sms-infobip.d.ts.map +1 -1
  346. package/dist/cjs/nodes/sms/sms-infobip.js +11 -8
  347. package/dist/cjs/nodes/sms/sms-infobip.js.map +1 -1
  348. package/dist/cjs/nodes/sms/sms-smsapi.d.ts.map +1 -1
  349. package/dist/cjs/nodes/sms/sms-smsapi.js +11 -8
  350. package/dist/cjs/nodes/sms/sms-smsapi.js.map +1 -1
  351. package/dist/cjs/nodes/sms/sms-textmagic.d.ts.map +1 -1
  352. package/dist/cjs/nodes/sms/sms-textmagic.js +11 -8
  353. package/dist/cjs/nodes/sms/sms-textmagic.js.map +1 -1
  354. package/dist/cjs/nodes/sms/sms-twilio.d.ts.map +1 -1
  355. package/dist/cjs/nodes/sms/sms-twilio.js +12 -10
  356. package/dist/cjs/nodes/sms/sms-twilio.js.map +1 -1
  357. package/dist/cjs/nodes/types.d.ts +8 -1
  358. package/dist/cjs/nodes/types.d.ts.map +1 -1
  359. package/dist/cjs/nodes/utility/utility-similarity-scoring.d.ts.map +1 -1
  360. package/dist/cjs/nodes/utility/utility-similarity-scoring.js +29 -10
  361. package/dist/cjs/nodes/utility/utility-similarity-scoring.js.map +1 -1
  362. package/dist/cjs/page-access-guard-generator.d.ts +23 -0
  363. package/dist/cjs/page-access-guard-generator.d.ts.map +1 -0
  364. package/dist/cjs/page-access-guard-generator.js +135 -0
  365. package/dist/cjs/page-access-guard-generator.js.map +1 -0
  366. package/dist/cjs/password-reset-route-generator.d.ts +119 -0
  367. package/dist/cjs/password-reset-route-generator.d.ts.map +1 -0
  368. package/dist/cjs/password-reset-route-generator.js +460 -0
  369. package/dist/cjs/password-reset-route-generator.js.map +1 -0
  370. package/dist/cjs/payments/payment-core-code.d.ts.map +1 -1
  371. package/dist/cjs/payments/payment-core-code.js +12 -7
  372. package/dist/cjs/payments/payment-core-code.js.map +1 -1
  373. package/dist/cjs/project-code-scanner.d.ts +11 -0
  374. package/dist/cjs/project-code-scanner.d.ts.map +1 -0
  375. package/dist/cjs/project-code-scanner.js +80 -0
  376. package/dist/cjs/project-code-scanner.js.map +1 -0
  377. package/dist/cjs/public-step-guards.d.ts +122 -0
  378. package/dist/cjs/public-step-guards.d.ts.map +1 -0
  379. package/dist/cjs/public-step-guards.js +288 -0
  380. package/dist/cjs/public-step-guards.js.map +1 -0
  381. package/dist/cjs/publish-refusals.d.ts +66 -0
  382. package/dist/cjs/publish-refusals.d.ts.map +1 -0
  383. package/dist/cjs/publish-refusals.js +278 -0
  384. package/dist/cjs/publish-refusals.js.map +1 -0
  385. package/dist/cjs/realtime-generator.d.ts +63 -1
  386. package/dist/cjs/realtime-generator.d.ts.map +1 -1
  387. package/dist/cjs/realtime-generator.js +404 -230
  388. package/dist/cjs/realtime-generator.js.map +1 -1
  389. package/dist/cjs/runtime-storage-generator.d.ts +32 -1
  390. package/dist/cjs/runtime-storage-generator.d.ts.map +1 -1
  391. package/dist/cjs/runtime-storage-generator.js +265 -12
  392. package/dist/cjs/runtime-storage-generator.js.map +1 -1
  393. package/dist/cjs/security-runtime/index.d.ts +14 -0
  394. package/dist/cjs/security-runtime/index.d.ts.map +1 -0
  395. package/dist/cjs/security-runtime/index.js +48 -0
  396. package/dist/cjs/security-runtime/index.js.map +1 -0
  397. package/dist/cjs/security-runtime/security-runtime-code.d.ts +53 -0
  398. package/dist/cjs/security-runtime/security-runtime-code.d.ts.map +1 -0
  399. package/dist/cjs/security-runtime/security-runtime-code.js +580 -0
  400. package/dist/cjs/security-runtime/security-runtime-code.js.map +1 -0
  401. package/dist/cjs/security-scanner.d.ts +72 -3
  402. package/dist/cjs/security-scanner.d.ts.map +1 -1
  403. package/dist/cjs/security-scanner.js +220 -8
  404. package/dist/cjs/security-scanner.js.map +1 -1
  405. package/dist/cjs/segment-splitter.d.ts.map +1 -1
  406. package/dist/cjs/segment-splitter.js +10 -2
  407. package/dist/cjs/segment-splitter.js.map +1 -1
  408. package/dist/cjs/segment-trust.d.ts +36 -0
  409. package/dist/cjs/segment-trust.d.ts.map +1 -1
  410. package/dist/cjs/segment-trust.js +147 -3
  411. package/dist/cjs/segment-trust.js.map +1 -1
  412. package/dist/cjs/self-call-headers.d.ts +31 -0
  413. package/dist/cjs/self-call-headers.d.ts.map +1 -0
  414. package/dist/cjs/self-call-headers.js +48 -0
  415. package/dist/cjs/self-call-headers.js.map +1 -0
  416. package/dist/cjs/sent-email-log/index.d.ts +7 -0
  417. package/dist/cjs/sent-email-log/index.d.ts.map +1 -1
  418. package/dist/cjs/sent-email-log/index.js +17 -1
  419. package/dist/cjs/sent-email-log/index.js.map +1 -1
  420. package/dist/cjs/sent-email-log/sent-email-log-code.d.ts +7 -0
  421. package/dist/cjs/sent-email-log/sent-email-log-code.d.ts.map +1 -1
  422. package/dist/cjs/sent-email-log/sent-email-log-code.js +17 -3
  423. package/dist/cjs/sent-email-log/sent-email-log-code.js.map +1 -1
  424. package/dist/cjs/server-custom-node-call.d.ts +79 -0
  425. package/dist/cjs/server-custom-node-call.d.ts.map +1 -0
  426. package/dist/cjs/server-custom-node-call.js +285 -0
  427. package/dist/cjs/server-custom-node-call.js.map +1 -0
  428. package/dist/cjs/server-runtime-code.d.ts +39 -12
  429. package/dist/cjs/server-runtime-code.d.ts.map +1 -1
  430. package/dist/cjs/server-runtime-code.js +484 -41
  431. package/dist/cjs/server-runtime-code.js.map +1 -1
  432. package/dist/cjs/session-cookie-resolver.d.ts +3 -1
  433. package/dist/cjs/session-cookie-resolver.d.ts.map +1 -1
  434. package/dist/cjs/session-revocation-generator.d.ts +39 -0
  435. package/dist/cjs/session-revocation-generator.d.ts.map +1 -0
  436. package/dist/cjs/session-revocation-generator.js +177 -0
  437. package/dist/cjs/session-revocation-generator.js.map +1 -0
  438. package/dist/cjs/slack-message-values.d.ts +6 -0
  439. package/dist/cjs/slack-message-values.d.ts.map +1 -0
  440. package/dist/cjs/slack-message-values.js +73 -0
  441. package/dist/cjs/slack-message-values.js.map +1 -0
  442. package/dist/cjs/transactional-email-code.d.ts +11 -0
  443. package/dist/cjs/transactional-email-code.d.ts.map +1 -1
  444. package/dist/cjs/transactional-email-code.js +29 -5
  445. package/dist/cjs/transactional-email-code.js.map +1 -1
  446. package/dist/cjs/trigger-generator.d.ts +2 -0
  447. package/dist/cjs/trigger-generator.d.ts.map +1 -1
  448. package/dist/cjs/trigger-generator.js +17 -4
  449. package/dist/cjs/trigger-generator.js.map +1 -1
  450. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  451. package/dist/cjs/visitor-step-limits.d.ts +66 -0
  452. package/dist/cjs/visitor-step-limits.d.ts.map +1 -0
  453. package/dist/cjs/visitor-step-limits.js +187 -0
  454. package/dist/cjs/visitor-step-limits.js.map +1 -0
  455. package/dist/cjs/webhook-generator.d.ts.map +1 -1
  456. package/dist/cjs/webhook-generator.js +93 -15
  457. package/dist/cjs/webhook-generator.js.map +1 -1
  458. package/dist/cjs/webhook-signature-verification.d.ts.map +1 -1
  459. package/dist/cjs/webhook-signature-verification.js +13 -3
  460. package/dist/cjs/webhook-signature-verification.js.map +1 -1
  461. package/dist/cjs/workflow-auth-generator.d.ts +4 -1
  462. package/dist/cjs/workflow-auth-generator.d.ts.map +1 -1
  463. package/dist/cjs/workflow-auth-generator.js +8 -3
  464. package/dist/cjs/workflow-auth-generator.js.map +1 -1
  465. package/dist/cjs/workflow-component-plugin.js +11 -3
  466. package/dist/cjs/workflow-component-plugin.js.map +1 -1
  467. package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
  468. package/dist/cjs/workflow-project-plugin.js +302 -56
  469. package/dist/cjs/workflow-project-plugin.js.map +1 -1
  470. package/dist/cjs/workflow-scope.d.ts.map +1 -1
  471. package/dist/cjs/workflow-scope.js +5 -0
  472. package/dist/cjs/workflow-scope.js.map +1 -1
  473. package/dist/esm/account-delete-route-generator.d.ts +1 -0
  474. package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
  475. package/dist/esm/account-delete-route-generator.js +18 -3
  476. package/dist/esm/account-delete-route-generator.js.map +1 -1
  477. package/dist/esm/api-route-generator.d.ts +52 -3
  478. package/dist/esm/api-route-generator.d.ts.map +1 -1
  479. package/dist/esm/api-route-generator.js +595 -142
  480. package/dist/esm/api-route-generator.js.map +1 -1
  481. package/dist/esm/auth-generator.d.ts +126 -3
  482. package/dist/esm/auth-generator.d.ts.map +1 -1
  483. package/dist/esm/auth-generator.js +1350 -140
  484. package/dist/esm/auth-generator.js.map +1 -1
  485. package/dist/esm/automations/activity-kicks.d.ts +12 -0
  486. package/dist/esm/automations/activity-kicks.d.ts.map +1 -0
  487. package/dist/esm/automations/activity-kicks.js +118 -0
  488. package/dist/esm/automations/activity-kicks.js.map +1 -0
  489. package/dist/esm/automations/app-events-source.d.ts +33 -0
  490. package/dist/esm/automations/app-events-source.d.ts.map +1 -0
  491. package/dist/esm/automations/app-events-source.js +104 -0
  492. package/dist/esm/automations/app-events-source.js.map +1 -0
  493. package/dist/esm/automations/automations-project-plugin.d.ts +21 -0
  494. package/dist/esm/automations/automations-project-plugin.d.ts.map +1 -0
  495. package/dist/esm/automations/automations-project-plugin.js +98 -0
  496. package/dist/esm/automations/automations-project-plugin.js.map +1 -0
  497. package/dist/esm/automations/automations-scope.d.ts +104 -0
  498. package/dist/esm/automations/automations-scope.d.ts.map +1 -0
  499. package/dist/esm/automations/automations-scope.js +165 -0
  500. package/dist/esm/automations/automations-scope.js.map +1 -0
  501. package/dist/esm/automations/dispatcher-source.d.ts +72 -0
  502. package/dist/esm/automations/dispatcher-source.d.ts.map +1 -0
  503. package/dist/esm/automations/dispatcher-source.js +999 -0
  504. package/dist/esm/automations/dispatcher-source.js.map +1 -0
  505. package/dist/esm/automations/event-bus-source.d.ts +44 -0
  506. package/dist/esm/automations/event-bus-source.d.ts.map +1 -0
  507. package/dist/esm/automations/event-bus-source.js +421 -0
  508. package/dist/esm/automations/event-bus-source.js.map +1 -0
  509. package/dist/esm/automations/handler-module-generator.d.ts +25 -0
  510. package/dist/esm/automations/handler-module-generator.d.ts.map +1 -0
  511. package/dist/esm/automations/handler-module-generator.js +71 -0
  512. package/dist/esm/automations/handler-module-generator.js.map +1 -0
  513. package/dist/esm/automations/hydrators-source.d.ts +33 -0
  514. package/dist/esm/automations/hydrators-source.d.ts.map +1 -0
  515. package/dist/esm/automations/hydrators-source.js +567 -0
  516. package/dist/esm/automations/hydrators-source.js.map +1 -0
  517. package/dist/esm/booking-calendar-feed-route-generator.d.ts.map +1 -1
  518. package/dist/esm/booking-calendar-feed-route-generator.js +8 -0
  519. package/dist/esm/booking-calendar-feed-route-generator.js.map +1 -1
  520. package/dist/esm/configured-nextauth-url.d.ts +17 -0
  521. package/dist/esm/configured-nextauth-url.d.ts.map +1 -0
  522. package/dist/esm/configured-nextauth-url.js +17 -0
  523. package/dist/esm/configured-nextauth-url.js.map +1 -0
  524. package/dist/esm/cron-request-verification.d.ts +22 -1
  525. package/dist/esm/cron-request-verification.d.ts.map +1 -1
  526. package/dist/esm/cron-request-verification.js +48 -5
  527. package/dist/esm/cron-request-verification.js.map +1 -1
  528. package/dist/esm/data-api-route-generator.d.ts +19 -0
  529. package/dist/esm/data-api-route-generator.d.ts.map +1 -1
  530. package/dist/esm/data-api-route-generator.js +710 -43
  531. package/dist/esm/data-api-route-generator.js.map +1 -1
  532. package/dist/esm/ecommerce/parameterized-stock-chain.d.ts +3 -1
  533. package/dist/esm/ecommerce/parameterized-stock-chain.d.ts.map +1 -1
  534. package/dist/esm/ecommerce/parameterized-stock-chain.js +43 -4
  535. package/dist/esm/ecommerce/parameterized-stock-chain.js.map +1 -1
  536. package/dist/esm/ecommerce/place-order-guards.d.ts +126 -0
  537. package/dist/esm/ecommerce/place-order-guards.d.ts.map +1 -0
  538. package/dist/esm/ecommerce/place-order-guards.js +655 -0
  539. package/dist/esm/ecommerce/place-order-guards.js.map +1 -0
  540. package/dist/esm/ecommerce-customhandler-rewriter.d.ts +2 -0
  541. package/dist/esm/ecommerce-customhandler-rewriter.d.ts.map +1 -1
  542. package/dist/esm/ecommerce-customhandler-rewriter.js +38 -2
  543. package/dist/esm/ecommerce-customhandler-rewriter.js.map +1 -1
  544. package/dist/esm/executor-generator.d.ts.map +1 -1
  545. package/dist/esm/executor-generator.js +368 -60
  546. package/dist/esm/executor-generator.js.map +1 -1
  547. package/dist/esm/graph-utils.d.ts +9 -0
  548. package/dist/esm/graph-utils.d.ts.map +1 -1
  549. package/dist/esm/graph-utils.js +30 -0
  550. package/dist/esm/graph-utils.js.map +1 -1
  551. package/dist/esm/index.d.ts +20 -1
  552. package/dist/esm/index.d.ts.map +1 -1
  553. package/dist/esm/index.js +27 -1
  554. package/dist/esm/index.js.map +1 -1
  555. package/dist/esm/invoice/api-routes-code.d.ts +9 -3
  556. package/dist/esm/invoice/api-routes-code.d.ts.map +1 -1
  557. package/dist/esm/invoice/api-routes-code.js +9 -7
  558. package/dist/esm/invoice/api-routes-code.js.map +1 -1
  559. package/dist/esm/invoice/index.d.ts.map +1 -1
  560. package/dist/esm/invoice/index.js +12 -7
  561. package/dist/esm/invoice/index.js.map +1 -1
  562. package/dist/esm/invoice/invoice-assembly-code.d.ts.map +1 -1
  563. package/dist/esm/invoice/invoice-assembly-code.js +8 -3
  564. package/dist/esm/invoice/invoice-assembly-code.js.map +1 -1
  565. package/dist/esm/invoice/pdf-generator-code.d.ts.map +1 -1
  566. package/dist/esm/invoice/pdf-generator-code.js +2 -1
  567. package/dist/esm/invoice/pdf-generator-code.js.map +1 -1
  568. package/dist/esm/invoice/pdf-service-client-code.d.ts.map +1 -1
  569. package/dist/esm/invoice/pdf-service-client-code.js +15 -7
  570. package/dist/esm/invoice/pdf-service-client-code.js.map +1 -1
  571. package/dist/esm/newsletter/admin-route-code.d.ts +60 -0
  572. package/dist/esm/newsletter/admin-route-code.d.ts.map +1 -0
  573. package/dist/esm/newsletter/admin-route-code.js +295 -0
  574. package/dist/esm/newsletter/admin-route-code.js.map +1 -0
  575. package/dist/esm/newsletter/helper-code.d.ts +109 -0
  576. package/dist/esm/newsletter/helper-code.d.ts.map +1 -0
  577. package/dist/esm/newsletter/helper-code.js +656 -0
  578. package/dist/esm/newsletter/helper-code.js.map +1 -0
  579. package/dist/esm/newsletter/newsletter-scope.d.ts +65 -0
  580. package/dist/esm/newsletter/newsletter-scope.d.ts.map +1 -0
  581. package/dist/esm/newsletter/newsletter-scope.js +97 -0
  582. package/dist/esm/newsletter/newsletter-scope.js.map +1 -0
  583. package/dist/esm/newsletter/project-plugin.d.ts +18 -0
  584. package/dist/esm/newsletter/project-plugin.d.ts.map +1 -0
  585. package/dist/esm/newsletter/project-plugin.js +93 -0
  586. package/dist/esm/newsletter/project-plugin.js.map +1 -0
  587. package/dist/esm/newsletter/routes-code.d.ts +6 -0
  588. package/dist/esm/newsletter/routes-code.d.ts.map +1 -0
  589. package/dist/esm/newsletter/routes-code.js +175 -0
  590. package/dist/esm/newsletter/routes-code.js.map +1 -0
  591. package/dist/esm/nodes/account/account-compare-passwords.d.ts.map +1 -1
  592. package/dist/esm/nodes/account/account-compare-passwords.js +16 -6
  593. package/dist/esm/nodes/account/account-compare-passwords.js.map +1 -1
  594. package/dist/esm/nodes/account/account-delete-current.js +1 -1
  595. package/dist/esm/nodes/account/account-get-current.js +1 -1
  596. package/dist/esm/nodes/account/account-hash-password.d.ts.map +1 -1
  597. package/dist/esm/nodes/account/account-hash-password.js +15 -3
  598. package/dist/esm/nodes/account/account-hash-password.js.map +1 -1
  599. package/dist/esm/nodes/account/account-login.d.ts.map +1 -1
  600. package/dist/esm/nodes/account/account-login.js +23 -6
  601. package/dist/esm/nodes/account/account-login.js.map +1 -1
  602. package/dist/esm/nodes/account/account-logout.js +1 -1
  603. package/dist/esm/nodes/account/account-refresh-session.js +1 -1
  604. package/dist/esm/nodes/account/account-request-password-reset.d.ts +3 -0
  605. package/dist/esm/nodes/account/account-request-password-reset.d.ts.map +1 -0
  606. package/dist/esm/nodes/account/account-request-password-reset.js +43 -0
  607. package/dist/esm/nodes/account/account-request-password-reset.js.map +1 -0
  608. package/dist/esm/nodes/account/account-reset-password.d.ts +3 -0
  609. package/dist/esm/nodes/account/account-reset-password.d.ts.map +1 -0
  610. package/dist/esm/nodes/account/account-reset-password.js +41 -0
  611. package/dist/esm/nodes/account/account-reset-password.js.map +1 -0
  612. package/dist/esm/nodes/account/account-signup.d.ts.map +1 -1
  613. package/dist/esm/nodes/account/account-signup.js +82 -19
  614. package/dist/esm/nodes/account/account-signup.js.map +1 -1
  615. package/dist/esm/nodes/account/account-social-login.js +1 -1
  616. package/dist/esm/nodes/ai/ai-custom-prompt.d.ts.map +1 -1
  617. package/dist/esm/nodes/ai/ai-custom-prompt.js +20 -5
  618. package/dist/esm/nodes/ai/ai-custom-prompt.js.map +1 -1
  619. package/dist/esm/nodes/ai/ai-detect-language.d.ts.map +1 -1
  620. package/dist/esm/nodes/ai/ai-detect-language.js +20 -5
  621. package/dist/esm/nodes/ai/ai-detect-language.js.map +1 -1
  622. package/dist/esm/nodes/ai/ai-generate-text-embedding.d.ts.map +1 -1
  623. package/dist/esm/nodes/ai/ai-generate-text-embedding.js +10 -2
  624. package/dist/esm/nodes/ai/ai-generate-text-embedding.js.map +1 -1
  625. package/dist/esm/nodes/ai/ai-provider-utils-sources.generated.js +2 -2
  626. package/dist/esm/nodes/ai/ai-provider-utils-sources.generated.js.map +1 -1
  627. package/dist/esm/nodes/ai/ai-provider-utils.d.ts +31 -2
  628. package/dist/esm/nodes/ai/ai-provider-utils.d.ts.map +1 -1
  629. package/dist/esm/nodes/ai/ai-provider-utils.js +124 -10
  630. package/dist/esm/nodes/ai/ai-provider-utils.js.map +1 -1
  631. package/dist/esm/nodes/ai/ai-select-database-data.d.ts.map +1 -1
  632. package/dist/esm/nodes/ai/ai-select-database-data.js +6 -2
  633. package/dist/esm/nodes/ai/ai-select-database-data.js.map +1 -1
  634. package/dist/esm/nodes/ai/ai-sentiment-analysis.d.ts.map +1 -1
  635. package/dist/esm/nodes/ai/ai-sentiment-analysis.js +20 -5
  636. package/dist/esm/nodes/ai/ai-sentiment-analysis.js.map +1 -1
  637. package/dist/esm/nodes/ai/ai-summarization.d.ts.map +1 -1
  638. package/dist/esm/nodes/ai/ai-summarization.js +20 -5
  639. package/dist/esm/nodes/ai/ai-summarization.js.map +1 -1
  640. package/dist/esm/nodes/ai/ai-text-classifier.d.ts.map +1 -1
  641. package/dist/esm/nodes/ai/ai-text-classifier.js +20 -5
  642. package/dist/esm/nodes/ai/ai-text-classifier.js.map +1 -1
  643. package/dist/esm/nodes/ai/ai-text-transform.d.ts.map +1 -1
  644. package/dist/esm/nodes/ai/ai-text-transform.js +20 -5
  645. package/dist/esm/nodes/ai/ai-text-transform.js.map +1 -1
  646. package/dist/esm/nodes/analytics/analytics-track-outcome.d.ts +3 -0
  647. package/dist/esm/nodes/analytics/analytics-track-outcome.d.ts.map +1 -0
  648. package/dist/esm/nodes/analytics/analytics-track-outcome.js +107 -0
  649. package/dist/esm/nodes/analytics/analytics-track-outcome.js.map +1 -0
  650. package/dist/esm/nodes/cart/cart-price-order.d.ts.map +1 -1
  651. package/dist/esm/nodes/cart/cart-price-order.js +161 -2
  652. package/dist/esm/nodes/cart/cart-price-order.js.map +1 -1
  653. package/dist/esm/nodes/cost-bearing-node-types.d.ts +4 -0
  654. package/dist/esm/nodes/cost-bearing-node-types.d.ts.map +1 -0
  655. package/dist/esm/nodes/cost-bearing-node-types.js +39 -0
  656. package/dist/esm/nodes/cost-bearing-node-types.js.map +1 -0
  657. package/dist/esm/nodes/data/data-select.d.ts.map +1 -1
  658. package/dist/esm/nodes/data/data-select.js +12 -2
  659. package/dist/esm/nodes/data/data-select.js.map +1 -1
  660. package/dist/esm/nodes/email/email-mailersend.d.ts.map +1 -1
  661. package/dist/esm/nodes/email/email-mailersend.js +15 -11
  662. package/dist/esm/nodes/email/email-mailersend.js.map +1 -1
  663. package/dist/esm/nodes/email/email-mailgun.d.ts.map +1 -1
  664. package/dist/esm/nodes/email/email-mailgun.js +15 -15
  665. package/dist/esm/nodes/email/email-mailgun.js.map +1 -1
  666. package/dist/esm/nodes/email/email-postmark.d.ts.map +1 -1
  667. package/dist/esm/nodes/email/email-postmark.js +15 -15
  668. package/dist/esm/nodes/email/email-postmark.js.map +1 -1
  669. package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
  670. package/dist/esm/nodes/email/email-resend.js +15 -15
  671. package/dist/esm/nodes/email/email-resend.js.map +1 -1
  672. package/dist/esm/nodes/email/email-sendgrid.d.ts.map +1 -1
  673. package/dist/esm/nodes/email/email-sendgrid.js +15 -15
  674. package/dist/esm/nodes/email/email-sendgrid.js.map +1 -1
  675. package/dist/esm/nodes/email/html-escape-code.d.ts +3 -0
  676. package/dist/esm/nodes/email/html-escape-code.d.ts.map +1 -0
  677. package/dist/esm/nodes/email/html-escape-code.js +18 -0
  678. package/dist/esm/nodes/email/html-escape-code.js.map +1 -0
  679. package/dist/esm/nodes/email/message-trust.d.ts +40 -0
  680. package/dist/esm/nodes/email/message-trust.d.ts.map +1 -0
  681. package/dist/esm/nodes/email/message-trust.js +161 -0
  682. package/dist/esm/nodes/email/message-trust.js.map +1 -0
  683. package/dist/esm/nodes/email/outbound-message-guard.d.ts +113 -0
  684. package/dist/esm/nodes/email/outbound-message-guard.d.ts.map +1 -0
  685. package/dist/esm/nodes/email/outbound-message-guard.js +452 -0
  686. package/dist/esm/nodes/email/outbound-message-guard.js.map +1 -0
  687. package/dist/esm/nodes/email/trusted-html-code.d.ts +22 -0
  688. package/dist/esm/nodes/email/trusted-html-code.d.ts.map +1 -0
  689. package/dist/esm/nodes/email/trusted-html-code.js +49 -0
  690. package/dist/esm/nodes/email/trusted-html-code.js.map +1 -0
  691. package/dist/esm/nodes/general/general-custom-js.d.ts.map +1 -1
  692. package/dist/esm/nodes/general/general-custom-js.js +44 -7
  693. package/dist/esm/nodes/general/general-custom-js.js.map +1 -1
  694. package/dist/esm/nodes/general/general-http-request.d.ts.map +1 -1
  695. package/dist/esm/nodes/general/general-http-request.js +431 -45
  696. package/dist/esm/nodes/general/general-http-request.js.map +1 -1
  697. package/dist/esm/nodes/general/general-rate-limiter.d.ts.map +1 -1
  698. package/dist/esm/nodes/general/general-rate-limiter.js +46 -78
  699. package/dist/esm/nodes/general/general-rate-limiter.js.map +1 -1
  700. package/dist/esm/nodes/general/general-wait.d.ts +3 -0
  701. package/dist/esm/nodes/general/general-wait.d.ts.map +1 -0
  702. package/dist/esm/nodes/general/general-wait.js +78 -0
  703. package/dist/esm/nodes/general/general-wait.js.map +1 -0
  704. package/dist/esm/nodes/general/visitor-request.d.ts +44 -0
  705. package/dist/esm/nodes/general/visitor-request.d.ts.map +1 -0
  706. package/dist/esm/nodes/general/visitor-request.js +65 -0
  707. package/dist/esm/nodes/general/visitor-request.js.map +1 -0
  708. package/dist/esm/nodes/index.d.ts.map +1 -1
  709. package/dist/esm/nodes/index.js +7 -1
  710. package/dist/esm/nodes/index.js.map +1 -1
  711. package/dist/esm/nodes/integrations/integration-slack.d.ts.map +1 -1
  712. package/dist/esm/nodes/integrations/integration-slack.js +17 -6
  713. package/dist/esm/nodes/integrations/integration-slack.js.map +1 -1
  714. package/dist/esm/nodes/newsletter/newsletter-confirm.d.ts +3 -0
  715. package/dist/esm/nodes/newsletter/newsletter-confirm.d.ts.map +1 -0
  716. package/dist/esm/nodes/newsletter/newsletter-confirm.js +38 -0
  717. package/dist/esm/nodes/newsletter/newsletter-confirm.js.map +1 -0
  718. package/dist/esm/nodes/newsletter/newsletter-subscribe.d.ts +7 -0
  719. package/dist/esm/nodes/newsletter/newsletter-subscribe.d.ts.map +1 -0
  720. package/dist/esm/nodes/newsletter/newsletter-subscribe.js +66 -0
  721. package/dist/esm/nodes/newsletter/newsletter-subscribe.js.map +1 -0
  722. package/dist/esm/nodes/newsletter/newsletter-unsubscribe.d.ts +3 -0
  723. package/dist/esm/nodes/newsletter/newsletter-unsubscribe.d.ts.map +1 -0
  724. package/dist/esm/nodes/newsletter/newsletter-unsubscribe.js +39 -0
  725. package/dist/esm/nodes/newsletter/newsletter-unsubscribe.js.map +1 -0
  726. package/dist/esm/nodes/realtime/realtime-join-channel.d.ts.map +1 -1
  727. package/dist/esm/nodes/realtime/realtime-join-channel.js +5 -4
  728. package/dist/esm/nodes/realtime/realtime-join-channel.js.map +1 -1
  729. package/dist/esm/nodes/realtime/realtime-send-channel-event.d.ts.map +1 -1
  730. package/dist/esm/nodes/realtime/realtime-send-channel-event.js +11 -2
  731. package/dist/esm/nodes/realtime/realtime-send-channel-event.js.map +1 -1
  732. package/dist/esm/nodes/realtime/realtime-send-channel-message.d.ts.map +1 -1
  733. package/dist/esm/nodes/realtime/realtime-send-channel-message.js +11 -2
  734. package/dist/esm/nodes/realtime/realtime-send-channel-message.js.map +1 -1
  735. package/dist/esm/nodes/sms/sms-infobip.d.ts.map +1 -1
  736. package/dist/esm/nodes/sms/sms-infobip.js +11 -8
  737. package/dist/esm/nodes/sms/sms-infobip.js.map +1 -1
  738. package/dist/esm/nodes/sms/sms-smsapi.d.ts.map +1 -1
  739. package/dist/esm/nodes/sms/sms-smsapi.js +11 -8
  740. package/dist/esm/nodes/sms/sms-smsapi.js.map +1 -1
  741. package/dist/esm/nodes/sms/sms-textmagic.d.ts.map +1 -1
  742. package/dist/esm/nodes/sms/sms-textmagic.js +11 -8
  743. package/dist/esm/nodes/sms/sms-textmagic.js.map +1 -1
  744. package/dist/esm/nodes/sms/sms-twilio.d.ts.map +1 -1
  745. package/dist/esm/nodes/sms/sms-twilio.js +12 -10
  746. package/dist/esm/nodes/sms/sms-twilio.js.map +1 -1
  747. package/dist/esm/nodes/types.d.ts +8 -1
  748. package/dist/esm/nodes/types.d.ts.map +1 -1
  749. package/dist/esm/nodes/utility/utility-similarity-scoring.d.ts.map +1 -1
  750. package/dist/esm/nodes/utility/utility-similarity-scoring.js +29 -10
  751. package/dist/esm/nodes/utility/utility-similarity-scoring.js.map +1 -1
  752. package/dist/esm/page-access-guard-generator.d.ts +23 -0
  753. package/dist/esm/page-access-guard-generator.d.ts.map +1 -0
  754. package/dist/esm/page-access-guard-generator.js +131 -0
  755. package/dist/esm/page-access-guard-generator.js.map +1 -0
  756. package/dist/esm/password-reset-route-generator.d.ts +119 -0
  757. package/dist/esm/password-reset-route-generator.d.ts.map +1 -0
  758. package/dist/esm/password-reset-route-generator.js +448 -0
  759. package/dist/esm/password-reset-route-generator.js.map +1 -0
  760. package/dist/esm/payments/payment-core-code.d.ts.map +1 -1
  761. package/dist/esm/payments/payment-core-code.js +12 -7
  762. package/dist/esm/payments/payment-core-code.js.map +1 -1
  763. package/dist/esm/project-code-scanner.d.ts +11 -0
  764. package/dist/esm/project-code-scanner.d.ts.map +1 -0
  765. package/dist/esm/project-code-scanner.js +75 -0
  766. package/dist/esm/project-code-scanner.js.map +1 -0
  767. package/dist/esm/public-step-guards.d.ts +122 -0
  768. package/dist/esm/public-step-guards.d.ts.map +1 -0
  769. package/dist/esm/public-step-guards.js +280 -0
  770. package/dist/esm/public-step-guards.js.map +1 -0
  771. package/dist/esm/publish-refusals.d.ts +66 -0
  772. package/dist/esm/publish-refusals.d.ts.map +1 -0
  773. package/dist/esm/publish-refusals.js +271 -0
  774. package/dist/esm/publish-refusals.js.map +1 -0
  775. package/dist/esm/realtime-generator.d.ts +63 -1
  776. package/dist/esm/realtime-generator.d.ts.map +1 -1
  777. package/dist/esm/realtime-generator.js +401 -229
  778. package/dist/esm/realtime-generator.js.map +1 -1
  779. package/dist/esm/runtime-storage-generator.d.ts +32 -1
  780. package/dist/esm/runtime-storage-generator.d.ts.map +1 -1
  781. package/dist/esm/runtime-storage-generator.js +264 -11
  782. package/dist/esm/runtime-storage-generator.js.map +1 -1
  783. package/dist/esm/security-runtime/index.d.ts +14 -0
  784. package/dist/esm/security-runtime/index.d.ts.map +1 -0
  785. package/dist/esm/security-runtime/index.js +37 -0
  786. package/dist/esm/security-runtime/index.js.map +1 -0
  787. package/dist/esm/security-runtime/security-runtime-code.d.ts +53 -0
  788. package/dist/esm/security-runtime/security-runtime-code.d.ts.map +1 -0
  789. package/dist/esm/security-runtime/security-runtime-code.js +575 -0
  790. package/dist/esm/security-runtime/security-runtime-code.js.map +1 -0
  791. package/dist/esm/security-scanner.d.ts +72 -3
  792. package/dist/esm/security-scanner.d.ts.map +1 -1
  793. package/dist/esm/security-scanner.js +217 -8
  794. package/dist/esm/security-scanner.js.map +1 -1
  795. package/dist/esm/segment-splitter.d.ts.map +1 -1
  796. package/dist/esm/segment-splitter.js +10 -2
  797. package/dist/esm/segment-splitter.js.map +1 -1
  798. package/dist/esm/segment-trust.d.ts +36 -0
  799. package/dist/esm/segment-trust.d.ts.map +1 -1
  800. package/dist/esm/segment-trust.js +146 -2
  801. package/dist/esm/segment-trust.js.map +1 -1
  802. package/dist/esm/self-call-headers.d.ts +31 -0
  803. package/dist/esm/self-call-headers.d.ts.map +1 -0
  804. package/dist/esm/self-call-headers.js +43 -0
  805. package/dist/esm/self-call-headers.js.map +1 -0
  806. package/dist/esm/sent-email-log/index.d.ts +7 -0
  807. package/dist/esm/sent-email-log/index.d.ts.map +1 -1
  808. package/dist/esm/sent-email-log/index.js +15 -0
  809. package/dist/esm/sent-email-log/index.js.map +1 -1
  810. package/dist/esm/sent-email-log/sent-email-log-code.d.ts +7 -0
  811. package/dist/esm/sent-email-log/sent-email-log-code.d.ts.map +1 -1
  812. package/dist/esm/sent-email-log/sent-email-log-code.js +17 -3
  813. package/dist/esm/sent-email-log/sent-email-log-code.js.map +1 -1
  814. package/dist/esm/server-custom-node-call.d.ts +79 -0
  815. package/dist/esm/server-custom-node-call.d.ts.map +1 -0
  816. package/dist/esm/server-custom-node-call.js +278 -0
  817. package/dist/esm/server-custom-node-call.js.map +1 -0
  818. package/dist/esm/server-runtime-code.d.ts +39 -12
  819. package/dist/esm/server-runtime-code.d.ts.map +1 -1
  820. package/dist/esm/server-runtime-code.js +483 -40
  821. package/dist/esm/server-runtime-code.js.map +1 -1
  822. package/dist/esm/session-cookie-resolver.d.ts +3 -1
  823. package/dist/esm/session-cookie-resolver.d.ts.map +1 -1
  824. package/dist/esm/session-revocation-generator.d.ts +39 -0
  825. package/dist/esm/session-revocation-generator.d.ts.map +1 -0
  826. package/dist/esm/session-revocation-generator.js +172 -0
  827. package/dist/esm/session-revocation-generator.js.map +1 -0
  828. package/dist/esm/slack-message-values.d.ts +6 -0
  829. package/dist/esm/slack-message-values.d.ts.map +1 -0
  830. package/dist/esm/slack-message-values.js +68 -0
  831. package/dist/esm/slack-message-values.js.map +1 -0
  832. package/dist/esm/transactional-email-code.d.ts +11 -0
  833. package/dist/esm/transactional-email-code.d.ts.map +1 -1
  834. package/dist/esm/transactional-email-code.js +28 -4
  835. package/dist/esm/transactional-email-code.js.map +1 -1
  836. package/dist/esm/trigger-generator.d.ts +2 -0
  837. package/dist/esm/trigger-generator.d.ts.map +1 -1
  838. package/dist/esm/trigger-generator.js +15 -3
  839. package/dist/esm/trigger-generator.js.map +1 -1
  840. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  841. package/dist/esm/visitor-step-limits.d.ts +66 -0
  842. package/dist/esm/visitor-step-limits.d.ts.map +1 -0
  843. package/dist/esm/visitor-step-limits.js +181 -0
  844. package/dist/esm/visitor-step-limits.js.map +1 -0
  845. package/dist/esm/webhook-generator.d.ts.map +1 -1
  846. package/dist/esm/webhook-generator.js +93 -15
  847. package/dist/esm/webhook-generator.js.map +1 -1
  848. package/dist/esm/webhook-signature-verification.d.ts.map +1 -1
  849. package/dist/esm/webhook-signature-verification.js +13 -3
  850. package/dist/esm/webhook-signature-verification.js.map +1 -1
  851. package/dist/esm/workflow-auth-generator.d.ts +4 -1
  852. package/dist/esm/workflow-auth-generator.d.ts.map +1 -1
  853. package/dist/esm/workflow-auth-generator.js +8 -3
  854. package/dist/esm/workflow-auth-generator.js.map +1 -1
  855. package/dist/esm/workflow-component-plugin.js +12 -4
  856. package/dist/esm/workflow-component-plugin.js.map +1 -1
  857. package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
  858. package/dist/esm/workflow-project-plugin.js +306 -60
  859. package/dist/esm/workflow-project-plugin.js.map +1 -1
  860. package/dist/esm/workflow-scope.d.ts.map +1 -1
  861. package/dist/esm/workflow-scope.js +5 -0
  862. package/dist/esm/workflow-scope.js.map +1 -1
  863. package/package.json +4 -4
  864. package/src/account-delete-route-generator.ts +28 -4
  865. package/src/api-route-generator.ts +712 -134
  866. package/src/auth-generator.ts +1471 -142
  867. package/src/automations/activity-kicks.ts +139 -0
  868. package/src/automations/app-events-source.ts +117 -0
  869. package/src/automations/automations-project-plugin.ts +202 -0
  870. package/src/automations/automations-scope.ts +196 -0
  871. package/src/automations/dispatcher-source.ts +1072 -0
  872. package/src/automations/event-bus-source.ts +428 -0
  873. package/src/automations/handler-module-generator.ts +107 -0
  874. package/src/automations/hydrators-source.ts +647 -0
  875. package/src/booking-calendar-feed-route-generator.ts +8 -0
  876. package/src/configured-nextauth-url.ts +17 -0
  877. package/src/cron-request-verification.ts +55 -5
  878. package/src/data-api-route-generator.ts +780 -43
  879. package/src/ecommerce/parameterized-stock-chain.ts +52 -5
  880. package/src/ecommerce/place-order-guards.ts +785 -0
  881. package/src/ecommerce-customhandler-rewriter.ts +55 -2
  882. package/src/executor-generator.ts +372 -60
  883. package/src/graph-utils.ts +33 -0
  884. package/src/index.ts +70 -0
  885. package/src/invoice/api-routes-code.ts +20 -7
  886. package/src/invoice/index.ts +11 -6
  887. package/src/invoice/invoice-assembly-code.ts +8 -3
  888. package/src/invoice/pdf-generator-code.ts +2 -1
  889. package/src/invoice/pdf-service-client-code.ts +15 -7
  890. package/src/newsletter/admin-route-code.ts +319 -0
  891. package/src/newsletter/helper-code.ts +748 -0
  892. package/src/newsletter/newsletter-scope.ts +116 -0
  893. package/src/newsletter/project-plugin.ts +119 -0
  894. package/src/newsletter/routes-code.ts +186 -0
  895. package/src/nodes/account/account-compare-passwords.ts +17 -6
  896. package/src/nodes/account/account-delete-current.ts +1 -1
  897. package/src/nodes/account/account-get-current.ts +1 -1
  898. package/src/nodes/account/account-hash-password.ts +19 -3
  899. package/src/nodes/account/account-login.ts +24 -7
  900. package/src/nodes/account/account-logout.ts +1 -1
  901. package/src/nodes/account/account-refresh-session.ts +1 -1
  902. package/src/nodes/account/account-request-password-reset.ts +47 -0
  903. package/src/nodes/account/account-reset-password.ts +45 -0
  904. package/src/nodes/account/account-signup.ts +86 -20
  905. package/src/nodes/account/account-social-login.ts +1 -1
  906. package/src/nodes/ai/ai-custom-prompt.ts +25 -6
  907. package/src/nodes/ai/ai-detect-language.ts +25 -6
  908. package/src/nodes/ai/ai-generate-text-embedding.ts +16 -3
  909. package/src/nodes/ai/ai-provider-utils-sources.generated.ts +2 -2
  910. package/src/nodes/ai/ai-provider-utils.ts +135 -10
  911. package/src/nodes/ai/ai-select-database-data.ts +8 -3
  912. package/src/nodes/ai/ai-sentiment-analysis.ts +25 -6
  913. package/src/nodes/ai/ai-summarization.ts +25 -6
  914. package/src/nodes/ai/ai-text-classifier.ts +25 -6
  915. package/src/nodes/ai/ai-text-transform.ts +25 -6
  916. package/src/nodes/analytics/analytics-track-outcome.ts +111 -0
  917. package/src/nodes/cart/cart-price-order.ts +200 -2
  918. package/src/nodes/cost-bearing-node-types.ts +45 -0
  919. package/src/nodes/data/data-select.ts +11 -2
  920. package/src/nodes/email/email-mailersend.ts +16 -8
  921. package/src/nodes/email/email-mailgun.ts +17 -12
  922. package/src/nodes/email/email-postmark.ts +17 -12
  923. package/src/nodes/email/email-resend.ts +17 -12
  924. package/src/nodes/email/email-sendgrid.ts +17 -12
  925. package/src/nodes/email/html-escape-code.ts +21 -0
  926. package/src/nodes/email/message-trust.ts +198 -0
  927. package/src/nodes/email/outbound-message-guard.ts +572 -0
  928. package/src/nodes/email/trusted-html-code.ts +49 -0
  929. package/src/nodes/general/general-custom-js.ts +46 -7
  930. package/src/nodes/general/general-http-request.ts +449 -46
  931. package/src/nodes/general/general-rate-limiter.ts +54 -81
  932. package/src/nodes/general/general-wait.ts +80 -0
  933. package/src/nodes/general/visitor-request.ts +79 -0
  934. package/src/nodes/index.ts +12 -0
  935. package/src/nodes/integrations/integration-slack.ts +21 -6
  936. package/src/nodes/newsletter/newsletter-confirm.ts +44 -0
  937. package/src/nodes/newsletter/newsletter-subscribe.ts +78 -0
  938. package/src/nodes/newsletter/newsletter-unsubscribe.ts +43 -0
  939. package/src/nodes/realtime/realtime-join-channel.ts +5 -4
  940. package/src/nodes/realtime/realtime-send-channel-event.ts +13 -2
  941. package/src/nodes/realtime/realtime-send-channel-message.ts +13 -2
  942. package/src/nodes/sms/sms-infobip.ts +14 -4
  943. package/src/nodes/sms/sms-smsapi.ts +14 -4
  944. package/src/nodes/sms/sms-textmagic.ts +14 -4
  945. package/src/nodes/sms/sms-twilio.ts +14 -4
  946. package/src/nodes/types.ts +9 -1
  947. package/src/nodes/utility/utility-similarity-scoring.ts +33 -12
  948. package/src/page-access-guard-generator.ts +154 -0
  949. package/src/password-reset-route-generator.ts +531 -0
  950. package/src/payments/payment-core-code.ts +12 -7
  951. package/src/project-code-scanner.ts +98 -0
  952. package/src/public-step-guards.ts +448 -0
  953. package/src/publish-refusals.ts +459 -0
  954. package/src/realtime-generator.ts +457 -234
  955. package/src/runtime-storage-generator.ts +270 -11
  956. package/src/security-runtime/index.ts +53 -0
  957. package/src/security-runtime/security-runtime-code.ts +579 -0
  958. package/src/security-scanner.ts +253 -10
  959. package/src/segment-splitter.ts +10 -2
  960. package/src/segment-trust.ts +205 -2
  961. package/src/self-call-headers.ts +46 -0
  962. package/src/sent-email-log/index.ts +16 -0
  963. package/src/sent-email-log/sent-email-log-code.ts +30 -4
  964. package/src/server-custom-node-call.ts +331 -0
  965. package/src/server-runtime-code.ts +488 -40
  966. package/src/session-revocation-generator.ts +177 -0
  967. package/src/slack-message-values.ts +84 -0
  968. package/src/transactional-email-code.ts +30 -4
  969. package/src/trigger-generator.ts +19 -3
  970. package/src/visitor-step-limits.ts +228 -0
  971. package/src/webhook-generator.ts +94 -14
  972. package/src/webhook-signature-verification.ts +13 -3
  973. package/src/workflow-auth-generator.ts +9 -4
  974. package/src/workflow-component-plugin.ts +14 -4
  975. package/src/workflow-project-plugin.ts +374 -61
  976. package/src/workflow-scope.ts +6 -0
@@ -0,0 +1,1248 @@
1
+ /* tslint:disable:max-classes-per-file */
2
+ import * as nodeCrypto from 'crypto'
3
+ import type { UIDLNewsletterSettings } from '@teleporthq/teleport-types'
4
+ import { NextNewsletterProjectPlugin } from '../src/newsletter/project-plugin'
5
+ import {
6
+ NEWSLETTER_SQL,
7
+ generateNewsletterHelperCode,
8
+ newsletterTokenSpec,
9
+ } from '../src/newsletter/helper-code'
10
+ import {
11
+ generateNewsletterConfirmRoute,
12
+ generateNewsletterSubscribeRoute,
13
+ generateNewsletterUnsubscribeRoute,
14
+ } from '../src/newsletter/routes-code'
15
+ import { nodeRegistry } from '../src/nodes'
16
+ import { needsVisitorRequest } from '../src/nodes/general/visitor-request'
17
+ import { generateSignupRouteFile } from '../src/auth-generator'
18
+ import { newsletterSignupSubscribes } from '../src/newsletter/newsletter-scope'
19
+ import { buildVisitorStepLimitGuard } from '../src/visitor-step-limits'
20
+ import { isCostBearingNodeType } from '../src/nodes/cost-bearing-node-types'
21
+ import { assertWorkflowPublishGates, collectWorkflowPublishRefusals } from '../src/publish-refusals'
22
+ import type { WorkflowSegment } from '../src/types'
23
+ import { loadHandler } from './_helpers/load-handler'
24
+
25
+ /**
26
+ * Newsletter Task 2: the generated helper (`utils/newsletter/newsletter.js`),
27
+ * the public routes and the three nodes.
28
+ *
29
+ * The helper runs against a fake `users` table that answers exactly the
30
+ * statements it is generated with (`NEWSLETTER_SQL`), with every module it
31
+ * requires stubbed: the security runtime (limits counted in memory), the email
32
+ * locale, the sent-email log, the provider and the event bus.
33
+ */
34
+
35
+ type Row = Record<string, any>
36
+
37
+ const SECRET = newsletterTokenSpec.testVector.secret
38
+ const MINUTE = 60 * 1000
39
+
40
+ class FakeUsers {
41
+ rows: Row[] = []
42
+ queries: Array<{ sql: string; values: unknown[] }> = []
43
+ /** The next statement fails with this (a pg error). */
44
+ failWith: Row | null = null
45
+ private nextId = 1
46
+
47
+ public async query(sql: string, values: any[] = []): Promise<{ rows: Row[] }> {
48
+ this.queries.push({ sql, values })
49
+ if (this.failWith) {
50
+ throw Object.assign(new Error(this.failWith.message), this.failWith)
51
+ }
52
+ const person = (row: Row | undefined) =>
53
+ row
54
+ ? [
55
+ {
56
+ id: row.id,
57
+ email: row.email,
58
+ source: row.source ?? null,
59
+ email_unsubscribed: row.email_unsubscribed ?? false,
60
+ newsletter_status: row.newsletter_status ?? null,
61
+ newsletter_source: row.newsletter_source ?? null,
62
+ newsletter_unsubscribed_at: row.newsletter_unsubscribed_at ?? null,
63
+ },
64
+ ]
65
+ : []
66
+ const byId = (id: unknown) => this.rows.find((row) => row.id === id)
67
+ switch (sql) {
68
+ case NEWSLETTER_SQL.findPerson: {
69
+ const matches = this.rows.filter((row) => String(row.email).toLowerCase() === values[0])
70
+ const exact = matches.find((row) => row.email === values[0])
71
+ return { rows: person(exact || matches[0]) }
72
+ }
73
+ case NEWSLETTER_SQL.insertPending:
74
+ case NEWSLETTER_SQL.insertSubscribed: {
75
+ const subscribed = sql === NEWSLETTER_SQL.insertSubscribed
76
+ const row: Row = {
77
+ id: 'u' + this.nextId++,
78
+ email: values[0],
79
+ source: values[1],
80
+ marketing_consent: subscribed,
81
+ email_unsubscribed: false,
82
+ newsletter_status: subscribed ? 'subscribed' : 'pending',
83
+ newsletter_source: values[2],
84
+ newsletter_subscribed_at: subscribed ? new Date() : null,
85
+ }
86
+ this.rows.push(row)
87
+ return { rows: person(row) }
88
+ }
89
+ case NEWSLETTER_SQL.markPending:
90
+ case NEWSLETTER_SQL.markSubscribed: {
91
+ const row = byId(values[0])
92
+ // The statements' guard (asserted as text below): never on the list, or
93
+ // pending, and not opted out of email.
94
+ if (
95
+ !row ||
96
+ (row.newsletter_status != null && row.newsletter_status !== 'pending') ||
97
+ row.email_unsubscribed === true
98
+ ) {
99
+ return { rows: [] }
100
+ }
101
+ row.newsletter_source = values[1]
102
+ if (sql === NEWSLETTER_SQL.markSubscribed) {
103
+ Object.assign(row, {
104
+ newsletter_status: 'subscribed',
105
+ newsletter_subscribed_at: new Date(),
106
+ marketing_consent: true,
107
+ email_unsubscribed: false,
108
+ })
109
+ } else {
110
+ row.newsletter_status = 'pending'
111
+ }
112
+ return { rows: person(row) }
113
+ }
114
+ case NEWSLETTER_SQL.confirm: {
115
+ const row = byId(values[0])
116
+ if (!row || row.newsletter_status === 'subscribed') {
117
+ return { rows: [] }
118
+ }
119
+ Object.assign(row, {
120
+ newsletter_status: 'subscribed',
121
+ newsletter_subscribed_at: new Date(),
122
+ marketing_consent: true,
123
+ email_unsubscribed: false,
124
+ })
125
+ return { rows: person(row) }
126
+ }
127
+ case NEWSLETTER_SQL.unsubscribe: {
128
+ const row = byId(values[0])
129
+ if (!row || row.newsletter_status === 'unsubscribed') {
130
+ return { rows: [] }
131
+ }
132
+ Object.assign(row, {
133
+ newsletter_status: 'unsubscribed',
134
+ newsletter_unsubscribed_at: new Date(),
135
+ marketing_consent: false,
136
+ email_unsubscribed: true,
137
+ })
138
+ return { rows: person(row) }
139
+ }
140
+ default:
141
+ throw new Error('unexpected SQL: ' + sql)
142
+ }
143
+ }
144
+
145
+ /** The statements that changed a row. */
146
+ writes() {
147
+ return this.queries.filter((query) => !query.sql.startsWith('SELECT'))
148
+ }
149
+ }
150
+
151
+ const SETTINGS: UIDLNewsletterSettings = {
152
+ dataSourceId: 'ds-1',
153
+ dataSourceType: 'teleport',
154
+ confirmation: true,
155
+ welcome: true,
156
+ sender: { name: 'Studio', email: 'hello@studio.test' },
157
+ email: { provider: 'resend', apiKeyEnv: 'EMAIL_RESEND_APIKEY', domainEnv: null },
158
+ templates: {
159
+ confirmation: {
160
+ subject: 'Confirm {{site_name}}',
161
+ body: '<p>Confirm at <a href="{{confirm_url}}">this link</a> for {{email}}</p>',
162
+ },
163
+ welcome: {
164
+ subject: 'Welcome',
165
+ body: '<p>Welcome! <a href="{{unsubscribe_url}}">Unsubscribe</a></p>',
166
+ },
167
+ },
168
+ pages: { confirm: '/newsletter/confirm', unsubscribe: '/newsletter/unsubscribe' },
169
+ signUp: { signupCheckbox: false },
170
+ adminRoutes: false,
171
+ }
172
+
173
+ interface App {
174
+ db: FakeUsers
175
+ sent: Row[]
176
+ ledger: Row[]
177
+ events: Row[]
178
+ limits: string[]
179
+ visitorLimits: Row[]
180
+ clientErrors: unknown[]
181
+ logs: unknown[][]
182
+ helper: any
183
+ load: (name: string) => any
184
+ }
185
+
186
+ /** One app instance: the helper and the routes, required from each other as the files are. */
187
+ const loadApp = (
188
+ options: {
189
+ settings?: Partial<UIDLNewsletterSettings>
190
+ withAutomations?: boolean
191
+ signupSendsAccountWelcome?: boolean
192
+ env?: Record<string, string | undefined>
193
+ provider?: Row
194
+ } = {}
195
+ ): App => {
196
+ const db = new FakeUsers()
197
+ const sent: Row[] = []
198
+ const ledger: Row[] = []
199
+ const events: Row[] = []
200
+ const limits: string[] = []
201
+ const visitorLimits: Row[] = []
202
+ const clientErrors: unknown[] = []
203
+ const logs: unknown[][] = []
204
+ const record = (...args: unknown[]): void => {
205
+ logs.push(args)
206
+ }
207
+ const quiet = { error: record, warn: record, log: record }
208
+ const sources: Record<string, string> = {
209
+ helper: generateNewsletterHelperCode({
210
+ settings: { ...SETTINGS, ...(options.settings || {}) } as UIDLNewsletterSettings,
211
+ siteName: 'Studio',
212
+ withAutomations: options.withAutomations !== false,
213
+ signupSendsAccountWelcome: options.signupSendsAccountWelcome === true,
214
+ }),
215
+ subscribe: generateNewsletterSubscribeRoute(),
216
+ confirm: generateNewsletterConfirmRoute(),
217
+ unsubscribe: generateNewsletterUnsubscribeRoute(),
218
+ }
219
+ const security = {
220
+ rateLimit: async ({ key, limit }: { key: string; limit: number }) => {
221
+ limits.push(key)
222
+ return { allowed: limits.filter((k) => k === key).length <= limit, retryAfter: 0 }
223
+ },
224
+ rateLimitRequest: async (_req: unknown, _res: unknown, limit: Row) => {
225
+ visitorLimits.push(limit)
226
+ return true
227
+ },
228
+ sendClientError: (res: any, status: number, message: string, err?: unknown) => {
229
+ clientErrors.push(err)
230
+ return res.status(status).json({ error: message })
231
+ },
232
+ }
233
+ const pg = {
234
+ Client: class {
235
+ public async connect(): Promise<void> {
236
+ return undefined
237
+ }
238
+ public async end(): Promise<void> {
239
+ return undefined
240
+ }
241
+ public on(): void {
242
+ return undefined
243
+ }
244
+ public async query(sql: string, values?: any[]) {
245
+ return db.query(sql, values)
246
+ }
247
+ },
248
+ }
249
+ const external: Record<string, unknown> = {
250
+ crypto: nodeCrypto,
251
+ pg,
252
+ resend: options.provider || {
253
+ Resend: class {
254
+ public emails = {
255
+ send: async (msg: Row) => {
256
+ sent.push(msg)
257
+ return {}
258
+ },
259
+ }
260
+ },
261
+ },
262
+ '../../utils/security-runtime': security,
263
+ '../../../utils/security-runtime': security,
264
+ '../../utils/email/email-locale': {
265
+ resolveRequestLocale: () => '',
266
+ pickLocalizedTemplate: (base: Row) => base,
267
+ },
268
+ '../../utils/email/sent-email-log': {
269
+ recordSentEmail: (entry: Row) => ledger.push(entry),
270
+ settleSentEmailLog: async (): Promise<void> => undefined,
271
+ },
272
+ '../../lib/automations/event-bus': {
273
+ emitBusinessEvent: async (event: Row) => {
274
+ events.push(event)
275
+ return { recorded: true, id: String(events.length) }
276
+ },
277
+ },
278
+ }
279
+ const files: Record<string, string> = { '../../../utils/newsletter/newsletter': 'helper' }
280
+ const cache: Record<string, { exports: any }> = {}
281
+ const load = (name: string): any => {
282
+ if (cache[name]) {
283
+ return cache[name].exports
284
+ }
285
+ const module = { exports: {} as any }
286
+ cache[name] = module
287
+ const req = (id: string): unknown => {
288
+ if (Object.prototype.hasOwnProperty.call(files, id)) {
289
+ return load(files[id])
290
+ }
291
+ if (Object.prototype.hasOwnProperty.call(external, id)) {
292
+ return external[id]
293
+ }
294
+ throw new Error(name + ' required an unexpected module: ' + id)
295
+ }
296
+ const setTimeoutShim = (fn: () => void) => {
297
+ fn()
298
+ return 0
299
+ }
300
+ // eslint-disable-next-line no-new-func
301
+ new Function(
302
+ 'module',
303
+ 'exports',
304
+ 'require',
305
+ 'process',
306
+ 'console',
307
+ 'Buffer',
308
+ 'setTimeout',
309
+ sources[name]
310
+ )(
311
+ module,
312
+ module.exports,
313
+ req,
314
+ {
315
+ env: {
316
+ NEXTAUTH_SECRET: SECRET,
317
+ NEXTAUTH_URL: 'https://studio.example',
318
+ EMAIL_RESEND_APIKEY: 're_test_key',
319
+ ...(options.env || {}),
320
+ },
321
+ },
322
+ quiet,
323
+ Buffer,
324
+ setTimeoutShim
325
+ )
326
+ return module.exports
327
+ }
328
+ return {
329
+ db,
330
+ sent,
331
+ ledger,
332
+ events,
333
+ limits,
334
+ visitorLimits,
335
+ clientErrors,
336
+ logs,
337
+ helper: load('helper'),
338
+ load,
339
+ }
340
+ }
341
+
342
+ const REQ = { headers: { host: 'studio.example' } }
343
+
344
+ const fakeResponse = () => {
345
+ const res: any = { statusCode: 0, body: undefined, headers: {} as Row }
346
+ res.status = (code: number) => {
347
+ res.statusCode = code
348
+ return res
349
+ }
350
+ res.json = (body: unknown) => {
351
+ res.body = body
352
+ return res
353
+ }
354
+ res.setHeader = (name: string, value: string) => {
355
+ res.headers[name] = value
356
+ }
357
+ res.end = (body: string) => {
358
+ res.body = body
359
+ }
360
+ return res
361
+ }
362
+
363
+ const route = async (app: App, name: string, request: Row) => {
364
+ const res = fakeResponse()
365
+ await app.load(name)({ headers: { host: 'studio.example' }, query: {}, ...request }, res)
366
+ return res
367
+ }
368
+
369
+ /** `<type>:<id>:<unix minute>`, the minute this test ran in. */
370
+ const expectMinuteKey = (key: string, prefix: string) => {
371
+ expect(key.startsWith(prefix)).toBe(true)
372
+ const minute = Number(key.slice(prefix.length))
373
+ const now = Math.floor(Date.now() / MINUTE)
374
+ expect(minute === now || minute === now - 1).toBe(true)
375
+ }
376
+
377
+ /** The `#t=` (or, for the site's route, `?t=`) token of the first link of an email. */
378
+ const linkToken = (html: string): string => {
379
+ const match = /[?#]t=([A-Za-z0-9_.%-]+)/.exec(html)
380
+ return match ? decodeURIComponent(match[1]) : ''
381
+ }
382
+
383
+ describe('NextNewsletterProjectPlugin', () => {
384
+ const structureFor = (extra: Row) => ({
385
+ uidl: {
386
+ name: 'Studio',
387
+ globals: { settings: { title: 'Studio', language: 'en' }, env: {} },
388
+ root: { name: 'Root', node: { type: 'element', content: { elementType: 'container' } } },
389
+ dataSources: { 'ds-1': { id: 'ds-1', type: 'teleport', name: 'Site', config: {} } },
390
+ ...extra,
391
+ },
392
+ files: new Map<string, { path: string[]; files: Array<{ name: string; content: string }> }>(),
393
+ dependencies: {} as Record<string, string>,
394
+ devDependencies: {},
395
+ })
396
+ const paths = (structure: ReturnType<typeof structureFor>) =>
397
+ Array.from(structure.files.values()).flatMap((entry) =>
398
+ entry.files.map((file) => [...entry.path, file.name].join('/'))
399
+ )
400
+
401
+ it('emits the helper and the three routes when uidl.newsletterSettings is set', async () => {
402
+ const structure = structureFor({ newsletterSettings: SETTINGS })
403
+ await new NextNewsletterProjectPlugin().runAfter(structure as never)
404
+ expect(paths(structure)).toEqual(
405
+ expect.arrayContaining([
406
+ 'utils/newsletter/newsletter',
407
+ 'pages/api/newsletter/subscribe',
408
+ 'pages/api/newsletter/confirm',
409
+ 'pages/api/newsletter/unsubscribe',
410
+ 'utils/security-runtime',
411
+ 'utils/email/email-locale',
412
+ 'utils/email/sent-email-log',
413
+ ])
414
+ )
415
+ expect(structure.dependencies.pg).toBeDefined()
416
+ expect(structure.dependencies.resend).toBeDefined()
417
+ const helper = structure.files.get('newsletter-helper')!.files[0].content
418
+ expect(helper).toContain('module.exports = {')
419
+ expect(helper).toContain("require('../../utils/security-runtime')")
420
+ })
421
+
422
+ it('emits nothing when it is not set, or not on the TeleportHQ database', async () => {
423
+ for (const extra of [
424
+ {},
425
+ { newsletterSettings: { ...SETTINGS, dataSourceType: 'postgresql' } },
426
+ { newsletterSettings: { ...SETTINGS, dataSourceId: '' } },
427
+ ]) {
428
+ const structure = structureFor(extra)
429
+ await new NextNewsletterProjectPlugin().runAfter(structure as never)
430
+ expect(paths(structure)).toEqual([])
431
+ expect(structure.dependencies).toEqual({})
432
+ }
433
+ })
434
+ })
435
+
436
+ describe('the newsletter helper', () => {
437
+ it('subscribe: unsubscribed person gets a confirmation, status unchanged', async () => {
438
+ const app = loadApp({ settings: { confirmation: false } })
439
+ app.db.rows.push({
440
+ id: 'u-left',
441
+ email: 'left@site.test',
442
+ source: 'newsletter',
443
+ marketing_consent: false,
444
+ email_unsubscribed: true,
445
+ newsletter_status: 'unsubscribed',
446
+ newsletter_source: 'footer',
447
+ })
448
+ const result = await app.helper.subscribeNewsletterPerson({
449
+ db: app.db,
450
+ email: ' Left@Site.TEST ',
451
+ signUpPoint: 'popup',
452
+ req: REQ,
453
+ })
454
+ expect(result).toEqual({ outcome: 'confirm-sent' })
455
+ expect(app.db.writes()).toEqual([])
456
+ expect(app.db.rows[0]).toMatchObject({
457
+ newsletter_status: 'unsubscribed',
458
+ marketing_consent: false,
459
+ email_unsubscribed: true,
460
+ })
461
+ expect(app.events).toEqual([])
462
+ expect(app.sent).toHaveLength(1)
463
+ expect(app.sent[0].to).toEqual(['left@site.test'])
464
+ expect(app.sent[0].from).toBe('Studio <hello@studio.test>')
465
+ expect(app.sent[0].html).toContain('https://studio.example/newsletter/confirm#t=')
466
+ expect(app.sent[0].html).not.toContain('/newsletter/confirm?t=')
467
+ // The emailed link is the person's way back: opening it subscribes them.
468
+ const confirmed = await app.helper.confirmNewsletter({
469
+ db: app.db,
470
+ token: linkToken(app.sent[0].html),
471
+ req: REQ,
472
+ })
473
+ expect(confirmed).toEqual({ ok: true, email: 'left@site.test' })
474
+ expect(app.db.rows[0]).toMatchObject({
475
+ newsletter_status: 'subscribed',
476
+ marketing_consent: true,
477
+ })
478
+ // The token never lands in the sent-email log.
479
+ expect(app.ledger[0].emailType).toBe('newsletter-confirmation')
480
+ expect(app.ledger[0].html).toContain('[hidden]')
481
+ expect(app.ledger[0].html).not.toContain(linkToken(app.sent[0].html))
482
+ })
483
+
484
+ it('subscribe: new address with confirmation off is subscribed with source and event', async () => {
485
+ const app = loadApp({ settings: { confirmation: false } })
486
+ const result = await app.helper.subscribeNewsletterPerson({
487
+ db: app.db,
488
+ email: 'New@Reader.test',
489
+ signUpPoint: 'footer',
490
+ req: REQ,
491
+ })
492
+ expect(result).toEqual({ outcome: 'subscribed' })
493
+ expect(app.db.writes().map((query) => query.sql)).toEqual([NEWSLETTER_SQL.insertSubscribed])
494
+ expect(app.db.rows[0]).toMatchObject({
495
+ email: 'new@reader.test',
496
+ source: 'newsletter',
497
+ newsletter_source: 'footer',
498
+ newsletter_status: 'subscribed',
499
+ marketing_consent: true,
500
+ email_unsubscribed: false,
501
+ })
502
+ expect(app.events).toHaveLength(1)
503
+ expect(app.events[0]).toMatchObject({
504
+ type: 'newsletter-subscribed',
505
+ entityType: 'subscriber',
506
+ entityId: 'u1',
507
+ payload: { subscriber: { email: 'new@reader.test', source: 'newsletter', consent: true } },
508
+ actorType: 'customer',
509
+ })
510
+ expectMinuteKey(app.events[0].dedupeKey, 'newsletter-subscribed:u1:')
511
+ // The welcome email carries the unsubscribe link.
512
+ expect(app.sent).toHaveLength(1)
513
+ expect(app.sent[0].html).toContain('https://studio.example/newsletter/unsubscribe#t=')
514
+ expect(app.helper.publicSubscribeAnswer()).toEqual({ ok: true, pending: false })
515
+ })
516
+
517
+ it('subscribe: a new address with double opt-in waits, with no consent, for its link', async () => {
518
+ const app = loadApp()
519
+ const result = await app.helper.subscribeNewsletterPerson({
520
+ db: app.db,
521
+ email: 'wait@reader.test',
522
+ signUpPoint: 'block',
523
+ req: REQ,
524
+ })
525
+ expect(result).toEqual({ outcome: 'confirm-sent' })
526
+ expect(app.db.rows[0]).toMatchObject({
527
+ newsletter_status: 'pending',
528
+ marketing_consent: false,
529
+ newsletter_source: 'block',
530
+ })
531
+ expect(app.events).toEqual([])
532
+ expect(app.helper.publicSubscribeAnswer()).toEqual({ ok: true, pending: true })
533
+ })
534
+
535
+ it('subscribe: an address already subscribed is left alone, silently', async () => {
536
+ const app = loadApp()
537
+ app.db.rows.push({ id: 'u-in', email: 'in@site.test', newsletter_status: 'subscribed' })
538
+ const result = await app.helper.subscribeNewsletterPerson({
539
+ db: app.db,
540
+ email: 'in@site.test',
541
+ signUpPoint: 'popup',
542
+ req: REQ,
543
+ })
544
+ expect(result).toEqual({ outcome: 'already' })
545
+ expect(app.db.writes()).toEqual([])
546
+ expect(app.sent).toEqual([])
547
+ })
548
+
549
+ it('confirm: expired token refused', async () => {
550
+ const app = loadApp()
551
+ app.db.rows.push({ id: 'u-p', email: 'late@site.test', newsletter_status: 'pending' })
552
+ const eightDaysAgo = Math.floor(Date.now() / 1000) - 8 * 24 * 3600
553
+ const expired = app.helper.signNewsletterToken('late@site.test', 'confirm', eightDaysAgo)
554
+ expect(await app.helper.confirmNewsletter({ db: app.db, token: expired, req: REQ })).toEqual({
555
+ ok: false,
556
+ })
557
+ expect(app.db.writes()).toEqual([])
558
+ // An unsubscribe token is not a confirmation, however fresh.
559
+ const wrongPurpose = app.helper.signNewsletterToken('late@site.test', 'unsubscribe')
560
+ expect(
561
+ await app.helper.confirmNewsletter({ db: app.db, token: wrongPurpose, req: REQ })
562
+ ).toEqual({ ok: false })
563
+ const fresh = app.helper.signNewsletterToken('late@site.test', 'confirm')
564
+ expect(await app.helper.confirmNewsletter({ db: app.db, token: fresh, req: REQ })).toEqual({
565
+ ok: true,
566
+ email: 'late@site.test',
567
+ })
568
+ })
569
+
570
+ it('confirm: a link mailed before the person last unsubscribed cannot bring them back', async () => {
571
+ const app = loadApp()
572
+ const issuedAt = Math.floor(Date.now() / 1000) - 3600
573
+ app.db.rows.push({
574
+ id: 'u-gone',
575
+ email: 'gone@site.test',
576
+ newsletter_status: 'unsubscribed',
577
+ newsletter_unsubscribed_at: new Date(Date.now() - 60 * 1000),
578
+ })
579
+ const old = app.helper.signNewsletterToken('gone@site.test', 'confirm', issuedAt)
580
+ expect(await app.helper.confirmNewsletter({ db: app.db, token: old, req: REQ })).toEqual({
581
+ ok: false,
582
+ })
583
+ expect(app.db.writes()).toEqual([])
584
+ })
585
+
586
+ it('unsubscribe: idempotent, records the event once per minute key', async () => {
587
+ const app = loadApp()
588
+ app.db.rows.push({
589
+ id: 'u-sub',
590
+ email: 'reader@site.test',
591
+ source: 'checkout',
592
+ newsletter_status: 'subscribed',
593
+ marketing_consent: true,
594
+ })
595
+ const token = app.helper.signNewsletterToken('reader@site.test', 'unsubscribe')
596
+ const first = await app.helper.unsubscribeNewsletter({ db: app.db, token, req: REQ })
597
+ const second = await app.helper.unsubscribeNewsletter({ db: app.db, token, req: REQ })
598
+ expect(first).toEqual({ ok: true, email: 'reader@site.test' })
599
+ expect(second).toEqual(first)
600
+ expect(app.db.rows[0]).toMatchObject({
601
+ newsletter_status: 'unsubscribed',
602
+ email_unsubscribed: true,
603
+ marketing_consent: false,
604
+ })
605
+ expect(app.db.writes().map((query) => query.sql)).toEqual([NEWSLETTER_SQL.unsubscribe])
606
+ expect(app.events).toHaveLength(1)
607
+ expect(app.events[0]).toMatchObject({
608
+ type: 'newsletter-unsubscribed',
609
+ entityType: 'subscriber',
610
+ entityId: 'u-sub',
611
+ payload: { subscriber: { email: 'reader@site.test', source: 'checkout', consent: false } },
612
+ })
613
+ expectMinuteKey(app.events[0].dedupeKey, 'newsletter-unsubscribed:u-sub:')
614
+ expect(
615
+ await app.helper.unsubscribeNewsletter({ db: app.db, token: 'not.a-token', req: REQ })
616
+ ).toEqual({ ok: false })
617
+ })
618
+
619
+ it('records no event without automations', async () => {
620
+ const app = loadApp({ settings: { confirmation: false }, withAutomations: false })
621
+ await app.helper.subscribeNewsletterPerson({
622
+ db: app.db,
623
+ email: 'quiet@reader.test',
624
+ signUpPoint: 'block',
625
+ req: REQ,
626
+ })
627
+ expect(app.db.rows[0].newsletter_status).toBe('subscribed')
628
+ expect(app.events).toEqual([])
629
+ })
630
+
631
+ it('mints tokens exactly as newsletterTokenSpec describes (the worker reproduces them)', () => {
632
+ const app = loadApp()
633
+ const vector = newsletterTokenSpec.testVector
634
+ expect(app.helper.signNewsletterToken(vector.email, vector.purpose, vector.issuedAt)).toBe(
635
+ vector.token
636
+ )
637
+ // An independent implementation of the spec.
638
+ const key = nodeCrypto
639
+ .createHmac('sha256', vector.secret)
640
+ .update(newsletterTokenSpec.keyLabel)
641
+ .digest()
642
+ const payload = Buffer.from(
643
+ [vector.email, vector.purpose, vector.issuedAt].join(newsletterTokenSpec.separator),
644
+ 'utf8'
645
+ )
646
+ const b64u = (buffer: Buffer) =>
647
+ buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
648
+ expect(
649
+ b64u(payload) + '.' + b64u(nodeCrypto.createHmac('sha256', key).update(payload).digest())
650
+ ).toBe(vector.token)
651
+ // An unsubscribe token never expires; a forged one never verifies.
652
+ expect(app.helper.verifyNewsletterToken(vector.token, 'unsubscribe')).toEqual({
653
+ email: vector.email,
654
+ purpose: 'unsubscribe',
655
+ issuedAt: vector.issuedAt,
656
+ })
657
+ const forged = vector.token.slice(0, -2) + (vector.token.endsWith('AA') ? 'AB' : 'AA')
658
+ expect(app.helper.verifyNewsletterToken(forged, 'unsubscribe')).toBeNull()
659
+ })
660
+ })
661
+
662
+ describe('the newsletter routes', () => {
663
+ it('subscribe: honeypot answers ok without a query', async () => {
664
+ const app = loadApp()
665
+ const res = await route(app, 'subscribe', {
666
+ method: 'POST',
667
+ body: { email: 'bot@spam.test', website: 'http://spam.test', signUpPoint: 'footer' },
668
+ })
669
+ expect(res.statusCode).toBe(200)
670
+ // The same answer a person gets.
671
+ expect(res.body).toEqual(app.helper.publicSubscribeAnswer())
672
+ expect(app.db.queries).toEqual([])
673
+ expect(app.limits).toEqual([])
674
+ // One visitor budget with the sign-up workflows' steps (visitorLimit scope newsletter-signup).
675
+ expect(app.visitorLimits).toEqual([
676
+ { scope: 'visitor-limit:newsletter-signup', limit: 5, windowSeconds: 3600 },
677
+ ])
678
+ expect(app.sent).toEqual([])
679
+ })
680
+
681
+ it('subscribe: answers every address the same, and refuses only a malformed one', async () => {
682
+ const app = loadApp()
683
+ app.db.rows.push({ id: 'u-in', email: 'in@site.test', newsletter_status: 'subscribed' })
684
+ const known = await route(app, 'subscribe', { method: 'POST', body: { email: 'in@site.test' } })
685
+ const fresh = await route(app, 'subscribe', {
686
+ method: 'POST',
687
+ body: { email: 'fresh@site.test', signUpPoint: 'admin' },
688
+ })
689
+ expect(known.body).toEqual({ ok: true, pending: true })
690
+ expect(fresh.body).toEqual(known.body)
691
+ // A visitor never names an owner's sign-up point.
692
+ expect(app.db.rows[1].newsletter_source).toBe('block')
693
+ const bad = await route(app, 'subscribe', { method: 'POST', body: { email: 'nope' } })
694
+ expect(bad.statusCode).toBe(400)
695
+ const get = await route(app, 'subscribe', { method: 'GET' })
696
+ expect(get.statusCode).toBe(405)
697
+ })
698
+
699
+ it('unsubscribe: a GET only shows the button; its POST unsubscribes', async () => {
700
+ const app = loadApp()
701
+ app.db.rows.push({ id: 'u-sub', email: 'reader@site.test', newsletter_status: 'subscribed' })
702
+ const token = app.helper.signNewsletterToken('reader@site.test', 'unsubscribe')
703
+ const page = await route(app, 'unsubscribe', { method: 'GET', query: { t: token } })
704
+ expect(page.statusCode).toBe(200)
705
+ expect(page.body).toContain('<form method="post" action="/api/newsletter/unsubscribe">')
706
+ expect(app.db.writes()).toEqual([])
707
+ const done = await route(app, 'unsubscribe', {
708
+ method: 'POST',
709
+ headers: { host: 'studio.example', 'content-type': 'application/x-www-form-urlencoded' },
710
+ body: { t: token },
711
+ })
712
+ expect(done.body).toContain('<h1>You&#39;re unsubscribed</h1>')
713
+ expect(app.db.rows[0].newsletter_status).toBe('unsubscribed')
714
+ const json = await route(app, 'unsubscribe', { method: 'POST', body: { token } })
715
+ expect(json.body).toEqual({ ok: true, email: 'reader@site.test' })
716
+ })
717
+
718
+ it('confirm: answers { ok, email } for a live link and { ok: false } otherwise', async () => {
719
+ const app = loadApp()
720
+ app.db.rows.push({ id: 'u-p', email: 'wait@site.test', newsletter_status: 'pending' })
721
+ const token = app.helper.signNewsletterToken('wait@site.test', 'confirm')
722
+ const ok = await route(app, 'confirm', { method: 'POST', body: { token } })
723
+ expect(ok.body).toEqual({ ok: true, email: 'wait@site.test' })
724
+ const bad = await route(app, 'confirm', { method: 'POST', body: { token: 'x.y' } })
725
+ expect(bad.body).toEqual({ ok: false })
726
+ })
727
+ })
728
+
729
+ describe('the newsletter nodes', () => {
730
+ it('registers the sign-up on the server and the link steps in the browser', () => {
731
+ expect(nodeRegistry['newsletter-subscribe'].executionEnv).toBe('server')
732
+ expect(nodeRegistry['newsletter-confirm'].executionEnv).toBe('client')
733
+ expect(nodeRegistry['newsletter-unsubscribe'].executionEnv).toBe('client')
734
+ // The sign-up's route hands it the visitor's request (origin, language).
735
+ expect(needsVisitorRequest(['newsletter-subscribe'])).toBe(true)
736
+ })
737
+
738
+ it('newsletter-subscribe calls the helper and answers { ok, pending }', async () => {
739
+ const app = loadApp({ settings: { confirmation: false } })
740
+ const source = nodeRegistry['newsletter-subscribe'].generateServerHandler!({
741
+ relativePrefix: '../../..',
742
+ })
743
+ expect(source).toContain("require('../../../utils/newsletter/newsletter')")
744
+ // eslint-disable-next-line no-new-func
745
+ const handler = new Function(
746
+ 'require',
747
+ 'setTimeout',
748
+ `${source}\nreturn newsletter_subscribe;`
749
+ )(
750
+ (id: string) => {
751
+ if (id === '../../../utils/newsletter/newsletter') {
752
+ return app.helper
753
+ }
754
+ throw new Error('unexpected module ' + id)
755
+ },
756
+ (fn: () => void) => fn()
757
+ )
758
+ const context = { __request: REQ }
759
+ expect(await handler({ email: 'node@reader.test', signUpPoint: 'popup' }, context)).toEqual({
760
+ ok: true,
761
+ pending: false,
762
+ })
763
+ expect(app.db.rows[0]).toMatchObject({
764
+ newsletter_source: 'popup',
765
+ newsletter_status: 'subscribed',
766
+ })
767
+ expect(await handler({ email: 'bot@reader.test', website: 'x' }, context)).toEqual({
768
+ ok: true,
769
+ pending: false,
770
+ })
771
+ expect(app.db.rows).toHaveLength(1)
772
+ await expect(handler({ email: 'nope' }, context)).rejects.toThrow(
773
+ 'Enter a valid email address.'
774
+ )
775
+ // Without the helper's place, the step refuses.
776
+ expect(nodeRegistry['newsletter-subscribe'].generateHandler()).not.toContain('require(')
777
+ })
778
+
779
+ it('newsletter-confirm and newsletter-unsubscribe post the token to their routes', async () => {
780
+ const calls: Array<{ url: string; body: Row }> = []
781
+ const realFetch = (global as any).fetch
782
+ ;(global as any).fetch = async (url: string, init: { body: string }) => {
783
+ calls.push({ url, body: JSON.parse(init.body) })
784
+ return { ok: true, json: async () => ({ ok: true, email: 'reader@site.test' }) }
785
+ }
786
+ try {
787
+ const confirm = loadHandler('newsletter-confirm')
788
+ const unsubscribe = loadHandler('newsletter-unsubscribe')
789
+ expect(await confirm({ token: ' tok ' }, { __baseUrl: 'https://studio.example' })).toEqual({
790
+ ok: true,
791
+ email: 'reader@site.test',
792
+ })
793
+ expect(await unsubscribe({ token: 'tok' }, {})).toEqual({
794
+ ok: true,
795
+ email: 'reader@site.test',
796
+ })
797
+ expect(await confirm({}, {})).toEqual({ ok: false })
798
+ } finally {
799
+ ;(global as any).fetch = realFetch
800
+ }
801
+ expect(calls).toEqual([
802
+ { url: 'https://studio.example/api/newsletter/confirm', body: { token: 'tok' } },
803
+ { url: '/api/newsletter/unsubscribe', body: { token: 'tok' } },
804
+ ])
805
+ })
806
+ })
807
+
808
+ describe("the sign-up form's consent box (R-N15)", () => {
809
+ const AUTH = {
810
+ enabled: true,
811
+ dataSourceId: 'ds-1',
812
+ dataSourceType: 'teleport',
813
+ passwordAuthEnabled: true,
814
+ providers: [],
815
+ roles: ['user'],
816
+ tables: {},
817
+ customUserProperties: [],
818
+ } as never
819
+
820
+ it('subscribes a ticked box through the helper, from the signup point', () => {
821
+ const signupRoute = generateSignupRouteFile(AUTH, {}, {}, { newsletter: true })
822
+ expect(signupRoute).toContain(
823
+ " if (userData.marketing_consent) {\n try {\n const __newsletter = require('../../../utils/newsletter/newsletter');"
824
+ )
825
+ expect(signupRoute).toContain(
826
+ "return __newsletter.subscribeNewsletterPerson({ db: db, email: email, signUpPoint: 'signup', req: req });"
827
+ )
828
+ // After the account exists, before the answer.
829
+ expect(signupRoute.indexOf('subscribeNewsletterPerson')).toBeGreaterThan(
830
+ signupRoute.indexOf('const newUser = await createUser(userData);')
831
+ )
832
+ expect(generateSignupRouteFile(AUTH, {}, {}, {})).not.toContain('utils/newsletter')
833
+ })
834
+
835
+ it('is on only when the newsletter is generated and its settings say so', () => {
836
+ expect(newsletterSignupSubscribes({})).toBe(false)
837
+ expect(newsletterSignupSubscribes({ newsletterSettings: SETTINGS })).toBe(false)
838
+ expect(
839
+ newsletterSignupSubscribes({
840
+ newsletterSettings: { ...SETTINGS, signUp: { signupCheckbox: true } },
841
+ })
842
+ ).toBe(true)
843
+ expect(
844
+ newsletterSignupSubscribes({
845
+ newsletterSettings: {
846
+ ...SETTINGS,
847
+ dataSourceType: 'postgresql' as never,
848
+ signUp: { signupCheckbox: true },
849
+ },
850
+ })
851
+ ).toBe(false)
852
+ })
853
+
854
+ it("sends one welcome per sign-up: the account's, when the sign-up route sends one", async () => {
855
+ const both = loadApp({ settings: { confirmation: false }, signupSendsAccountWelcome: true })
856
+ both.db.rows.push({ id: 'u-acc', email: 'acc@site.test', source: 'signup' })
857
+ expect(
858
+ await both.helper.subscribeNewsletterPerson({
859
+ db: both.db,
860
+ email: 'acc@site.test',
861
+ signUpPoint: 'signup',
862
+ req: REQ,
863
+ })
864
+ ).toEqual({ outcome: 'subscribed' })
865
+ expect(both.sent).toEqual([])
866
+ expect(both.events).toHaveLength(1)
867
+ // From the footer the same person is welcomed by the newsletter.
868
+ const footer = loadApp({ settings: { confirmation: false }, signupSendsAccountWelcome: true })
869
+ await footer.helper.subscribeNewsletterPerson({
870
+ db: footer.db,
871
+ email: 'acc@site.test',
872
+ signUpPoint: 'footer',
873
+ req: REQ,
874
+ })
875
+ expect(footer.sent).toHaveLength(1)
876
+ })
877
+
878
+ it('R-N35: the account page switch is its own sign-up point, and is welcomed', async () => {
879
+ const app = loadApp({ settings: { confirmation: false }, signupSendsAccountWelcome: true })
880
+ app.db.rows.push({ id: 'u-acc', email: 'acc@site.test', source: 'signup' })
881
+ expect(
882
+ await app.helper.subscribeNewsletterPerson({
883
+ db: app.db,
884
+ email: 'acc@site.test',
885
+ signUpPoint: 'account',
886
+ req: REQ,
887
+ })
888
+ ).toEqual({ outcome: 'subscribed' })
889
+ expect(app.db.rows[0]).toMatchObject({ newsletter_source: 'account', source: 'signup' })
890
+ expect(app.sent).toHaveLength(1)
891
+ expect(app.sent[0].html).toContain('/newsletter/unsubscribe#t=')
892
+ // With double opt-in the welcome follows the confirmation, still not suppressed.
893
+ const confirming = loadApp({
894
+ settings: { confirmation: true },
895
+ signupSendsAccountWelcome: true,
896
+ })
897
+ confirming.db.rows.push({ id: 'u-acc', email: 'acc@site.test', source: 'signup' })
898
+ await confirming.helper.subscribeNewsletterPerson({
899
+ db: confirming.db,
900
+ email: 'acc@site.test',
901
+ signUpPoint: 'account',
902
+ req: REQ,
903
+ })
904
+ await confirming.helper.confirmNewsletter({
905
+ db: confirming.db,
906
+ token: linkToken(confirming.sent[0].html),
907
+ req: REQ,
908
+ })
909
+ expect(confirming.sent).toHaveLength(2)
910
+ expect(confirming.sent[1].html).toContain('/newsletter/unsubscribe#t=')
911
+ })
912
+
913
+ it("R-N34: a link to the site's own unsubscribe route keeps the token in its query", async () => {
914
+ const app = loadApp({
915
+ settings: {
916
+ confirmation: false,
917
+ pages: { confirm: '/newsletter/confirm', unsubscribe: '/api/newsletter/unsubscribe' },
918
+ },
919
+ })
920
+ await app.helper.subscribeNewsletterPerson({
921
+ db: app.db,
922
+ email: 'route@reader.test',
923
+ signUpPoint: 'block',
924
+ req: REQ,
925
+ })
926
+ expect(app.sent[0].html).toContain('https://studio.example/api/newsletter/unsubscribe?t=')
927
+ expect(app.sent[0].html).not.toContain('#t=')
928
+ })
929
+ })
930
+
931
+ describe('the newsletter, after review (fix round 1)', () => {
932
+ it('subscribe: an address opted out of email gets a confirmation, flags unchanged', async () => {
933
+ for (const status of [null, 'pending']) {
934
+ const app = loadApp({ settings: { confirmation: false } })
935
+ app.db.rows.push({
936
+ id: 'u-out',
937
+ email: 'out@site.test',
938
+ source: 'checkout',
939
+ marketing_consent: false,
940
+ email_unsubscribed: true,
941
+ newsletter_status: status,
942
+ })
943
+ const result = await app.helper.subscribeNewsletterPerson({
944
+ db: app.db,
945
+ email: 'out@site.test',
946
+ signUpPoint: 'footer',
947
+ req: REQ,
948
+ })
949
+ expect(result).toEqual({ outcome: 'confirm-sent' })
950
+ expect(app.db.writes()).toEqual([])
951
+ expect(app.db.rows[0]).toMatchObject({
952
+ email_unsubscribed: true,
953
+ marketing_consent: false,
954
+ newsletter_status: status,
955
+ })
956
+ expect(app.events).toEqual([])
957
+ expect(app.sent).toHaveLength(1)
958
+ expect(app.sent[0].html).toContain('/newsletter/confirm#t=')
959
+ // The person's own click on that link is what brings them back.
960
+ await app.helper.confirmNewsletter({
961
+ db: app.db,
962
+ token: linkToken(app.sent[0].html),
963
+ req: REQ,
964
+ })
965
+ expect(app.db.rows[0]).toMatchObject({
966
+ newsletter_status: 'subscribed',
967
+ email_unsubscribed: false,
968
+ marketing_consent: true,
969
+ })
970
+ }
971
+ })
972
+
973
+ it('guards every change of state in its own statement', () => {
974
+ const neverOnTheList = "(newsletter_status IS NULL OR newsletter_status = 'pending')"
975
+ for (const sql of [NEWSLETTER_SQL.markPending, NEWSLETTER_SQL.markSubscribed]) {
976
+ expect(sql).toContain(
977
+ `WHERE id = $1 AND ${neverOnTheList} AND email_unsubscribed IS NOT TRUE`
978
+ )
979
+ }
980
+ expect(NEWSLETTER_SQL.markSubscribed).toContain('email_unsubscribed = FALSE')
981
+ expect(NEWSLETTER_SQL.markSubscribed).toContain('marketing_consent = TRUE')
982
+ expect(NEWSLETTER_SQL.insertPending).toContain("VALUES ($1, $2, FALSE, 'pending', $3)")
983
+ expect(NEWSLETTER_SQL.confirm).toContain(
984
+ "WHERE id = $1 AND newsletter_status IS DISTINCT FROM 'subscribed'"
985
+ )
986
+ expect(NEWSLETTER_SQL.confirm).toContain('email_unsubscribed = FALSE')
987
+ expect(NEWSLETTER_SQL.unsubscribe).toContain(
988
+ "WHERE id = $1 AND newsletter_status IS DISTINCT FROM 'unsubscribed'"
989
+ )
990
+ expect(NEWSLETTER_SQL.findPerson).toContain('email_unsubscribed')
991
+ })
992
+
993
+ it('subscribe: the sixth sign-up of an address in an hour is limited, and writes nothing', async () => {
994
+ const app = loadApp()
995
+ for (let i = 0; i < 5; i++) {
996
+ app.db.rows.length = 0
997
+ await app.helper.subscribeNewsletterPerson({
998
+ db: app.db,
999
+ email: 'many@site.test',
1000
+ signUpPoint: 'block',
1001
+ req: REQ,
1002
+ })
1003
+ }
1004
+ app.db.rows.length = 0
1005
+ app.db.queries.length = 0
1006
+ const sixth = await app.helper.subscribeNewsletterPerson({
1007
+ db: app.db,
1008
+ email: 'many@site.test',
1009
+ signUpPoint: 'block',
1010
+ req: REQ,
1011
+ })
1012
+ expect(sixth).toEqual({ outcome: 'limited' })
1013
+ expect(app.db.queries).toEqual([])
1014
+ })
1015
+
1016
+ it('without the app secret nothing is mailed and no link verifies', async () => {
1017
+ const app = loadApp({ env: { NEXTAUTH_SECRET: undefined } })
1018
+ const result = await app.helper.subscribeNewsletterPerson({
1019
+ db: app.db,
1020
+ email: 'nosecret@site.test',
1021
+ signUpPoint: 'block',
1022
+ req: REQ,
1023
+ })
1024
+ expect(result).toEqual({ outcome: 'pending' })
1025
+ expect(app.sent).toEqual([])
1026
+ expect(app.helper.signNewsletterToken('nosecret@site.test', 'confirm')).toBeNull()
1027
+ expect(
1028
+ app.helper.verifyNewsletterToken(newsletterTokenSpec.testVector.token, 'unsubscribe')
1029
+ ).toBeNull()
1030
+ })
1031
+
1032
+ it('unsubscribe: a pending person leaves without a newsletter-unsubscribed event', async () => {
1033
+ const app = loadApp()
1034
+ app.db.rows.push({ id: 'u-p', email: 'wait@site.test', newsletter_status: 'pending' })
1035
+ const token = app.helper.signNewsletterToken('wait@site.test', 'unsubscribe')
1036
+ expect(await app.helper.unsubscribeNewsletter({ db: app.db, token, req: REQ })).toEqual({
1037
+ ok: true,
1038
+ email: 'wait@site.test',
1039
+ })
1040
+ expect(app.db.rows[0].newsletter_status).toBe('unsubscribed')
1041
+ expect(app.events).toEqual([])
1042
+ })
1043
+
1044
+ it('subscribe: an opted-out address is sent at most one confirmation a day', async () => {
1045
+ const app = loadApp()
1046
+ app.db.rows.push({ id: 'u-left', email: 'left@site.test', newsletter_status: 'unsubscribed' })
1047
+ const outcomes = []
1048
+ for (let i = 0; i < 3; i++) {
1049
+ outcomes.push(
1050
+ (
1051
+ await app.helper.subscribeNewsletterPerson({
1052
+ db: app.db,
1053
+ email: 'left@site.test',
1054
+ signUpPoint: 'block',
1055
+ req: REQ,
1056
+ })
1057
+ ).outcome
1058
+ )
1059
+ }
1060
+ expect(outcomes).toEqual(['confirm-sent', 'limited', 'limited'])
1061
+ expect(app.sent).toHaveLength(1)
1062
+ expect(app.limits).toContain('newsletter-resubscribe:left@site.test')
1063
+ // The visitor is answered the same either way.
1064
+ const res = await route(app, 'subscribe', {
1065
+ method: 'POST',
1066
+ body: { email: 'left@site.test' },
1067
+ })
1068
+ expect(res.body).toEqual(app.helper.publicSubscribeAnswer())
1069
+ })
1070
+
1071
+ it('the public route names only the sign-up points of the site pages', async () => {
1072
+ const app = loadApp()
1073
+ for (const [point, kept] of [
1074
+ ['footer', 'footer'],
1075
+ ['popup', 'popup'],
1076
+ ['form', 'form'],
1077
+ ['signup', 'block'],
1078
+ ['account', 'block'],
1079
+ ['checkout', 'block'],
1080
+ ['booking', 'block'],
1081
+ ['import', 'block'],
1082
+ ]) {
1083
+ const email = `${point}@site.test`
1084
+ await route(app, 'subscribe', { method: 'POST', body: { email, signUpPoint: point } })
1085
+ const row = app.db.rows.find((candidate) => candidate.email === email)
1086
+ expect([point, row && row.newsletter_source, row && row.source]).toEqual([
1087
+ point,
1088
+ kept,
1089
+ kept === 'form' ? 'form' : 'newsletter',
1090
+ ])
1091
+ }
1092
+ })
1093
+
1094
+ it('never logs a database row or the recipient', async () => {
1095
+ const app = loadApp()
1096
+ app.db.failWith = {
1097
+ message: 'null value in column "name" of relation "users" violates not-null constraint',
1098
+ code: '23502',
1099
+ detail: 'Failing row contains (u9, leak@site.test, null).',
1100
+ }
1101
+ const res = await route(app, 'subscribe', { method: 'POST', body: { email: 'leak@site.test' } })
1102
+ expect(res.statusCode).toBe(503)
1103
+ expect(app.clientErrors).toEqual([
1104
+ {
1105
+ code: '23502',
1106
+ message: 'null value in column "name" of relation "users" violates not-null constraint',
1107
+ },
1108
+ ])
1109
+ // A provider that echoes the recipient.
1110
+ const echoing = loadApp({
1111
+ provider: {
1112
+ Resend: class {
1113
+ public emails = {
1114
+ send: async (msg: Row) => ({
1115
+ error: { message: `Invalid 'To' address: '${msg.to[0]}'` },
1116
+ }),
1117
+ }
1118
+ },
1119
+ },
1120
+ })
1121
+ await echoing.helper.sendNewsletterEmail('confirmation', 'echo@site.test', '', REQ)
1122
+ const logged = JSON.stringify(echoing.logs)
1123
+ expect(logged).toContain('[address]')
1124
+ expect(logged).not.toContain('echo@site.test')
1125
+ })
1126
+
1127
+ it('every welcome carries a way out: a footer link when the body has none, and List-Unsubscribe', async () => {
1128
+ const app = loadApp({
1129
+ settings: {
1130
+ confirmation: false,
1131
+ templates: {
1132
+ ...SETTINGS.templates,
1133
+ welcome: { subject: 'Welcome', body: '<html><body><p>Hello!</p></body></html>' },
1134
+ },
1135
+ },
1136
+ })
1137
+ await app.helper.subscribeNewsletterPerson({
1138
+ db: app.db,
1139
+ email: 'hello@reader.test',
1140
+ signUpPoint: 'block',
1141
+ req: REQ,
1142
+ })
1143
+ expect(app.sent).toHaveLength(1)
1144
+ const welcome = app.sent[0]
1145
+ const token = linkToken(welcome.html)
1146
+ expect(welcome.html).toMatch(
1147
+ /<a href="https:\/\/studio\.example\/newsletter\/unsubscribe#t=[^"]+">Unsubscribe<\/a>.*<\/body><\/html>$/
1148
+ )
1149
+ expect(welcome.headers).toEqual({
1150
+ // The route that reads the token on the server (a fragment never reaches it).
1151
+ 'List-Unsubscribe': `<https://studio.example/api/newsletter/unsubscribe?t=${token}>`,
1152
+ })
1153
+ // A confirmation carries none.
1154
+ const pending = loadApp()
1155
+ await pending.helper.subscribeNewsletterPerson({
1156
+ db: pending.db,
1157
+ email: 'wait@reader.test',
1158
+ signUpPoint: 'block',
1159
+ req: REQ,
1160
+ })
1161
+ expect(pending.sent[0].headers).toBeUndefined()
1162
+ })
1163
+ })
1164
+
1165
+ describe('the newsletter sign-up step, in the generator (fix round 1)', () => {
1166
+ const bareStep = (config: Row): WorkflowSegment =>
1167
+ ({
1168
+ id: 'server-1',
1169
+ env: 'server',
1170
+ nodeIds: ['join'],
1171
+ nodes: [
1172
+ { id: 'join', type: 'newsletter-subscribe', config, executionEnv: 'server', stepNumber: 1 },
1173
+ ],
1174
+ edges: [],
1175
+ } as unknown as WorkflowSegment)
1176
+
1177
+ it('limits a bare step per visitor with the sign-up budget, and counts it as cost-bearing', () => {
1178
+ const guard = buildVisitorStepLimitGuard(bareStep({ email: 'a@b.co' }))
1179
+ expect(guard).toContain(
1180
+ '{"scope":"visitor-limit:newsletter-signup","limit":5,"windowSeconds":3600}'
1181
+ )
1182
+ expect(guard).toContain('__logStepLimitSkip("visitor-limit:newsletter-signup")')
1183
+ // A declared limit is the step's own.
1184
+ expect(
1185
+ buildVisitorStepLimitGuard(
1186
+ bareStep({ visitorLimit: { limit: 2, windowSeconds: 600, scope: 'newsletter-signup' } })
1187
+ )
1188
+ ).toContain('{"scope":"visitor-limit:newsletter-signup","limit":2,"windowSeconds":600}')
1189
+ expect(isCostBearingNodeType('newsletter-subscribe')).toBe(true)
1190
+ })
1191
+
1192
+ it('refuses the sign-up step outside a workflow a form submit starts', () => {
1193
+ const workflowWith = (trigger: string) => ({
1194
+ workflows: {
1195
+ 'wf-1': {
1196
+ id: 'wf-1',
1197
+ name: 'Join',
1198
+ trigger: { type: trigger, config: {} },
1199
+ nodes: [
1200
+ { id: 't', type: trigger, config: {} },
1201
+ {
1202
+ id: 'join',
1203
+ type: 'newsletter-subscribe',
1204
+ label: 'Join',
1205
+ config: { email: 'a@b.co' },
1206
+ },
1207
+ ],
1208
+ edges: [{ id: 'e', source: 't', target: 'join' }],
1209
+ },
1210
+ },
1211
+ })
1212
+ for (const trigger of [
1213
+ 'event-page-loaded',
1214
+ 'event-element-clicked',
1215
+ 'event-cron-triggered',
1216
+ 'event-webhook-received',
1217
+ 'event-business-event',
1218
+ ]) {
1219
+ const refusals = collectWorkflowPublishRefusals(workflowWith(trigger) as never).filter(
1220
+ (refusal) => refusal.check === 'newsletter-sign-up'
1221
+ )
1222
+ expect([trigger, refusals]).toEqual([
1223
+ trigger,
1224
+ [
1225
+ {
1226
+ kind: 'refused',
1227
+ check: 'newsletter-sign-up',
1228
+ ownerId: 'wf-1',
1229
+ ownerKind: 'workflow',
1230
+ nodeId: 'join',
1231
+ message:
1232
+ 'The step "Join" in "Join": The newsletter sign-up step runs only when a visitor submits a form.',
1233
+ },
1234
+ ],
1235
+ ])
1236
+ expect(() => assertWorkflowPublishGates(workflowWith(trigger) as never)).toThrow(
1237
+ 'The newsletter sign-up step runs only when a visitor submits a form.'
1238
+ )
1239
+ }
1240
+ const formWorkflow = workflowWith('event-form-submitted')
1241
+ expect(
1242
+ collectWorkflowPublishRefusals(formWorkflow as never).filter(
1243
+ (refusal) => refusal.check === 'newsletter-sign-up'
1244
+ )
1245
+ ).toEqual([])
1246
+ expect(() => assertWorkflowPublishGates(formWorkflow as never)).not.toThrow()
1247
+ })
1248
+ })