@woodsportal/hubspot-kit 1.0.41-dev.4 → 1.0.41-dev.6

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 (580) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +191 -157
  3. package/dist/{chunk-S2YJDWZU.js → chunk-NWBK3XBZ.js} +1 -1
  4. package/dist/chunk-NWBK3XBZ.js.map +1 -0
  5. package/dist/chunk-WHYTQEZX.js +554 -0
  6. package/dist/chunk-WHYTQEZX.js.map +1 -0
  7. package/dist/cli.js +1494 -650
  8. package/dist/cli.js.map +1 -1
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.js +6 -2
  11. package/dist/index.js.map +1 -1
  12. package/dist/scaffold-page-lifecycle-EHMPWPPE.js +194 -0
  13. package/dist/scaffold-page-lifecycle-EHMPWPPE.js.map +1 -0
  14. package/dist/vite/index.js +1 -1
  15. package/examples/greenfield/module-hybrid-cdn/public/fields.json +73 -28
  16. package/examples/greenfield/module-hybrid-cdn/public/module.html +21 -5
  17. package/examples/greenfield/module-hybrid-cdn/src/assets/css/main.dev.css +15 -0
  18. package/examples/greenfield/module-hybrid-cdn/src/components/layout/starter-nav.tsx +12 -15
  19. package/examples/greenfield/module-hybrid-cdn/src/config/starter-nav.ts +9 -0
  20. package/examples/greenfield/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +25 -29
  21. package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +60 -0
  22. package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  23. package/examples/greenfield/module-hybrid-cdn/src/features/starter/hooks/use-starter-preview.ts +20 -37
  24. package/examples/greenfield/module-hybrid-cdn-auth-full/.env.dev.example +3 -0
  25. package/examples/greenfield/module-hybrid-cdn-auth-full/.env.local.example +6 -0
  26. package/examples/greenfield/module-hybrid-cdn-auth-full/.env.prod.example +5 -0
  27. package/examples/greenfield/module-hybrid-cdn-auth-full/.env.stg.example +3 -0
  28. package/examples/greenfield/module-hybrid-cdn-auth-full/.wphs/README.md +17 -0
  29. package/examples/greenfield/module-hybrid-cdn-auth-full/README.md +59 -0
  30. package/examples/greenfield/module-hybrid-cdn-auth-full/hubspot.config.yml.example +10 -0
  31. package/examples/greenfield/module-hybrid-cdn-auth-full/index.html +12 -0
  32. package/examples/greenfield/module-hybrid-cdn-auth-full/package.json +34 -0
  33. package/examples/greenfield/module-hybrid-cdn-auth-full/portal.cdn.json.example +8 -0
  34. package/examples/greenfield/module-hybrid-cdn-auth-full/public/fields.json +100 -0
  35. package/examples/greenfield/module-hybrid-cdn-auth-full/public/meta.json +9 -0
  36. package/examples/greenfield/module-hybrid-cdn-auth-full/public/module.html +38 -0
  37. package/examples/greenfield/module-hybrid-cdn-auth-full/src/app.tsx +172 -0
  38. package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/main.dev.css +23 -0
  39. package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/main.prod.css +12 -0
  40. package/examples/greenfield/module-hybrid-cdn-auth-full/src/assets/css/tailwind.css.config.css +7 -0
  41. package/examples/greenfield/module-hybrid-cdn-auth-full/src/cdn/app-entry.ts +48 -0
  42. package/examples/greenfield/module-hybrid-cdn-auth-full/src/cdn/vendor-entry.ts +28 -0
  43. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
  44. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/AuthLayout.tsx +7 -0
  45. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/DefaultLayout.tsx +5 -0
  46. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/MainLayout.tsx +42 -0
  47. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
  48. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/Layouts/index.ts +13 -0
  49. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/section-card.tsx +20 -0
  50. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/starter-nav.tsx +28 -0
  51. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/layout/starter-shell.tsx +18 -0
  52. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Button.tsx +22 -0
  53. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Form.tsx +22 -0
  54. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Input.tsx +12 -0
  55. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/Toaster.tsx +5 -0
  56. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/auth/SsoLoginButton.tsx +34 -0
  57. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/link.tsx +10 -0
  58. package/examples/greenfield/module-hybrid-cdn-auth-full/src/components/ui/loader/loader.tsx +18 -0
  59. package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/auth-tier.ts +4 -0
  60. package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/nav-scaffold.ts +4 -0
  61. package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/portal-nav.ts +9 -0
  62. package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/router.ts +1 -0
  63. package/examples/greenfield/module-hybrid-cdn-auth-full/src/config/routes.ts +10 -0
  64. package/examples/greenfield/module-hybrid-cdn-auth-full/src/data/hubSpotContext.ts +57 -0
  65. package/examples/greenfield/module-hybrid-cdn-auth-full/src/data/sync-sdk-hub-context.ts +44 -0
  66. package/examples/greenfield/module-hybrid-cdn-auth-full/src/defaultData.ts +2 -0
  67. package/examples/greenfield/module-hybrid-cdn-auth-full/src/env.ts +59 -0
  68. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/app-bootstrap-guards.ts +14 -0
  69. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-navigation.ts +81 -0
  70. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-route-paths.ts +29 -0
  71. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-session-events.ts +2 -0
  72. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/auth-session.ts +51 -0
  73. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/clear-client-auth-session.ts +62 -0
  74. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/components/starter-login.tsx +83 -0
  75. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/login-email-storage.ts +64 -0
  76. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/login-session-bridge.ts +74 -0
  77. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-otp-utils.ts +24 -0
  78. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-pending-storage.ts +108 -0
  79. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/mfa-portal.ts +45 -0
  80. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/post-login-path.ts +17 -0
  81. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/resolve-auth-login-error-message.ts +52 -0
  82. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/resolve-post-login-path.ts +21 -0
  83. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/sso-callback.ts +120 -0
  84. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-mfa-login-otp-flow.ts +78 -0
  85. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-portal-session.ts +25 -0
  86. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-signed-in-email.ts +55 -0
  87. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/auth/use-sso-callback-handler.ts +92 -0
  88. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-guide-page.tsx +210 -0
  89. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-home-page.tsx +21 -0
  90. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-home.tsx +48 -0
  91. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-page-frame.tsx +25 -0
  92. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-protected-page.tsx +54 -0
  93. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/components/starter-single-page.tsx +17 -0
  94. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/content/auth-guide-content.ts +85 -0
  95. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/content/woodscli-guide-content.ts +86 -0
  96. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/starter-preview-values.ts +60 -0
  97. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  98. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/hooks/use-starter-preview.ts +30 -0
  99. package/examples/greenfield/module-hybrid-cdn-auth-full/src/features/starter/index.ts +6 -0
  100. package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/bootstrap.ts +16 -0
  101. package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/default-data.ts +7 -0
  102. package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/is-cms-editor.ts +78 -0
  103. package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/module-entry.tsx +9 -0
  104. package/examples/greenfield/module-hybrid-cdn-auth-full/src/hubspot/types.ts +17 -0
  105. package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/configure-woodsportal-sdk.ts +98 -0
  106. package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/tanstack-query/root-provider.tsx +18 -0
  107. package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/tanstack-router/router.tsx +39 -0
  108. package/examples/greenfield/module-hybrid-cdn-auth-full/src/integrations/woodsportal-sdk.ts +37 -0
  109. package/examples/greenfield/module-hybrid-cdn-auth-full/src/main.tsx +5 -0
  110. package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/api-endpoint.ts +11 -0
  111. package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
  112. package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/mount-dev-preview.tsx +12 -0
  113. package/examples/greenfield/module-hybrid-cdn-auth-full/src/portal/mount-portal.tsx +57 -0
  114. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routeTree.gen.ts +182 -0
  115. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/__root.tsx +12 -0
  116. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_app/portal.tsx +10 -0
  117. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/login/sso.tsx +27 -0
  118. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/login.tsx +10 -0
  119. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/_auth/two-fa.tsx +91 -0
  120. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/guide.tsx +9 -0
  121. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/index.tsx +8 -0
  122. package/examples/greenfield/module-hybrid-cdn-auth-full/src/routes/unauthorized.tsx +50 -0
  123. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/ValidationSchema.ts +1 -0
  124. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/cookie.ts +23 -0
  125. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/normalize-router-path.ts +10 -0
  126. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/private-route.tsx +100 -0
  127. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/public-route.tsx +64 -0
  128. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/resolve-api-base-url.ts +44 -0
  129. package/examples/greenfield/module-hybrid-cdn-auth-full/src/utils/safe-router-navigation.ts +82 -0
  130. package/examples/greenfield/module-hybrid-cdn-auth-full/tsconfig.json +20 -0
  131. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-app.mjs +1 -0
  132. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-esm.mjs +1 -0
  133. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.cdn-vendor.mjs +1 -0
  134. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.dev.mjs +1 -0
  135. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.hubspot.mjs +1 -0
  136. package/examples/greenfield/module-hybrid-cdn-auth-full/vite.config.monolith.mjs +8 -0
  137. package/examples/greenfield/module-hybrid-cdn-auth-full/woodscli.json +38 -0
  138. package/examples/greenfield/module-hybrid-cdn-auth-full/wphs.adapter.ts +7 -0
  139. package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.dev.example +3 -0
  140. package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.local.example +6 -0
  141. package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.prod.example +5 -0
  142. package/examples/greenfield/module-hybrid-cdn-auth-sso/.env.stg.example +3 -0
  143. package/examples/greenfield/module-hybrid-cdn-auth-sso/.wphs/README.md +17 -0
  144. package/examples/greenfield/module-hybrid-cdn-auth-sso/README.md +59 -0
  145. package/examples/greenfield/module-hybrid-cdn-auth-sso/hubspot.config.yml.example +10 -0
  146. package/examples/greenfield/module-hybrid-cdn-auth-sso/index.html +12 -0
  147. package/examples/greenfield/module-hybrid-cdn-auth-sso/package.json +34 -0
  148. package/examples/greenfield/module-hybrid-cdn-auth-sso/portal.cdn.json.example +8 -0
  149. package/examples/greenfield/module-hybrid-cdn-auth-sso/public/fields.json +100 -0
  150. package/examples/greenfield/module-hybrid-cdn-auth-sso/public/meta.json +9 -0
  151. package/examples/greenfield/module-hybrid-cdn-auth-sso/public/module.html +38 -0
  152. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/app.tsx +180 -0
  153. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/main.dev.css +23 -0
  154. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/main.prod.css +12 -0
  155. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/assets/css/tailwind.css.config.css +7 -0
  156. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/cdn/app-entry.ts +48 -0
  157. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/cdn/vendor-entry.ts +28 -0
  158. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
  159. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/AuthLayout.tsx +7 -0
  160. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/DefaultLayout.tsx +5 -0
  161. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/MainLayout.tsx +42 -0
  162. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
  163. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/Layouts/index.ts +13 -0
  164. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/section-card.tsx +20 -0
  165. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/starter-nav.tsx +28 -0
  166. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/layout/starter-shell.tsx +18 -0
  167. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Button.tsx +22 -0
  168. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Form.tsx +22 -0
  169. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Input.tsx +12 -0
  170. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/Toaster.tsx +5 -0
  171. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/auth/SsoLoginButton.tsx +34 -0
  172. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/link.tsx +10 -0
  173. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/components/ui/loader/loader.tsx +18 -0
  174. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/auth-tier.ts +4 -0
  175. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/nav-scaffold.ts +4 -0
  176. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/portal-nav.ts +9 -0
  177. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/router.ts +1 -0
  178. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/config/routes.ts +10 -0
  179. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/data/hubSpotContext.ts +57 -0
  180. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/data/sync-sdk-hub-context.ts +44 -0
  181. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/defaultData.ts +2 -0
  182. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/env.ts +59 -0
  183. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/app-bootstrap-guards.ts +14 -0
  184. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-navigation.ts +81 -0
  185. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-route-paths.ts +29 -0
  186. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-session-events.ts +2 -0
  187. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/auth-session.ts +26 -0
  188. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/clear-client-auth-session.ts +62 -0
  189. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/components/starter-login.tsx +83 -0
  190. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/login-email-storage.ts +64 -0
  191. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/login-session-bridge.ts +72 -0
  192. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/post-login-path.ts +17 -0
  193. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/resolve-auth-login-error-message.ts +52 -0
  194. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/resolve-post-login-path.ts +21 -0
  195. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/sso-callback.ts +120 -0
  196. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-portal-session.ts +25 -0
  197. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-signed-in-email.ts +55 -0
  198. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/auth/use-sso-callback-handler.ts +92 -0
  199. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-guide-page.tsx +210 -0
  200. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-home-page.tsx +21 -0
  201. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-home.tsx +48 -0
  202. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-page-frame.tsx +25 -0
  203. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-protected-page.tsx +54 -0
  204. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/components/starter-single-page.tsx +17 -0
  205. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/content/auth-guide-content.ts +85 -0
  206. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/content/woodscli-guide-content.ts +86 -0
  207. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/starter-preview-values.ts +60 -0
  208. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  209. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/hooks/use-starter-preview.ts +30 -0
  210. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/features/starter/index.ts +6 -0
  211. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/bootstrap.ts +16 -0
  212. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/default-data.ts +7 -0
  213. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/is-cms-editor.ts +78 -0
  214. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/module-entry.tsx +9 -0
  215. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/hubspot/types.ts +17 -0
  216. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/configure-woodsportal-sdk.ts +94 -0
  217. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/tanstack-query/root-provider.tsx +18 -0
  218. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/tanstack-router/router.tsx +39 -0
  219. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/integrations/woodsportal-sdk.ts +37 -0
  220. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/main.tsx +5 -0
  221. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/api-endpoint.ts +11 -0
  222. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
  223. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/mount-dev-preview.tsx +12 -0
  224. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/portal/mount-portal.tsx +57 -0
  225. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routeTree.gen.ts +155 -0
  226. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/__root.tsx +12 -0
  227. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_app/portal.tsx +10 -0
  228. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_auth/login/sso.tsx +27 -0
  229. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/_auth/login.tsx +10 -0
  230. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/guide.tsx +9 -0
  231. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/index.tsx +8 -0
  232. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/routes/unauthorized.tsx +50 -0
  233. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/ValidationSchema.ts +1 -0
  234. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/cookie.ts +23 -0
  235. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/normalize-router-path.ts +10 -0
  236. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/private-route.tsx +100 -0
  237. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/public-route.tsx +64 -0
  238. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/resolve-api-base-url.ts +44 -0
  239. package/examples/greenfield/module-hybrid-cdn-auth-sso/src/utils/safe-router-navigation.ts +82 -0
  240. package/examples/greenfield/module-hybrid-cdn-auth-sso/tsconfig.json +20 -0
  241. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-app.mjs +1 -0
  242. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-esm.mjs +1 -0
  243. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.cdn-vendor.mjs +1 -0
  244. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.dev.mjs +1 -0
  245. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.hubspot.mjs +1 -0
  246. package/examples/greenfield/module-hybrid-cdn-auth-sso/vite.config.monolith.mjs +8 -0
  247. package/examples/greenfield/module-hybrid-cdn-auth-sso/woodscli.json +38 -0
  248. package/examples/greenfield/module-hybrid-cdn-auth-sso/wphs.adapter.ts +7 -0
  249. package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.dev.example +3 -0
  250. package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.local.example +6 -0
  251. package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.prod.example +5 -0
  252. package/examples/greenfield/module-hybrid-cdn-auth-starter/.env.stg.example +3 -0
  253. package/examples/greenfield/module-hybrid-cdn-auth-starter/.wphs/README.md +17 -0
  254. package/examples/greenfield/module-hybrid-cdn-auth-starter/README.md +59 -0
  255. package/examples/greenfield/module-hybrid-cdn-auth-starter/hubspot.config.yml.example +10 -0
  256. package/examples/greenfield/module-hybrid-cdn-auth-starter/index.html +12 -0
  257. package/examples/greenfield/module-hybrid-cdn-auth-starter/package.json +34 -0
  258. package/examples/greenfield/module-hybrid-cdn-auth-starter/portal.cdn.json.example +8 -0
  259. package/examples/greenfield/module-hybrid-cdn-auth-starter/public/fields.json +100 -0
  260. package/examples/greenfield/module-hybrid-cdn-auth-starter/public/meta.json +9 -0
  261. package/examples/greenfield/module-hybrid-cdn-auth-starter/public/module.html +38 -0
  262. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/app.tsx +173 -0
  263. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/main.dev.css +23 -0
  264. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/main.prod.css +12 -0
  265. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/assets/css/tailwind.css.config.css +7 -0
  266. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/cdn/app-entry.ts +48 -0
  267. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/cdn/vendor-entry.ts +28 -0
  268. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
  269. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/AuthLayout.tsx +7 -0
  270. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/DefaultLayout.tsx +5 -0
  271. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/MainLayout.tsx +42 -0
  272. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
  273. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/Layouts/index.ts +13 -0
  274. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/section-card.tsx +20 -0
  275. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/starter-nav.tsx +28 -0
  276. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/layout/starter-shell.tsx +18 -0
  277. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Button.tsx +22 -0
  278. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Form.tsx +22 -0
  279. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Input.tsx +12 -0
  280. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/Toaster.tsx +5 -0
  281. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/link.tsx +10 -0
  282. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/components/ui/loader/loader.tsx +18 -0
  283. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/auth-tier.ts +4 -0
  284. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/nav-scaffold.ts +4 -0
  285. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/portal-nav.ts +9 -0
  286. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/router.ts +1 -0
  287. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/config/routes.ts +10 -0
  288. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/data/hubSpotContext.ts +57 -0
  289. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/data/sync-sdk-hub-context.ts +44 -0
  290. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/defaultData.ts +2 -0
  291. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/env.ts +59 -0
  292. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/app-bootstrap-guards.ts +14 -0
  293. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-navigation.ts +81 -0
  294. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-route-paths.ts +29 -0
  295. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-session-events.ts +2 -0
  296. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/auth-session.ts +26 -0
  297. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/clear-client-auth-session.ts +62 -0
  298. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/components/starter-login.tsx +76 -0
  299. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/login-email-storage.ts +64 -0
  300. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/login-session-bridge.ts +72 -0
  301. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/post-login-path.ts +17 -0
  302. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/resolve-auth-login-error-message.ts +52 -0
  303. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/resolve-post-login-path.ts +21 -0
  304. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/use-portal-session.ts +25 -0
  305. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/auth/use-signed-in-email.ts +55 -0
  306. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-guide-page.tsx +218 -0
  307. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-home-page.tsx +21 -0
  308. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-home.tsx +48 -0
  309. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-page-frame.tsx +25 -0
  310. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-protected-page.tsx +54 -0
  311. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/components/starter-single-page.tsx +17 -0
  312. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/content/auth-guide-content.ts +91 -0
  313. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/content/woodscli-guide-content.ts +86 -0
  314. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/starter-preview-values.ts +60 -0
  315. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  316. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/hooks/use-starter-preview.ts +30 -0
  317. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/features/starter/index.ts +6 -0
  318. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/bootstrap.ts +16 -0
  319. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/default-data.ts +7 -0
  320. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/is-cms-editor.ts +78 -0
  321. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/module-entry.tsx +9 -0
  322. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/hubspot/types.ts +17 -0
  323. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/configure-woodsportal-sdk.ts +94 -0
  324. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/tanstack-query/root-provider.tsx +18 -0
  325. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/tanstack-router/router.tsx +39 -0
  326. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/integrations/woodsportal-sdk.ts +37 -0
  327. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/main.tsx +5 -0
  328. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/api-endpoint.ts +11 -0
  329. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
  330. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/mount-dev-preview.tsx +12 -0
  331. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/portal/mount-portal.tsx +52 -0
  332. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routeTree.gen.ts +134 -0
  333. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/__root.tsx +12 -0
  334. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/_app/portal.tsx +10 -0
  335. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/_auth/login.tsx +10 -0
  336. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/guide.tsx +9 -0
  337. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/index.tsx +8 -0
  338. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/routes/unauthorized.tsx +50 -0
  339. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/ValidationSchema.ts +1 -0
  340. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/cookie.ts +23 -0
  341. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/normalize-router-path.ts +10 -0
  342. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/private-route.tsx +100 -0
  343. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/public-route.tsx +64 -0
  344. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/resolve-api-base-url.ts +44 -0
  345. package/examples/greenfield/module-hybrid-cdn-auth-starter/src/utils/safe-router-navigation.ts +82 -0
  346. package/examples/greenfield/module-hybrid-cdn-auth-starter/tsconfig.json +20 -0
  347. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-app.mjs +1 -0
  348. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-esm.mjs +1 -0
  349. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.cdn-vendor.mjs +1 -0
  350. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.dev.mjs +1 -0
  351. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.hubspot.mjs +1 -0
  352. package/examples/greenfield/module-hybrid-cdn-auth-starter/vite.config.monolith.mjs +8 -0
  353. package/examples/greenfield/module-hybrid-cdn-auth-starter/woodscli.json +38 -0
  354. package/examples/greenfield/module-hybrid-cdn-auth-starter/wphs.adapter.ts +7 -0
  355. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.dev.example +3 -0
  356. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.local.example +6 -0
  357. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.prod.example +5 -0
  358. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.env.stg.example +3 -0
  359. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/.wphs/README.md +17 -0
  360. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/README.md +59 -0
  361. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/hubspot.config.yml.example +10 -0
  362. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/index.html +12 -0
  363. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/package.json +34 -0
  364. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/portal.cdn.json.example +8 -0
  365. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/fields.json +100 -0
  366. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/meta.json +9 -0
  367. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/public/module.html +38 -0
  368. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/app.tsx +173 -0
  369. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/main.dev.css +23 -0
  370. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/main.prod.css +12 -0
  371. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/assets/css/tailwind.css.config.css +7 -0
  372. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/cdn/app-entry.ts +48 -0
  373. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/cdn/vendor-entry.ts +28 -0
  374. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
  375. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/AuthLayout.tsx +7 -0
  376. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/DefaultLayout.tsx +5 -0
  377. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/MainLayout.tsx +7 -0
  378. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
  379. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/Layouts/index.ts +13 -0
  380. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/portal-sidebar-nav.tsx +33 -0
  381. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/portal-sidebar.tsx +139 -0
  382. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/section-card.tsx +20 -0
  383. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/starter-nav.tsx +28 -0
  384. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/layout/starter-shell.tsx +18 -0
  385. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Button.tsx +22 -0
  386. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Form.tsx +22 -0
  387. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Input.tsx +12 -0
  388. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/Toaster.tsx +5 -0
  389. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/link.tsx +10 -0
  390. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/components/ui/loader/loader.tsx +18 -0
  391. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/auth-tier.ts +4 -0
  392. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/nav-scaffold.ts +4 -0
  393. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/portal-nav.ts +9 -0
  394. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/router.ts +1 -0
  395. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/config/routes.ts +10 -0
  396. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/data/hubSpotContext.ts +57 -0
  397. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/data/sync-sdk-hub-context.ts +44 -0
  398. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/defaultData.ts +2 -0
  399. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/env.ts +59 -0
  400. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/app-bootstrap-guards.ts +14 -0
  401. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-navigation.ts +81 -0
  402. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-route-paths.ts +29 -0
  403. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-session-events.ts +2 -0
  404. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/auth-session.ts +26 -0
  405. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/clear-client-auth-session.ts +62 -0
  406. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/components/starter-login.tsx +76 -0
  407. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/login-email-storage.ts +64 -0
  408. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/login-session-bridge.ts +72 -0
  409. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/post-login-path.ts +17 -0
  410. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/resolve-auth-login-error-message.ts +52 -0
  411. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/resolve-post-login-path.ts +21 -0
  412. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/use-portal-session.ts +25 -0
  413. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/auth/use-signed-in-email.ts +55 -0
  414. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-guide-page.tsx +210 -0
  415. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-home-page.tsx +21 -0
  416. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-home.tsx +48 -0
  417. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-page-frame.tsx +25 -0
  418. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-protected-page.tsx +54 -0
  419. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/components/starter-single-page.tsx +17 -0
  420. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/content/auth-guide-content.ts +85 -0
  421. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/content/woodscli-guide-content.ts +86 -0
  422. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/starter-preview-values.ts +60 -0
  423. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  424. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/hooks/use-starter-preview.ts +30 -0
  425. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/features/starter/index.ts +6 -0
  426. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/bootstrap.ts +16 -0
  427. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/default-data.ts +7 -0
  428. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/is-cms-editor.ts +78 -0
  429. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/module-entry.tsx +9 -0
  430. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/hubspot/types.ts +17 -0
  431. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/configure-woodsportal-sdk.ts +94 -0
  432. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/tanstack-query/root-provider.tsx +18 -0
  433. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/tanstack-router/router.tsx +39 -0
  434. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/integrations/woodsportal-sdk.ts +37 -0
  435. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/main.tsx +5 -0
  436. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/api-endpoint.ts +11 -0
  437. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/ensure-portal-cdn-stylesheet.ts +115 -0
  438. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/mount-dev-preview.tsx +12 -0
  439. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/portal/mount-portal.tsx +52 -0
  440. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routeTree.gen.ts +134 -0
  441. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/__root.tsx +12 -0
  442. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/_app/portal.tsx +10 -0
  443. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/_auth/login.tsx +10 -0
  444. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/guide.tsx +9 -0
  445. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/index.tsx +8 -0
  446. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/routes/unauthorized.tsx +50 -0
  447. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/ValidationSchema.ts +1 -0
  448. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/cookie.ts +23 -0
  449. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/normalize-router-path.ts +10 -0
  450. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/private-route.tsx +100 -0
  451. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/public-route.tsx +64 -0
  452. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/resolve-api-base-url.ts +44 -0
  453. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/src/utils/safe-router-navigation.ts +82 -0
  454. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/tsconfig.json +20 -0
  455. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-app.mjs +1 -0
  456. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-esm.mjs +1 -0
  457. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.cdn-vendor.mjs +1 -0
  458. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.dev.mjs +1 -0
  459. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.hubspot.mjs +1 -0
  460. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/vite.config.monolith.mjs +8 -0
  461. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/woodscli.json +38 -0
  462. package/examples/greenfield/module-hybrid-cdn-auth-starter-sidebar/wphs.adapter.ts +7 -0
  463. package/examples/greenfield/module-monolith/public/fields.json +66 -16
  464. package/examples/greenfield/module-monolith/src/assets/css/main.dev.css +7 -0
  465. package/examples/greenfield/module-monolith/src/components/layout/starter-nav.tsx +12 -15
  466. package/examples/greenfield/module-monolith/src/config/starter-nav.ts +9 -0
  467. package/examples/greenfield/module-monolith/src/features/starter/components/starter-home.tsx +25 -29
  468. package/examples/greenfield/module-monolith/src/features/starter/hooks/starter-preview-values.ts +60 -0
  469. package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.dev.ts +18 -0
  470. package/examples/greenfield/module-monolith/src/features/starter/hooks/use-starter-preview.ts +20 -37
  471. package/examples/manifest.json +5 -1
  472. package/examples/smoke/run-matrix.mjs +37 -0
  473. package/package.json +7 -5
  474. package/scripts/cli-smoke-helpers.mjs +150 -0
  475. package/scripts/regenerate-route-tree.mjs +71 -0
  476. package/scripts/sync-auth-scaffold-from-client.mjs +41 -0
  477. package/scripts/wp-cli-smoke.test.mjs +263 -0
  478. package/src/dev/bootstrap-greenfield.tsx +6 -0
  479. package/templates/module-hybrid-cdn/public/fields.json +47 -7
  480. package/templates/module-hybrid-cdn/public/module.html +7 -6
  481. package/templates/module-hybrid-cdn/src/assets/css/main.dev.css +7 -0
  482. package/templates/module-hybrid-cdn/src/components/layout/starter-nav.tsx +12 -15
  483. package/templates/module-hybrid-cdn/src/config/starter-nav.ts +9 -0
  484. package/templates/module-hybrid-cdn/src/features/starter/components/starter-home.tsx +25 -27
  485. package/templates/module-hybrid-cdn/src/features/starter/hooks/starter-preview-values.ts +5 -5
  486. package/templates/module-monolith/public/fields.json +49 -6
  487. package/templates/module-monolith/src/assets/css/main.dev.css +7 -0
  488. package/templates/module-monolith/src/components/layout/starter-nav.tsx +12 -15
  489. package/templates/module-monolith/src/config/starter-nav.ts +9 -0
  490. package/templates/module-monolith/src/features/starter/components/starter-home.tsx +25 -27
  491. package/templates/module-monolith/src/features/starter/hooks/starter-preview-values.ts +5 -5
  492. package/templates/scaffold/auth/_full/src/app.tsx +172 -0
  493. package/templates/scaffold/auth/_full/src/config/auth-tier.ts +4 -0
  494. package/templates/scaffold/auth/_full/src/features/auth/auth-session.ts +51 -0
  495. package/templates/scaffold/auth/_full/src/features/auth/login-session-bridge.ts +74 -0
  496. package/templates/scaffold/auth/_full/src/features/auth/mfa-otp-utils.ts +24 -0
  497. package/templates/scaffold/auth/_full/src/features/auth/mfa-pending-storage.ts +108 -0
  498. package/templates/scaffold/auth/_full/src/features/auth/mfa-portal.ts +45 -0
  499. package/templates/scaffold/auth/_full/src/features/auth/use-mfa-login-otp-flow.ts +78 -0
  500. package/templates/scaffold/auth/_full/src/routeTree.gen.ts +182 -0
  501. package/templates/scaffold/auth/_full/src/routes/_auth/two-fa.tsx +91 -0
  502. package/templates/scaffold/auth/_nav-sidebar/src/components/Layouts/MainLayout.tsx +7 -0
  503. package/templates/scaffold/auth/_nav-sidebar/src/components/layout/portal-sidebar-nav.tsx +33 -0
  504. package/templates/scaffold/auth/_nav-sidebar/src/components/layout/portal-sidebar.tsx +139 -0
  505. package/templates/scaffold/auth/_nav-top/src/components/Layouts/MainLayout.tsx +42 -0
  506. package/templates/scaffold/auth/_shared/src/app.tsx +178 -0
  507. package/templates/scaffold/auth/_shared/src/components/Layouts/AppLayoutWrapper.tsx +38 -0
  508. package/templates/scaffold/auth/_shared/src/components/Layouts/AuthLayout.tsx +7 -0
  509. package/templates/scaffold/auth/_shared/src/components/Layouts/DefaultLayout.tsx +5 -0
  510. package/templates/scaffold/auth/_shared/src/components/Layouts/UnauthorizedLayout.tsx +5 -0
  511. package/templates/scaffold/auth/_shared/src/components/Layouts/index.ts +13 -0
  512. package/templates/scaffold/auth/_shared/src/components/ui/Button.tsx +22 -0
  513. package/templates/scaffold/auth/_shared/src/components/ui/Form.tsx +22 -0
  514. package/templates/scaffold/auth/_shared/src/components/ui/Input.tsx +12 -0
  515. package/templates/scaffold/auth/_shared/src/components/ui/Toaster.tsx +5 -0
  516. package/templates/scaffold/auth/_shared/src/components/ui/link.tsx +10 -0
  517. package/templates/scaffold/auth/_shared/src/components/ui/loader/loader.tsx +18 -0
  518. package/templates/scaffold/auth/_shared/src/config/auth-tier.ts +4 -0
  519. package/templates/scaffold/auth/_shared/src/config/nav-scaffold.ts +4 -0
  520. package/templates/scaffold/auth/_shared/src/config/portal-nav.ts +9 -0
  521. package/templates/scaffold/auth/_shared/src/config/router.ts +1 -0
  522. package/templates/scaffold/auth/_shared/src/config/routes.ts +10 -0
  523. package/templates/scaffold/auth/_shared/src/data/hubSpotContext.ts +57 -0
  524. package/templates/scaffold/auth/_shared/src/data/sync-sdk-hub-context.ts +44 -0
  525. package/templates/scaffold/auth/_shared/src/env.ts +59 -0
  526. package/templates/scaffold/auth/_shared/src/features/auth/app-bootstrap-guards.ts +14 -0
  527. package/templates/scaffold/auth/_shared/src/features/auth/auth-navigation.ts +81 -0
  528. package/templates/scaffold/auth/_shared/src/features/auth/auth-route-paths.ts +29 -0
  529. package/templates/scaffold/auth/_shared/src/features/auth/auth-session-events.ts +2 -0
  530. package/templates/scaffold/auth/_shared/src/features/auth/auth-session.ts +26 -0
  531. package/templates/scaffold/auth/_shared/src/features/auth/clear-client-auth-session.ts +62 -0
  532. package/templates/scaffold/auth/_shared/src/features/auth/components/starter-login.tsx +76 -0
  533. package/templates/scaffold/auth/_shared/src/features/auth/login-email-storage.ts +64 -0
  534. package/templates/scaffold/auth/_shared/src/features/auth/login-session-bridge.ts +72 -0
  535. package/templates/scaffold/auth/_shared/src/features/auth/post-login-path.ts +17 -0
  536. package/templates/scaffold/auth/_shared/src/features/auth/resolve-auth-login-error-message.ts +52 -0
  537. package/templates/scaffold/auth/_shared/src/features/auth/resolve-post-login-path.ts +21 -0
  538. package/templates/scaffold/auth/_shared/src/features/auth/use-portal-session.ts +25 -0
  539. package/templates/scaffold/auth/_shared/src/features/auth/use-signed-in-email.ts +55 -0
  540. package/templates/scaffold/auth/_shared/src/features/starter/components/starter-guide-page.tsx +218 -0
  541. package/templates/scaffold/auth/_shared/src/features/starter/components/starter-page-frame.tsx +25 -0
  542. package/templates/scaffold/auth/_shared/src/features/starter/components/starter-protected-page.tsx +54 -0
  543. package/templates/scaffold/auth/_shared/src/features/starter/content/auth-guide-content.ts +91 -0
  544. package/templates/scaffold/auth/_shared/src/hubspot/is-cms-editor.ts +78 -0
  545. package/templates/scaffold/auth/_shared/src/integrations/configure-woodsportal-sdk.ts +100 -0
  546. package/templates/scaffold/auth/_shared/src/integrations/tanstack-query/root-provider.tsx +18 -0
  547. package/templates/scaffold/auth/_shared/src/integrations/tanstack-router/router.tsx +39 -0
  548. package/templates/scaffold/auth/_shared/src/integrations/woodsportal-sdk.ts +37 -0
  549. package/templates/scaffold/auth/_shared/src/portal/mount-portal.tsx +59 -0
  550. package/templates/scaffold/auth/_shared/src/routeTree.gen.ts +134 -0
  551. package/templates/scaffold/auth/_shared/src/routes/__root.tsx +12 -0
  552. package/templates/scaffold/auth/_shared/src/routes/_app/portal.tsx +10 -0
  553. package/templates/scaffold/auth/_shared/src/routes/_auth/login.tsx +10 -0
  554. package/templates/scaffold/auth/_shared/src/routes/guide.tsx +9 -0
  555. package/templates/scaffold/auth/_shared/src/routes/index.tsx +8 -0
  556. package/templates/scaffold/auth/_shared/src/routes/unauthorized.tsx +50 -0
  557. package/templates/scaffold/auth/_shared/src/utils/ValidationSchema.ts +1 -0
  558. package/templates/scaffold/auth/_shared/src/utils/cookie.ts +23 -0
  559. package/templates/scaffold/auth/_shared/src/utils/normalize-router-path.ts +10 -0
  560. package/templates/scaffold/auth/_shared/src/utils/private-route.tsx +100 -0
  561. package/templates/scaffold/auth/_shared/src/utils/public-route.tsx +64 -0
  562. package/templates/scaffold/auth/_shared/src/utils/resolve-api-base-url.ts +44 -0
  563. package/templates/scaffold/auth/_shared/src/utils/safe-router-navigation.ts +82 -0
  564. package/templates/scaffold/auth/_sso/src/app.tsx +180 -0
  565. package/templates/scaffold/auth/_sso/src/components/ui/auth/SsoLoginButton.tsx +34 -0
  566. package/templates/scaffold/auth/_sso/src/config/auth-tier.ts +4 -0
  567. package/templates/scaffold/auth/_sso/src/features/auth/components/starter-login.tsx +83 -0
  568. package/templates/scaffold/auth/_sso/src/features/auth/sso-callback.ts +120 -0
  569. package/templates/scaffold/auth/_sso/src/features/auth/use-sso-callback-handler.ts +92 -0
  570. package/templates/scaffold/auth/_sso/src/routeTree.gen.ts +155 -0
  571. package/templates/scaffold/auth/_sso/src/routes/_auth/login/sso.tsx +27 -0
  572. package/templates/scaffold/auth/nav-manifest.json +8 -0
  573. package/templates/scaffold/auth/sync-manifest.json +16 -0
  574. package/templates/scaffold/auth/tier-manifest.json +18 -0
  575. package/templates/scaffold/page/page-component-auth.tsx.tpl +10 -0
  576. package/templates/scaffold/page/page-component-plain.tsx.tpl +11 -0
  577. package/templates/scaffold/page/route-protected-auth.tsx.tpl +10 -0
  578. package/templates/scaffold/page/route-public-auth.tsx.tpl +9 -0
  579. package/templates/scaffold/page/route-public-plain.tsx.tpl +6 -0
  580. package/dist/chunk-S2YJDWZU.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -94,10 +94,18 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
