@teleporthq/teleport-plugin-next-workflows 0.43.9 → 0.43.11

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 (249) hide show
  1. package/__tests__/account-node-output-contract.test.ts +69 -0
  2. package/__tests__/data-create-item-cart-mark-ordered.test.ts +30 -0
  3. package/__tests__/navigation-go-to-page.test.ts +34 -0
  4. package/__tests__/node-audit-fixes.test.ts +144 -0
  5. package/__tests__/runtime-trigger-element-roundtrip.test.ts +156 -0
  6. package/__tests__/state-update-default-value.test.ts +286 -0
  7. package/__tests__/transform-generate-output.test.ts +47 -0
  8. package/dist/cjs/executor-generator.d.ts.map +1 -1
  9. package/dist/cjs/executor-generator.js +72 -8
  10. package/dist/cjs/executor-generator.js.map +1 -1
  11. package/dist/cjs/nodes/account/account-compare-passwords.d.ts.map +1 -1
  12. package/dist/cjs/nodes/account/account-compare-passwords.js +3 -1
  13. package/dist/cjs/nodes/account/account-compare-passwords.js.map +1 -1
  14. package/dist/cjs/nodes/account/account-get-current.d.ts.map +1 -1
  15. package/dist/cjs/nodes/account/account-get-current.js +23 -4
  16. package/dist/cjs/nodes/account/account-get-current.js.map +1 -1
  17. package/dist/cjs/nodes/account/account-login.d.ts.map +1 -1
  18. package/dist/cjs/nodes/account/account-login.js +15 -5
  19. package/dist/cjs/nodes/account/account-login.js.map +1 -1
  20. package/dist/cjs/nodes/account/account-signup.d.ts.map +1 -1
  21. package/dist/cjs/nodes/account/account-signup.js +15 -5
  22. package/dist/cjs/nodes/account/account-signup.js.map +1 -1
  23. package/dist/cjs/nodes/ai/ai-provider-utils.d.ts.map +1 -1
  24. package/dist/cjs/nodes/ai/ai-provider-utils.js +24 -9
  25. package/dist/cjs/nodes/ai/ai-provider-utils.js.map +1 -1
  26. package/dist/cjs/nodes/data/data-create-item.d.ts.map +1 -1
  27. package/dist/cjs/nodes/data/data-create-item.js +15 -0
  28. package/dist/cjs/nodes/data/data-create-item.js.map +1 -1
  29. package/dist/cjs/nodes/element/element-add-class.d.ts.map +1 -1
  30. package/dist/cjs/nodes/element/element-add-class.js +14 -5
  31. package/dist/cjs/nodes/element/element-add-class.js.map +1 -1
  32. package/dist/cjs/nodes/element/element-remove-class.d.ts.map +1 -1
  33. package/dist/cjs/nodes/element/element-remove-class.js +14 -5
  34. package/dist/cjs/nodes/element/element-remove-class.js.map +1 -1
  35. package/dist/cjs/nodes/element/element-set-attribute.d.ts.map +1 -1
  36. package/dist/cjs/nodes/element/element-set-attribute.js +11 -1
  37. package/dist/cjs/nodes/element/element-set-attribute.js.map +1 -1
  38. package/dist/cjs/nodes/element/element-toggle-class.d.ts.map +1 -1
  39. package/dist/cjs/nodes/element/element-toggle-class.js +20 -4
  40. package/dist/cjs/nodes/element/element-toggle-class.js.map +1 -1
  41. package/dist/cjs/nodes/email/email-resend.d.ts.map +1 -1
  42. package/dist/cjs/nodes/email/email-resend.js +3 -1
  43. package/dist/cjs/nodes/email/email-resend.js.map +1 -1
  44. package/dist/cjs/nodes/integrations/integration-amazon-s3.d.ts.map +1 -1
  45. package/dist/cjs/nodes/integrations/integration-amazon-s3.js +21 -5
  46. package/dist/cjs/nodes/integrations/integration-amazon-s3.js.map +1 -1
  47. package/dist/cjs/nodes/integrations/integration-amplitude.d.ts.map +1 -1
  48. package/dist/cjs/nodes/integrations/integration-amplitude.js +5 -3
  49. package/dist/cjs/nodes/integrations/integration-amplitude.js.map +1 -1
  50. package/dist/cjs/nodes/integrations/integration-discord.d.ts.map +1 -1
  51. package/dist/cjs/nodes/integrations/integration-discord.js +4 -1
  52. package/dist/cjs/nodes/integrations/integration-discord.js.map +1 -1
  53. package/dist/cjs/nodes/integrations/integration-generic.d.ts +1 -1
  54. package/dist/cjs/nodes/integrations/integration-generic.d.ts.map +1 -1
  55. package/dist/cjs/nodes/integrations/integration-generic.js +7 -2
  56. package/dist/cjs/nodes/integrations/integration-generic.js.map +1 -1
  57. package/dist/cjs/nodes/integrations/integration-greenhouse.d.ts.map +1 -1
  58. package/dist/cjs/nodes/integrations/integration-greenhouse.js +16 -2
  59. package/dist/cjs/nodes/integrations/integration-greenhouse.js.map +1 -1
  60. package/dist/cjs/nodes/integrations/integration-linear.d.ts.map +1 -1
  61. package/dist/cjs/nodes/integrations/integration-linear.js +3 -1
  62. package/dist/cjs/nodes/integrations/integration-linear.js.map +1 -1
  63. package/dist/cjs/nodes/integrations/integration-mailchimp.d.ts.map +1 -1
  64. package/dist/cjs/nodes/integrations/integration-mailchimp.js +3 -0
  65. package/dist/cjs/nodes/integrations/integration-mailchimp.js.map +1 -1
  66. package/dist/cjs/nodes/integrations/integration-monday.d.ts.map +1 -1
  67. package/dist/cjs/nodes/integrations/integration-monday.js +8 -0
  68. package/dist/cjs/nodes/integrations/integration-monday.js.map +1 -1
  69. package/dist/cjs/nodes/integrations/integration-segment.d.ts.map +1 -1
  70. package/dist/cjs/nodes/integrations/integration-segment.js +3 -1
  71. package/dist/cjs/nodes/integrations/integration-segment.js.map +1 -1
  72. package/dist/cjs/nodes/integrations/integration-slack.d.ts.map +1 -1
  73. package/dist/cjs/nodes/integrations/integration-slack.js +17 -6
  74. package/dist/cjs/nodes/integrations/integration-slack.js.map +1 -1
  75. package/dist/cjs/nodes/integrations/integration-woocommerce.d.ts.map +1 -1
  76. package/dist/cjs/nodes/integrations/integration-woocommerce.js +7 -1
  77. package/dist/cjs/nodes/integrations/integration-woocommerce.js.map +1 -1
  78. package/dist/cjs/nodes/integrations/integration-youtube.js +7 -7
  79. package/dist/cjs/nodes/integrations/integration-youtube.js.map +1 -1
  80. package/dist/cjs/nodes/navigation/navigation-go-to-page.d.ts.map +1 -1
  81. package/dist/cjs/nodes/navigation/navigation-go-to-page.js +18 -4
  82. package/dist/cjs/nodes/navigation/navigation-go-to-page.js.map +1 -1
  83. package/dist/cjs/nodes/payment/payment-create-customer.d.ts.map +1 -1
  84. package/dist/cjs/nodes/payment/payment-create-customer.js +14 -1
  85. package/dist/cjs/nodes/payment/payment-create-customer.js.map +1 -1
  86. package/dist/cjs/nodes/sms/sms-infobip.d.ts.map +1 -1
  87. package/dist/cjs/nodes/sms/sms-infobip.js +3 -1
  88. package/dist/cjs/nodes/sms/sms-infobip.js.map +1 -1
  89. package/dist/cjs/nodes/toast/toast-show.js +1 -1
  90. package/dist/cjs/nodes/transform/transform-date-time.d.ts.map +1 -1
  91. package/dist/cjs/nodes/transform/transform-date-time.js +1 -0
  92. package/dist/cjs/nodes/transform/transform-date-time.js.map +1 -1
  93. package/dist/cjs/nodes/transform/transform-generate.d.ts.map +1 -1
  94. package/dist/cjs/nodes/transform/transform-generate.js +6 -3
  95. package/dist/cjs/nodes/transform/transform-generate.js.map +1 -1
  96. package/dist/cjs/nodes/transform/transform-validate.d.ts.map +1 -1
  97. package/dist/cjs/nodes/transform/transform-validate.js +1 -2
  98. package/dist/cjs/nodes/transform/transform-validate.js.map +1 -1
  99. package/dist/cjs/nodes/utility/utility-scrape-website.d.ts.map +1 -1
  100. package/dist/cjs/nodes/utility/utility-scrape-website.js +11 -2
  101. package/dist/cjs/nodes/utility/utility-scrape-website.js.map +1 -1
  102. package/dist/cjs/nodes/utility/utility-xml-parse.d.ts.map +1 -1
  103. package/dist/cjs/nodes/utility/utility-xml-parse.js +11 -2
  104. package/dist/cjs/nodes/utility/utility-xml-parse.js.map +1 -1
  105. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  106. package/dist/cjs/workflow-component-plugin.js +23 -4
  107. package/dist/cjs/workflow-component-plugin.js.map +1 -1
  108. package/dist/cjs/workflow-project-plugin.d.ts.map +1 -1
  109. package/dist/cjs/workflow-project-plugin.js +15 -10
  110. package/dist/cjs/workflow-project-plugin.js.map +1 -1
  111. package/dist/esm/executor-generator.d.ts.map +1 -1
  112. package/dist/esm/executor-generator.js +72 -8
  113. package/dist/esm/executor-generator.js.map +1 -1
  114. package/dist/esm/nodes/account/account-compare-passwords.d.ts.map +1 -1
  115. package/dist/esm/nodes/account/account-compare-passwords.js +3 -1
  116. package/dist/esm/nodes/account/account-compare-passwords.js.map +1 -1
  117. package/dist/esm/nodes/account/account-get-current.d.ts.map +1 -1
  118. package/dist/esm/nodes/account/account-get-current.js +23 -4
  119. package/dist/esm/nodes/account/account-get-current.js.map +1 -1
  120. package/dist/esm/nodes/account/account-login.d.ts.map +1 -1
  121. package/dist/esm/nodes/account/account-login.js +15 -5
  122. package/dist/esm/nodes/account/account-login.js.map +1 -1
  123. package/dist/esm/nodes/account/account-signup.d.ts.map +1 -1
  124. package/dist/esm/nodes/account/account-signup.js +15 -5
  125. package/dist/esm/nodes/account/account-signup.js.map +1 -1
  126. package/dist/esm/nodes/ai/ai-provider-utils.d.ts.map +1 -1
  127. package/dist/esm/nodes/ai/ai-provider-utils.js +24 -9
  128. package/dist/esm/nodes/ai/ai-provider-utils.js.map +1 -1
  129. package/dist/esm/nodes/data/data-create-item.d.ts.map +1 -1
  130. package/dist/esm/nodes/data/data-create-item.js +15 -0
  131. package/dist/esm/nodes/data/data-create-item.js.map +1 -1
  132. package/dist/esm/nodes/element/element-add-class.d.ts.map +1 -1
  133. package/dist/esm/nodes/element/element-add-class.js +14 -5
  134. package/dist/esm/nodes/element/element-add-class.js.map +1 -1
  135. package/dist/esm/nodes/element/element-remove-class.d.ts.map +1 -1
  136. package/dist/esm/nodes/element/element-remove-class.js +14 -5
  137. package/dist/esm/nodes/element/element-remove-class.js.map +1 -1
  138. package/dist/esm/nodes/element/element-set-attribute.d.ts.map +1 -1
  139. package/dist/esm/nodes/element/element-set-attribute.js +11 -1
  140. package/dist/esm/nodes/element/element-set-attribute.js.map +1 -1
  141. package/dist/esm/nodes/element/element-toggle-class.d.ts.map +1 -1
  142. package/dist/esm/nodes/element/element-toggle-class.js +20 -4
  143. package/dist/esm/nodes/element/element-toggle-class.js.map +1 -1
  144. package/dist/esm/nodes/email/email-resend.d.ts.map +1 -1
  145. package/dist/esm/nodes/email/email-resend.js +3 -1
  146. package/dist/esm/nodes/email/email-resend.js.map +1 -1
  147. package/dist/esm/nodes/integrations/integration-amazon-s3.d.ts.map +1 -1
  148. package/dist/esm/nodes/integrations/integration-amazon-s3.js +21 -5
  149. package/dist/esm/nodes/integrations/integration-amazon-s3.js.map +1 -1
  150. package/dist/esm/nodes/integrations/integration-amplitude.d.ts.map +1 -1
  151. package/dist/esm/nodes/integrations/integration-amplitude.js +5 -3
  152. package/dist/esm/nodes/integrations/integration-amplitude.js.map +1 -1
  153. package/dist/esm/nodes/integrations/integration-discord.d.ts.map +1 -1
  154. package/dist/esm/nodes/integrations/integration-discord.js +4 -1
  155. package/dist/esm/nodes/integrations/integration-discord.js.map +1 -1
  156. package/dist/esm/nodes/integrations/integration-generic.d.ts +1 -1
  157. package/dist/esm/nodes/integrations/integration-generic.d.ts.map +1 -1
  158. package/dist/esm/nodes/integrations/integration-generic.js +7 -2
  159. package/dist/esm/nodes/integrations/integration-generic.js.map +1 -1
  160. package/dist/esm/nodes/integrations/integration-greenhouse.d.ts.map +1 -1
  161. package/dist/esm/nodes/integrations/integration-greenhouse.js +16 -2
  162. package/dist/esm/nodes/integrations/integration-greenhouse.js.map +1 -1
  163. package/dist/esm/nodes/integrations/integration-linear.d.ts.map +1 -1
  164. package/dist/esm/nodes/integrations/integration-linear.js +3 -1
  165. package/dist/esm/nodes/integrations/integration-linear.js.map +1 -1
  166. package/dist/esm/nodes/integrations/integration-mailchimp.d.ts.map +1 -1
  167. package/dist/esm/nodes/integrations/integration-mailchimp.js +3 -0
  168. package/dist/esm/nodes/integrations/integration-mailchimp.js.map +1 -1
  169. package/dist/esm/nodes/integrations/integration-monday.d.ts.map +1 -1
  170. package/dist/esm/nodes/integrations/integration-monday.js +8 -0
  171. package/dist/esm/nodes/integrations/integration-monday.js.map +1 -1
  172. package/dist/esm/nodes/integrations/integration-segment.d.ts.map +1 -1
  173. package/dist/esm/nodes/integrations/integration-segment.js +3 -1
  174. package/dist/esm/nodes/integrations/integration-segment.js.map +1 -1
  175. package/dist/esm/nodes/integrations/integration-slack.d.ts.map +1 -1
  176. package/dist/esm/nodes/integrations/integration-slack.js +17 -6
  177. package/dist/esm/nodes/integrations/integration-slack.js.map +1 -1
  178. package/dist/esm/nodes/integrations/integration-woocommerce.d.ts.map +1 -1
  179. package/dist/esm/nodes/integrations/integration-woocommerce.js +7 -1
  180. package/dist/esm/nodes/integrations/integration-woocommerce.js.map +1 -1
  181. package/dist/esm/nodes/integrations/integration-youtube.js +7 -7
  182. package/dist/esm/nodes/integrations/integration-youtube.js.map +1 -1
  183. package/dist/esm/nodes/navigation/navigation-go-to-page.d.ts.map +1 -1
  184. package/dist/esm/nodes/navigation/navigation-go-to-page.js +18 -4
  185. package/dist/esm/nodes/navigation/navigation-go-to-page.js.map +1 -1
  186. package/dist/esm/nodes/payment/payment-create-customer.d.ts.map +1 -1
  187. package/dist/esm/nodes/payment/payment-create-customer.js +14 -1
  188. package/dist/esm/nodes/payment/payment-create-customer.js.map +1 -1
  189. package/dist/esm/nodes/sms/sms-infobip.d.ts.map +1 -1
  190. package/dist/esm/nodes/sms/sms-infobip.js +3 -1
  191. package/dist/esm/nodes/sms/sms-infobip.js.map +1 -1
  192. package/dist/esm/nodes/toast/toast-show.js +1 -1
  193. package/dist/esm/nodes/transform/transform-date-time.d.ts.map +1 -1
  194. package/dist/esm/nodes/transform/transform-date-time.js +1 -0
  195. package/dist/esm/nodes/transform/transform-date-time.js.map +1 -1
  196. package/dist/esm/nodes/transform/transform-generate.d.ts.map +1 -1
  197. package/dist/esm/nodes/transform/transform-generate.js +6 -3
  198. package/dist/esm/nodes/transform/transform-generate.js.map +1 -1
  199. package/dist/esm/nodes/transform/transform-validate.d.ts.map +1 -1
  200. package/dist/esm/nodes/transform/transform-validate.js +1 -2
  201. package/dist/esm/nodes/transform/transform-validate.js.map +1 -1
  202. package/dist/esm/nodes/utility/utility-scrape-website.d.ts.map +1 -1
  203. package/dist/esm/nodes/utility/utility-scrape-website.js +11 -2
  204. package/dist/esm/nodes/utility/utility-scrape-website.js.map +1 -1
  205. package/dist/esm/nodes/utility/utility-xml-parse.d.ts.map +1 -1
  206. package/dist/esm/nodes/utility/utility-xml-parse.js +11 -2
  207. package/dist/esm/nodes/utility/utility-xml-parse.js.map +1 -1
  208. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  209. package/dist/esm/workflow-component-plugin.js +23 -4
  210. package/dist/esm/workflow-component-plugin.js.map +1 -1
  211. package/dist/esm/workflow-project-plugin.d.ts.map +1 -1
  212. package/dist/esm/workflow-project-plugin.js +15 -10
  213. package/dist/esm/workflow-project-plugin.js.map +1 -1
  214. package/package.json +2 -2
  215. package/src/executor-generator.ts +72 -8
  216. package/src/nodes/account/account-compare-passwords.ts +3 -1
  217. package/src/nodes/account/account-get-current.ts +23 -4
  218. package/src/nodes/account/account-login.ts +15 -5
  219. package/src/nodes/account/account-signup.ts +15 -5
  220. package/src/nodes/ai/ai-provider-utils.ts +26 -9
  221. package/src/nodes/data/data-create-item.ts +15 -0
  222. package/src/nodes/element/element-add-class.ts +14 -5
  223. package/src/nodes/element/element-remove-class.ts +14 -5
  224. package/src/nodes/element/element-set-attribute.ts +10 -1
  225. package/src/nodes/element/element-toggle-class.ts +19 -4
  226. package/src/nodes/email/email-resend.ts +3 -1
  227. package/src/nodes/integrations/integration-amazon-s3.ts +21 -5
  228. package/src/nodes/integrations/integration-amplitude.ts +5 -3
  229. package/src/nodes/integrations/integration-discord.ts +4 -1
  230. package/src/nodes/integrations/integration-generic.ts +9 -2
  231. package/src/nodes/integrations/integration-greenhouse.ts +16 -2
  232. package/src/nodes/integrations/integration-linear.ts +3 -1
  233. package/src/nodes/integrations/integration-mailchimp.ts +3 -0
  234. package/src/nodes/integrations/integration-monday.ts +8 -0
  235. package/src/nodes/integrations/integration-segment.ts +3 -1
  236. package/src/nodes/integrations/integration-slack.ts +17 -6
  237. package/src/nodes/integrations/integration-woocommerce.ts +7 -1
  238. package/src/nodes/integrations/integration-youtube.ts +7 -7
  239. package/src/nodes/navigation/navigation-go-to-page.ts +16 -4
  240. package/src/nodes/payment/payment-create-customer.ts +13 -1
  241. package/src/nodes/sms/sms-infobip.ts +3 -1
  242. package/src/nodes/toast/toast-show.ts +1 -1
  243. package/src/nodes/transform/transform-date-time.ts +1 -0
  244. package/src/nodes/transform/transform-generate.ts +6 -3
  245. package/src/nodes/transform/transform-validate.ts +1 -2
  246. package/src/nodes/utility/utility-scrape-website.ts +11 -3
  247. package/src/nodes/utility/utility-xml-parse.ts +11 -3
  248. package/src/workflow-component-plugin.ts +23 -4
  249. package/src/workflow-project-plugin.ts +12 -7
