@nextsparkjs/theme-default 0.1.0-beta.17 → 0.1.0-beta.170

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 (332) hide show
  1. package/api/ai/chat/stream/route.ts +4 -1
  2. package/api/ai/orchestrator/route.ts +10 -3
  3. package/api/ai/single-agent/route.ts +10 -3
  4. package/api/ai/usage/route.ts +4 -1
  5. package/blocks/benefits/component.tsx +4 -4
  6. package/blocks/cta-section/component.tsx +4 -4
  7. package/blocks/faq-accordion/component.tsx +2 -2
  8. package/blocks/features-grid/component.tsx +5 -5
  9. package/blocks/hero/component.tsx +2 -2
  10. package/blocks/hero/fields.ts +1 -1
  11. package/blocks/hero-with-form/component.tsx +7 -7
  12. package/blocks/hero-with-form/fields.ts +1 -1
  13. package/blocks/jumbotron/component.tsx +7 -7
  14. package/blocks/jumbotron/fields.ts +1 -1
  15. package/blocks/logo-cloud/component.tsx +6 -6
  16. package/blocks/logo-cloud/fields.ts +1 -1
  17. package/blocks/post-content/component.tsx +2 -2
  18. package/blocks/pricing-table/component.tsx +5 -5
  19. package/blocks/split-content/component.tsx +5 -5
  20. package/blocks/split-content/fields.ts +1 -1
  21. package/blocks/stats-counter/component.tsx +9 -9
  22. package/blocks/testimonials/component.tsx +4 -4
  23. package/blocks/testimonials/fields.ts +1 -1
  24. package/blocks/text-content/component.tsx +12 -10
  25. package/blocks/timeline/component.tsx +12 -12
  26. package/blocks/video-hero/component.tsx +7 -7
  27. package/blocks/video-hero/fields.ts +1 -1
  28. package/components/ai-chat/ChatPanel.tsx +7 -7
  29. package/components/ai-chat/Message.tsx +2 -2
  30. package/components/ai-chat/MessageInput.tsx +3 -3
  31. package/components/ai-chat/MessageList.tsx +3 -3
  32. package/components/ai-chat/TypingIndicator.tsx +2 -2
  33. package/config/app.config.ts +90 -62
  34. package/config/billing.config.ts +5 -10
  35. package/config/dashboard.config.ts +14 -0
  36. package/config/features.config.ts +10 -0
  37. package/config/permissions.config.ts +22 -23
  38. package/docs/{01-overview → public/01-overview}/01-introduction.md +5 -0
  39. package/docs/{01-overview → public/01-overview}/02-customization.md +5 -0
  40. package/docs/{02-features → public/02-features}/03-tasks-entity.md +5 -0
  41. package/docs/{03-ai → public/03-ai}/01-overview.md +5 -0
  42. package/docs/{03-ai → public/03-ai}/02-customization.md +5 -0
  43. package/docs/superadmin/01-setup/01-configuration.md +79 -0
  44. package/docs/superadmin/01-setup/02-deployment.md +82 -0
  45. package/docs/superadmin/02-management/01-users.md +83 -0
  46. package/docs/superadmin/03-integrations/01-langchain.md +139 -0
  47. package/emails/verify-email.ts +33 -0
  48. package/entities/customers/api/docs.md +107 -0
  49. package/entities/customers/api/presets.ts +80 -0
  50. package/entities/pages/api/docs.md +114 -0
  51. package/entities/pages/api/presets.ts +72 -0
  52. package/entities/posts/api/docs.md +120 -0
  53. package/entities/posts/api/presets.ts +74 -0
  54. package/entities/tasks/api/docs.md +126 -0
  55. package/entities/tasks/api/presets.ts +84 -0
  56. package/lib/selectors.ts +7 -4
  57. package/messages/de/admin.json +45 -0
  58. package/messages/en/admin.json +56 -0
  59. package/messages/en/navigation.json +2 -1
  60. package/messages/es/admin.json +56 -0
  61. package/messages/es/navigation.json +2 -1
  62. package/messages/fr/admin.json +45 -0
  63. package/messages/it/admin.json +45 -0
  64. package/messages/pt/admin.json +45 -0
  65. package/migrations/089_add_editor_team_role.sql +15 -23
  66. package/migrations/090_demo_users_teams.sql +11 -11
  67. package/migrations/091_greek_teams_billing.sql +15 -15
  68. package/migrations/093_pages_sample_data.sql +7 -7
  69. package/migrations/098_patterns_sample_data.sql +234 -0
  70. package/nextsparkjs-theme-default-0.1.0-beta.137.tgz +0 -0
  71. package/package.json +4 -3
  72. package/styles/globals.css +42 -0
  73. package/templates/(public)/page.tsx +1 -1
  74. package/tests/cypress/e2e/_utils/devtools/access.bdd.md +262 -0
  75. package/tests/cypress/e2e/_utils/devtools/access.cy.ts +171 -0
  76. package/tests/cypress/e2e/_utils/devtools/navigation.bdd.md +261 -0
  77. package/tests/cypress/e2e/_utils/devtools/navigation.cy.ts +157 -0
  78. package/tests/cypress/e2e/_utils/devtools/pages.bdd.md +303 -0
  79. package/tests/cypress/e2e/_utils/devtools/pages.cy.ts +184 -0
  80. package/tests/cypress/e2e/_utils/docs/README.md +215 -0
  81. package/tests/cypress/e2e/_utils/selectors/auth.bdd.md +354 -0
  82. package/tests/cypress/e2e/_utils/selectors/auth.cy.ts +310 -0
  83. package/tests/cypress/e2e/_utils/selectors/billing.bdd.md +276 -0
  84. package/tests/cypress/e2e/_utils/selectors/billing.cy.ts +182 -0
  85. package/tests/cypress/e2e/_utils/selectors/block-editor.bdd.md +615 -0
  86. package/tests/cypress/e2e/_utils/selectors/block-editor.cy.ts +783 -0
  87. package/tests/cypress/e2e/_utils/selectors/dashboard-container.cy.ts +52 -0
  88. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.bdd.md +205 -0
  89. package/tests/cypress/e2e/_utils/selectors/dashboard-mobile.cy.ts +137 -0
  90. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.bdd.md +147 -0
  91. package/tests/cypress/e2e/_utils/selectors/dashboard-navigation.cy.ts +114 -0
  92. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.bdd.md +76 -0
  93. package/tests/cypress/e2e/_utils/selectors/dashboard-sidebar.cy.ts +68 -0
  94. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.bdd.md +326 -0
  95. package/tests/cypress/e2e/_utils/selectors/dashboard-topnav.cy.ts +177 -0
  96. package/tests/cypress/e2e/_utils/selectors/devtools.bdd.md +306 -0
  97. package/tests/cypress/e2e/_utils/selectors/devtools.cy.ts +273 -0
  98. package/tests/cypress/e2e/_utils/selectors/global-search.bdd.md +115 -0
  99. package/tests/cypress/e2e/_utils/selectors/global-search.cy.ts +93 -0
  100. package/tests/cypress/e2e/_utils/selectors/patterns.bdd.md +388 -0
  101. package/tests/cypress/e2e/_utils/selectors/patterns.cy.ts +559 -0
  102. package/tests/cypress/e2e/_utils/selectors/public.cy.ts +112 -0
  103. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.bdd.md +266 -0
  104. package/tests/cypress/e2e/_utils/selectors/settings-api-keys.cy.ts +233 -0
  105. package/tests/cypress/e2e/_utils/selectors/settings-billing.bdd.md +78 -0
  106. package/tests/cypress/e2e/_utils/selectors/settings-billing.cy.ts +108 -0
  107. package/tests/cypress/e2e/_utils/selectors/settings-layout.bdd.md +129 -0
  108. package/tests/cypress/e2e/_utils/selectors/settings-layout.cy.ts +115 -0
  109. package/tests/cypress/e2e/_utils/selectors/settings-password.bdd.md +82 -0
  110. package/tests/cypress/e2e/_utils/selectors/settings-password.cy.ts +74 -0
  111. package/tests/cypress/e2e/_utils/selectors/settings-profile.bdd.md +77 -0
  112. package/tests/cypress/e2e/_utils/selectors/settings-profile.cy.ts +79 -0
  113. package/tests/cypress/e2e/_utils/selectors/settings-teams.bdd.md +130 -0
  114. package/tests/cypress/e2e/_utils/selectors/settings-teams.cy.ts +86 -0
  115. package/tests/cypress/e2e/_utils/selectors/superadmin.bdd.md +261 -0
  116. package/tests/cypress/e2e/_utils/selectors/superadmin.cy.ts +193 -0
  117. package/tests/cypress/e2e/_utils/selectors/tasks.bdd.md +593 -0
  118. package/tests/cypress/e2e/_utils/selectors/tasks.cy.ts +864 -0
  119. package/tests/cypress/e2e/_utils/selectors/taxonomies.cy.ts +126 -0
  120. package/tests/cypress/e2e/_utils/selectors/teams.bdd.md +278 -0
  121. package/tests/cypress/e2e/_utils/selectors/teams.cy.ts +195 -0
  122. package/tests/cypress/e2e/_utils/superadmin/all-teams.bdd.md +261 -0
  123. package/tests/cypress/e2e/_utils/superadmin/all-teams.cy.ts +177 -0
  124. package/tests/cypress/e2e/_utils/superadmin/all-users.bdd.md +406 -0
  125. package/tests/cypress/e2e/_utils/superadmin/all-users.cy.ts +294 -0
  126. package/tests/cypress/e2e/_utils/superadmin/dashboard.bdd.md +235 -0
  127. package/tests/cypress/e2e/_utils/superadmin/dashboard.cy.ts +149 -0
  128. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.bdd.md +290 -0
  129. package/tests/cypress/e2e/_utils/superadmin/subscriptions-overview.cy.ts +194 -0
  130. package/tests/cypress/e2e/ai/ai-usage.cy.ts +209 -0
  131. package/tests/cypress/e2e/ai/chat-api.cy.ts +119 -0
  132. package/tests/cypress/e2e/ai/guardrails.cy.ts +332 -0
  133. package/tests/cypress/e2e/api/_core/billing/BillingAPIController.js +319 -0
  134. package/tests/cypress/e2e/api/_core/billing/check-action.cy.ts +326 -0
  135. package/tests/cypress/e2e/api/_core/billing/checkout.cy.ts +358 -0
  136. package/tests/cypress/e2e/api/_core/billing/lifecycle.cy.ts +423 -0
  137. package/tests/cypress/e2e/api/_core/billing/plans/README.md +345 -0
  138. package/tests/cypress/e2e/api/_core/billing/plans/business.cy.ts +412 -0
  139. package/tests/cypress/e2e/api/_core/billing/plans/downgrade.cy.ts +510 -0
  140. package/tests/cypress/e2e/api/_core/billing/plans/fixtures/billing-plans.json +163 -0
  141. package/tests/cypress/e2e/api/_core/billing/plans/free.cy.ts +500 -0
  142. package/tests/cypress/e2e/api/_core/billing/plans/pro.cy.ts +497 -0
  143. package/tests/cypress/e2e/api/_core/billing/plans/starter.cy.ts +342 -0
  144. package/tests/cypress/e2e/api/_core/billing/portal.cy.ts +313 -0
  145. package/tests/cypress/e2e/api/_core/devtools/registries.bdd.md +300 -0
  146. package/tests/cypress/e2e/api/_core/devtools/registries.cy.ts +368 -0
  147. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.bdd.md +375 -0
  148. package/tests/cypress/e2e/api/_core/scheduled-actions/cron-endpoint.cy.ts +346 -0
  149. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.bdd.md +451 -0
  150. package/tests/cypress/e2e/api/_core/scheduled-actions/devtools-endpoint.cy.ts +447 -0
  151. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.bdd.md +649 -0
  152. package/tests/cypress/e2e/api/_core/scheduled-actions/scheduling.cy.ts +333 -0
  153. package/tests/cypress/e2e/api/_core/security/security-headers.cy.ts +601 -0
  154. package/tests/cypress/e2e/api/_core/settings/api-keys.crud.cy.ts +923 -0
  155. package/tests/cypress/e2e/api/_core/teams/teams-security.cy.ts +415 -0
  156. package/tests/cypress/e2e/api/_core/users/users-crud.cy.ts +469 -0
  157. package/tests/cypress/e2e/api/_core/users/users-metas.cy.ts +913 -0
  158. package/tests/cypress/e2e/api/_core/users/users-security.cy.ts +375 -0
  159. package/tests/cypress/e2e/api/entities/customers/customers-crud.cy.ts +648 -0
  160. package/tests/cypress/e2e/api/entities/customers/customers-metas.cy.ts +839 -0
  161. package/tests/cypress/e2e/api/entities/media/media-crud.cy.ts +600 -0
  162. package/tests/cypress/e2e/api/entities/media/media-role-permissions.cy.ts +617 -0
  163. package/tests/cypress/e2e/api/entities/media/media-team-isolation.cy.ts +464 -0
  164. package/tests/cypress/e2e/api/entities/pages/blocks-scope.cy.ts +396 -0
  165. package/tests/cypress/e2e/api/entities/pages/pages-crud.cy.ts +425 -0
  166. package/tests/cypress/e2e/api/entities/pages/pages-status.cy.ts +335 -0
  167. package/tests/cypress/e2e/api/entities/posts/post-categories-crud.cy.ts +610 -0
  168. package/tests/cypress/e2e/api/entities/posts/posts-crud.cy.ts +709 -0
  169. package/tests/cypress/e2e/api/entities/posts/posts-status.cy.ts +396 -0
  170. package/tests/cypress/e2e/api/entities/tasks/tasks-crud.cy.ts +602 -0
  171. package/tests/cypress/e2e/api/entities/tasks/tasks-metas.cy.ts +878 -0
  172. package/tests/cypress/e2e/patterns/patterns-in-pages.cy.ts +367 -0
  173. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.bdd.md +231 -0
  174. package/tests/cypress/e2e/uat/_core/auth/app-roles/developer-login.cy.ts +144 -0
  175. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.bdd.md +118 -0
  176. package/tests/cypress/e2e/uat/_core/auth/app-roles/superadmin-login.cy.ts +84 -0
  177. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.bdd.md +288 -0
  178. package/tests/cypress/e2e/uat/_core/auth/custom-roles/editor-login.cy.ts +188 -0
  179. package/tests/cypress/e2e/uat/_core/auth/login-logout.bdd.md +160 -0
  180. package/tests/cypress/e2e/uat/_core/auth/login-logout.cy.ts +116 -0
  181. package/tests/cypress/e2e/uat/_core/auth/password-reset.bdd.md +289 -0
  182. package/tests/cypress/e2e/uat/_core/auth/password-reset.cy.ts +200 -0
  183. package/tests/cypress/e2e/uat/_core/auth/registration-control-invitation.cy.ts +176 -0
  184. package/tests/cypress/e2e/uat/_core/auth/registration-control-open.cy.ts +131 -0
  185. package/tests/cypress/e2e/uat/_core/auth/registration-control.cy.ts +140 -0
  186. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.bdd.md +225 -0
  187. package/tests/cypress/e2e/uat/_core/auth/team-roles/admin-login.cy.ts +148 -0
  188. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.bdd.md +251 -0
  189. package/tests/cypress/e2e/uat/_core/auth/team-roles/member-login.cy.ts +163 -0
  190. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.bdd.md +231 -0
  191. package/tests/cypress/e2e/uat/_core/auth/team-roles/owner-login.cy.ts +141 -0
  192. package/tests/cypress/e2e/uat/_core/billing/extended.bdd.md +273 -0
  193. package/tests/cypress/e2e/uat/_core/billing/extended.cy.ts +209 -0
  194. package/tests/cypress/e2e/uat/_core/billing/feature-gates.bdd.md +407 -0
  195. package/tests/cypress/e2e/uat/_core/billing/feature-gates.cy.ts +307 -0
  196. package/tests/cypress/e2e/uat/_core/billing/page.bdd.md +329 -0
  197. package/tests/cypress/e2e/uat/_core/billing/page.cy.ts +250 -0
  198. package/tests/cypress/e2e/uat/_core/billing/status.bdd.md +190 -0
  199. package/tests/cypress/e2e/uat/_core/billing/status.cy.ts +145 -0
  200. package/tests/cypress/e2e/uat/_core/billing/team-switch.bdd.md +156 -0
  201. package/tests/cypress/e2e/uat/_core/billing/team-switch.cy.ts +122 -0
  202. package/tests/cypress/e2e/uat/_core/billing/usage.bdd.md +218 -0
  203. package/tests/cypress/e2e/uat/_core/billing/usage.cy.ts +176 -0
  204. package/tests/cypress/e2e/uat/_core/blocks/hero.bdd.md +124 -0
  205. package/tests/cypress/e2e/uat/_core/blocks/hero.cy.ts +56 -0
  206. package/tests/cypress/e2e/uat/_core/devtools/api-tester.cy.ts +390 -0
  207. package/tests/cypress/e2e/uat/_core/performance/suspense-loading.cy.ts +134 -0
  208. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.bdd.md +736 -0
  209. package/tests/cypress/e2e/uat/_core/scheduled-actions/devtools-ui.cy.ts +740 -0
  210. package/tests/cypress/e2e/uat/_core/teams/inline-edit.cy.ts +278 -0
  211. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.bdd.md +553 -0
  212. package/tests/cypress/e2e/uat/_core/teams/roles-matrix.cy.ts +185 -0
  213. package/tests/cypress/e2e/uat/_core/teams/switcher.bdd.md +1151 -0
  214. package/tests/cypress/e2e/uat/_core/teams/switcher.cy.ts +497 -0
  215. package/tests/cypress/e2e/uat/_core/teams/team-switcher.md +198 -0
  216. package/tests/cypress/e2e/uat/entities/customers/member.bdd.md +275 -0
  217. package/tests/cypress/e2e/uat/entities/customers/member.cy.ts +122 -0
  218. package/tests/cypress/e2e/uat/entities/customers/owner.bdd.md +243 -0
  219. package/tests/cypress/e2e/uat/entities/customers/owner.cy.ts +165 -0
  220. package/tests/cypress/e2e/uat/entities/pages/block-crud.bdd.md +476 -0
  221. package/tests/cypress/e2e/uat/entities/pages/block-crud.cy.ts +486 -0
  222. package/tests/cypress/e2e/uat/entities/pages/block-editor.bdd.md +460 -0
  223. package/tests/cypress/e2e/uat/entities/pages/block-editor.cy.ts +301 -0
  224. package/tests/cypress/e2e/uat/entities/pages/list.bdd.md +432 -0
  225. package/tests/cypress/e2e/uat/entities/pages/list.cy.ts +273 -0
  226. package/tests/cypress/e2e/uat/entities/pages/public-rendering.bdd.md +696 -0
  227. package/tests/cypress/e2e/uat/entities/pages/public-rendering.cy.ts +340 -0
  228. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.bdd.md +161 -0
  229. package/tests/cypress/e2e/uat/entities/posts/categories-api-aware.cy.ts +104 -0
  230. package/tests/cypress/e2e/uat/entities/posts/categories.bdd.md +375 -0
  231. package/tests/cypress/e2e/uat/entities/posts/categories.cy.ts +241 -0
  232. package/tests/cypress/e2e/uat/entities/posts/editor.bdd.md +429 -0
  233. package/tests/cypress/e2e/uat/entities/posts/editor.cy.ts +257 -0
  234. package/tests/cypress/e2e/uat/entities/posts/list.bdd.md +340 -0
  235. package/tests/cypress/e2e/uat/entities/posts/list.cy.ts +177 -0
  236. package/tests/cypress/e2e/uat/entities/posts/public.bdd.md +614 -0
  237. package/tests/cypress/e2e/uat/entities/posts/public.cy.ts +249 -0
  238. package/tests/cypress/e2e/uat/entities/tasks/member.bdd.md +222 -0
  239. package/tests/cypress/e2e/uat/entities/tasks/member.cy.ts +165 -0
  240. package/tests/cypress/e2e/uat/entities/tasks/owner.bdd.md +419 -0
  241. package/tests/cypress/e2e/uat/entities/tasks/owner.cy.ts +191 -0
  242. package/tests/cypress/e2e/uat/features/roles/editor-role.bdd.md +552 -0
  243. package/tests/cypress/e2e/uat/features/roles/editor-role.cy.ts +210 -0
  244. package/tests/cypress/e2e/uat/features/roles/member-restrictions.bdd.md +450 -0
  245. package/tests/cypress/e2e/uat/features/roles/member-restrictions.cy.ts +189 -0
  246. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.bdd.md +530 -0
  247. package/tests/cypress/e2e/uat/features/roles/owner-full-crud.cy.ts +247 -0
  248. package/tests/cypress/fixtures/blocks.json +218 -0
  249. package/tests/cypress/fixtures/entities.json +87 -0
  250. package/tests/cypress/fixtures/page-builder.json +21 -0
  251. package/tests/cypress/src/components/CategoriesPOM.ts +382 -0
  252. package/tests/cypress/src/components/CustomersPOM.ts +439 -0
  253. package/tests/cypress/src/components/DevKeyringPOM.ts +160 -0
  254. package/tests/cypress/src/components/EntityForm.ts +375 -0
  255. package/tests/cypress/src/components/EntityList.ts +389 -0
  256. package/tests/cypress/src/components/PageBuilderPOM.ts +710 -0
  257. package/tests/cypress/src/components/PostEditorPOM.ts +370 -0
  258. package/tests/cypress/src/components/PostsListPOM.ts +223 -0
  259. package/tests/cypress/src/components/PublicPagePOM.ts +447 -0
  260. package/tests/cypress/src/components/PublicPostPOM.ts +146 -0
  261. package/tests/cypress/src/components/TasksPOM.ts +272 -0
  262. package/tests/cypress/src/components/TeamSwitcherPOM.ts +450 -0
  263. package/tests/cypress/src/components/index.ts +21 -0
  264. package/tests/cypress/src/controllers/ApiKeysAPIController.js +178 -0
  265. package/tests/cypress/src/controllers/BaseAPIController.js +317 -0
  266. package/tests/cypress/src/controllers/CustomerAPIController.js +251 -0
  267. package/tests/cypress/src/controllers/MediaAPIController.js +231 -0
  268. package/tests/cypress/src/controllers/PagesAPIController.js +226 -0
  269. package/tests/cypress/src/controllers/PostsAPIController.js +250 -0
  270. package/tests/cypress/src/controllers/TaskAPIController.js +240 -0
  271. package/tests/cypress/src/controllers/UsersAPIController.js +242 -0
  272. package/tests/cypress/src/controllers/index.js +25 -0
  273. package/tests/cypress/src/core/AuthPOM.ts +450 -0
  274. package/tests/cypress/src/core/BasePOM.ts +33 -0
  275. package/tests/cypress/src/core/BlockEditorBasePOM.ts +874 -0
  276. package/tests/cypress/src/core/DashboardEntityPOM.ts +41 -0
  277. package/tests/cypress/src/core/index.ts +14 -0
  278. package/tests/cypress/src/entities/CustomersPOM.ts +172 -0
  279. package/tests/cypress/src/entities/PagesPOM.ts +137 -0
  280. package/tests/cypress/src/entities/PatternsPOM.ts +329 -0
  281. package/tests/cypress/src/entities/PostsPOM.ts +137 -0
  282. package/tests/cypress/src/entities/TasksPOM.ts +246 -0
  283. package/tests/cypress/src/entities/index.ts +16 -0
  284. package/tests/cypress/src/features/BillingPOM.ts +385 -0
  285. package/tests/cypress/src/features/DashboardPOM.ts +271 -0
  286. package/tests/cypress/src/features/DevtoolsPOM.ts +750 -0
  287. package/tests/cypress/src/features/PageBuilderPOM.ts +283 -0
  288. package/tests/cypress/src/features/PostEditorPOM.ts +313 -0
  289. package/tests/cypress/src/features/ScheduledActionsPOM.ts +463 -0
  290. package/tests/cypress/src/features/SettingsPOM.ts +707 -0
  291. package/tests/cypress/src/features/SuperadminPOM.ts +851 -0
  292. package/tests/cypress/src/features/SuperadminTeamRolesPOM.ts +285 -0
  293. package/tests/cypress/src/features/index.ts +28 -0
  294. package/tests/cypress/src/helpers/ApiInterceptor.ts +20 -0
  295. package/tests/cypress/src/index.ts +101 -0
  296. package/tests/cypress/src/pages/dashboard/Dashboard.js +677 -0
  297. package/tests/cypress/src/pages/dashboard/DashboardPage.js +43 -0
  298. package/tests/cypress/src/pages/dashboard/DashboardStats.js +546 -0
  299. package/tests/cypress/src/pages/dashboard/index.js +6 -0
  300. package/tests/cypress/src/pages/index.js +5 -0
  301. package/tests/cypress/src/pages/public/FeaturesPage.js +28 -0
  302. package/tests/cypress/src/pages/public/LandingPage.js +69 -0
  303. package/tests/cypress/src/pages/public/PricingPage.js +33 -0
  304. package/tests/cypress/src/pages/public/index.js +6 -0
  305. package/tests/cypress/src/selectors.ts +46 -0
  306. package/tests/cypress/src/session-helpers.ts +518 -0
  307. package/tests/cypress/support/doc-commands.ts +260 -0
  308. package/tests/cypress/support/e2e.ts +90 -0
  309. package/tests/cypress.config.ts +178 -0
  310. package/tests/jest/__mocks__/@nextsparkjs/core/components/ui/badge.js +16 -0
  311. package/tests/jest/__mocks__/@nextsparkjs/core/lib/db.js +11 -0
  312. package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +160 -0
  313. package/tests/jest/__mocks__/@nextsparkjs/registries/theme-registry.ts +68 -0
  314. package/tests/jest/__mocks__/jose.js +22 -0
  315. package/tests/jest/__mocks__/next/image.js +15 -0
  316. package/tests/jest/__mocks__/next-server.js +56 -0
  317. package/tests/jest/components/post-header.test.tsx +377 -0
  318. package/tests/jest/jest.config.cjs +154 -0
  319. package/tests/jest/langchain/COVERAGE.md +372 -0
  320. package/tests/jest/langchain/guardrails.test.ts +465 -0
  321. package/tests/jest/langchain/streaming.test.ts +370 -0
  322. package/tests/jest/langchain/token-tracker.test.ts +455 -0
  323. package/tests/jest/langchain/tracer-callbacks.test.ts +881 -0
  324. package/tests/jest/langchain/tracer.test.ts +823 -0
  325. package/tests/jest/services/tasks.service.test.ts +707 -0
  326. package/tests/jest/setup.ts +170 -0
  327. package/tests/jest/tsconfig.jest.json +6 -0
  328. package/tests/jest/validation/categories.test.ts +429 -0
  329. package/tests/jest/validation/posts.test.ts +546 -0
  330. package/tests/tsconfig.json +21 -0
  331. /package/docs/{02-features → public/02-features}/01-components.md +0 -0
  332. /package/docs/{02-features → public/02-features}/02-styling.md +0 -0