94
94
  scaffold: z.ZodOptional<z.ZodObject<{
95
95
  /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */
96
96
  router: z.ZodDefault<z.ZodEnum<["hash", "browser", "none"]>>;
97
+ /** Portal user auth tier (module templates only) */
98
+ auth: z.ZodDefault<z.ZodEnum<["none", "starter", "sso", "full"]>>;
99
+ /** Authenticated shell navigation — only when auth ≠ none */
100
+ nav: z.ZodOptional<z.ZodDefault<z.ZodEnum<["top", "sidebar"]>>>;
97
101
  }, "strip", z.ZodTypeAny, {
98
102
  router: "none" | "hash" | "browser";
103
+ auth: "none" | "starter" | "sso" | "full";
104
+ nav?: "top" | "sidebar" | undefined;
99
105
  }, {
100
106
  router?: "none" | "hash" | "browser" | undefined;
107
+ auth?: "none" | "starter" | "sso" | "full" | undefined;
108
+ nav?: "top" | "sidebar" | undefined;
101
109
  }>>;
102
110
  }, "strip", z.ZodTypeAny, {
103
111
  configVersion: number;
@@ -136,6 +144,8 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
136
144
  } | undefined;
137
145
  scaffold?: {
138
146
  router: "none" | "hash" | "browser";
147
+ auth: "none" | "starter" | "sso" | "full";
148
+ nav?: "top" | "sidebar" | undefined;
139
149
  } | undefined;