@@ -0,0 +1,69 @@
1
+ /* tslint:disable:no-eval */
2
+ import { accountGetCurrent } from '../src/nodes/account/account-get-current'
3
+ import { accountLogin } from '../src/nodes/account/account-login'
4
+ import { accountSignup } from '../src/nodes/account/account-signup'
5
+ import { accountComparePasswords } from '../src/nodes/account/account-compare-passwords'
6
+
7
+ // Regression: these account nodes returned the user NESTED under `.user`, but
8
+ // their declared output contract (node-context-schemas) and the workflow
9
+ // builders read the user's fields FLAT (e.g. wfCtx(node, ['id']) for a row's
10
+ // user_id in follow/like/loyalty/review inserts). Nested-only output meant
11
+ // `user_id` resolved to undefined. The handlers must expose the fields flat
12
+ // (and keep `.user`). account-compare-passwords emitted `isMatch` but the
13
+ // delete-account template read `.match`.
14
+
15
+ function evalHandler(code: string): any {
16
+ return eval('(' + code + ')')
17
+ }
18
+
19
+ describe('account node output contracts (flat user fields)', () => {
20
+ it('account-get-current exposes user fields flat AND keeps .user', async () => {
21
+ const handler = evalHandler(accountGetCurrent.generateHandler())
22
+ const fakeUser = { id: 'u-123', email: 'a@b.com', name: 'Ada', image: 'x.png' }
23
+ ;(global as any).fetch = async () => ({ ok: true, json: async () => ({ user: fakeUser }) })
24
+ ;(global as any).window = undefined
25
+ try {
26
+ const out = await handler({}, {})
27
+ expect(out.id).toBe('u-123')
28
+ expect(out.email).toBe('a@b.com')
29
+ expect(out.name).toBe('Ada')
30
+ expect(out.user).toEqual(fakeUser) // nested still works
31
+ } finally {
32
+ delete (global as any).fetch
33
+ }
34
+ })
35
+
36
+ it('account-get-current with no user returns user:null and no flat fields', async () => {
37
+ const handler = evalHandler(accountGetCurrent.generateHandler())
38
+ ;(global as any).fetch = async () => ({ ok: true, json: async () => ({ user: null }) })
39
+ try {
40
+ const out = await handler({}, {})
41
+ expect(out.user).toBeNull()
42
+ expect(out.id).toBeUndefined()
43
+ } finally {
44
+ delete (global as any).fetch
45
+ }
46
+ })
47
+
48
+ it('account-login / account-signup client handlers expose flat fields + user + success', async () => {
49
+ for (const gen of [accountLogin, accountSignup]) {
50
+ const code = gen.generateHandler()
51
+ // Both copy the user fields onto a fresh object then set user + success
52
+ // (no Object.assign/spread — would down-level to a missing tslib helper).
53
+ expect(code).toContain('__out.user = user || null')
54
+ expect(code).toContain('__out.success = true')
55
+ }
56
+ })
57
+
58
+ it('account-login / account-signup SERVER handlers also flatten the sanitized user', () => {
59
+ for (const gen of [accountLogin, accountSignup]) {
60
+ const code = (gen as any).generateServerHandler()
61
+ expect(code).toContain('Object.assign({}, __su || {}, { user: __su, success: true })')
62
+ }
63
+ })
64
+
65
+ it('account-compare-passwords emits both isMatch (contract) and match (alias)', () => {
66
+ const code = accountComparePasswords.generateHandler()
67
+ expect(code).toContain('isMatch, match: isMatch')
68
+ })
69
+ })
@@ -0,0 +1,30 @@
1
+ import { dataCreateItem } from '../src/nodes/data/data-create-item'
2
+
3
+ // When the place-order workflow inserts a teleport_orders row, the
4
+ // data-create-item node must also retire the buyer's active database cart via
5
+ // a fire-and-forget POST to /api/cart/mark-ordered. This covers the AI
6
+ // place-order path that bypasses /api/ecommerce/checkout. It is keyed by the
7
+ // order's owner id (the real user_id when logged in, else the guest anon id)
8
+ // because the server-to-server call carries no auth cookies. It must never
9
+ // affect the placed order.
10
+
11
+ describe('data-create-item — marks the database cart ordered on order insert', () => {
12
+ const handler = dataCreateItem.generateHandler()
13
+
14
+ it('fires a mark-ordered call only for teleport_orders inserts', () => {
15
+ expect(handler).toContain('/api/cart/mark-ordered')
16
+ // Inside the orders-only branch.
17
+ const ordersBranch = handler.indexOf("tableName === 'teleport_orders'")
18
+ const markCall = handler.indexOf('/api/cart/mark-ordered')
19
+ expect(ordersBranch).toBeGreaterThan(-1)
20
+ expect(markCall).toBeGreaterThan(ordersBranch)
21
+ })
22
+
23
+ it('keys by the order owner id (user_id or guest anon id) and is fire-and-forget', () => {
24
+ expect(handler).toContain('item.user_id')
25
+ expect(handler).toContain('__anonymousUserId')
26
+ expect(handler).toContain('anonymousUserId: __orderOwnerId')
27
+ // Fire-and-forget + guarded so it never breaks the order.
28
+ expect(handler).toMatch(/mark-ordered[\s\S]*\.catch\(/)
29
+ })
30
+ })
@@ -90,6 +90,40 @@ describe('navigation-go-to-page', () => {
90
90
  )
91
91
  expect(win.location.href).toBe('/about-us')
92
92
  })
