@teleporthq/teleport-plugin-next-workflows 0.43.75 → 0.44.1

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 (248) hide show
  1. package/__tests__/_helpers/fake-store-database.ts +428 -0
  2. package/__tests__/_helpers/invoice-store.ts +382 -0
  3. package/__tests__/booking-calendar-feed-route.test.ts +494 -0
  4. package/__tests__/custom-node-result-taken-branch.test.ts +107 -0
  5. package/__tests__/data-api-date-parser.test.ts +45 -0
  6. package/__tests__/email-locale-senders.test.ts +3 -1
  7. package/__tests__/email-sender-name.test.ts +513 -0
  8. package/__tests__/graph-utils-topological-order.test.ts +155 -1
  9. package/__tests__/invoice-billing-details.test.ts +384 -0
  10. package/__tests__/invoice-data-access-replace.test.ts +236 -2
  11. package/__tests__/invoice-order-items-settle.test.ts +1 -1
  12. package/__tests__/invoice-payment-email.test.ts +191 -0
  13. package/__tests__/invoice-regenerate-email.test.ts +288 -0
  14. package/__tests__/invoice-regenerate-route.test.ts +9 -7
  15. package/__tests__/invoice-reservation.test.ts +1 -1
  16. package/__tests__/invoice-tender.test.ts +1 -1
  17. package/__tests__/middleware-admin-panel-roles.test.ts +127 -0
  18. package/__tests__/page-arrival-remount.test.ts +247 -0
  19. package/__tests__/payment-drivers-module.test.ts +19 -0
  20. package/__tests__/raw-query-error-code.test.ts +47 -0
  21. package/__tests__/runtime-template-token-resolution.test.ts +16 -0
  22. package/__tests__/segment-reply-changed-keys.test.ts +31 -0
  23. package/__tests__/segment-route-skipped-branch.test.ts +101 -0
  24. package/__tests__/segment-splitter-checkout-order.test.ts +306 -0
  25. package/__tests__/streaming-on-end-env-filter.test.ts +18 -18
  26. package/__tests__/streaming-on-end-order.test.ts +387 -0
  27. package/dist/cjs/account-delete-route-generator.d.ts +1 -0
  28. package/dist/cjs/account-delete-route-generator.d.ts.map +1 -1
  29. package/dist/cjs/account-delete-route-generator.js +2 -1
  30. package/dist/cjs/account-delete-route-generator.js.map +1 -1
  31. package/dist/cjs/api-route-generator.d.ts.map +1 -1
  32. package/dist/cjs/api-route-generator.js +23 -13
  33. package/dist/cjs/api-route-generator.js.map +1 -1
  34. package/dist/cjs/auth-generator.d.ts +1 -0
  35. package/dist/cjs/auth-generator.d.ts.map +1 -1
  36. package/dist/cjs/auth-generator.js +77 -28
  37. package/dist/cjs/auth-generator.js.map +1 -1
  38. package/dist/cjs/booking-calendar-feed-route-generator.d.ts +28 -0
  39. package/dist/cjs/booking-calendar-feed-route-generator.d.ts.map +1 -0
  40. package/dist/cjs/booking-calendar-feed-route-generator.js +296 -0
  41. package/dist/cjs/booking-calendar-feed-route-generator.js.map +1 -0
  42. package/dist/cjs/data-api-route-generator.d.ts.map +1 -1
  43. package/dist/cjs/data-api-route-generator.js +6 -1
  44. package/dist/cjs/data-api-route-generator.js.map +1 -1
  45. package/dist/cjs/executor-generator.d.ts.map +1 -1
  46. package/dist/cjs/executor-generator.js +39 -28
  47. package/dist/cjs/executor-generator.js.map +1 -1
  48. package/dist/cjs/graph-utils.d.ts +31 -1
  49. package/dist/cjs/graph-utils.d.ts.map +1 -1
  50. package/dist/cjs/graph-utils.js +112 -10
  51. package/dist/cjs/graph-utils.js.map +1 -1
  52. package/dist/cjs/index.d.ts +1 -1
  53. package/dist/cjs/index.d.ts.map +1 -1
  54. package/dist/cjs/index.js +2 -1
  55. package/dist/cjs/index.js.map +1 -1
  56. package/dist/cjs/invoice/api-routes-code.d.ts +12 -3
  57. package/dist/cjs/invoice/api-routes-code.d.ts.map +1 -1
  58. package/dist/cjs/invoice/api-routes-code.js +152 -21
  59. package/dist/cjs/invoice/api-routes-code.js.map +1 -1
  60. package/dist/cjs/invoice/data-access-code.d.ts.map +1 -1
  61. package/dist/cjs/invoice/data-access-code.js +133 -21
  62. package/dist/cjs/invoice/data-access-code.js.map +1 -1
  63. package/dist/cjs/invoice/email-sender-code.d.ts.map +1 -1
  64. package/dist/cjs/invoice/email-sender-code.js +15 -25
  65. package/dist/cjs/invoice/email-sender-code.js.map +1 -1
  66. package/dist/cjs/invoice/index.js +1 -1
  67. package/dist/cjs/invoice/index.js.map +1 -1
  68. package/dist/cjs/invoice/invoice-assembly-code.d.ts.map +1 -1
  69. package/dist/cjs/invoice/invoice-assembly-code.js +50 -6
  70. package/dist/cjs/invoice/invoice-assembly-code.js.map +1 -1
  71. package/dist/cjs/invoice/invoice-html-code.d.ts.map +1 -1
  72. package/dist/cjs/invoice/invoice-html-code.js +13 -2
  73. package/dist/cjs/invoice/invoice-html-code.js.map +1 -1
  74. package/dist/cjs/nodes/data/data-raw-query.d.ts.map +1 -1
  75. package/dist/cjs/nodes/data/data-raw-query.js +11 -1
  76. package/dist/cjs/nodes/data/data-raw-query.js.map +1 -1
  77. package/dist/cjs/nodes/email/email-mailgun.d.ts.map +1 -1
  78. package/dist/cjs/nodes/email/email-mailgun.js +4 -2
  79. package/dist/cjs/nodes/email/email-mailgun.js.map +1 -1
  80. package/dist/cjs/nodes/email/email-postmark.d.ts.map +1 -1
  81. package/dist/cjs/nodes/email/email-postmark.js +5 -2
  82. package/dist/cjs/nodes/email/email-postmark.js.map +1 -1
  83. package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
  84. package/dist/cjs/nodes/email/email-resend.js +5 -2
  85. package/dist/cjs/nodes/email/email-resend.js.map +1 -1
  86. package/dist/cjs/nodes/email/email-sendgrid.d.ts.map +1 -1
  87. package/dist/cjs/nodes/email/email-sendgrid.js +8 -2
  88. package/dist/cjs/nodes/email/email-sendgrid.js.map +1 -1
  89. package/dist/cjs/nodes/general/general-custom-js.d.ts.map +1 -1
  90. package/dist/cjs/nodes/general/general-custom-js.js +11 -0
  91. package/dist/cjs/nodes/general/general-custom-js.js.map +1 -1
  92. package/dist/cjs/nodes/storage/storage-local-remove.d.ts.map +1 -1
  93. package/dist/cjs/nodes/storage/storage-local-remove.js +2 -0
  94. package/dist/cjs/nodes/storage/storage-local-remove.js.map +1 -1
  95. package/dist/cjs/nodes/storage/storage-local-set.d.ts.map +1 -1
  96. package/dist/cjs/nodes/storage/storage-local-set.js +4 -0
  97. package/dist/cjs/nodes/storage/storage-local-set.js.map +1 -1
  98. package/dist/cjs/page-arrival.d.ts +10 -0
  99. package/dist/cjs/page-arrival.d.ts.map +1 -0
  100. package/dist/cjs/page-arrival.js +86 -0
  101. package/dist/cjs/page-arrival.js.map +1 -0
  102. package/dist/cjs/payments/index.d.ts +10 -4
  103. package/dist/cjs/payments/index.d.ts.map +1 -1
  104. package/dist/cjs/payments/index.js +18 -7
  105. package/dist/cjs/payments/index.js.map +1 -1
  106. package/dist/cjs/segment-splitter.d.ts.map +1 -1
  107. package/dist/cjs/segment-splitter.js +65 -27
  108. package/dist/cjs/segment-splitter.js.map +1 -1
  109. package/dist/cjs/transactional-email-code.d.ts.map +1 -1
  110. package/dist/cjs/transactional-email-code.js +32 -18
  111. package/dist/cjs/transactional-email-code.js.map +1 -1
  112. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  113. package/dist/cjs/workflow-component-plugin.d.ts.map +1 -1
  114. package/dist/cjs/workflow-component-plugin.js +35 -149
  115. package/dist/cjs/workflow-component-plugin.js.map +1 -1
  116. package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
  117. package/dist/cjs/workflow-project-plugin.js +16 -4
  118. package/dist/cjs/workflow-project-plugin.js.map +1 -1
  119. package/dist/cjs/workflow-scope.d.ts +19 -0
  120. package/dist/cjs/workflow-scope.d.ts.map +1 -0
  121. package/dist/cjs/workflow-scope.js +147 -0
  122. package/dist/cjs/workflow-scope.js.map +1 -0
  123. package/dist/esm/account-delete-route-generator.d.ts +1 -0
  124. package/dist/esm/account-delete-route-generator.d.ts.map +1 -1
  125. package/dist/esm/account-delete-route-generator.js +2 -1
  126. package/dist/esm/account-delete-route-generator.js.map +1 -1
  127. package/dist/esm/api-route-generator.d.ts.map +1 -1
  128. package/dist/esm/api-route-generator.js +23 -13
  129. package/dist/esm/api-route-generator.js.map +1 -1
  130. package/dist/esm/auth-generator.d.ts +1 -0
  131. package/dist/esm/auth-generator.d.ts.map +1 -1
  132. package/dist/esm/auth-generator.js +77 -28
  133. package/dist/esm/auth-generator.js.map +1 -1
  134. package/dist/esm/booking-calendar-feed-route-generator.d.ts +28 -0
  135. package/dist/esm/booking-calendar-feed-route-generator.d.ts.map +1 -0
  136. package/dist/esm/booking-calendar-feed-route-generator.js +291 -0
  137. package/dist/esm/booking-calendar-feed-route-generator.js.map +1 -0
  138. package/dist/esm/data-api-route-generator.d.ts.map +1 -1
  139. package/dist/esm/data-api-route-generator.js +6 -1
  140. package/dist/esm/data-api-route-generator.js.map +1 -1
  141. package/dist/esm/executor-generator.d.ts.map +1 -1
  142. package/dist/esm/executor-generator.js +39 -28
  143. package/dist/esm/executor-generator.js.map +1 -1
  144. package/dist/esm/graph-utils.d.ts +31 -1
  145. package/dist/esm/graph-utils.d.ts.map +1 -1
  146. package/dist/esm/graph-utils.js +109 -9
  147. package/dist/esm/graph-utils.js.map +1 -1
  148. package/dist/esm/index.d.ts +1 -1
  149. package/dist/esm/index.d.ts.map +1 -1
  150. package/dist/esm/index.js +1 -1
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/invoice/api-routes-code.d.ts +12 -3
  153. package/dist/esm/invoice/api-routes-code.d.ts.map +1 -1
  154. package/dist/esm/invoice/api-routes-code.js +152 -21
  155. package/dist/esm/invoice/api-routes-code.js.map +1 -1
  156. package/dist/esm/invoice/data-access-code.d.ts.map +1 -1
  157. package/dist/esm/invoice/data-access-code.js +133 -21
  158. package/dist/esm/invoice/data-access-code.js.map +1 -1
  159. package/dist/esm/invoice/email-sender-code.d.ts.map +1 -1
  160. package/dist/esm/invoice/email-sender-code.js +15 -25
  161. package/dist/esm/invoice/email-sender-code.js.map +1 -1
  162. package/dist/esm/invoice/index.js +1 -1
  163. package/dist/esm/invoice/index.js.map +1 -1
  164. package/dist/esm/invoice/invoice-assembly-code.d.ts.map +1 -1
  165. package/dist/esm/invoice/invoice-assembly-code.js +50 -6
  166. package/dist/esm/invoice/invoice-assembly-code.js.map +1 -1
  167. package/dist/esm/invoice/invoice-html-code.d.ts.map +1 -1
  168. package/dist/esm/invoice/invoice-html-code.js +13 -2
  169. package/dist/esm/invoice/invoice-html-code.js.map +1 -1
  170. package/dist/esm/nodes/data/data-raw-query.d.ts.map +1 -1
  171. package/dist/esm/nodes/data/data-raw-query.js +11 -1
  172. package/dist/esm/nodes/data/data-raw-query.js.map +1 -1
  173. package/dist/esm/nodes/email/email-mailgun.d.ts.map +1 -1
  174. package/dist/esm/nodes/email/email-mailgun.js +4 -2
  175. package/dist/esm/nodes/email/email-mailgun.js.map +1 -1
  176. package/dist/esm/nodes/email/email-postmark.d.ts.map +1 -1
  177. package/dist/esm/nodes/email/email-postmark.js +5 -2
  178. package/dist/esm/nodes/email/email-postmark.js.map +1 -1
  179. package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
  180. package/dist/esm/nodes/email/email-resend.js +5 -2
  181. package/dist/esm/nodes/email/email-resend.js.map +1 -1
  182. package/dist/esm/nodes/email/email-sendgrid.d.ts.map +1 -1
  183. package/dist/esm/nodes/email/email-sendgrid.js +8 -2
  184. package/dist/esm/nodes/email/email-sendgrid.js.map +1 -1
  185. package/dist/esm/nodes/general/general-custom-js.d.ts.map +1 -1
  186. package/dist/esm/nodes/general/general-custom-js.js +11 -0
  187. package/dist/esm/nodes/general/general-custom-js.js.map +1 -1
  188. package/dist/esm/nodes/storage/storage-local-remove.d.ts.map +1 -1
  189. package/dist/esm/nodes/storage/storage-local-remove.js +2 -0
  190. package/dist/esm/nodes/storage/storage-local-remove.js.map +1 -1
  191. package/dist/esm/nodes/storage/storage-local-set.d.ts.map +1 -1
  192. package/dist/esm/nodes/storage/storage-local-set.js +4 -0
  193. package/dist/esm/nodes/storage/storage-local-set.js.map +1 -1
  194. package/dist/esm/page-arrival.d.ts +10 -0
  195. package/dist/esm/page-arrival.d.ts.map +1 -0
  196. package/dist/esm/page-arrival.js +81 -0
  197. package/dist/esm/page-arrival.js.map +1 -0
  198. package/dist/esm/payments/index.d.ts +10 -4
  199. package/dist/esm/payments/index.d.ts.map +1 -1
  200. package/dist/esm/payments/index.js +16 -6
  201. package/dist/esm/payments/index.js.map +1 -1
  202. package/dist/esm/segment-splitter.d.ts.map +1 -1
  203. package/dist/esm/segment-splitter.js +66 -28
  204. package/dist/esm/segment-splitter.js.map +1 -1
  205. package/dist/esm/transactional-email-code.d.ts.map +1 -1
  206. package/dist/esm/transactional-email-code.js +32 -18
  207. package/dist/esm/transactional-email-code.js.map +1 -1
  208. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  209. package/dist/esm/workflow-component-plugin.d.ts.map +1 -1
  210. package/dist/esm/workflow-component-plugin.js +32 -146
  211. package/dist/esm/workflow-component-plugin.js.map +1 -1
  212. package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
  213. package/dist/esm/workflow-project-plugin.js +16 -4
  214. package/dist/esm/workflow-project-plugin.js.map +1 -1
  215. package/dist/esm/workflow-scope.d.ts +19 -0
  216. package/dist/esm/workflow-scope.d.ts.map +1 -0
  217. package/dist/esm/workflow-scope.js +142 -0
  218. package/dist/esm/workflow-scope.js.map +1 -0
  219. package/package.json +4 -4
  220. package/src/account-delete-route-generator.ts +3 -1
  221. package/src/api-route-generator.ts +23 -13
  222. package/src/auth-generator.ts +83 -26
  223. package/src/booking-calendar-feed-route-generator.ts +293 -0
  224. package/src/data-api-route-generator.ts +6 -1
  225. package/src/executor-generator.ts +39 -28
  226. package/src/graph-utils.ts +143 -11
  227. package/src/index.ts +1 -0
  228. package/src/invoice/api-routes-code.ts +166 -21
  229. package/src/invoice/data-access-code.ts +139 -21
  230. package/src/invoice/email-sender-code.ts +15 -25
  231. package/src/invoice/index.ts +1 -1
  232. package/src/invoice/invoice-assembly-code.ts +50 -6
  233. package/src/invoice/invoice-html-code.ts +13 -2
  234. package/src/nodes/data/data-raw-query.ts +11 -1
  235. package/src/nodes/email/email-mailgun.ts +11 -2
  236. package/src/nodes/email/email-postmark.ts +12 -2
  237. package/src/nodes/email/email-resend.ts +12 -2
  238. package/src/nodes/email/email-sendgrid.ts +15 -2
  239. package/src/nodes/general/general-custom-js.ts +10 -0
  240. package/src/nodes/storage/storage-local-remove.ts +4 -0
  241. package/src/nodes/storage/storage-local-set.ts +6 -0
  242. package/src/page-arrival.ts +115 -0
  243. package/src/payments/index.ts +18 -5
  244. package/src/segment-splitter.ts +90 -36
  245. package/src/transactional-email-code.ts +32 -21
  246. package/src/workflow-component-plugin.ts +43 -188
  247. package/src/workflow-project-plugin.ts +17 -4
  248. package/src/workflow-scope.ts +184 -0