140
150
  }, {
141
151
  projectType: "module-hybrid-cdn" | "module-monolith" | "theme-starter";
@@ -174,6 +184,8 @@ declare const WoodsPortalHsSchema: z.ZodObject<{
174
184
  } | undefined;
175
185
  scaffold?: {
176
186
  router?: "none" | "hash" | "browser" | undefined;
187
+ auth?: "none" | "starter" | "sso" | "full" | undefined;
188
+ nav?: "top" | "sidebar" | undefined;
177
189
  } | undefined;
178
190
  }>;
179
191
  type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>;
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  getViteConfigPath,
9
9
  kitPath,
10
10
  resolveProjectConfig
11
- } from "./chunk-S2YJDWZU.js";
11
+ } from "./chunk-NWBK3XBZ.js";
12
12
 
13
13
  // src/schema/woodsportal-hs.ts
14
14
  import { z } from "zod";
@@ -52,7 +52,11 @@ var WoodsPortalHsSchema = z.object({
52
52
  }).default({}),
53
53
  scaffold: z.object({
54
54
  /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */
55
- router: z.enum(["hash", "browser", "none"]).default("hash")
55
+ router: z.enum(["hash", "browser", "none"]).default("hash"),
56
+ /** Portal user auth tier (module templates only) */
57
+ auth: z.enum(["none", "starter", "sso", "full"]).default("none"),
58
+ /** Authenticated shell navigation — only when auth ≠ none */
59
+ nav: z.enum(["top", "sidebar"]).default("top").optional()
56
60
  }).optional()