93
+
94
+ // Regression: a guest clicking "add to favourites" was sent to
95
+ // `/TQ_oiC1MlnMiO` (404) instead of `/sign-in`. The auth sign-in page was
96
+ // missing from the route map, so runBefore left `pageId` as the raw page
97
+ // id. The runtime must recognise that a non-route pageId can't be a real
98
+ // URL and fall back to the mapper's staticUrl.
99
+ it('falls back to targetPage.staticUrl when pageId is an unresolved page id (not a route)', async () => {
100
+ await handler(
101
+ {
102
+ pageId: 'TQ_oiC1MlnMiO',
103
+ openInNewTab: false,
104
+ targetPage: {
105
+ pageId: 'TQ_oiC1MlnMiO',
106
+ pageName: 'sign-in',
107
+ staticUrl: '/sign-in',
108
+ isDetailsPage: false,
109
+ },
110
+ },
111
+ {}
112
+ )
113
+ expect(win.location.href).toBe('/sign-in')
114
+ })
115
+
116
+ it('still uses a raw pageId as a last resort when no staticUrl is available', async () => {
117
+ await handler(
118
+ {
119
+ pageId: 'TQ_oiC1MlnMiO',
120
+ openInNewTab: false,
121
+ targetPage: { pageId: 'TQ_oiC1MlnMiO', isDetailsPage: false },
122
+ },
123
+ {}
124
+ )
125
+ expect(win.location.href).toBe('TQ_oiC1MlnMiO')
126
+ })
93
127
  })