@@ -0,0 +1,881 @@
1
+ /**
2
+ * Unit Tests - Tracing Callback Handler
3
+ *
4
+ * Tests LangChain callback handler integration:
5
+ * - LLM event handling (start, end, error)
6
+ * - Tool event handling (start, end, error)
7
+ * - Chain event handling (start, end, error)
8
+ * - Parent-child span relationships
9
+ * - Depth tracking
10
+ *
11
+ * Focus: Event transformation and span lifecycle.
12
+ */
13
+
14
+ import {
15
+ TracingCallbackHandler,
16
+ createTracingCallbacks,
17
+ } from '@/plugins/langchain/lib/tracer-callbacks'
18
+ import { tracer } from '@/plugins/langchain/lib/tracer'
19
+ import type { Serialized } from '@langchain/core/load/serializable'
20
+
21
+ // Mock tracer service
22
+ jest.mock('@/contents/plugins/langchain/lib/tracer', () => ({
23
+ tracer: {
24
+ startSpan: jest.fn(),
25
+ endSpan: jest.fn(),
26
+ },
27
+ }))
28
+
29
+ const mockStartSpan = tracer.startSpan as jest.MockedFunction<typeof tracer.startSpan>
30
+ const mockEndSpan = tracer.endSpan as jest.MockedFunction<typeof tracer.endSpan>
31
+
32
+ describe('TracingCallbackHandler', () => {
33
+ const context = { userId: 'user-123', teamId: 'team-456' }
34
+ const traceId = 'trace-abc'
35
+ let handler: TracingCallbackHandler
36
+
37
+ beforeEach(() => {
38
+ jest.clearAllMocks()
39
+ handler = new TracingCallbackHandler({ context, traceId })
40
+ })
41
+
42
+ describe('initialization', () => {
43
+ it('should initialize with correct name', () => {
44
+ expect(handler.name).toBe('tracing_callback_handler')
45
+ })
46
+
47
+ it('should store context and traceId', () => {
48
+ // Context and traceId are private, so we test indirectly via method calls
49
+ mockStartSpan.mockResolvedValue({
50
+ spanId: 'span-123',
51
+ traceId,
52
+ name: 'test',
53
+ type: 'llm',
54
+ depth: 0,
55
+ startedAt: new Date(),
56
+ })
57
+
58
+ handler.handleLLMStart(
59
+ { id: ['langchain', 'llms', 'openai'] } as Serialized,
60
+ ['test prompt'],
61
+ 'run-123'
62
+ )
63
+
64
+ expect(mockStartSpan).toHaveBeenCalledWith(
65
+ context,
66
+ traceId,
67
+ expect.any(Object)
68
+ )
69
+ })
70
+ })
71
+
72
+ describe('handleLLMStart', () => {
73
+ it('should create span for LLM call', async () => {
74
+ const llm: Serialized = {
75
+ id: ['langchain', 'llms', 'openai'],
76
+ kwargs: { model: 'gpt-4o' },
77
+ }
78
+ mockStartSpan.mockResolvedValue({
79
+ spanId: 'span-123',
80
+ traceId,
81
+ name: 'LLM: gpt-4o',
82
+ type: 'llm',
83
+ depth: 0,
84
+ startedAt: new Date(),
85
+ })
86
+
87
+ await handler.handleLLMStart(llm, ['test prompt'], 'run-123')
88
+
89
+ expect(mockStartSpan).toHaveBeenCalledWith(context, traceId, {
90
+ name: 'LLM: gpt-4o',
91
+ type: 'llm',
92
+ provider: 'openai',
93
+ model: 'gpt-4o',
94
+ parentSpanId: undefined,
95
+ depth: 0,
96
+ input: { prompts: ['test prompt'] },
97
+ })
98
+ })
99
+
100
+ it('should extract provider from LLM serialized id', async () => {
101
+ const llm: Serialized = {
102
+ id: ['langchain', 'chat_models', 'anthropic'],
103
+ kwargs: { model: 'claude-3-5-sonnet' },
104
+ }
105
+ mockStartSpan.mockResolvedValue(null)
106
+
107
+ await handler.handleLLMStart(llm, ['test'], 'run-123')
108
+
109
+ expect(mockStartSpan).toHaveBeenCalledWith(
110
+ context,
111
+ traceId,
112
+ expect.objectContaining({
113
+ provider: 'anthropic',
114
+ })
115
+ )
116
+ })
117
+
118
+ it('should use "unknown" for missing model', async () => {
119
+ const llm: Serialized = {
120
+ id: ['langchain', 'llms', 'custom'],
121
+ }
122
+ mockStartSpan.mockResolvedValue(null)
123
+
124
+ await handler.handleLLMStart(llm, ['test'], 'run-123')
125
+
126
+ expect(mockStartSpan).toHaveBeenCalledWith(
127
+ context,
128
+ traceId,
129
+ expect.objectContaining({
130
+ model: 'unknown',
131
+ })
132
+ )
133
+ })
134
+
135
+ it('should handle nested LLM calls with depth', async () => {
136
+ const llm: Serialized = {
137
+ id: ['langchain', 'llms', 'openai'],
138
+ kwargs: { model: 'gpt-4o' },
139
+ }
140
+
141
+ // First, create a parent span from a different parent to set up parentSpans map
142
+ mockStartSpan.mockResolvedValueOnce({
143
+ spanId: 'span-grandparent',
144
+ traceId,
145
+ name: 'LLM: gpt-4o',
146
+ type: 'llm',
147
+ depth: 0,
148
+ startedAt: new Date(),
149
+ })
150
+ await handler.handleLLMStart(llm, ['prompt 0'], 'run-grandparent')
151
+
152
+ // Second call with grandparent as parent (depth 1)
153
+ mockStartSpan.mockResolvedValueOnce({
154
+ spanId: 'span-parent',
155
+ traceId,
156
+ name: 'LLM: gpt-4o',
157
+ type: 'llm',
158
+ depth: 1,
159
+ startedAt: new Date(),
160
+ })
161
+ await handler.handleLLMStart(llm, ['prompt 1'], 'run-parent', 'run-grandparent')
162
+
163
+ // Third call nested under parent (depth 2)
164
+ mockStartSpan.mockResolvedValueOnce({
165
+ spanId: 'span-child',
166
+ traceId,
167
+ name: 'LLM: gpt-4o',
168
+ type: 'llm',
169
+ depth: 2,
170
+ startedAt: new Date(),
171
+ })
172
+ await handler.handleLLMStart(llm, ['prompt 2'], 'run-child', 'run-parent')
173
+
174
+ expect(mockStartSpan).toHaveBeenLastCalledWith(
175
+ context,
176
+ traceId,
177
+ expect.objectContaining({
178
+ depth: 2,
179
+ parentSpanId: 'span-parent',
180
+ })
181
+ )
182
+ })
183
+
184
+ it('should handle errors gracefully', async () => {
185
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
186
+ mockStartSpan.mockRejectedValue(new Error('Database error'))
187
+
188
+ await handler.handleLLMStart(
189
+ { id: ['langchain', 'llms', 'openai'] } as Serialized,
190
+ ['test'],
191
+ 'run-123'
192
+ )
193
+
194
+ expect(consoleSpy).toHaveBeenCalledWith(
195
+ '[TracingCallbackHandler] handleLLMStart error:',
196
+ expect.any(Error)
197
+ )
198
+ consoleSpy.mockRestore()
199
+ })
200
+ })
201
+
202
+ describe('handleLLMEnd', () => {
203
+ beforeEach(async () => {
204
+ // Setup: Start an LLM span first
205
+ mockStartSpan.mockResolvedValue({
206
+ spanId: 'span-123',
207
+ traceId,
208
+ name: 'LLM: gpt-4o',
209
+ type: 'llm',
210
+ depth: 0,
211
+ startedAt: new Date(),
212
+ })
213
+ await handler.handleLLMStart(
214
+ { id: ['langchain', 'llms', 'openai'], kwargs: { model: 'gpt-4o' } } as Serialized,
215
+ ['test prompt'],
216
+ 'run-123'
217
+ )
218
+ jest.clearAllMocks()
219
+ })
220
+
221
+ it('should end span with output and token usage', async () => {
222
+ mockEndSpan.mockResolvedValue(undefined)
223
+
224
+ await handler.handleLLMEnd(
225
+ {
226
+ generations: [{ text: 'Generated response' }],
227
+ llmOutput: {
228
+ tokenUsage: {
229
+ promptTokens: 100,
230
+ completionTokens: 50,
231
+ },
232
+ },
233
+ },
234
+ 'run-123'
235
+ )
236
+
237
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
238
+ output: {
239
+ generations: ['Generated response'],
240
+ },
241
+ tokens: {
242
+ input: 100,
243
+ output: 50,
244
+ },
245
+ })
246
+ })
247
+
248
+ it('should handle alternate token usage format (input_tokens/output_tokens)', async () => {
249
+ mockEndSpan.mockResolvedValue(undefined)
250
+
251
+ await handler.handleLLMEnd(
252
+ {
253
+ generations: [{ text: 'response' }],
254
+ llmOutput: {
255
+ tokenUsage: {
256
+ input_tokens: 200,
257
+ output_tokens: 75,
258
+ },
259
+ },
260
+ },
261
+ 'run-123'
262
+ )
263
+
264
+ expect(mockEndSpan).toHaveBeenCalledWith(
265
+ context,
266
+ traceId,
267
+ 'span-123',
268
+ expect.objectContaining({
269
+ tokens: {
270
+ input: 200,
271
+ output: 75,
272
+ },
273
+ })
274
+ )
275
+ })
276
+
277
+ it('should handle message format generations', async () => {
278
+ mockEndSpan.mockResolvedValue(undefined)
279
+
280
+ await handler.handleLLMEnd(
281
+ {
282
+ generations: [{ message: { content: 'Message response' } }],
283
+ llmOutput: { tokenUsage: {} },
284
+ },
285
+ 'run-123'
286
+ )
287
+
288
+ expect(mockEndSpan).toHaveBeenCalledWith(
289
+ context,
290
+ traceId,
291
+ 'span-123',
292
+ expect.objectContaining({
293
+ output: {
294
+ generations: ['Message response'],
295
+ },
296
+ })
297
+ )
298
+ })
299
+
300
+ it('should handle missing token usage', async () => {
301
+ mockEndSpan.mockResolvedValue(undefined)
302
+
303
+ await handler.handleLLMEnd(
304
+ {
305
+ generations: [{ text: 'response' }],
306
+ llmOutput: {},
307
+ },
308
+ 'run-123'
309
+ )
310
+
311
+ expect(mockEndSpan).toHaveBeenCalledWith(
312
+ context,
313
+ traceId,
314
+ 'span-123',
315
+ expect.objectContaining({
316
+ tokens: undefined,
317
+ })
318
+ )
319
+ })
320
+
321
+ it('should do nothing if span was not started', async () => {
322
+ await handler.handleLLMEnd({ generations: [] }, 'unknown-run-id')
323
+
324
+ expect(mockEndSpan).not.toHaveBeenCalled()
325
+ })
326
+
327
+ it('should handle errors gracefully', async () => {
328
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
329
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
330
+
331
+ await handler.handleLLMEnd({ generations: [] }, 'run-123')
332
+
333
+ expect(consoleSpy).toHaveBeenCalledWith(
334
+ '[TracingCallbackHandler] handleLLMEnd error:',
335
+ expect.any(Error)
336
+ )
337
+ consoleSpy.mockRestore()
338
+ })
339
+ })
340
+
341
+ describe('handleLLMError', () => {
342
+ beforeEach(async () => {
343
+ // Setup: Start an LLM span first
344
+ mockStartSpan.mockResolvedValue({
345
+ spanId: 'span-123',
346
+ traceId,
347
+ name: 'LLM: gpt-4o',
348
+ type: 'llm',
349
+ depth: 0,
350
+ startedAt: new Date(),
351
+ })
352
+ await handler.handleLLMStart(
353
+ { id: ['langchain', 'llms', 'openai'] } as Serialized,
354
+ ['test'],
355
+ 'run-123'
356
+ )
357
+ jest.clearAllMocks()
358
+ })
359
+
360
+ it('should end span with error', async () => {
361
+ mockEndSpan.mockResolvedValue(undefined)
362
+ const error = new Error('LLM execution failed')
363
+
364
+ await handler.handleLLMError(error, 'run-123')
365
+
366
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
367
+ error,
368
+ })
369
+ })
370
+
371
+ it('should do nothing if span was not started', async () => {
372
+ await handler.handleLLMError(new Error('test'), 'unknown-run-id')
373
+
374
+ expect(mockEndSpan).not.toHaveBeenCalled()
375
+ })
376
+
377
+ it('should handle errors in error handling', async () => {
378
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
379
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
380
+
381
+ await handler.handleLLMError(new Error('LLM error'), 'run-123')
382
+
383
+ expect(consoleSpy).toHaveBeenCalledWith(
384
+ '[TracingCallbackHandler] handleLLMError error:',
385
+ expect.any(Error)
386
+ )
387
+ consoleSpy.mockRestore()
388
+ })
389
+ })
390
+
391
+ describe('handleToolStart', () => {
392
+ it('should create span for tool call', async () => {
393
+ const tool: Serialized = {
394
+ id: ['langchain', 'tools', 'search'],
395
+ }
396
+ mockStartSpan.mockResolvedValue({
397
+ spanId: 'span-123',
398
+ traceId,
399
+ name: 'Tool: search',
400
+ type: 'tool',
401
+ depth: 0,
402
+ startedAt: new Date(),
403
+ })
404
+
405
+ await handler.handleToolStart(tool, 'query term', 'run-123')
406
+
407
+ expect(mockStartSpan).toHaveBeenCalledWith(context, traceId, {
408
+ name: 'Tool: search',
409
+ type: 'tool',
410
+ toolName: 'search',
411
+ parentSpanId: undefined,
412
+ depth: 0,
413
+ input: { toolInput: 'query term' },
414
+ })
415
+ })
416
+
417
+ it('should extract tool name from serialized id', async () => {
418
+ const tool: Serialized = {
419
+ id: ['langchain', 'tools', 'calculator'],
420
+ }
421
+ mockStartSpan.mockResolvedValue(null)
422
+
423
+ await handler.handleToolStart(tool, '2 + 2', 'run-123')
424
+
425
+ expect(mockStartSpan).toHaveBeenCalledWith(
426
+ context,
427
+ traceId,
428
+ expect.objectContaining({
429
+ toolName: 'calculator',
430
+ })
431
+ )
432
+ })
433
+
434
+ it('should use "unknown" for missing tool name', async () => {
435
+ const tool: Serialized = { id: [] }
436
+ mockStartSpan.mockResolvedValue(null)
437
+
438
+ await handler.handleToolStart(tool, 'input', 'run-123')
439
+
440
+ expect(mockStartSpan).toHaveBeenCalledWith(
441
+ context,
442
+ traceId,
443
+ expect.objectContaining({
444
+ toolName: 'unknown',
445
+ })
446
+ )
447
+ })
448
+
449
+ it('should handle nested tool calls with depth', async () => {
450
+ // Grandparent span
451
+ mockStartSpan.mockResolvedValueOnce({
452
+ spanId: 'span-grandparent',
453
+ traceId,
454
+ name: 'Tool: search',
455
+ type: 'tool',
456
+ depth: 0,
457
+ startedAt: new Date(),
458
+ })
459
+ await handler.handleToolStart(
460
+ { id: ['langchain', 'tools', 'search'] } as Serialized,
461
+ 'query1',
462
+ 'run-grandparent'
463
+ )
464
+
465
+ // Parent span
466
+ mockStartSpan.mockResolvedValueOnce({
467
+ spanId: 'span-parent',
468
+ traceId,
469
+ name: 'Tool: search',
470
+ type: 'tool',
471
+ depth: 1,
472
+ startedAt: new Date(),
473
+ })
474
+ await handler.handleToolStart(
475
+ { id: ['langchain', 'tools', 'search'] } as Serialized,
476
+ 'query',
477
+ 'run-parent',
478
+ 'run-grandparent'
479
+ )
480
+
481
+ // Child span
482
+ mockStartSpan.mockResolvedValueOnce({
483
+ spanId: 'span-child',
484
+ traceId,
485
+ name: 'Tool: calculator',
486
+ type: 'tool',
487
+ depth: 2,
488
+ startedAt: new Date(),
489
+ })
490
+ await handler.handleToolStart(
491
+ { id: ['langchain', 'tools', 'calculator'] } as Serialized,
492
+ '2 + 2',
493
+ 'run-child',
494
+ 'run-parent'
495
+ )
496
+
497
+ expect(mockStartSpan).toHaveBeenLastCalledWith(
498
+ context,
499
+ traceId,
500
+ expect.objectContaining({
501
+ depth: 2,
502
+ parentSpanId: 'span-parent',
503
+ })
504
+ )
505
+ })
506
+
507
+ it('should handle errors gracefully', async () => {
508
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
509
+ mockStartSpan.mockRejectedValue(new Error('Database error'))
510
+
511
+ await handler.handleToolStart(
512
+ { id: ['langchain', 'tools', 'search'] } as Serialized,
513
+ 'query',
514
+ 'run-123'
515
+ )
516
+
517
+ expect(consoleSpy).toHaveBeenCalledWith(
518
+ '[TracingCallbackHandler] handleToolStart error:',
519
+ expect.any(Error)
520
+ )
521
+ consoleSpy.mockRestore()
522
+ })
523
+ })
524
+
525
+ describe('handleToolEnd', () => {
526
+ beforeEach(async () => {
527
+ // Setup: Start a tool span first
528
+ mockStartSpan.mockResolvedValue({
529
+ spanId: 'span-123',
530
+ traceId,
531
+ name: 'Tool: search',
532
+ type: 'tool',
533
+ depth: 0,
534
+ startedAt: new Date(),
535
+ })
536
+ await handler.handleToolStart(
537
+ { id: ['langchain', 'tools', 'search'] } as Serialized,
538
+ 'query',
539
+ 'run-123'
540
+ )
541
+ jest.clearAllMocks()
542
+ })
543
+
544
+ it('should end span with tool output', async () => {
545
+ mockEndSpan.mockResolvedValue(undefined)
546
+
547
+ await handler.handleToolEnd('Search results: item1, item2', 'run-123')
548
+
549
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
550
+ toolOutput: 'Search results: item1, item2',
551
+ })
552
+ })
553
+
554
+ it('should do nothing if span was not started', async () => {
555
+ await handler.handleToolEnd('output', 'unknown-run-id')
556
+
557
+ expect(mockEndSpan).not.toHaveBeenCalled()
558
+ })
559
+
560
+ it('should handle errors gracefully', async () => {
561
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
562
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
563
+
564
+ await handler.handleToolEnd('output', 'run-123')
565
+
566
+ expect(consoleSpy).toHaveBeenCalledWith(
567
+ '[TracingCallbackHandler] handleToolEnd error:',
568
+ expect.any(Error)
569
+ )
570
+ consoleSpy.mockRestore()
571
+ })
572
+ })
573
+
574
+ describe('handleToolError', () => {
575
+ beforeEach(async () => {
576
+ // Setup: Start a tool span first
577
+ mockStartSpan.mockResolvedValue({
578
+ spanId: 'span-123',
579
+ traceId,
580
+ name: 'Tool: search',
581
+ type: 'tool',
582
+ depth: 0,
583
+ startedAt: new Date(),
584
+ })
585
+ await handler.handleToolStart(
586
+ { id: ['langchain', 'tools', 'search'] } as Serialized,
587
+ 'query',
588
+ 'run-123'
589
+ )
590
+ jest.clearAllMocks()
591
+ })
592
+
593
+ it('should end span with error', async () => {
594
+ mockEndSpan.mockResolvedValue(undefined)
595
+ const error = new Error('Tool execution failed')
596
+
597
+ await handler.handleToolError(error, 'run-123')
598
+
599
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
600
+ error,
601
+ })
602
+ })
603
+
604
+ it('should do nothing if span was not started', async () => {
605
+ await handler.handleToolError(new Error('test'), 'unknown-run-id')
606
+
607
+ expect(mockEndSpan).not.toHaveBeenCalled()
608
+ })
609
+
610
+ it('should handle errors in error handling', async () => {
611
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
612
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
613
+
614
+ await handler.handleToolError(new Error('Tool error'), 'run-123')
615
+
616
+ expect(consoleSpy).toHaveBeenCalledWith(
617
+ '[TracingCallbackHandler] handleToolError error:',
618
+ expect.any(Error)
619
+ )
620
+ consoleSpy.mockRestore()
621
+ })
622
+ })
623
+
624
+ describe('handleChainStart', () => {
625
+ it('should create span for chain execution', async () => {
626
+ const chain: Serialized = {
627
+ id: ['langchain', 'chains', 'llm_chain'],
628
+ }
629
+ mockStartSpan.mockResolvedValue({
630
+ spanId: 'span-123',
631
+ traceId,
632
+ name: 'Chain: llm_chain',
633
+ type: 'chain',
634
+ depth: 0,
635
+ startedAt: new Date(),
636
+ })
637
+
638
+ await handler.handleChainStart(chain, { input: 'test' }, 'run-123')
639
+
640
+ expect(mockStartSpan).toHaveBeenCalledWith(context, traceId, {
641
+ name: 'Chain: llm_chain',
642
+ type: 'chain',
643
+ parentSpanId: undefined,
644
+ depth: 0,
645
+ input: { input: 'test' },
646
+ })
647
+ })
648
+
649
+ it('should extract chain name from serialized id', async () => {
650
+ const chain: Serialized = {
651
+ id: ['langchain', 'chains', 'sequential_chain'],
652
+ }
653
+ mockStartSpan.mockResolvedValue(null)
654
+
655
+ await handler.handleChainStart(chain, {}, 'run-123')
656
+
657
+ expect(mockStartSpan).toHaveBeenCalledWith(
658
+ context,
659
+ traceId,
660
+ expect.objectContaining({
661
+ name: 'Chain: sequential_chain',
662
+ })
663
+ )
664
+ })
665
+
666
+ it('should use "unknown" for missing chain name', async () => {
667
+ const chain: Serialized = { id: [] }
668
+ mockStartSpan.mockResolvedValue(null)
669
+
670
+ await handler.handleChainStart(chain, {}, 'run-123')
671
+
672
+ expect(mockStartSpan).toHaveBeenCalledWith(
673
+ context,
674
+ traceId,
675
+ expect.objectContaining({
676
+ name: 'Chain: unknown',
677
+ })
678
+ )
679
+ })
680
+
681
+ it('should handle nested chains with depth', async () => {
682
+ // Grandparent chain
683
+ mockStartSpan.mockResolvedValueOnce({
684
+ spanId: 'span-grandparent',
685
+ traceId,
686
+ name: 'Chain: root',
687
+ type: 'chain',
688
+ depth: 0,
689
+ startedAt: new Date(),
690
+ })
691
+ await handler.handleChainStart(
692
+ { id: ['langchain', 'chains', 'root'] } as Serialized,
693
+ {},
694
+ 'run-grandparent'
695
+ )
696
+
697
+ // Parent chain
698
+ mockStartSpan.mockResolvedValueOnce({
699
+ spanId: 'span-parent',
700
+ traceId,
701
+ name: 'Chain: main',
702
+ type: 'chain',
703
+ depth: 1,
704
+ startedAt: new Date(),
705
+ })
706
+ await handler.handleChainStart(
707
+ { id: ['langchain', 'chains', 'main'] } as Serialized,
708
+ {},
709
+ 'run-parent',
710
+ 'run-grandparent'
711
+ )
712
+
713
+ // Child chain
714
+ mockStartSpan.mockResolvedValueOnce({
715
+ spanId: 'span-child',
716
+ traceId,
717
+ name: 'Chain: sub',
718
+ type: 'chain',
719
+ depth: 2,
720
+ startedAt: new Date(),
721
+ })
722
+ await handler.handleChainStart(
723
+ { id: ['langchain', 'chains', 'sub'] } as Serialized,
724
+ {},
725
+ 'run-child',
726
+ 'run-parent'
727
+ )
728
+
729
+ expect(mockStartSpan).toHaveBeenLastCalledWith(
730
+ context,
731
+ traceId,
732
+ expect.objectContaining({
733
+ depth: 2,
734
+ parentSpanId: 'span-parent',
735
+ })
736
+ )
737
+ })
738
+
739
+ it('should handle errors gracefully', async () => {
740
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
741
+ mockStartSpan.mockRejectedValue(new Error('Database error'))
742
+
743
+ await handler.handleChainStart(
744
+ { id: ['langchain', 'chains', 'test'] } as Serialized,
745
+ {},
746
+ 'run-123'
747
+ )
748
+
749
+ expect(consoleSpy).toHaveBeenCalledWith(
750
+ '[TracingCallbackHandler] handleChainStart error:',
751
+ expect.any(Error)
752
+ )
753
+ consoleSpy.mockRestore()
754
+ })
755
+ })
756
+
757
+ describe('handleChainEnd', () => {
758
+ beforeEach(async () => {
759
+ // Setup: Start a chain span first
760
+ mockStartSpan.mockResolvedValue({
761
+ spanId: 'span-123',
762
+ traceId,
763
+ name: 'Chain: test',
764
+ type: 'chain',
765
+ depth: 0,
766
+ startedAt: new Date(),
767
+ })
768
+ await handler.handleChainStart(
769
+ { id: ['langchain', 'chains', 'test'] } as Serialized,
770
+ {},
771
+ 'run-123'
772
+ )
773
+ jest.clearAllMocks()
774
+ })
775
+
776
+ it('should end span with chain outputs', async () => {
777
+ mockEndSpan.mockResolvedValue(undefined)
778
+
779
+ await handler.handleChainEnd({ output: 'result', metadata: { duration: 100 } }, 'run-123')
780
+
781
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
782
+ output: { output: 'result', metadata: { duration: 100 } },
783
+ })
784
+ })
785
+
786
+ it('should do nothing if span was not started', async () => {
787
+ await handler.handleChainEnd({}, 'unknown-run-id')
788
+
789
+ expect(mockEndSpan).not.toHaveBeenCalled()
790
+ })
791
+
792
+ it('should handle errors gracefully', async () => {
793
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
794
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
795
+
796
+ await handler.handleChainEnd({}, 'run-123')
797
+
798
+ expect(consoleSpy).toHaveBeenCalledWith(
799
+ '[TracingCallbackHandler] handleChainEnd error:',
800
+ expect.any(Error)
801
+ )
802
+ consoleSpy.mockRestore()
803
+ })
804
+ })
805
+
806
+ describe('handleChainError', () => {
807
+ beforeEach(async () => {
808
+ // Setup: Start a chain span first
809
+ mockStartSpan.mockResolvedValue({
810
+ spanId: 'span-123',
811
+ traceId,
812
+ name: 'Chain: test',
813
+ type: 'chain',
814
+ depth: 0,
815
+ startedAt: new Date(),
816
+ })
817
+ await handler.handleChainStart(
818
+ { id: ['langchain', 'chains', 'test'] } as Serialized,
819
+ {},
820
+ 'run-123'
821
+ )
822
+ jest.clearAllMocks()
823
+ })
824
+
825
+ it('should end span with error', async () => {
826
+ mockEndSpan.mockResolvedValue(undefined)
827
+ const error = new Error('Chain execution failed')
828
+
829
+ await handler.handleChainError(error, 'run-123')
830
+
831
+ expect(mockEndSpan).toHaveBeenCalledWith(context, traceId, 'span-123', {
832
+ error,
833
+ })
834
+ })
835
+
836
+ it('should do nothing if span was not started', async () => {
837
+ await handler.handleChainError(new Error('test'), 'unknown-run-id')
838
+
839
+ expect(mockEndSpan).not.toHaveBeenCalled()
840
+ })
841
+
842
+ it('should handle errors in error handling', async () => {
843
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation()
844
+ mockEndSpan.mockRejectedValue(new Error('Database error'))
845
+
846
+ await handler.handleChainError(new Error('Chain error'), 'run-123')
847
+
848
+ expect(consoleSpy).toHaveBeenCalledWith(
849
+ '[TracingCallbackHandler] handleChainError error:',
850
+ expect.any(Error)
851
+ )
852
+ consoleSpy.mockRestore()
853
+ })
854
+ })
855
+
856
+ describe('createTracingCallbacks factory', () => {
857
+ it('should create TracingCallbackHandler instance', () => {
858
+ const callbacks = createTracingCallbacks(context, traceId)
859
+
860
+ expect(callbacks).toBeInstanceOf(TracingCallbackHandler)
861
+ expect(callbacks.name).toBe('tracing_callback_handler')
862
+ })
863
+
864
+ it('should pass context and traceId to handler', async () => {
865
+ const callbacks = createTracingCallbacks(context, 'trace-xyz')
866
+ mockStartSpan.mockResolvedValue(null)
867
+
868
+ await callbacks.handleLLMStart(
869
+ { id: ['langchain', 'llms', 'openai'] } as Serialized,
870
+ ['test'],
871
+ 'run-123'
872
+ )
873
+
874
+ expect(mockStartSpan).toHaveBeenCalledWith(
875
+ context,
876
+ 'trace-xyz',
877
+ expect.any(Object)
878
+ )
879
+ })
880
+ })
881
+ })