57
61
  });
58
62
  var DEFAULT_WOODSPORTAL_HS = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/schema/woodsportal-hs.ts","../src/lib/config.ts","../src/adapters/types.ts","../src/lib/generic-field-mapper.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const CONFIG_VERSION = 1\n\nexport const WPHS_WORKSPACE_DIR = '.wphs' as const\n\nexport const WoodsPortalHsSchema = z.object({\n configVersion: z.number().int().default(CONFIG_VERSION),\n projectType: z.enum(['module-hybrid-cdn', 'module-monolith', 'theme-starter']),\n module: z.object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n outDir: z.string().default('dist/WoodsPortal.Module'),\n }),\n theme: z\n .object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n srcDir: z.string().default('src/theme'),\n themeJson: z.string().default('src/theme/theme.json'),\n })\n .optional(),\n cdn: z\n .object({\n mode: z.enum(['jsdelivr', 'custom', 'local', 'none']).default('jsdelivr'),\n buildIdFile: z.string().default('.module-build-id'),\n mirror: z.string().default('portal.cdn.json'),\n })\n .default({}),\n entries: z\n .object({\n app: z.string().default('src/cdn/app-entry.ts'),\n vendor: z.string().default('src/cdn/vendor-entry.ts'),\n hubspotBootstrap: z.string().default('src/hubspot/bootstrap.ts'),\n /** Monolith production IIFE entry (wp build --hubspot-only) */\n module: z.string().default('src/hubspot/module-entry.tsx'),\n localDev: z.string().default('src/main.tsx'),\n })\n .default({}),\n env: z\n .object({\n tiers: z.array(z.enum(['dev', 'stg', 'prod'])).default(['dev', 'stg', 'prod']),\n })\n .default({}),\n dev: z\n .object({\n moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),\n adapter: z.string().default('./wphs.adapter.ts'),\n fieldsJson: z.string().default('public/fields.json'),\n themeFieldsJson: z.string().optional(),\n })\n .default({}),\n scaffold: z\n .object({\n /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */\n router: z.enum(['hash', 'browser', 'none']).default('hash'),\n })\n .optional(),\n})\n\nexport type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>\n\nexport const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig = {\n configVersion: CONFIG_VERSION,\n projectType: 'module-hybrid-cdn',\n module: {\n name: 'woodsportal-module',\n uploadPath: 'dw-modules/woodsportal-module.module',\n outDir: 'dist/WoodsPortal.Module',\n },\n cdn: {\n mode: 'jsdelivr',\n buildIdFile: '.module-build-id',\n mirror: 'portal.cdn.json',\n },\n entries: {\n app: 'src/cdn/app-entry.ts',\n vendor: 'src/cdn/vendor-entry.ts',\n hubspotBootstrap: 'src/hubspot/bootstrap.ts',\n module: 'src/hubspot/module-entry.tsx',\n localDev: 'src/main.tsx',\n },\n env: {\n tiers: ['dev', 'stg', 'prod'],\n },\n dev: {\n moduleConfigDir: WPHS_WORKSPACE_DIR,\n adapter: './wphs.adapter.ts',\n fieldsJson: 'public/fields.json',\n },\n}\n\nexport function isModuleProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' || config.projectType === 'module-monolith'\n}\n\nexport function isThemeProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'theme-starter'\n}\n\nexport function usesCdnPipeline(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' && config.cdn.mode !== 'none'\n}\n","import { readFileSync } from 'node:fs'\n\nimport {\n resolveProjectConfig,\n WOODSCLI_CONFIG_FILE,\n} from './config-files.js'\nimport { WoodsPortalHsSchema, type WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { WpHsError, ExitCode } from './errors.js'\nimport { findProjectRoot } from './paths.js'\n\nexport type LoadedProjectConfig = {\n root: string\n config: WoodsPortalHsConfig\n configPath: string\n configFileName: string\n}\n\nexport function loadConfig(projectRoot?: string): LoadedProjectConfig {\n const root = projectRoot ?? findProjectRoot()\n const resolved = resolveProjectConfig(root)\n\n if (!resolved) {\n throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init. Legacy woodsportal.hs.json → run wp migrate --yes.',\n })\n }\n\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(resolved.path, 'utf8'))\n } catch (cause) {\n throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {\n exitCode: ExitCode.USER_ERROR,\n cause,\n })\n }\n\n const parsed = WoodsPortalHsSchema.safeParse(raw)\n if (!parsed.success) {\n throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp config validate for details.',\n })\n }\n\n return {\n root,\n config: parsed.data,\n configPath: resolved.path,\n configFileName: resolved.fileName,\n }\n}\n\nexport function validateConfigFile(projectRoot?: string): {\n valid: boolean\n errors: string[]\n config?: WoodsPortalHsConfig\n} {\n try {\n const { config } = loadConfig(projectRoot)\n return { valid: true, errors: [], config }\n } catch (error) {\n if (error instanceof WpHsError) {\n return { valid: false, errors: [error.message] }\n }\n return { valid: false, errors: [String(error)] }\n }\n}\n","/** Must match dev-module-config/schema/types.ts */\nexport const MODULE_CONFIG_CHANGED = 'woodsportal:module-config-changed'\n\nexport type ModuleConfigBuiltState = {\n built: Record<string, unknown>\n}\n\n/** WoodsCLI consumer hook — mount app + sync module-config live data. */\nexport interface HubSpotKitAdapter {\n syncLiveData(built: Record<string, unknown>): void\n getDefaultHubSpotData?(): Record<string, unknown>\n mountDevApp?(root: unknown): void | Promise<void>\n}\n\nexport function bindAdapterToModuleConfig(adapter: HubSpotKitAdapter): void {\n if (typeof globalThis.window === 'undefined') return\n globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event: Event) => {\n const detail = (event as CustomEvent<ModuleConfigBuiltState>).detail\n adapter.syncLiveData(detail.built)\n })\n}\n\nexport function syncAdapterFromModuleState(\n adapter: HubSpotKitAdapter,\n state: ModuleConfigBuiltState,\n): void {\n adapter.syncLiveData(state.built)\n}\n\n/** Minimal adapter for greenfield templates without a full app. */\nexport const defaultHubSpotKitAdapter: HubSpotKitAdapter = {\n syncLiveData(built) {\n const w = globalThis as typeof globalThis & { __WPHS_DATA__?: Record<string, unknown> }\n w.__WPHS_DATA__ = built\n },\n getDefaultHubSpotData() {\n return {}\n },\n}\n","/**\n * Generic fields.json → flat runtime object (WoodsCLI default transform).\n */\nexport type GenericFieldDefinition = {\n name: string\n type: string\n default?: unknown\n children?: GenericFieldDefinition[]\n}\n\nexport type GenericFieldValues = Record<string, unknown>\n\nfunction flattenField(\n field: GenericFieldDefinition,\n values: GenericFieldValues,\n out: Record<string, unknown>,\n): void {\n const key = field.name\n const value = values[key]\n\n if (field.type === 'group' && field.children && value && typeof value === 'object') {\n const groupValues = value as GenericFieldValues\n for (const child of field.children) {\n flattenField(child, groupValues, out)\n }\n return\n }\n\n if (field.type === 'repeater' && Array.isArray(value)) {\n out[key] = value\n return\n }\n\n out[key] = value ?? field.default ?? null\n}\n\nexport function mapFieldsToRuntime(\n schema: GenericFieldDefinition[],\n values: GenericFieldValues,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const field of schema) {\n flattenField(field, values, out)\n }\n return out\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/schema/woodsportal-hs.ts","../src/lib/config.ts","../src/adapters/types.ts","../src/lib/generic-field-mapper.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const CONFIG_VERSION = 1\n\nexport const WPHS_WORKSPACE_DIR = '.wphs' as const\n\nexport const WoodsPortalHsSchema = z.object({\n configVersion: z.number().int().default(CONFIG_VERSION),\n projectType: z.enum(['module-hybrid-cdn', 'module-monolith', 'theme-starter']),\n module: z.object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n outDir: z.string().default('dist/WoodsPortal.Module'),\n }),\n theme: z\n .object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n srcDir: z.string().default('src/theme'),\n themeJson: z.string().default('src/theme/theme.json'),\n })\n .optional(),\n cdn: z\n .object({\n mode: z.enum(['jsdelivr', 'custom', 'local', 'none']).default('jsdelivr'),\n buildIdFile: z.string().default('.module-build-id'),\n mirror: z.string().default('portal.cdn.json'),\n })\n .default({}),\n entries: z\n .object({\n app: z.string().default('src/cdn/app-entry.ts'),\n vendor: z.string().default('src/cdn/vendor-entry.ts'),\n hubspotBootstrap: z.string().default('src/hubspot/bootstrap.ts'),\n /** Monolith production IIFE entry (wp build --hubspot-only) */\n module: z.string().default('src/hubspot/module-entry.tsx'),\n localDev: z.string().default('src/main.tsx'),\n })\n .default({}),\n env: z\n .object({\n tiers: z.array(z.enum(['dev', 'stg', 'prod'])).default(['dev', 'stg', 'prod']),\n })\n .default({}),\n dev: z\n .object({\n moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),\n adapter: z.string().default('./wphs.adapter.ts'),\n fieldsJson: z.string().default('public/fields.json'),\n themeFieldsJson: z.string().optional(),\n })\n .default({}),\n scaffold: z\n .object({\n /** hash = woodsportal-client-frontend (HubSpot CMS); browser = local clean URLs; none = single page */\n router: z.enum(['hash', 'browser', 'none']).default('hash'),\n /** Portal user auth tier (module templates only) */\n auth: z.enum(['none', 'starter', 'sso', 'full']).default('none'),\n /** Authenticated shell navigation — only when auth ≠ none */\n nav: z.enum(['top', 'sidebar']).default('top').optional(),\n })\n .optional(),\n})\n\nexport type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>\n\nexport const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig = {\n configVersion: CONFIG_VERSION,\n projectType: 'module-hybrid-cdn',\n module: {\n name: 'woodsportal-module',\n uploadPath: 'dw-modules/woodsportal-module.module',\n outDir: 'dist/WoodsPortal.Module',\n },\n cdn: {\n mode: 'jsdelivr',\n buildIdFile: '.module-build-id',\n mirror: 'portal.cdn.json',\n },\n entries: {\n app: 'src/cdn/app-entry.ts',\n vendor: 'src/cdn/vendor-entry.ts',\n hubspotBootstrap: 'src/hubspot/bootstrap.ts',\n module: 'src/hubspot/module-entry.tsx',\n localDev: 'src/main.tsx',\n },\n env: {\n tiers: ['dev', 'stg', 'prod'],\n },\n dev: {\n moduleConfigDir: WPHS_WORKSPACE_DIR,\n adapter: './wphs.adapter.ts',\n fieldsJson: 'public/fields.json',\n },\n}\n\nexport function isModuleProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' || config.projectType === 'module-monolith'\n}\n\nexport function isThemeProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'theme-starter'\n}\n\nexport function usesCdnPipeline(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' && config.cdn.mode !== 'none'\n}\n","import { readFileSync } from 'node:fs'\n\nimport {\n resolveProjectConfig,\n WOODSCLI_CONFIG_FILE,\n} from './config-files.js'\nimport { WoodsPortalHsSchema, type WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { WpHsError, ExitCode } from './errors.js'\nimport { findProjectRoot } from './paths.js'\n\nexport type LoadedProjectConfig = {\n root: string\n config: WoodsPortalHsConfig\n configPath: string\n configFileName: string\n}\n\nexport function loadConfig(projectRoot?: string): LoadedProjectConfig {\n const root = projectRoot ?? findProjectRoot()\n const resolved = resolveProjectConfig(root)\n\n if (!resolved) {\n throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init. Legacy woodsportal.hs.json → run wp migrate --yes.',\n })\n }\n\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(resolved.path, 'utf8'))\n } catch (cause) {\n throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {\n exitCode: ExitCode.USER_ERROR,\n cause,\n })\n }\n\n const parsed = WoodsPortalHsSchema.safeParse(raw)\n if (!parsed.success) {\n throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp config validate for details.',\n })\n }\n\n return {\n root,\n config: parsed.data,\n configPath: resolved.path,\n configFileName: resolved.fileName,\n }\n}\n\nexport function validateConfigFile(projectRoot?: string): {\n valid: boolean\n errors: string[]\n config?: WoodsPortalHsConfig\n} {\n try {\n const { config } = loadConfig(projectRoot)\n return { valid: true, errors: [], config }\n } catch (error) {\n if (error instanceof WpHsError) {\n return { valid: false, errors: [error.message] }\n }\n return { valid: false, errors: [String(error)] }\n }\n}\n","/** Must match dev-module-config/schema/types.ts */\nexport const MODULE_CONFIG_CHANGED = 'woodsportal:module-config-changed'\n\nexport type ModuleConfigBuiltState = {\n built: Record<string, unknown>\n}\n\n/** WoodsCLI consumer hook — mount app + sync module-config live data. */\nexport interface HubSpotKitAdapter {\n syncLiveData(built: Record<string, unknown>): void\n getDefaultHubSpotData?(): Record<string, unknown>\n mountDevApp?(root: unknown): void | Promise<void>\n}\n\nexport function bindAdapterToModuleConfig(adapter: HubSpotKitAdapter): void {\n if (typeof globalThis.window === 'undefined') return\n globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event: Event) => {\n const detail = (event as CustomEvent<ModuleConfigBuiltState>).detail\n adapter.syncLiveData(detail.built)\n })\n}\n\nexport function syncAdapterFromModuleState(\n adapter: HubSpotKitAdapter,\n state: ModuleConfigBuiltState,\n): void {\n adapter.syncLiveData(state.built)\n}\n\n/** Minimal adapter for greenfield templates without a full app. */\nexport const defaultHubSpotKitAdapter: HubSpotKitAdapter = {\n syncLiveData(built) {\n const w = globalThis as typeof globalThis & { __WPHS_DATA__?: Record<string, unknown> }\n w.__WPHS_DATA__ = built\n },\n getDefaultHubSpotData() {\n return {}\n },\n}\n","/**\n * Generic fields.json → flat runtime object (WoodsCLI default transform).\n */\nexport type GenericFieldDefinition = {\n name: string\n type: string\n default?: unknown\n children?: GenericFieldDefinition[]\n}\n\nexport type GenericFieldValues = Record<string, unknown>\n\nfunction flattenField(\n field: GenericFieldDefinition,\n values: GenericFieldValues,\n out: Record<string, unknown>,\n): void {\n const key = field.name\n const value = values[key]\n\n if (field.type === 'group' && field.children && value && typeof value === 'object') {\n const groupValues = value as GenericFieldValues\n for (const child of field.children) {\n flattenField(child, groupValues, out)\n }\n return\n }\n\n if (field.type === 'repeater' && Array.isArray(value)) {\n out[key] = value\n return\n }\n\n out[key] = value ?? field.default ?? null\n}\n\nexport function mapFieldsToRuntime(\n schema: GenericFieldDefinition[],\n values: GenericFieldValues,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const field of schema) {\n flattenField(field, values, out)\n }\n return out\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isModuleProject,