94
128
 
95
129
  describe('details page differentiator', () => {
@@ -0,0 +1,144 @@
1
+ /* tslint:disable:no-eval */
2
+ import { transformValidate } from '../src/nodes/transform/transform-validate'
3
+ import { elementAddClass } from '../src/nodes/element/element-add-class'
4
+ import { elementToggleClass } from '../src/nodes/element/element-toggle-class'
5
+ import { integrationMailchimp } from '../src/nodes/integrations/integration-mailchimp'
6
+ import { integrationSegment } from '../src/nodes/integrations/integration-segment'
7
+ import { integrationSlack } from '../src/nodes/integrations/integration-slack'
8
+ import { integrationYoutube } from '../src/nodes/integrations/integration-youtube'
9
+ import { integrationWoocommerce } from '../src/nodes/integrations/integration-woocommerce'
10
+ import { integrationAmazonS3 } from '../src/nodes/integrations/integration-amazon-s3'
11
+ import { integrationGreenhouse } from '../src/nodes/integrations/integration-greenhouse'
12
+ import { createGenericIntegration } from '../src/nodes/integrations/integration-generic'
13
+ import { transformDateTime } from '../src/nodes/transform/transform-date-time'
14
+
15
+ // Regression guards for the 26 bugs found by the full node audit. Functional
16
+ // where the fix is logic (eval the emitted handler and run it); string-level
17
+ // where the fix is an API contract embedded in the generated source.
18
+
19
+ function evalHandler(code: string): any {
20
+ return eval('(' + code + ')')
21
+ }
22
+
23
+ describe('node-audit fixes — functional', () => {
24
+ it('transform-validate accepts valid emails and rejects invalid (regex no longer double-escaped)', async () => {
25
+ const handler = evalHandler(transformValidate.generateHandler())
26
+ const cfg = {
27
+ input: { a: 'john@example.com', b: 'nope' },
28
+ rules: [
29
+ { field: 'a', rule: 'email' },
30
+ { field: 'b', rule: 'email' },
31
+ ],
32
+ }
33
+ const res = await handler(cfg, {})
34
+ // field a is valid, field b is invalid → exactly one error, for field b.
35
+ expect(res.isValid).toBe(false)
36
+ expect(res.errors.length).toBe(1)
37
+ // And a fully-valid set passes.
38
+ const ok = await handler(
39
+ { input: { a: 'jane.doe@sub.example.co' }, rules: [{ field: 'a', rule: 'email' }] },
40
+ {}
41
+ )
42
+ expect(ok.isValid).toBe(true)
43
+ })
44
+
45
+ it('element-add-class handles a multi-class string and empty value without throwing', async () => {
46
+ const added: string[] = []
47
+ const el = {
48
+ classList: {
49
+ add: (c: string) => {
50
+ if (!c || /\s/.test(c)) throw new Error('InvalidCharacterError')
51
+ added.push(c)
52
+ },
53
+ },
54
+ }
55
+ ;(global as any).document = { getElementById: () => el }
56
+ try {
57
+ const handler = evalHandler(elementAddClass.generateHandler())
58
+ const res = await handler({ nodeId: 'x', className: 'btn active' }, {})
59
+ expect(res).toEqual({ success: true })
60
+ expect(added).toEqual(['btn', 'active'])
61
+ // Empty className must not throw.
62
+ const res2 = await handler({ nodeId: 'x', className: '' }, {})
63
+ expect(res2).toEqual({ success: true })
64
+ } finally {
65
+ delete (global as any).document
66
+ }
67
+ })
68
+
69
+ it('element-toggle-class splits a multi-class string instead of throwing', async () => {
70
+ const toggled: string[] = []
71
+ const el = {
72
+ classList: {
73
+ toggle: (c: string) => {
74
+ if (!c || /\s/.test(c)) throw new Error('InvalidCharacterError')
75
+ toggled.push(c)
76
+ },
77
+ },
78
+ }
79
+ ;(global as any).document = { getElementById: () => el }
80
+ try {
81
+ const handler = evalHandler(elementToggleClass.generateHandler())
82
+ const res = await handler({ nodeId: 'x', className: 'is-open active' }, {})
83
+ expect(res).toEqual({ success: true })
84
+ expect(toggled).toEqual(['is-open', 'active'])
85
+ } finally {
86
+ delete (global as any).document
87
+ }
88
+ })
89
+
90
+ it('transform-date-time is-weekend operation is now reachable', () => {
91
+ const code = transformDateTime.generateHandler()
92
+ expect(code).toContain("case 'is-weekend':")
93
+ })
94
+ })
95
+
96
+ describe('node-audit fixes — emitted API contracts', () => {
97
+ it('mailchimp guards a missing apiKey before calling .split', () => {
98
+ const code = integrationMailchimp.generateHandler()
99
+ expect(code).toMatch(/Mailchimp API key is not configured/)
100
+ // the guard precedes the .split('-') call
101
+ expect(code.indexOf('is not configured')).toBeLessThan(code.indexOf(".split('-')"))
102
+ })
103
+
104
+ it('segment get-profile uses Basic auth, not the wrong "Segment " scheme', () => {
105
+ const code = integrationSegment.generateHandler()
106
+ expect(code).toContain("'Basic ' + btoa(config.personasToken")
107
+ expect(code).not.toContain("'Segment ' + config.personasToken")
108
+ })
109
+
110
+ it('slack files.upload sends form-urlencoded, not JSON', () => {
111
+ const code = integrationSlack.generateHandler()
112
+ expect(code).toContain('application/x-www-form-urlencoded')
113
+ expect(code).toContain('files.upload')
114
+ })
115
+
116
+ it('youtube uses authParam (OAuth or key) on every action, not hardcoded key', () => {
117
+ const code = integrationYoutube.generateHandler()
118
+ expect(code).not.toContain("?key=' + apiKey")
119
+ })
120
+
121
+ it('woocommerce guards a missing storeUrl and honours explicit force=false', () => {
122
+ const code = integrationWoocommerce.generateHandler()
123
+ expect(code).toContain('storeUrl is not configured')
124
+ expect(code).toContain('config.force === false ? false : true')
125
+ })
126
+
127
+ it('amazon-s3 set-acl signs acl= (SigV4 key=value form)', () => {
128
+ const code = integrationAmazonS3.generateHandler()
129
+ expect(code).toContain("'acl='")
130
+ })
131
+
132
+ it('greenhouse write actions attach the On-Behalf-Of header', () => {
133
+ const code = integrationGreenhouse.generateHandler()
134
+ expect(code).toContain('On-Behalf-Of')
135
+ })
136
+
137
+ it('generic integration supports a raw (non-Bearer) auth scheme', () => {
138
+ const bearer = createGenericIntegration('integration-x').generateHandler()
139
+ const raw = createGenericIntegration('integration-x', 'raw').generateHandler()
140
+ expect(bearer).toContain("'Bearer ' + tok")
141
+ expect(raw).not.toContain("'Bearer ' + tok")
142
+ expect(raw).toContain("'' + tok")
143
+ })
144
+ })
@@ -0,0 +1,156 @@
1
+ import { generateClientRuntimeCode } from '../src/executor-generator'
2
+
3
+ // Regression guard for the "/cart + button does nothing in production" bug.
4
+ //
5
+ // A workflow that mixes a server segment (stock check, which filters a product
6
+ // by `trigger.element.dataset.productId`) with a client segment (cart-update,
7
+ // which reads `trigger.element.dataset.cartItemId`) used to break on deploy:
8
+ //
9
+ // 1. `pruneContext` could not serialize the live DOM trigger element, so it
10
+ // crossed to the server as `{ __serializationError: true }`. The server's
11
+ // data-select then filtered on an undefined productId and matched the
12
+ // wrong product.
13
+ // 2. The server echoed that `{ __serializationError: true }` placeholder
14
+ // back, and `Object.assign(context, serverResults)` overwrote the
15
+ // client's REAL trigger element with it — so the subsequent client
16
+ // cart-update read `undefined` for cartItemId and silently no-opped.
17
+ //
18
+ // The fix makes `pruneContext` emit a serializable DOM snapshot (carrying the
19
+ // dataset) and `mergeServerResults` keep the client's authoritative live
20
+ // element. These tests lock both halves in.
21
+
22
+ type Fn = (...args: unknown[]) => unknown
23
+
24
+ function extractFn(name: string, returnExpr = name): Fn {
25
+ const src = generateClientRuntimeCode()
26
+ // Each helper is generated as `function <name>(...) { ... }\n}` — grab it up
27
+ // to the closing brace on its own line, then eval to expose it. Same
28
+ // approach as runtime-absolutize-url.test.ts.
29
+ const re = new RegExp(`function ${name}\\b[\\s\\S]*?\\n\\}`)
30
+ const match = src.match(re)
31
+ if (!match) {
32
+ throw new Error(`${name} not found in generated runtime`)
33
+ }
34
+ // pruneContext depends on isDomNode/snapshotDomNode/domSerializationReplacer;
35
+ // mergeServerResults depends on isDomNode. Pull those in too when present.
36
+ const deps = ['isDomNode', 'snapshotDomNode', 'domSerializationReplacer']
37
+ .filter((d) => d !== name)
38
+ .map((d) => {
39
+ const m = src.match(new RegExp(`function ${d}\\b[\\s\\S]*?\\n\\}`))
40
+ return m ? m[0] : ''
41
+ })
42
+ .join('\n')
43
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
44
+ return new Function(`${deps}\n${match[0]}\nreturn ${returnExpr};`)() as Fn
45
+ }
46
+
47
+ // Minimal stand-in for a DOM element with a dataset (jsdom is not loaded here).
48
+ function fakeButton(dataset: Record<string, string>) {
49
+ return {
50
+ nodeType: 1,
51
+ tagName: 'BUTTON',
52
+ id: 'inc-btn',
53
+ name: '',
54
+ type: 'button',
55
+ className: 'qty-up',
56
+ dataset,
57
+ // Live DOM method that must NOT survive the network — its presence proves
58
+ // we kept the real element rather than a frozen snapshot.
59
+ closest: () => 'real-element',
60
+ }
61
+ }
62
+
63
+ describe('trigger element survives the server-segment round-trip', () => {
64
+ const pruneContext = extractFn('pruneContext') as (
65
+ ctx: Record<string, unknown>
66
+ ) => Record<string, any>
67
+ const mergeServerResults = extractFn('mergeServerResults') as (
68
+ ctx: Record<string, unknown>,
69
+ results: Record<string, unknown>,
70
+ triggerNodeId?: string
71
+ ) => void
72
+
73
+ const TRIGGER_ID = 'c0bc751c-250c-41b0-b4db-0adbd2867574'
74
+
75
+ it('pruneContext keeps the trigger element dataset (no __serializationError)', () => {
76
+ const el = fakeButton({
77
+ productId: '1117ddda-a771-4198-80e0-45fd3e7babe0',
78
+ cartItemId: 'cart_1780265668470_nwqtc3wtm',
79
+ currentQuantity: '1',
80
+ })
81
+ const context = {
82
+ [TRIGGER_ID]: { element: el, triggerElement: el },
83
+ triggerElement: el,
84
+ __baseUrl: 'https://example.teleporthq.dev',
85
+ }
86
+
87
+ const pruned = pruneContext(context)
88
+
89
+ // The pruned trigger node must carry the dataset the server filters on —
90
+ // not the old { __serializationError: true } placeholder.
91
+ expect(pruned[TRIGGER_ID].__serializationError).toBeUndefined()
92
+ expect(pruned[TRIGGER_ID].element.dataset.productId).toBe(
93
+ '1117ddda-a771-4198-80e0-45fd3e7babe0'
94
+ )
95
+ expect(pruned[TRIGGER_ID].element.dataset.cartItemId).toBe('cart_1780265668470_nwqtc3wtm')
96
+ expect(pruned.triggerElement.dataset.currentQuantity).toBe('1')
97
+ // The whole pruned context must be JSON-serializable for the fetch body —
98
+ // i.e. no live DOM node / method leaked through.
99
+ expect(() => JSON.stringify({ context: pruned })).not.toThrow()
100
+ expect(pruned[TRIGGER_ID].element.closest).toBeUndefined()
101
+ expect(pruned.__baseUrl).toBe('https://example.teleporthq.dev')
102
+ })
103
+
104
+ it('mergeServerResults preserves the live client trigger element', () => {
105
+ const el = fakeButton({
106
+ productId: '1117ddda-a771-4198-80e0-45fd3e7babe0',
107
+ cartItemId: 'cart_1780265668470_nwqtc3wtm',
108
+ currentQuantity: '1',
109
+ })
110
+ const context: Record<string, any> = {
111
+ [TRIGGER_ID]: { element: el, triggerElement: el },
112
+ triggerElement: el,
113
+ }
114
+
115
+ // What the server returns: its own node results PLUS the trigger echoed
116
+ // back as the unhelpful placeholder (pre-fix) and snapshot (post-fix).
117
+ const serverResults = {
118
+ [TRIGGER_ID]: { __serializationError: true },
119
+ triggerElement: { __serializationError: true },
120
+ 'c83827c1-stock': { rows: [{ id: 'x', quantity: 25 }], count: 12 },
121
+ '3d274382-if': { result: true },
122
+ }
123
+
124
+ mergeServerResults(context, serverResults, TRIGGER_ID)
125
+
126
+ // Server node results merged in...
127
+ expect(context['3d274382-if']).toEqual({ result: true })
128
+ expect(context['c83827c1-stock'].count).toBe(12)
129
+ // ...but the trigger element is still the REAL one (method intact), so the
130
+ // downstream client cart-update reads the genuine cartItemId.
131
+ expect(context[TRIGGER_ID].element.closest()).toBe('real-element')
132
+ expect(context[TRIGGER_ID].element.dataset.cartItemId).toBe('cart_1780265668470_nwqtc3wtm')
133
+ expect(context.triggerElement.dataset.cartItemId).toBe('cart_1780265668470_nwqtc3wtm')
134
+ })
135
+
136
+ it('mergeServerResults never lets a placeholder clobber a real value', () => {
137
+ const context: Record<string, any> = {
138
+ keep: { real: true },
139
+ }
140
+ mergeServerResults(context, { keep: { __truncated: true }, fresh: { ok: 1 } }, undefined)
141
+ expect(context.keep).toEqual({ real: true })
142
+ expect(context.fresh).toEqual({ ok: 1 })
143
+ })
144
+
145
+ it('mergeServerResults still protects the trigger node without an id (custom node)', () => {
146
+ const el = fakeButton({ cartItemId: 'cart_abc' })
147
+ const context: Record<string, any> = {
148
+ [TRIGGER_ID]: { element: el },
149
+ triggerElement: el,
150
+ }
151
+ // Custom-node executor calls without the outer trigger id; the generic
152
+ // .element guard must still preserve it.
153
+ mergeServerResults(context, { [TRIGGER_ID]: { __serializationError: true } }, undefined)
154
+ expect(context[TRIGGER_ID].element.dataset.cartItemId).toBe('cart_abc')
155
+ })
156
+ })