@@ -12,7 +12,7 @@ import {
12
12
  UIDLCustomWorkflowNode,
13
13
  } from '@teleporthq/teleport-types'
14
14
  import * as types from '@babel/types'
15
- import { GenericUtils, JSIdentifiers, StringUtils } from '@teleporthq/teleport-shared'
15
+ import { GenericUtils, JSIdentifiers, PageArrival, StringUtils } from '@teleporthq/teleport-shared'
16
16
  import {
17
17
  splitIntoSegments,
18
18
  resolveNodeExecutionEnv,
@@ -34,6 +34,8 @@ import { elementVisibleObserverCode } from './element-visible-trigger'
34
34
  import { restoreControlledSelectValue } from './controlled-select'
35
35
  import { resolveScrollPoint } from './scroll-points'
36
36
  import { findFormStateKeyForHydration } from './admin-form-hydration'
37
+ import { getRelevantWorkflows, resolveLifecycleTriggerElementId } from './workflow-scope'
38
+ import { isPageArrivalWorkflow, pageRendersArrivalComponent } from './page-arrival'
37
39
 
38
40
  interface WorkflowPluginConfig {
39
41
  isPage?: boolean
@@ -85,29 +87,6 @@ const DOM_TO_REACT_EVENT: Record<string, string> = {
85
87
  drag: 'onDrag',
86
88
  }
87
89
 
88
- /**
89
- * Trigger types that run as LIFECYCLE code (no React event prop) but are still
90
- * bound to ONE element, so the workflow only belongs on the page that renders
91
- * it. Returns the DOM id to prune against, or null when the trigger is not
92
- * element-scoped.
93
- *
94
- * Same fallback chain as the element-trigger branch: the HTML id first, the
95
- * project-document node id only as a last resort.
96
- */
97
- const resolveLifecycleTriggerElementId = (
98
- triggerType: string,
99
- config: Record<string, unknown>
100
- ): string | null => {
101
- if (
102
- triggerType !== 'event-element-visible' &&
103
- triggerType !== 'event-chapter-reached' &&
104
- triggerType !== 'event-scene-scrolled-past'
105
- ) {
106
- return null
107
- }
108
- return ((config.elementHtmlId || config.nodeId) as string) || null
109
- }
110
-
111
90
  const getReactEventProp = (triggerType: string, config: Record<string, unknown>): string | null => {
112
91
  switch (triggerType) {
113
92
  case 'event-element-clicked':
@@ -229,6 +208,12 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
229
208
  return structure
230
209
  }
231
210
 
211
+ // Decided before the page's own workflows: a page can have none and still
212
+ // render a component that runs one when it appears.
213
+ if (isPage && pageRendersArrivalComponent(uidl.node, workflows, options.projectComponents)) {
214
+ PageArrival.remountPageOnArrival(structure)
215
+ }
216
+
232
217
  const relevantWorkflows = getRelevantWorkflows(workflows, uidl, isPage)
233
218
 
234
219
  if (relevantWorkflows.length === 0) {
@@ -533,6 +518,13 @@ export const createNextWorkflowPlugin: ComponentPluginFactory<WorkflowPluginConf
533
518
  globalStateChangeWorkflows
534
519
  )
535
520
 
521
+ const runsOnPageArrival = lifecycleWorkflows.some(
522
+ (wf) => activeWorkflowIds.has(wf.id) && isPageArrivalWorkflow(wf)
523
+ )
524
+ if (isPage && runsOnPageArrival) {
525
+ PageArrival.remountPageOnArrival(structure)
526
+ }
527
+
536
528
  // For update/detail pages: initialize form state from props
537
529
  const initialPropsData = uidl.outputOptions?.initialPropsData
538
530
  if (initialPropsData?.exposeAs?.name && formDataStateKeyForHydration) {
@@ -2047,13 +2039,18 @@ const generateElementHandler = (et: ElementTriggerInfo): string => {
2047
2039
  (preventDefault ? ' event.preventDefault();\n' : '') +
2048
2040
  (stopPropagation ? ' event.stopPropagation();\n' : '') +
2049
2041
  ` const __te = event.currentTarget || event.target;\n` +
2042
+ // The control that fired is `event.target`, which differs from the
2043
+ // bound element when the event bubbled: a radio group's wrapper
2044
+ // listens for `change` and the chosen radio is the target.
2050
2045
  // `formControlPropertyReads(...)` injects `value`, `checked`, `files`
2051
- // so dropdown/input/checkbox/file-picker `change` events surface the
2052
- // user's choice as `triggerContext.value` (etc.) — without this every
2046
+ // from THAT control so `change` events surface the user's choice as
2047
+ // `triggerContext.value` (etc.) — without this every
2053
2048
  // `state-update-local-state` node bound to `Trigger.value` writes
2054
- // `undefined` and the picker becomes a no-op.
2055
- ` const triggerContext = { elementId: '${elementId}', eventType: '${eventType}', triggerElement: __te, element: __te, timestamp: Date.now(), clientX: event.clientX, clientY: event.clientY, offsetX: event.offsetX, offsetY: event.offsetY, button: event.button, deltaX: event.deltaX, deltaY: event.deltaY, deltaMode: event.deltaMode, key: event.key, ${formControlPropertyReads(
2056
- '__te'
2049
+ // `undefined` and the picker becomes a no-op; and `target` carries
2050
+ // the control itself (its name, id, dataset) for delegated handlers.
2051
+ ` const __tt = event.target || __te;\n` +
2052
+ ` const triggerContext = { elementId: '${elementId}', eventType: '${eventType}', triggerElement: __te, element: __te, target: __tt, currentTarget: __te, timestamp: Date.now(), clientX: event.clientX, clientY: event.clientY, offsetX: event.offsetX, offsetY: event.offsetY, button: event.button, deltaX: event.deltaX, deltaY: event.deltaY, deltaMode: event.deltaMode, key: event.key, ${formControlPropertyReads(
2053
+ '__tt'
2057
2054
  )} };\n` +
2058
2055
  (isDropEvent
2059
2056
  ? ' if (event.dataTransfer && event.dataTransfer.files) { triggerContext.files = Array.from(event.dataTransfer.files); }\n'
@@ -2147,6 +2144,21 @@ const normalizeKeyName = (key: string): string => {
2147
2144
  return keyMap[key] || key
2148
2145
  }
2149
2146
 
2147
+ /**
2148
+ * The run of a page/component-loaded trigger. A delayed run is cancelled when
2149
+ * the triggers are torn down: by then its page has unmounted, or has remounted
2150
+ * for a new arrival that schedules a run of its own.
2151
+ */
2152
+ const generateLoadedTriggerRun = (
2153
+ execCall: string,
2154
+ delay: number | undefined,
2155
+ safeId: string
2156
+ ): string =>
2157
+ delay
2158
+ ? ` const __delay_${safeId} = setTimeout(function() { ${execCall}; }, ${delay});\n` +
2159
+ ` cleanups.push(function() { clearTimeout(__delay_${safeId}); });\n`
2160
+ : ` ${execCall};\n`
2161
+
2150
2162
  const generateLifecycleTrigger = (wf: UIDLWorkflow, safeId: string): string => {
2151
2163
  const trigger = wf.trigger
2152
2164
  const config = (trigger.config || {}) as Record<string, unknown>
@@ -2159,9 +2171,7 @@ const generateLifecycleTrigger = (wf: UIDLWorkflow, safeId: string): string => {
2159
2171
  ` // Page loaded (${wf.name || wf.id})\n` +
2160
2172
  ` {\n` +
2161
2173
  ` const triggerContext = { url: window.location.href, timestamp: Date.now(), referrer: document.referrer };\n` +
2162
- (delay
2163
- ? ` setTimeout(function() { ${execCall}; }, ${delay});\n`
2164
- : ` ${execCall};\n`) +
2174
+ generateLoadedTriggerRun(execCall, delay, safeId) +
2165
2175
  ` }`
2166
2176
  )
2167
2177
  }
@@ -2174,9 +2184,7 @@ const generateLifecycleTrigger = (wf: UIDLWorkflow, safeId: string): string => {
2174
2184
  ` // Component loaded (${wf.name || wf.id})\n` +
2175
2185
  ` {\n` +
2176
2186
  ` const triggerContext = { componentId: '${componentId}', componentName: '${componentName}', url: window.location.href, timestamp: Date.now() };\n` +
2177
- (delay
2178
- ? ` setTimeout(function() { ${execCall}; }, ${delay});\n`
2179
- : ` ${execCall};\n`) +
2187
+ generateLoadedTriggerRun(execCall, delay, safeId) +
2180
2188
  ` }`
2181
2189
  )
2182
2190
  }
@@ -2778,156 +2786,3 @@ const generateRealtimeUnsubscribeCode = (
2778
2786
  return ''
2779
2787
  }
2780
2788
  }
2781
-
2782
- // Component-scope triggers match by emitted component name. The GUI stamps the
2783
- // name at export time, but generators may re-case or strip spaces during
2784
- // resolution, so equality is checked case/separator-insensitively.
2785
- const normalizeComponentName = (name: string): string =>
2786
- (name || '').toLowerCase().replace(/[^a-z0-9]/g, '')
2787
-
2788
- const getRelevantWorkflows = (
2789
- workflows: UIDLWorkflows,
2790
- uidl: any,
2791
- isPage: boolean
2792
- ): UIDLWorkflow[] => {
2793
- const relevant: UIDLWorkflow[] = []
2794
- const pageId = uidl.outputOptions?.pageId || uidl.outputOptions?.fileName || uidl.name || ''
2795
-
2796
- const allWorkflows = Object.values(workflows.workflows) as UIDLWorkflow[]
2797
- allWorkflows.forEach((wf: UIDLWorkflow) => {
2798
- const trigger = wf.trigger
2799
-
2800
- if (trigger.type === 'event-cron-triggered') {
2801
- return
2802
- }
2803
-
2804
- if (trigger.type === 'event-webhook-received') {
2805
- return
2806
- }
2807
-
2808
- if (trigger.type === 'event-state-change') {
2809
- const cfg = trigger.config || {}
2810
- const stateDefs = cfg.stateDefinitions as
2811
- | Array<{
2812
- pageOrComponentId?: string
2813
- pageOrComponentName?: string
2814
- name?: string
2815
- stateDefinitionId?: string
2816
- }>
2817
- | undefined
2818
-
2819
- let boundContainerType = cfg.boundContainerType as string | undefined
2820
- if (!boundContainerType) {
2821
- if (stateDefs?.some((s) => s.pageOrComponentName)) {
2822
- boundContainerType = 'component'
2823
- } else if (!isPage && stateDefs && stateDefs.length > 0) {
2824
- boundContainerType = 'component'
2825
- } else {
2826
- boundContainerType = 'page'
2827
- }
2828
- }
2829
-
2830
- const matchesContainer = boundContainerType === 'page' ? isPage : !isPage
2831
- if (!matchesContainer) {
2832
- return
2833
- }
2834
-
2835
- const boundFromConfig = cfg.boundPageOrComponentId as string | undefined
2836
- const matchesByName =
2837
- !!stateDefs &&
2838
- stateDefs.some((sd) => sd.pageOrComponentName && sd.pageOrComponentName === uidl.name)
2839
- const matchesById =
2840
- boundFromConfig === pageId ||
2841
- (!!stateDefs && stateDefs.some((sd) => sd.pageOrComponentId === pageId))
2842
-
2843
- if (matchesByName || matchesById) {
2844
- relevant.push(wf)
2845
- }
2846
- return
2847
- }
2848
-
2849
- if (trigger.type === 'event-global-state-change') {
2850
- if (!isPage) {
2851
- return
2852
- }
2853
- const selectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
2854
- if (
2855
- selectedPages &&
2856
- selectedPages.length > 0 &&
2857
- !selectedPages.some((p) => p.id === pageId)
2858
- ) {
2859
- return
2860
- }
2861
- relevant.push(wf)
2862
- return
2863
- }
2864
-
2865
- if (trigger.scope === 'global') {
2866
- return
2867
- }
2868
-
2869
- if (trigger.scope === 'component') {
2870
- if (isPage) {
2871
- return
2872
- }
2873
- const cfg = trigger.config || {}
2874
- const cfgName = (cfg.componentName as string) || ''
2875
- const cfgId = (cfg.componentId as string) || ''
2876
- const matches =
2877
- (cfgName &&
2878
- (cfgName === uidl.name ||
2879
- normalizeComponentName(cfgName) === normalizeComponentName(uidl.name))) ||
2880
- (cfgId && cfgId === pageId)
2881
- if (matches) {
2882
- relevant.push(wf)
2883
- }
2884
- return
2885
- }
2886
-
2887
- if (trigger.scope === 'page' && isPage) {
2888
- const triggerPageId = trigger.config.pageId as string
2889
- const selectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
2890
-
2891
- if (trigger.type === 'event-page-loaded') {
2892
- const allPages = trigger.config.allPages === true
2893
- if (allPages) {
2894
- relevant.push(wf)
2895
- return
2896
- }
2897
- // Unscoped page-loaded workflows are SKIPPED, not injected everywhere.
2898
- // Silently fanning out to every page masks mis-scoped workflows whose
2899
- // state target lives in a component (the setter no-ops on pages).
2900
- if (!triggerPageId && (!selectedPages || selectedPages.length === 0)) {
2901
- return
2902
- }
2903
- }
2904
-
2905
- if (triggerPageId && triggerPageId !== pageId) {
2906
- return
2907
- }
2908
-
2909
- if (selectedPages && !selectedPages.some((p) => p.id === pageId)) {
2910
- return
2911
- }
2912
-
2913
- relevant.push(wf)
2914
- return
2915
- }
2916
-
2917
- if (trigger.scope === 'element') {
2918
- // If the element-scoped trigger specifies which pages it belongs to, filter by page.
2919
- // This prevents including ALL element-scoped workflows on every page (massive bloat).
2920
- const elementSelectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
2921
- if (isPage && elementSelectedPages && elementSelectedPages.length > 0) {
2922
- if (!elementSelectedPages.some((p) => p.id === pageId)) {
2923
- return
2924
- }
2925
- }
2926
-
2927
- relevant.push(wf)
2928
- return
2929
- }
2930
- })
2931
-
2932
- return relevant
2933
- }
@@ -98,6 +98,7 @@ import {
98
98
  import { rewriteLowStockCustomHandlers } from './ecommerce-customhandler-rewriter'
99
99
  import { assertWorkflowsAreSecure } from './security-scanner'
100
100
  import { parameterizeAllWorkflowRawSql } from './raw-sql-param-binding'
101
+ import { generateBookingCalendarFeedFiles } from './booking-calendar-feed-route-generator'
101
102
 
102
103
  // Resolves the auth IDENTITY (users) table from the UIDL auth tables. Getting
103
104
  // this wrong is a security hole (the /api/data guard only protects this table),
@@ -351,6 +352,10 @@ export class NextWorkflowProjectPlugin implements ProjectPlugin {
351
352
  this.generateAuthFiles(uidl.authentication, structure)
352
353
  }
353
354
 
355
+ // The bookings' calendar feeds do not depend on any workflow, so they are
356
+ // emitted before the early return for a project without workflows.
357
+ generateBookingCalendarFeedFiles(structure)
358
+
354
359
  if (!uidl.workflows || !uidl.workflows.workflows) {
355
360
  if (uidl.invoiceSettings?.enabled) {
356
361
  const { dataSourceType, dataSourceConfig } = resolveInvoiceDataSource(structure)
@@ -778,6 +783,7 @@ export class NextWorkflowProjectPlugin implements ProjectPlugin {
778
783
  authUsersTableName: resolveAuthUsersTableName(uidl.authentication),
779
784
  emailProvider,
780
785
  fromEmail: typeof deleteConfig.from === 'string' ? deleteConfig.from : '',
786
+ fromName: typeof deleteConfig.fromName === 'string' ? deleteConfig.fromName : '',
781
787
  emailSecretEnvName,
782
788
  emailSubject: typeof deleteConfig.subject === 'string' ? deleteConfig.subject : '',
783
789
  emailBodyHtml: typeof deleteConfig.body === 'string' ? deleteConfig.body : '',
@@ -1616,11 +1622,14 @@ async function customNode_${safeId}(outerContext, parameters, nodeHandlers) {
1616
1622
 
1617
1623
  // The custom node returns its last executed node's output. A fire-and-forget
1618
1624
  // node is excluded: it always stores \`null\`, so if it happens to be last it
1619
- // would turn the whole custom node's result into null for every caller.
1625
+ // would turn the whole custom node's result into null for every caller. So is
1626
+ // a node of a branch not taken: whatever a segment answered for it must never
1627
+ // stand in for the terminal of the branch that ran.
1620
1628
  var __returnableNodes = nodes
1621
1629
  .slice()
1622
1630
  .sort(function(a, b) { return a.stepNumber - b.stepNumber; })
1623
- .filter(function(n) { return !__utils.isFireAndForgetNode(n); });
1631
+ .filter(function(n) { return !__utils.isFireAndForgetNode(n); })
1632
+ .filter(function(n) { return !(context.__skippedNodes && context.__skippedNodes[n.id]); });
1624
1633
  var lastNodeId = null;
1625
1634
  for (var __ri = __returnableNodes.length - 1; __ri >= 0; __ri--) {
1626
1635
  if (context[__returnableNodes[__ri].id] !== undefined) { lastNodeId = __returnableNodes[__ri].id; break; }
@@ -1682,11 +1691,14 @@ async function customNode_${safeId}(outerContext, parameters, nodeHandlers) {
1682
1691
 
1683
1692
  // The custom node returns its last executed node's output. A fire-and-forget
1684
1693
  // node is excluded: it always stores \`null\`, so if it happens to be last it
1685
- // would turn the whole custom node's result into null for every caller.
1694
+ // would turn the whole custom node's result into null for every caller. So is
1695
+ // a node of a branch not taken: whatever a segment answered for it must never
1696
+ // stand in for the terminal of the branch that ran.
1686
1697
  var __returnableNodes = nodes
1687
1698
  .slice()
1688
1699
  .sort(function(a, b) { return a.stepNumber - b.stepNumber; })
1689
- .filter(function(n) { return !__utils.isFireAndForgetNode(n); });
1700
+ .filter(function(n) { return !__utils.isFireAndForgetNode(n); })
1701
+ .filter(function(n) { return !(context.__skippedNodes && context.__skippedNodes[n.id]); });
1690
1702
  var lastNodeId = null;
1691
1703
  for (var __ri = __returnableNodes.length - 1; __ri >= 0; __ri--) {
1692
1704
  if (context[__returnableNodes[__ri].id] !== undefined) { lastNodeId = __returnableNodes[__ri].id; break; }
@@ -1796,6 +1808,7 @@ module.exports = __customNodeRegistry;
1796
1808
  const signupRouteCode = generateSignupRouteFile(auth, {
1797
1809
  emailProvider: welcomeProvider,
1798
1810
  fromEmail: typeof signupConfig.from === 'string' ? signupConfig.from : '',
1811
+ fromName: typeof signupConfig.fromName === 'string' ? signupConfig.fromName : '',
1799
1812
  emailSecretEnvName: welcomeSecretEnvName,
1800
1813
  emailSubject: typeof signupConfig.subject === 'string' ? signupConfig.subject : '',
1801
1814
  emailBodyHtml: typeof signupConfig.body === 'string' ? signupConfig.body : '',
@@ -0,0 +1,184 @@
1
+ import { UIDLWorkflow, UIDLWorkflows } from '@teleporthq/teleport-types'
2
+
3
+ /**
4
+ * Which workflows belong to a page or a component, and which element a
5
+ * lifecycle trigger is bound to. Shared by the workflow plugin, which emits the
6
+ * workflows, and by the page-arrival check, which has to reach the same verdict
7
+ * for the components a page renders.
8
+ */
9
+
10
+ /**
11
+ * Trigger types that run as LIFECYCLE code (no React event prop) but are still
12
+ * bound to ONE element, so the workflow only belongs on the page that renders
13
+ * it. Returns the DOM id to prune against, or null when the trigger is not
14
+ * element-scoped.
15
+ *
16
+ * Same fallback chain as the element-trigger branch: the HTML id first, the
17
+ * project-document node id only as a last resort.
18
+ */
19
+ export const resolveLifecycleTriggerElementId = (
20
+ triggerType: string,
21
+ config: Record<string, unknown>
22
+ ): string | null => {
23
+ if (
24
+ triggerType !== 'event-element-visible' &&
25
+ triggerType !== 'event-chapter-reached' &&
26
+ triggerType !== 'event-scene-scrolled-past'
27
+ ) {
28
+ return null
29
+ }
30
+ return ((config.elementHtmlId || config.nodeId) as string) || null
31
+ }
32
+
33
+ // Component-scope triggers match by emitted component name. The GUI stamps the
34
+ // name at export time, but generators may re-case or strip spaces during
35
+ // resolution, so equality is checked case/separator-insensitively.
36
+ const normalizeComponentName = (name: string): string =>
37
+ (name || '').toLowerCase().replace(/[^a-z0-9]/g, '')
38
+
39
+ export const getRelevantWorkflows = (
40
+ workflows: UIDLWorkflows,
41
+ uidl: any,
42
+ isPage: boolean
43
+ ): UIDLWorkflow[] => {
44
+ const relevant: UIDLWorkflow[] = []
45
+ const pageId = uidl.outputOptions?.pageId || uidl.outputOptions?.fileName || uidl.name || ''
46
+
47
+ const allWorkflows = Object.values(workflows.workflows) as UIDLWorkflow[]
48
+ allWorkflows.forEach((wf: UIDLWorkflow) => {
49
+ const trigger = wf.trigger
50
+
51
+ if (trigger.type === 'event-cron-triggered') {
52
+ return
53
+ }
54
+
55
+ if (trigger.type === 'event-webhook-received') {
56
+ return
57
+ }
58
+
59
+ if (trigger.type === 'event-state-change') {
60
+ const cfg = trigger.config || {}
61
+ const stateDefs = cfg.stateDefinitions as
62
+ | Array<{
63
+ pageOrComponentId?: string
64
+ pageOrComponentName?: string
65
+ name?: string
66
+ stateDefinitionId?: string
67
+ }>
68
+ | undefined
69
+
70
+ let boundContainerType = cfg.boundContainerType as string | undefined
71
+ if (!boundContainerType) {
72
+ if (stateDefs?.some((s) => s.pageOrComponentName)) {
73
+ boundContainerType = 'component'
74
+ } else if (!isPage && stateDefs && stateDefs.length > 0) {
75
+ boundContainerType = 'component'
76
+ } else {
77
+ boundContainerType = 'page'
78
+ }
79
+ }
80
+
81
+ const matchesContainer = boundContainerType === 'page' ? isPage : !isPage
82
+ if (!matchesContainer) {
83
+ return
84
+ }
85
+
86
+ const boundFromConfig = cfg.boundPageOrComponentId as string | undefined
87
+ const matchesByName =
88
+ !!stateDefs &&
89
+ stateDefs.some((sd) => sd.pageOrComponentName && sd.pageOrComponentName === uidl.name)
90
+ const matchesById =
91
+ boundFromConfig === pageId ||
92
+ (!!stateDefs && stateDefs.some((sd) => sd.pageOrComponentId === pageId))
93
+
94
+ if (matchesByName || matchesById) {
95
+ relevant.push(wf)
96
+ }
97
+ return
98
+ }
99
+
100
+ if (trigger.type === 'event-global-state-change') {
101
+ if (!isPage) {
102
+ return
103
+ }
104
+ const selectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
105
+ if (
106
+ selectedPages &&
107
+ selectedPages.length > 0 &&
108
+ !selectedPages.some((p) => p.id === pageId)
109
+ ) {
110
+ return
111
+ }
112
+ relevant.push(wf)
113
+ return
114
+ }
115
+
116
+ if (trigger.scope === 'global') {
117
+ return
118
+ }
119
+
120
+ if (trigger.scope === 'component') {
121
+ if (isPage) {
122
+ return
123
+ }
124
+ const cfg = trigger.config || {}
125
+ const cfgName = (cfg.componentName as string) || ''
126
+ const cfgId = (cfg.componentId as string) || ''
127
+ const matches =
128
+ (cfgName &&
129
+ (cfgName === uidl.name ||
130
+ normalizeComponentName(cfgName) === normalizeComponentName(uidl.name))) ||
131
+ (cfgId && cfgId === pageId)
132
+ if (matches) {
133
+ relevant.push(wf)
134
+ }
135
+ return
136
+ }
137
+
138
+ if (trigger.scope === 'page' && isPage) {
139
+ const triggerPageId = trigger.config.pageId as string
140
+ const selectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
141
+
142
+ if (trigger.type === 'event-page-loaded') {
143
+ const allPages = trigger.config.allPages === true
144
+ if (allPages) {
145
+ relevant.push(wf)
146
+ return
147
+ }
148
+ // Unscoped page-loaded workflows are SKIPPED, not injected everywhere.
149
+ // Silently fanning out to every page masks mis-scoped workflows whose
150
+ // state target lives in a component (the setter no-ops on pages).
151
+ if (!triggerPageId && (!selectedPages || selectedPages.length === 0)) {
152
+ return
153
+ }
154
+ }
155
+
156
+ if (triggerPageId && triggerPageId !== pageId) {
157
+ return
158
+ }
159
+
160
+ if (selectedPages && !selectedPages.some((p) => p.id === pageId)) {
161
+ return
162
+ }
163
+
164
+ relevant.push(wf)
165
+ return
166
+ }
167
+
168
+ if (trigger.scope === 'element') {
169
+ // If the element-scoped trigger specifies which pages it belongs to, filter by page.
170
+ // This prevents including ALL element-scoped workflows on every page (massive bloat).
171
+ const elementSelectedPages = trigger.config.selectedPages as Array<{ id: string }> | undefined
172
+ if (isPage && elementSelectedPages && elementSelectedPages.length > 0) {
173
+ if (!elementSelectedPages.some((p) => p.id === pageId)) {
174
+ return
175
+ }
176
+ }
177
+
178
+ relevant.push(wf)
179
+ return
180
+ }
181
+ })
182
+
183
+ return relevant
184
+ }