4
+ isThemeProject,
5
+ normalizePageSlug,
6
+ regenerateRouteTree,
7
+ removeNavItemsEntry,
8
+ resolveProjectNavConfig,
9
+ slugToCamelCase,
10
+ slugToPascalCase
11
+ } from "./chunk-WHYTQEZX.js";
12
+ import {
13
+ ExitCode,
14
+ WpHsError
15
+ } from "./chunk-4GOJPOW3.js";
16
+
17
+ // src/lib/scaffold-page-lifecycle.ts
18
+ import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
19
+ import { resolve } from "path";
20
+ var BUILTIN_ROUTE_KEYS = /* @__PURE__ */ new Set([
21
+ "home",
22
+ "guide",
23
+ "login",
24
+ "twoFa",
25
+ "unauthorized",
26
+ "ssoLogin",
27
+ "portal",
28
+ "app"
29
+ ]);
30
+ function assertModuleWithRouter(config) {
31
+ if (isThemeProject(config)) {
32
+ throw new WpHsError("wp page is only supported for module projects.", {
33
+ exitCode: ExitCode.USER_ERROR
34
+ });
35
+ }
36
+ if (!isModuleProject(config)) {
37
+ throw new WpHsError("wp page requires a module project.", { exitCode: ExitCode.USER_ERROR });
38
+ }
39
+ if ((config.scaffold?.router ?? "hash") === "none") {
40
+ throw new WpHsError("wp page requires client-side routing.", {
41
+ exitCode: ExitCode.USER_ERROR
42
+ });
43
+ }
44
+ }
45
+ function routesConfigPath(projectRoot) {
46
+ const path = resolve(projectRoot, "src/config/routes.ts");
47
+ if (!existsSync(path)) {
48
+ throw new WpHsError("Missing src/config/routes.ts", { exitCode: ExitCode.USER_ERROR });
49
+ }
50
+ return path;
51
+ }
52
+ function parseRoutePaths(routesFile) {
53
+ const content = readFileSync(routesFile, "utf8");
54
+ const entries = {};
55
+ const pattern = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*:\s*'([^']*)'/gm;
56
+ for (const match of content.matchAll(pattern)) {
57
+ entries[match[1]] = match[2];
58
+ }
59
+ return entries;
60
+ }
61
+ function navContainsRoutesKey(projectRoot, routesKey) {
62
+ const navConfig = resolveProjectNavConfig(projectRoot);
63
+ if (!navConfig) {
64
+ return false;
65
+ }
66
+ const content = readFileSync(navConfig.filePath, "utf8");
67
+ return new RegExp(`\\.${routesKey}\\b`).test(content);
68
+ }
69
+ function detectPageAccess(projectRoot, slug) {
70
+ const protectedRoute = resolve(projectRoot, "src/routes/_app", `${slug}.tsx`);
71
+ if (existsSync(protectedRoute)) {
72
+ return "protected";
73
+ }
74
+ return "public";
75
+ }
76
+ function listScaffoldedPages(projectRoot, config) {
77
+ assertModuleWithRouter(config);
78
+ const pagesDir = resolve(projectRoot, "src/features/pages");
79
+ if (!existsSync(pagesDir)) {
80
+ return [];
81
+ }
82
+ const routesFile = routesConfigPath(projectRoot);
83
+ const routePaths = parseRoutePaths(routesFile);
84
+ const pages = [];
85
+ for (const file of readdirSync(pagesDir)) {
86
+ if (!file.endsWith("-page.tsx")) {
87
+ continue;
88
+ }
89
+ const slug = file.replace(/-page\.tsx$/, "");
90
+ const routesKey = slugToCamelCase(slug);
91
+ if (BUILTIN_ROUTE_KEYS.has(routesKey)) {
92
+ continue;
93
+ }
94
+ const access = detectPageAccess(projectRoot, slug);
95
+ const routeRelative = access === "protected" ? `src/routes/_app/${slug}.tsx` : `src/routes/${slug}.tsx`;
96
+ pages.push({
97
+ slug,
98
+ routesKey,
99
+ path: routePaths[routesKey] ?? `/${slug}`,
100
+ access,
101
+ routeFile: routeRelative,
102
+ pageFile: `src/features/pages/${slug}-page.tsx`,
103
+ inNav: navContainsRoutesKey(projectRoot, routesKey)
104
+ });
105
+ }
106
+ return pages.sort((a, b) => a.slug.localeCompare(b.slug));
107
+ }
108
+ function removeRoutesKeyFromFile(routesFile, routesKey) {
109
+ const content = readFileSync(routesFile, "utf8");
110
+ const linePattern = new RegExp(`^\\s*${routesKey}\\s*:\\s*'[^']*',?\\n`, "m");
111
+ const updated = content.replace(linePattern, "");
112
+ if (updated === content) {
113
+ throw new WpHsError(`Routes key "${routesKey}" not found in src/config/routes.ts.`, {
114
+ exitCode: ExitCode.USER_ERROR
115
+ });
116
+ }
117
+ writeFileSync(routesFile, updated);
118
+ }
119
+ function removePagesBarrelExport(projectRoot, slug, componentName) {
120
+ const barrelPath = resolve(projectRoot, "src/features/pages/index.ts");
121
+ if (!existsSync(barrelPath)) {
122
+ return;
123
+ }
124
+ const exportLine = `export { ${componentName} } from './${slug}-page'`;
125
+ const content = readFileSync(barrelPath, "utf8");
126
+ const updated = content.split("\n").filter((line) => line.trim() !== exportLine).join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
127
+ if (updated) {
128
+ writeFileSync(barrelPath, `${updated}
129
+ `);
130
+ } else {
131
+ rmSync(barrelPath);
132
+ }
133
+ }
134
+ function removeScaffoldedPage(input) {
135
+ assertModuleWithRouter(input.config);
136
+ const slug = normalizePageSlug(input.slug);
137
+ const routesKey = slugToCamelCase(slug);
138
+ const componentName = `${slugToPascalCase(slug)}Page`;
139
+ if (BUILTIN_ROUTE_KEYS.has(routesKey)) {
140
+ throw new WpHsError(`Cannot remove built-in route key "${routesKey}".`, {
141
+ exitCode: ExitCode.USER_ERROR
142
+ });
143
+ }
144
+ const pageFile = resolve(input.projectRoot, "src/features/pages", `${slug}-page.tsx`);
145
+ const access = detectPageAccess(input.projectRoot, slug);
146
+ const routeRelative = access === "protected" ? `src/routes/_app/${slug}.tsx` : `src/routes/${slug}.tsx`;
147
+ const routeFile = resolve(input.projectRoot, routeRelative);
148
+ if (!existsSync(pageFile)) {
149
+ throw new WpHsError(`Scaffolded page not found: src/features/pages/${slug}-page.tsx`, {
150
+ exitCode: ExitCode.USER_ERROR,
151
+ remediation: "Use wp page list to see scaffolded pages."
152
+ });
153
+ }
154
+ if (input.dryRun) {
155
+ input.logger?.info(`[dry-run] Would remove ${routeRelative} and src/features/pages/${slug}-page.tsx`);
156
+ return {
157
+ slug,
158
+ removedRoute: routeRelative,
159
+ removedPage: `src/features/pages/${slug}-page.tsx`,
160
+ routeTreeRegenerated: false
161
+ };
162
+ }
163
+ const { projectRoot } = input;
164
+ rmSync(pageFile);
165
+ if (existsSync(routeFile)) {
166
+ rmSync(routeFile);
167
+ }
168
+ removeRoutesKeyFromFile(routesConfigPath(projectRoot), routesKey);
169
+ removePagesBarrelExport(projectRoot, slug, componentName);
170
+ const navConfig = resolveProjectNavConfig(projectRoot);
171
+ if (input.removeFromNav !== false && navConfig && navContainsRoutesKey(projectRoot, routesKey)) {
172
+ try {
173
+ removeNavItemsEntry(navConfig, routesKey);
174
+ } catch {
175
+ input.logger?.warn(`Nav entry for "${routesKey}" was not removed \u2014 edit ${navConfig.relativePath} manually.`);
176
+ }
177
+ }
178
+ const regen = regenerateRouteTree(projectRoot, { logger: input.logger, dryRun: input.dryRun });
179
+ if (regen.warning) {
180
+ input.logger?.warn(regen.warning);
181
+ }
182
+ return {
183
+ slug,
184
+ removedRoute: routeRelative,
185
+ removedPage: `src/features/pages/${slug}-page.tsx`,
186
+ routeTreeRegenerated: regen.ok,
187
+ routeTreeWarning: regen.warning
188
+ };
189
+ }
190
+ export {
191
+ listScaffoldedPages,
192
+ removeScaffoldedPage
193
+ };
194
+ //# sourceMappingURL=scaffold-page-lifecycle-EHMPWPPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/scaffold-page-lifecycle.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport type { WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { isModuleProject, isThemeProject } from '../schema/woodsportal-hs.js'\nimport { ExitCode, WpHsError } from './errors.js'\nimport type { Logger } from './logger.js'\nimport {\n removeNavItemsEntry,\n resolveProjectNavConfig,\n} from './project-nav-config.js'\nimport { regenerateRouteTree } from './regenerate-route-tree.js'\nimport {\n normalizePageSlug,\n type PageAccess,\n slugToCamelCase,\n slugToPascalCase,\n} from './scaffold-page.js'\n\nexport type ListedPage = {\n slug: string\n routesKey: string\n path: string\n access: PageAccess\n routeFile: string\n pageFile: string\n inNav: boolean\n}\n\nconst BUILTIN_ROUTE_KEYS = new Set([\n 'home',\n 'guide',\n 'login',\n 'twoFa',\n 'unauthorized',\n 'ssoLogin',\n 'portal',\n 'app',\n])\n\nfunction assertModuleWithRouter(config: WoodsPortalHsConfig): void {\n if (isThemeProject(config)) {\n throw new WpHsError('wp page is only supported for module projects.', {\n exitCode: ExitCode.USER_ERROR,\n })\n }\n if (!isModuleProject(config)) {\n throw new WpHsError('wp page requires a module project.', { exitCode: ExitCode.USER_ERROR })\n }\n if ((config.scaffold?.router ?? 'hash') === 'none') {\n throw new WpHsError('wp page requires client-side routing.', {\n exitCode: ExitCode.USER_ERROR,\n })\n }\n}\n\nfunction routesConfigPath(projectRoot: string): string {\n const path = resolve(projectRoot, 'src/config/routes.ts')\n if (!existsSync(path)) {\n throw new WpHsError('Missing src/config/routes.ts', { exitCode: ExitCode.USER_ERROR })\n }\n return path\n}\n\nfunction parseRoutePaths(routesFile: string): Record<string, string> {\n const content = readFileSync(routesFile, 'utf8')\n const entries: Record<string, string> = {}\n const pattern = /^\\s*([a-zA-Z][a-zA-Z0-9_]*)\\s*:\\s*'([^']*)'/gm\n for (const match of content.matchAll(pattern)) {\n entries[match[1]] = match[2]\n }\n return entries\n}\n\nfunction navContainsRoutesKey(projectRoot: string, routesKey: string): boolean {\n const navConfig = resolveProjectNavConfig(projectRoot)\n if (!navConfig) {\n return false\n }\n const content = readFileSync(navConfig.filePath, 'utf8')\n return new RegExp(`\\\\.${routesKey}\\\\b`).test(content)\n}\n\nfunction detectPageAccess(projectRoot: string, slug: string): PageAccess {\n const protectedRoute = resolve(projectRoot, 'src/routes/_app', `${slug}.tsx`)\n if (existsSync(protectedRoute)) {\n return 'protected'\n }\n return 'public'\n}\n\nexport function listScaffoldedPages(projectRoot: string, config: WoodsPortalHsConfig): ListedPage[] {\n assertModuleWithRouter(config)\n\n const pagesDir = resolve(projectRoot, 'src/features/pages')\n if (!existsSync(pagesDir)) {\n return []\n }\n\n const routesFile = routesConfigPath(projectRoot)\n const routePaths = parseRoutePaths(routesFile)\n\n const pages: ListedPage[] = []\n for (const file of readdirSync(pagesDir)) {\n if (!file.endsWith('-page.tsx')) {\n continue\n }\n const slug = file.replace(/-page\\.tsx$/, '')\n const routesKey = slugToCamelCase(slug)\n if (BUILTIN_ROUTE_KEYS.has(routesKey)) {\n continue\n }\n\n const access = detectPageAccess(projectRoot, slug)\n const routeRelative =\n access === 'protected' ? `src/routes/_app/${slug}.tsx` : `src/routes/${slug}.tsx`\n\n pages.push({\n slug,\n routesKey,\n path: routePaths[routesKey] ?? `/${slug}`,\n access,\n routeFile: routeRelative,\n pageFile: `src/features/pages/${slug}-page.tsx`,\n inNav: navContainsRoutesKey(projectRoot, routesKey),\n })\n }\n\n return pages.sort((a, b) => a.slug.localeCompare(b.slug))\n}\n\nfunction removeRoutesKeyFromFile(routesFile: string, routesKey: string): void {\n const content = readFileSync(routesFile, 'utf8')\n const linePattern = new RegExp(`^\\\\s*${routesKey}\\\\s*:\\\\s*'[^']*',?\\\\n`, 'm')\n const updated = content.replace(linePattern, '')\n if (updated === content) {\n throw new WpHsError(`Routes key \"${routesKey}\" not found in src/config/routes.ts.`, {\n exitCode: ExitCode.USER_ERROR,\n })\n }\n writeFileSync(routesFile, updated)\n}\n\nfunction removePagesBarrelExport(projectRoot: string, slug: string, componentName: string): void {\n const barrelPath = resolve(projectRoot, 'src/features/pages/index.ts')\n if (!existsSync(barrelPath)) {\n return\n }\n const exportLine = `export { ${componentName} } from './${slug}-page'`\n const content = readFileSync(barrelPath, 'utf8')\n const updated = content\n .split('\\n')\n .filter((line) => line.trim() !== exportLine)\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trimEnd()\n if (updated) {\n writeFileSync(barrelPath, `${updated}\\n`)\n } else {\n rmSync(barrelPath)\n }\n}\n\nexport type RemovePageInput = {\n projectRoot: string\n config: WoodsPortalHsConfig\n slug: string\n removeFromNav?: boolean\n dryRun?: boolean\n logger?: Logger\n}\n\nexport type RemovePageResult = {\n slug: string\n removedRoute: string\n removedPage: string\n routeTreeRegenerated: boolean\n routeTreeWarning?: string\n}\n\nexport function removeScaffoldedPage(input: RemovePageInput): RemovePageResult {\n assertModuleWithRouter(input.config)\n\n const slug = normalizePageSlug(input.slug)\n const routesKey = slugToCamelCase(slug)\n const componentName = `${slugToPascalCase(slug)}Page`\n\n if (BUILTIN_ROUTE_KEYS.has(routesKey)) {\n throw new WpHsError(`Cannot remove built-in route key \"${routesKey}\".`, {\n exitCode: ExitCode.USER_ERROR,\n })\n }\n\n const pageFile = resolve(input.projectRoot, 'src/features/pages', `${slug}-page.tsx`)\n const access = detectPageAccess(input.projectRoot, slug)\n const routeRelative =\n access === 'protected' ? `src/routes/_app/${slug}.tsx` : `src/routes/${slug}.tsx`\n const routeFile = resolve(input.projectRoot, routeRelative)\n\n if (!existsSync(pageFile)) {\n throw new WpHsError(`Scaffolded page not found: src/features/pages/${slug}-page.tsx`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Use wp page list to see scaffolded pages.',\n })\n }\n\n if (input.dryRun) {\n input.logger?.info(`[dry-run] Would remove ${routeRelative} and src/features/pages/${slug}-page.tsx`)\n return {\n slug,\n removedRoute: routeRelative,\n removedPage: `src/features/pages/${slug}-page.tsx`,\n routeTreeRegenerated: false,\n }\n }\n\n const { projectRoot } = input\n rmSync(pageFile)\n if (existsSync(routeFile)) {\n rmSync(routeFile)\n }\n\n removeRoutesKeyFromFile(routesConfigPath(projectRoot), routesKey)\n removePagesBarrelExport(projectRoot, slug, componentName)\n\n const navConfig = resolveProjectNavConfig(projectRoot)\n if (input.removeFromNav !== false && navConfig && navContainsRoutesKey(projectRoot, routesKey)) {\n try {\n removeNavItemsEntry(navConfig, routesKey)\n } catch {\n input.logger?.warn(`Nav entry for \"${routesKey}\" was not removed — edit ${navConfig.relativePath} manually.`)\n }\n }\n\n const regen = regenerateRouteTree(projectRoot, { logger: input.logger, dryRun: input.dryRun })\n if (regen.warning) {\n input.logger?.warn(regen.warning)\n }\n\n return {\n slug,\n removedRoute: routeRelative,\n removedPage: `src/features/pages/${slug}-page.tsx`,\n routeTreeRegenerated: regen.ok,\n routeTreeWarning: regen.warning,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createViteConfig,
3
3
  getViteConfigPath
4
- } from "../chunk-S2YJDWZU.js";
4
+ } from "../chunk-NWBK3XBZ.js";
5
5
  export {
6
6
  createViteConfig,
7
7
  getViteConfigPath
@@ -1,41 +1,86 @@
1
1
  [
2
2
  {
3
- "name": "title",
4
- "label": "Portal title",
5
- "type": "text",
6
- "tab": "CONTENT",
7
- "default": "My HubSpot Portal"
8
- },
9
- {
10
- "name": "tagline",
11
- "label": "Tagline",
12
- "type": "text",
13
- "display": "textarea",
14
- "tab": "CONTENT",
15
- "help_text": "Subtitle shown under the hero title.",
16
- "default": "Customize this preview with Ctrl+Shift+Z, then replace StarterPage with your portal app."
17
- },
18
- {
19
- "name": "show_feature_cards",
20
- "label": "Show feature cards",
21
- "type": "boolean",
22
- "display": "toggle",
3
+ "type": "group",
4
+ "name": "content",
5
+ "label": "Content",
23
6
  "tab": "CONTENT",
24
- "default": true
7
+ "children": [
8
+ {
9
+ "name": "title",
10
+ "label": "Portal title",
11
+ "type": "text",
12
+ "default": "My HubSpot Portal"
13
+ },
14
+ {
15
+ "name": "tagline",
16
+ "label": "Tagline",
17
+ "type": "text",
18
+ "display": "textarea",
19
+ "help_text": "Subtitle shown under the hero title.",
20
+ "default": "Customize this preview with Ctrl+Shift+Z, then replace StarterPage with your portal app."
21
+ },
22
+ {
23
+ "name": "show_feature_cards",
24
+ "label": "Show feature cards",
25
+ "type": "boolean",
26
+ "display": "toggle",
27
+ "default": true
28
+ }
29
+ ]
25
30
  },
26
31
  {
27
32
  "type": "group",
28
- "name": "styles",
29
- "label": "Styles",
33
+ "name": "style",
34
+ "label": "Style",
30
35
  "tab": "STYLE",
31
36
  "children": [
32
37
  {
33
- "name": "accent_color",
34
- "label": "Accent color",
35
- "type": "color",
38
+ "type": "group",
39
+ "name": "module_global_colors",
40
+ "label": "Module Global Colors",
41
+ "tab": "STYLE",
42
+ "children": [
43
+ {
44
+ "name": "primary_color",
45
+ "label": "Primary Color",
46
+ "type": "color",
47
+ "default": {
48
+ "color": "#FF7A59",
49
+ "opacity": 100
50
+ }
51
+ },
52
+ {
53
+ "name": "secondary_color",
54
+ "label": "Secondary Color",
55
+ "type": "color",
56
+ "default": {
57
+ "color": "#2797AF",
58
+ "opacity": 100
59
+ }
60
+ },
61
+ {
62
+ "name": "button_text_color",
63
+ "label": "Button Text Color",
64
+ "type": "color",
65
+ "default": {
66
+ "color": "#FFFFFF",
67
+ "opacity": 100
68
+ }
69
+ }
70
+ ],
36
71
  "default": {
37
- "color": "#0091ae",
38
- "opacity": 100
72
+ "primary_color": {
73
+ "color": "#FF7A59",
74
+ "opacity": 100
75
+ },
76
+ "secondary_color": {
77
+ "color": "#2797AF",
78
+ "opacity": 100
79
+ },
80
+ "button_text_color": {
81
+ "color": "#FFFFFF",
82
+ "opacity": 100
83
+ }
39
84
  }
40
85
  },
41
86
  {
@@ -1,13 +1,29 @@
1
1
  {# Hybrid CDN — React app loads from jsDelivr; module.js is a thin bootstrap only. #}
2
2
  {% set in_cms_editor = is_in_editor or is_in_page_editor or is_in_previewer %}
3
+ {% require_css %}
4
+ <style>
5
+ {% scope_css %}
6
+ :root {
7
+ --primary-color: {{ module.style.module_global_colors.primary_color.color|default('#FF7A59') }};
8
+ --secondary-color: {{ module.style.module_global_colors.secondary_color.color|default('#2797AF') }};
9
+ --button-text-color: {{ module.style.module_global_colors.button_text_color.color|default('#FFFFFF') }};
10
+ }
11
+ p:not([class]) {
12
+ margin: 0;
13
+ }
14
+ ul, ol {
15
+ margin: 0;
16
+ }
17
+ {% end_scope_css %}
18
+ </style>
19
+ {% end_require_css %}
3
20
  <script>
4
21
  window.hsInEditor = {{ in_cms_editor|lower }};
5
22
  window.hubSpotData = {
6
- title: {{ module.title|tojson }},
7
- tagline: {{ module.tagline|tojson }},
8
- show_feature_cards: {{ module.show_feature_cards|lower }},
9
- accent_color: {{ module.styles.accent_color|tojson }},
10
- hero_style: {{ module.styles.hero_style|tojson }},
23
+ title: {{ module.content.title|default(module.title)|tojson }},
24
+ tagline: {{ module.content.tagline|default(module.tagline)|tojson }},
25
+ show_feature_cards: {{ module.content.show_feature_cards|default(module.show_feature_cards)|lower }},
26
+ hero_style: {{ module.style.hero_style|tojson }},
11
27
  assetBaseUrl: "__PORTAL_CDN_BASE__",
12
28
  buildId: "__PORTAL_BUILD_ID__",
13
29
  isInEditor: {{ in_cms_editor|lower }}
@@ -1,4 +1,19 @@
1
+ /**
2
+ * LOCAL DEV ONLY (wp dev) — unprefixed Tailwind utilities.
3
+ *
4
+ * Never merge with main.prod.css:
5
+ * - Do not add dw prefix on this file
6
+ * - Do not point @source at .temp/ only (that is for HubSpot builds)
7
+ */
1
8
  @import 'tailwindcss';
9
+ @import './tailwind.css.config.css';
10
+
11
+ /* WoodsPortal module global colors — defaults until HubSpot fields / module-config apply overrides */
12
+ :root {
13
+ --primary-color: #ff7a59;
14
+ --secondary-color: #2797af;
15
+ --button-text-color: #ffffff;
16
+ }
2
17
 
3
18
  /* Kit dev-module-config — paths relative to this file (src/assets/css/) */
4
19
  @source '../../../.wphs/.kit-module-config/**/*.{ts,tsx}';
@@ -1,26 +1,23 @@
1
1
  import { Link } from '@tanstack/react-router'
2
2
 
3
- import { ROUTES } from '@/config/routes'
3
+ import { starterNavItems } from '@/config/starter-nav'
4
4
 
5
5
  export function StarterNav() {
6
6
  return (
7
7
  <header className="border-b border-[#cbd6e2] bg-white/90 backdrop-blur">
8
8
  <div className="mx-auto flex max-w-4xl items-center justify-between gap-4 px-6 py-3 sm:px-10">
9
9
  <span className="text-sm font-semibold text-[#0091ae]">WoodsCLI starter</span>
10
- <nav className="flex gap-4 text-sm font-medium">
11
- <Link
12
- to={ROUTES.home}
13
- className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
14
- activeOptions={{ exact: true }}
15
- >
16
- Home
17
- </Link>
18
- <Link
19
- to={ROUTES.guide}
20
- className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
21
- >
22
- Developer guide
23
- </Link>
10
+ <nav className="flex flex-wrap gap-4 text-sm font-medium">
11
+ {starterNavItems.map((item) => (
12
+ <Link
13
+ key={item.to}
14
+ to={item.to}
15
+ className="text-[#516f90] hover:text-[#0091ae] [&.active]:text-[#0091ae]"
16
+ activeOptions={{ exact: item.to === '/' }}
17
+ >
18
+ {item.label}
19
+ </Link>
20
+ ))}
24
21
  </nav>
25
22
  </div>
26
23
  </header>
@@ -0,0 +1,9 @@
1
+ import { ROUTES } from '@/config/routes'
2
+
3
+ /** No-auth module navigation — edit once; StarterNav reads this file. */
4
+ export const starterNavItems = [
5
+ { to: ROUTES.home, label: 'Home' },
6
+ { to: ROUTES.guide, label: 'Developer guide' },
7
+ ] as const
8
+
9
+ export type StarterNavItem = (typeof starterNavItems)[number]
@@ -1,38 +1,34 @@
1
1
  import { useStarterPreview } from '../hooks/use-starter-preview'
2
2
  import { STARTER_FEATURES, WOODSCLI_ABOUT } from '../content/woodscli-guide-content'
3
3
 
4
- function heroClass(style: ReturnType<typeof useStarterPreview>['heroStyle']): string {
5
- if (style === 'minimal') {
6
- return 'bg-[var(--mc-bg-shell,#f5f8fa)] text-[var(--mc-text,#33475b)] border-b border-[var(--mc-border,#cbd6e2)]'
7
- }
8
- if (style === 'bold') {
9
- return 'bg-[#1c1c1f] text-white'
10
- }
11
- return 'bg-gradient-to-br from-[var(--starter-accent,#0091ae)] to-[#ff7a59] text-white'
12
- }
13
-
14
4
  export function StarterHome() {
15
- const { title, tagline, showFeatureCards, accentColor, heroStyle } = useStarterPreview()
5
+ const { title, tagline, showFeatureCards, heroStyle } = useStarterPreview()
16
6
 
17
7
  return (
18
8
  <>
19
- <div style={{ ['--starter-accent' as string]: accentColor }}>
20
- <section className={`px-6 py-14 sm:px-10 sm:py-16 ${heroClass(heroStyle)}`}>
21
- <div className="mx-auto max-w-4xl">
22
- <p className="mb-2 text-xs font-semibold uppercase tracking-widest opacity-80">
23
- {WOODSCLI_ABOUT.package} · local dev
24
- </p>
25
- <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">{title}</h1>
26
- <p className="mt-4 max-w-2xl text-base leading-relaxed opacity-90 sm:text-lg">{tagline}</p>
27
- <p className="mt-6 text-sm opacity-75">
28
- Module config overlay:{' '}
29
- <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">Ctrl+Shift+Z</kbd>
30
- {' · '}
31
- Mac: <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">⌃⇧Z</kbd>
32
- </p>
33
- </div>
34
- </section>
35
- </div>
9
+ <section
10
+ className={
11
+ heroStyle === 'minimal'
12
+ ? 'px-6 py-14 sm:px-10 sm:py-16 bg-[var(--mc-bg-shell,#f5f8fa)] text-[var(--mc-text,#33475b)] border-b border-[var(--mc-border,#cbd6e2)]'
13
+ : heroStyle === 'bold'
14
+ ? 'px-6 py-14 sm:px-10 sm:py-16 bg-[#1c1c1f] text-white'
15
+ : 'px-6 py-14 sm:px-10 sm:py-16 bg-gradient-to-br from-[var(--primary-color,#FF7A59)] to-[var(--secondary-color,#2797AF)] text-white'
16
+ }
17
+ >
18
+ <div className="mx-auto max-w-4xl">
19
+ <p className="mb-2 text-xs font-semibold uppercase tracking-widest opacity-80">
20
+ {WOODSCLI_ABOUT.package} · local dev
21
+ </p>
22
+ <h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">{title}</h1>
23
+ <p className="mt-4 max-w-2xl text-base leading-relaxed opacity-90 sm:text-lg">{tagline}</p>
24
+ <p className="mt-6 text-sm opacity-75">
25
+ Module config overlay:{' '}
26
+ <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">Ctrl+Shift+Z</kbd>
27
+ {' · '}
28
+ Mac: <kbd className="rounded bg-black/10 px-1.5 py-0.5 font-mono text-xs">⌃⇧Z</kbd>
29
+ </p>
30
+ </div>
31
+ </section>
36
32
 
37
33
  {showFeatureCards ? (
38
34
  <div className="mx-auto grid max-w-4xl gap-4 px-6 py-10 sm:grid-cols-3 sm:px-10">
@@ -41,7 +37,7 @@ export function StarterHome() {
41
37
  key={feature.title}
42
38
  className="rounded-lg border border-[#cbd6e2] bg-white p-5 shadow-sm"
43
39
  >
44
- <h2 className="text-sm font-semibold text-[#0091ae]">{feature.title}</h2>
40
+ <h2 className="text-sm font-semibold text-[var(--primary-color,#FF7A59)]">{feature.title}</h2>
45
41
  <p className="mt-2 text-sm leading-relaxed text-[#516f90]">{feature.body}</p>
46
42
  </article>
47
43
  ))}