@funnelsgrove/cli 0.1.15 → 0.1.19

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 (293) hide show
  1. package/contracts/step-contract-v2.json +733 -0
  2. package/dist/analyticsOutput.d.ts +30 -15
  3. package/dist/analyticsOutput.js +45 -28
  4. package/dist/apiClient.d.ts +85 -0
  5. package/dist/apiClient.js +225 -8
  6. package/dist/cli.d.ts +149 -1
  7. package/dist/cli.js +1234 -71
  8. package/dist/diagnosticOutput.d.ts +42 -0
  9. package/dist/diagnosticOutput.js +83 -0
  10. package/dist/docsOutput.d.ts +19 -0
  11. package/dist/docsOutput.js +73 -0
  12. package/dist/docsTransaction.d.ts +81 -0
  13. package/dist/docsTransaction.js +1755 -0
  14. package/dist/funnelValidation.d.ts +14 -0
  15. package/dist/funnelValidation.js +297 -0
  16. package/dist/localSync.d.ts +79 -0
  17. package/dist/localSync.js +400 -29
  18. package/dist/projectValidator.d.ts +27 -0
  19. package/dist/projectValidator.js +208 -0
  20. package/dist/reskin.js +27 -1
  21. package/dist/stepContractMigration.d.ts +71 -0
  22. package/dist/stepContractMigration.js +1323 -0
  23. package/dist/templateDocs.js +8 -25
  24. package/docs-release-history.json +66 -0
  25. package/legacy-docs-catalog.json +1012 -0
  26. package/package.json +13 -4
  27. package/template_docs/.funnelsgrove-docs.json +144 -0
  28. package/template_docs/AGENTS.md +30 -58
  29. package/template_docs/CLAUDE.md +0 -2
  30. package/template_docs/docs/funnelsgrove/START-HERE.md +73 -0
  31. package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  32. package/template_docs/docs/funnelsgrove/contracts/content-answers.md +44 -0
  33. package/template_docs/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  34. package/template_docs/docs/funnelsgrove/contracts/payments.md +42 -0
  35. package/template_docs/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  36. package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  37. package/template_docs/docs/funnelsgrove/qa/analytics.md +43 -0
  38. package/template_docs/docs/funnelsgrove/qa/local.md +42 -0
  39. package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  40. package/template_docs/docs/funnelsgrove/qa/publish.md +32 -0
  41. package/template_docs/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  42. package/template_docs/docs/funnelsgrove/recipes/add-step.md +33 -0
  43. package/template_docs/docs/funnelsgrove/recipes/edit-step.md +31 -0
  44. package/template_docs/docs/funnelsgrove/steps/INDEX.md +26 -0
  45. package/template_docs/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  46. package/template_docs/docs/funnelsgrove/steps/checkout.md +120 -0
  47. package/template_docs/docs/funnelsgrove/steps/form_input.md +115 -0
  48. package/template_docs/docs/funnelsgrove/steps/intro_hero.md +110 -0
  49. package/template_docs/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  50. package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  51. package/template_docs/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  52. package/template_docs/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  53. package/template_docs/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  54. package/template_docs/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  55. package/template_docs/docs/funnelsgrove/steps/social_proof.md +110 -0
  56. package/template_docs/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  57. package/template_docs/docs/funnelsgrove/steps/subscription_management.md +112 -0
  58. package/template_docs/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  59. package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  60. package/template_docs/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  61. package/template_docs/funnel-docs.config.json +43 -0
  62. package/template_scaffold/.env.example +41 -0
  63. package/template_scaffold/.funnelsgrove-contract-tools.json +19 -0
  64. package/template_scaffold/.funnelsgrove-docs.json +144 -0
  65. package/template_scaffold/.funnelsgrove-scaffold.json +1078 -0
  66. package/template_scaffold/AGENTS.md +42 -0
  67. package/template_scaffold/CLAUDE.md +1 -0
  68. package/template_scaffold/PLAN.md +81 -0
  69. package/template_scaffold/PRODUCT_SENSE.md +28 -0
  70. package/template_scaffold/context/build-context.md +34 -0
  71. package/template_scaffold/docs/AGENT_FUNNEL_IMPLEMENTATION_SPEC.md +5 -0
  72. package/template_scaffold/docs/ARCHITECTURE.md +5 -0
  73. package/template_scaffold/docs/FLOW_CONFIG_AND_ROUTING.md +5 -0
  74. package/template_scaffold/docs/PAYWALL_AND_PAYMENTS.md +5 -0
  75. package/template_scaffold/docs/README.md +5 -0
  76. package/template_scaffold/docs/funnelsgrove/START-HERE.md +73 -0
  77. package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +59 -0
  78. package/template_scaffold/docs/funnelsgrove/contracts/content-answers.md +44 -0
  79. package/template_scaffold/docs/funnelsgrove/contracts/flow-routing.md +41 -0
  80. package/template_scaffold/docs/funnelsgrove/contracts/payments.md +42 -0
  81. package/template_scaffold/docs/funnelsgrove/contracts/step-metadata.md +57 -0
  82. package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +39 -0
  83. package/template_scaffold/docs/funnelsgrove/qa/analytics.md +43 -0
  84. package/template_scaffold/docs/funnelsgrove/qa/local.md +42 -0
  85. package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +55 -0
  86. package/template_scaffold/docs/funnelsgrove/qa/publish.md +32 -0
  87. package/template_scaffold/docs/funnelsgrove/recipes/add-experiment.md +34 -0
  88. package/template_scaffold/docs/funnelsgrove/recipes/add-step.md +33 -0
  89. package/template_scaffold/docs/funnelsgrove/recipes/edit-step.md +31 -0
  90. package/template_scaffold/docs/funnelsgrove/steps/INDEX.md +26 -0
  91. package/template_scaffold/docs/funnelsgrove/steps/cancellation_offer.md +112 -0
  92. package/template_scaffold/docs/funnelsgrove/steps/checkout.md +120 -0
  93. package/template_scaffold/docs/funnelsgrove/steps/form_input.md +115 -0
  94. package/template_scaffold/docs/funnelsgrove/steps/intro_hero.md +110 -0
  95. package/template_scaffold/docs/funnelsgrove/steps/multi_select_choice.md +123 -0
  96. package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +115 -0
  97. package/template_scaffold/docs/funnelsgrove/steps/progress_interstitial.md +111 -0
  98. package/template_scaffold/docs/funnelsgrove/steps/purchase_completed.md +117 -0
  99. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice.md +122 -0
  100. package/template_scaffold/docs/funnelsgrove/steps/single_step_choice_emoji.md +122 -0
  101. package/template_scaffold/docs/funnelsgrove/steps/social_proof.md +110 -0
  102. package/template_scaffold/docs/funnelsgrove/steps/subscription_handoff.md +113 -0
  103. package/template_scaffold/docs/funnelsgrove/steps/subscription_management.md +112 -0
  104. package/template_scaffold/docs/funnelsgrove/steps/summary_confirmation.md +110 -0
  105. package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +112 -0
  106. package/template_scaffold/docs/funnelsgrove/steps/value_prop_story.md +110 -0
  107. package/template_scaffold/docs/references/funnel-sdk-user-answers.md +5 -0
  108. package/template_scaffold/eslint.config.mjs +16 -0
  109. package/template_scaffold/funnel-agent-docs.test.ts +699 -0
  110. package/template_scaffold/funnel-docs.config.json +43 -0
  111. package/template_scaffold/funnel.config.json +5 -0
  112. package/template_scaffold/gitignore.template +5 -0
  113. package/template_scaffold/next-env.d.ts +6 -0
  114. package/template_scaffold/next.config.ts +53 -0
  115. package/template_scaffold/package-lock.json +7737 -0
  116. package/template_scaffold/package.json +37 -0
  117. package/template_scaffold/package.test.ts +23 -0
  118. package/template_scaffold/public/claimbee/bee.png +0 -0
  119. package/template_scaffold/public/claimbee/device-iphone.png +0 -0
  120. package/template_scaffold/public/claimbee/device-macbook.png +0 -0
  121. package/template_scaffold/public/claimbee/device-none.png +0 -0
  122. package/template_scaffold/public/claimbee/device-watch.png +0 -0
  123. package/template_scaffold/public/claimbee/glow.svg +3 -0
  124. package/template_scaffold/public/claimbee/steps-sprite-alt.png +0 -0
  125. package/template_scaffold/public/claimbee/steps-sprite-default.png +0 -0
  126. package/template_scaffold/public/claimbee/testimonial-avatar.png +0 -0
  127. package/template_scaffold/public/claimbee/testimonial-star.svg +3 -0
  128. package/template_scaffold/public/paywall/ai-claim-assistant.png +0 -0
  129. package/template_scaffold/public/paywall/claim-history.png +0 -0
  130. package/template_scaffold/public/paywall/clear-progress.png +0 -0
  131. package/template_scaffold/public/paywall/hero.png +0 -0
  132. package/template_scaffold/public/paywall/money-alerts.png +0 -0
  133. package/template_scaffold/public/paywall/money-back-badge.png +0 -0
  134. package/template_scaffold/public/paywall/paywall-image.png +0 -0
  135. package/template_scaffold/public/paywall/r1.png +0 -0
  136. package/template_scaffold/public/paywall/r2.png +0 -0
  137. package/template_scaffold/public/paywall/r3.png +0 -0
  138. package/template_scaffold/public/paywall/r4.png +0 -0
  139. package/template_scaffold/public/paywall/r5.png +0 -0
  140. package/template_scaffold/public/paywall/r6.png +0 -0
  141. package/template_scaffold/public/paywall/special-offer-gift.png +0 -0
  142. package/template_scaffold/public/please/bee.png +0 -0
  143. package/template_scaffold/public/please/device-iphone.png +0 -0
  144. package/template_scaffold/public/please/device-macbook.png +0 -0
  145. package/template_scaffold/public/please/device-none.png +0 -0
  146. package/template_scaffold/public/please/device-watch.png +0 -0
  147. package/template_scaffold/public/please/glow.svg +3 -0
  148. package/template_scaffold/public/please/steps-sprite-alt.png +0 -0
  149. package/template_scaffold/public/please/steps-sprite-default.png +0 -0
  150. package/template_scaffold/public/please/testimonial-avatar.png +0 -0
  151. package/template_scaffold/public/please/testimonial-star.svg +3 -0
  152. package/template_scaffold/public/upsell/upgrade-hero.png +0 -0
  153. package/template_scaffold/src/app/[stepId]/page.test.tsx +59 -0
  154. package/template_scaffold/src/app/[stepId]/page.tsx +62 -0
  155. package/template_scaffold/src/app/globals.css +49 -0
  156. package/template_scaffold/src/app/layout.test.ts +26 -0
  157. package/template_scaffold/src/app/layout.tsx +61 -0
  158. package/template_scaffold/src/app/page.tsx +8 -0
  159. package/template_scaffold/src/app/sdk/page.tsx +673 -0
  160. package/template_scaffold/src/app/subscription-dashboard/page.tsx +274 -0
  161. package/template_scaffold/src/assets/claimbee/bee.png +0 -0
  162. package/template_scaffold/src/assets/claimbee/device-iphone.png +0 -0
  163. package/template_scaffold/src/assets/claimbee/device-macbook.png +0 -0
  164. package/template_scaffold/src/assets/claimbee/device-none.png +0 -0
  165. package/template_scaffold/src/assets/claimbee/device-watch.png +0 -0
  166. package/template_scaffold/src/assets/claimbee/glow.svg +3 -0
  167. package/template_scaffold/src/assets/claimbee/steps-sprite-alt.png +0 -0
  168. package/template_scaffold/src/assets/claimbee/steps-sprite-default.png +0 -0
  169. package/template_scaffold/src/assets/claimbee/testimonial-avatar.png +0 -0
  170. package/template_scaffold/src/assets/claimbee/testimonial-star.svg +3 -0
  171. package/template_scaffold/src/components/FunnelEditorPanel.tsx +194 -0
  172. package/template_scaffold/src/components/FunnelFlow.test.tsx +17 -0
  173. package/template_scaffold/src/components/FunnelFlow.tsx +114 -0
  174. package/template_scaffold/src/components/FunnelStepImage.test.ts +15 -0
  175. package/template_scaffold/src/components/FunnelStepImage.tsx +28 -0
  176. package/template_scaffold/src/components/step/FunnelStepHeader.tsx +35 -0
  177. package/template_scaffold/src/config/billing.plans.test.ts +143 -0
  178. package/template_scaffold/src/config/billing.plans.ts +69 -0
  179. package/template_scaffold/src/config/billing.test.plans.ts +32 -0
  180. package/template_scaffold/src/config/experiments.generated.ts +4 -0
  181. package/template_scaffold/src/config/experiments.test.ts +12 -0
  182. package/template_scaffold/src/config/experiments.ts +9 -0
  183. package/template_scaffold/src/config/funnel.config.ts +12 -0
  184. package/template_scaffold/src/config/funnel.manifest.test.ts +166 -0
  185. package/template_scaffold/src/config/funnel.manifest.ts +139 -0
  186. package/template_scaffold/src/config/offer-sets.generated.ts +4 -0
  187. package/template_scaffold/src/contract/funnel-project.validation.test.ts +80 -0
  188. package/template_scaffold/src/contract/funnel-project.validation.ts +44 -0
  189. package/template_scaffold/src/contract/funnel-validator.vite.config.ts +11 -0
  190. package/template_scaffold/src/contract/golden/cancellation_offer.fixture.ts +29 -0
  191. package/template_scaffold/src/contract/golden/checkout.fixture.ts +37 -0
  192. package/template_scaffold/src/contract/golden/form_input.fixture.ts +28 -0
  193. package/template_scaffold/src/contract/golden/golden-fixtures.test.ts +190 -0
  194. package/template_scaffold/src/contract/golden/intro_hero.fixture.ts +27 -0
  195. package/template_scaffold/src/contract/golden/multi_select_choice.fixture.ts +37 -0
  196. package/template_scaffold/src/contract/golden/paywall_offer.fixture.ts +30 -0
  197. package/template_scaffold/src/contract/golden/progress_interstitial.fixture.ts +28 -0
  198. package/template_scaffold/src/contract/golden/purchase_completed.fixture.ts +33 -0
  199. package/template_scaffold/src/contract/golden/single_step_choice.fixture.ts +36 -0
  200. package/template_scaffold/src/contract/golden/single_step_choice_emoji.fixture.ts +36 -0
  201. package/template_scaffold/src/contract/golden/social_proof.fixture.ts +27 -0
  202. package/template_scaffold/src/contract/golden/subscription_handoff.fixture.ts +30 -0
  203. package/template_scaffold/src/contract/golden/subscription_management.fixture.ts +29 -0
  204. package/template_scaffold/src/contract/golden/summary_confirmation.fixture.ts +27 -0
  205. package/template_scaffold/src/contract/golden/upsell_offer.fixture.ts +29 -0
  206. package/template_scaffold/src/contract/golden/value_prop_story.fixture.ts +27 -0
  207. package/template_scaffold/src/contract/validate-funnel.cli.test.ts +180 -0
  208. package/template_scaffold/src/contract/validate-funnel.cli.ts +199 -0
  209. package/template_scaffold/src/runtime/checkout-runtime-config.test.ts +60 -0
  210. package/template_scaffold/src/runtime/checkout-runtime-config.ts +26 -0
  211. package/template_scaffold/src/runtime/funnel-runtime.test.ts +25 -0
  212. package/template_scaffold/src/runtime/funnel-runtime.ts +118 -0
  213. package/template_scaffold/src/runtime/step-content-context.test.ts +38 -0
  214. package/template_scaffold/src/runtime/step-content-context.ts +65 -0
  215. package/template_scaffold/src/runtime/step-registry.ts +66 -0
  216. package/template_scaffold/src/runtime/step-variable-values.ts +31 -0
  217. package/template_scaffold/src/runtime/use-funnel-flow-controller.integration.test.ts +203 -0
  218. package/template_scaffold/src/runtime/use-funnel-flow-controller.test.ts +76 -0
  219. package/template_scaffold/src/runtime/use-funnel-flow-controller.ts +53 -0
  220. package/template_scaffold/src/steps/content/email-capture.content.ts +29 -0
  221. package/template_scaffold/src/steps/content/manage-subscription.content.ts +62 -0
  222. package/template_scaffold/src/steps/content/paywall.content.ts +191 -0
  223. package/template_scaffold/src/steps/content/step-01.content.ts +23 -0
  224. package/template_scaffold/src/steps/content/step-02.content.ts +77 -0
  225. package/template_scaffold/src/steps/content/step-03.content.ts +40 -0
  226. package/template_scaffold/src/steps/content/step-04.content.ts +49 -0
  227. package/template_scaffold/src/steps/content/step-32-paywall.content.ts +352 -0
  228. package/template_scaffold/src/steps/content/subscription-started.content.ts +68 -0
  229. package/template_scaffold/src/steps/editor/email-capture.editor.ts +58 -0
  230. package/template_scaffold/src/steps/editor/manage-subscription.editor.ts +179 -0
  231. package/template_scaffold/src/steps/editor/paywall.editor.ts +232 -0
  232. package/template_scaffold/src/steps/editor/step-01.editor.ts +22 -0
  233. package/template_scaffold/src/steps/editor/step-02.editor.ts +41 -0
  234. package/template_scaffold/src/steps/editor/step-03.editor.ts +58 -0
  235. package/template_scaffold/src/steps/editor/step-04.editor.ts +98 -0
  236. package/template_scaffold/src/steps/editor/step-32-paywall.editor.ts +478 -0
  237. package/template_scaffold/src/steps/editor/subscription-started.editor.ts +131 -0
  238. package/template_scaffold/src/steps/index.ts +25 -0
  239. package/template_scaffold/src/steps/paywall-checkout-options.test.ts +104 -0
  240. package/template_scaffold/src/steps/paywall-checkout-options.ts +130 -0
  241. package/template_scaffold/src/steps/pricing/paywall-b.pricing.ts +12 -0
  242. package/template_scaffold/src/steps/pricing/paywall.pricing.ts +12 -0
  243. package/template_scaffold/src/steps/pricing/shared-plan-order.ts +17 -0
  244. package/template_scaffold/src/steps/pricing/upsell-form.pricing.ts +16 -0
  245. package/template_scaffold/src/steps/shared-header-contract.test.ts +27 -0
  246. package/template_scaffold/src/steps/step-01.test.ts +38 -0
  247. package/template_scaffold/src/steps/step-01.tsx +108 -0
  248. package/template_scaffold/src/steps/step-02.test.ts +43 -0
  249. package/template_scaffold/src/steps/step-02.tsx +140 -0
  250. package/template_scaffold/src/steps/step-03.test.ts +15 -0
  251. package/template_scaffold/src/steps/step-03.tsx +262 -0
  252. package/template_scaffold/src/steps/step-04.test.ts +32 -0
  253. package/template_scaffold/src/steps/step-04.tsx +673 -0
  254. package/template_scaffold/src/steps/step-31-email-capture.test.ts +22 -0
  255. package/template_scaffold/src/steps/step-31-email-capture.tsx +195 -0
  256. package/template_scaffold/src/steps/step-32-paywall.test.ts +770 -0
  257. package/template_scaffold/src/steps/step-32-paywall.tsx +2253 -0
  258. package/template_scaffold/src/steps/step-32b-paywall-b.tsx +35 -0
  259. package/template_scaffold/src/steps/step-33-subscription-started.test.ts +26 -0
  260. package/template_scaffold/src/steps/step-33-subscription-started.tsx +86 -0
  261. package/template_scaffold/src/steps/step-34-upsell-form.test.ts +27 -0
  262. package/template_scaffold/src/steps/step-35-manage-subscription.tsx +46 -0
  263. package/template_scaffold/src/steps/step-content.registry.ts +45 -0
  264. package/template_scaffold/src/steps/step-image-migration.test.ts +20 -0
  265. package/template_scaffold/src/steps/styles/index.css +2 -0
  266. package/template_scaffold/src/steps/styles/shared/final-flow-shared.css +55 -0
  267. package/template_scaffold/src/steps/styles/shared/funnel-base.css +543 -0
  268. package/template_scaffold/src/steps/styles/shared/info-shared.css +139 -0
  269. package/template_scaffold/src/steps/styles/shared/progress-shared.css +117 -0
  270. package/template_scaffold/src/steps/styles/shared/question-shared.css +76 -0
  271. package/template_scaffold/src/steps/styles/shared/responsive-container.css +176 -0
  272. package/template_scaffold/src/steps/types.ts +7 -0
  273. package/template_scaffold/src/steps/use-claimbee-step-content.ts +15 -0
  274. package/template_scaffold/src/theme/theme.ts +81 -0
  275. package/template_scaffold/tsconfig.json +38 -0
  276. package/template_scaffold/vitest.config.ts +32 -0
  277. package/template_validation/.funnelsgrove-contract-tools.json +19 -0
  278. package/template_validation/src/contract/funnel-project.validation.ts +44 -0
  279. package/template_validation/src/contract/funnel-validator.vite.config.ts +11 -0
  280. package/template_validation/src/contract/validate-funnel.cli.ts +199 -0
  281. package/template_docs/docs/ab-experiments.md +0 -111
  282. package/template_docs/docs/analytics.md +0 -65
  283. package/template_docs/docs/editing-flow.md +0 -136
  284. package/template_docs/docs/editing-step.md +0 -125
  285. package/template_docs/docs/editor-and-content.md +0 -58
  286. package/template_docs/docs/funnel-runtime-architecture.md +0 -125
  287. package/template_docs/docs/meta-pixel-conversions-api.md +0 -50
  288. package/template_docs/docs/payment-plans-and-discounts.md +0 -106
  289. package/template_docs/docs/publishing-and-versioning.md +0 -117
  290. package/template_docs/docs/qa-checklist.md +0 -80
  291. package/template_docs/docs/sdk-api-endpoints.md +0 -78
  292. package/template_docs/docs/step-ui-guidelines.md +0 -110
  293. package/template_docs/docs/theme.md +0 -35
@@ -0,0 +1,208 @@
1
+ import { spawn } from 'node:child_process';
2
+ import path from 'node:path';
3
+ import { CURRENT_STEP_CONTRACT_VERSION } from '@funnelsgrove/runtime';
4
+ import { createValidationDiagnostic, isValidationDiagnostic, } from './diagnosticOutput.js';
5
+ export const PROJECT_VALIDATOR_TIMEOUT_MS = 120_000;
6
+ export const PROJECT_VALIDATOR_CONTRACT_VERSION = CURRENT_STEP_CONTRACT_VERSION;
7
+ const PROJECT_VALIDATOR_TERMINATION_GRACE_MS = 250;
8
+ const PROJECT_VALIDATOR_PIPE_GRACE_MS = 250;
9
+ const ENVIRONMENT_ALLOWLIST = [
10
+ 'CI',
11
+ 'COLORTERM',
12
+ 'COMSPEC',
13
+ 'HOME',
14
+ 'LANG',
15
+ 'LC_ALL',
16
+ 'PATH',
17
+ 'PATHEXT',
18
+ 'Path',
19
+ 'SystemRoot',
20
+ 'TEMP',
21
+ 'TERM',
22
+ 'TMP',
23
+ 'TMPDIR',
24
+ 'USERPROFILE',
25
+ ];
26
+ export const buildProjectValidatorEnvironment = (parentEnv) => ({
27
+ ...Object.fromEntries(ENVIRONMENT_ALLOWLIST.flatMap((key) => {
28
+ const value = parentEnv[key];
29
+ return value === undefined ? [] : [[key, value]];
30
+ })),
31
+ npm_config_ignore_scripts: 'true',
32
+ npm_config_loglevel: 'silent',
33
+ });
34
+ const runProcessWithSpawn = async (request) => new Promise((resolve) => {
35
+ let stdout = '';
36
+ let stderr = '';
37
+ let timedOut = false;
38
+ let settled = false;
39
+ let terminationTimeout;
40
+ let pipeTimeout;
41
+ const windows = process.platform === 'win32';
42
+ const child = spawn(request.command, request.args, {
43
+ cwd: request.cwd,
44
+ env: request.env,
45
+ detached: !windows,
46
+ shell: false,
47
+ stdio: ['ignore', 'pipe', 'pipe'],
48
+ windowsHide: true,
49
+ });
50
+ child.stdout.setEncoding('utf8');
51
+ child.stderr.setEncoding('utf8');
52
+ child.stdout.on('data', (chunk) => {
53
+ stdout += chunk;
54
+ });
55
+ child.stderr.on('data', (chunk) => {
56
+ stderr += chunk;
57
+ });
58
+ const finish = (result) => {
59
+ if (settled)
60
+ return;
61
+ settled = true;
62
+ clearTimeout(timeout);
63
+ clearTimeout(terminationTimeout);
64
+ clearTimeout(pipeTimeout);
65
+ resolve(result);
66
+ };
67
+ const killProcessTree = (signal) => {
68
+ if (!windows && child.pid) {
69
+ try {
70
+ process.kill(-child.pid, signal);
71
+ return;
72
+ }
73
+ catch (error) {
74
+ if (error.code !== 'ESRCH')
75
+ return;
76
+ }
77
+ }
78
+ try {
79
+ child.kill(signal);
80
+ }
81
+ catch {
82
+ // The close/error handlers own completion when the process is already gone.
83
+ }
84
+ };
85
+ const timeout = setTimeout(() => {
86
+ timedOut = true;
87
+ killProcessTree('SIGTERM');
88
+ terminationTimeout = setTimeout(() => {
89
+ if (windows && child.pid) {
90
+ try {
91
+ const taskkill = spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], {
92
+ shell: false,
93
+ stdio: 'ignore',
94
+ windowsHide: true,
95
+ });
96
+ taskkill.unref();
97
+ }
98
+ catch {
99
+ killProcessTree('SIGKILL');
100
+ }
101
+ }
102
+ else {
103
+ killProcessTree('SIGKILL');
104
+ }
105
+ pipeTimeout = setTimeout(() => {
106
+ child.stdout.destroy();
107
+ child.stderr.destroy();
108
+ child.unref();
109
+ finish({
110
+ stdout,
111
+ stderr,
112
+ exitCode: null,
113
+ signal: 'SIGKILL',
114
+ timedOut: true,
115
+ });
116
+ }, PROJECT_VALIDATOR_PIPE_GRACE_MS);
117
+ }, PROJECT_VALIDATOR_TERMINATION_GRACE_MS);
118
+ }, request.timeoutMs);
119
+ child.once('error', (error) => {
120
+ finish({ stdout, stderr, exitCode: null, signal: null, timedOut, error });
121
+ });
122
+ child.once('close', (exitCode, signal) => {
123
+ finish({ stdout, stderr, exitCode, signal, timedOut });
124
+ });
125
+ });
126
+ const protocolDiagnostic = (received) => createValidationDiagnostic({
127
+ code: 'FG-CLIENT-001',
128
+ file: 'src/contract/validate-funnel.cli.ts',
129
+ expected: 'one schemaVersion 1 validator protocol object',
130
+ received,
131
+ reason: 'The checked-in project validator did not complete its required protocol.',
132
+ guide: 'docs/funnelsgrove/qa/local.md',
133
+ repair: 'Restore the managed validator runner and rerun fgrove validate.',
134
+ });
135
+ const failedResult = (received) => ({
136
+ schemaVersion: 1,
137
+ contractVersion: PROJECT_VALIDATOR_CONTRACT_VERSION,
138
+ valid: false,
139
+ diagnostics: [protocolDiagnostic(received)],
140
+ exitCode: 2,
141
+ });
142
+ const PROTOCOL_KEYS = [
143
+ 'schemaVersion',
144
+ 'contractVersion',
145
+ 'valid',
146
+ 'diagnostics',
147
+ ];
148
+ const parseProtocol = (stdout) => {
149
+ let value;
150
+ try {
151
+ value = JSON.parse(stdout.trim());
152
+ }
153
+ catch {
154
+ return null;
155
+ }
156
+ if (!value || typeof value !== 'object' || Array.isArray(value))
157
+ return null;
158
+ const protocol = value;
159
+ if (Object.keys(protocol).length !== PROTOCOL_KEYS.length
160
+ || PROTOCOL_KEYS.some((key) => !(key in protocol))
161
+ || protocol.schemaVersion !== 1
162
+ || protocol.contractVersion !== PROJECT_VALIDATOR_CONTRACT_VERSION
163
+ || typeof protocol.valid !== 'boolean'
164
+ || !Array.isArray(protocol.diagnostics)
165
+ || !protocol.diagnostics.every(isValidationDiagnostic))
166
+ return null;
167
+ return protocol;
168
+ };
169
+ const protocolMatchesExit = (exitCode, protocol) => {
170
+ if (exitCode === 0)
171
+ return protocol.valid && protocol.diagnostics.length === 0;
172
+ if (exitCode === 1 || exitCode === 2) {
173
+ return !protocol.valid && protocol.diagnostics.length > 0;
174
+ }
175
+ return false;
176
+ };
177
+ export const runProjectValidator = async (options) => {
178
+ const request = {
179
+ command: 'npm',
180
+ args: ['run', 'validate:funnel', '--', '--json'],
181
+ cwd: path.resolve(options.projectDir),
182
+ env: buildProjectValidatorEnvironment(options.parentEnv ?? process.env),
183
+ timeoutMs: PROJECT_VALIDATOR_TIMEOUT_MS,
184
+ ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
185
+ };
186
+ let processResult;
187
+ try {
188
+ processResult = await (options.runProcess ?? runProcessWithSpawn)(request);
189
+ }
190
+ catch {
191
+ return failedResult('project validator failed to start');
192
+ }
193
+ if (processResult.timedOut)
194
+ return failedResult('project validator timed out');
195
+ if (processResult.error)
196
+ return failedResult('project validator failed to start');
197
+ const protocol = parseProtocol(processResult.stdout);
198
+ if (!protocol || !protocolMatchesExit(processResult.exitCode, protocol)) {
199
+ return failedResult('invalid project validator protocol');
200
+ }
201
+ return {
202
+ schemaVersion: 1,
203
+ contractVersion: protocol.contractVersion,
204
+ valid: protocol.valid,
205
+ diagnostics: protocol.diagnostics,
206
+ exitCode: processResult.exitCode,
207
+ };
208
+ };
package/dist/reskin.js CHANGED
@@ -26,6 +26,23 @@ const replaceInFile = async (filePath, replacements) => {
26
26
  }
27
27
  return changed;
28
28
  };
29
+ const updateJsonFile = async (filePath, update) => {
30
+ let parsed;
31
+ try {
32
+ parsed = JSON.parse(await readFile(filePath, 'utf8'));
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ const next = update(parsed);
38
+ const currentContent = `${JSON.stringify(parsed, null, 2)}\n`;
39
+ const nextContent = `${JSON.stringify(next, null, 2)}\n`;
40
+ if (nextContent === currentContent) {
41
+ return false;
42
+ }
43
+ await writeFile(filePath, nextContent, 'utf8');
44
+ return true;
45
+ };
29
46
  export const reskinFunnel = async (dir, appName) => {
30
47
  const kebabName = toKebabCase(appName);
31
48
  const updated = [];
@@ -52,7 +69,16 @@ export const reskinFunnel = async (dir, appName) => {
52
69
  ])) {
53
70
  updated.push('src/config/funnel.config.ts');
54
71
  }
55
- // 4. Update HTML title if index.html exists
72
+ // 4. Update canonical funnel config metadata
73
+ const jsonConfigPath = path.join(dir, 'funnel.config.json');
74
+ if (await updateJsonFile(jsonConfigPath, (config) => ({
75
+ ...config,
76
+ name: appName,
77
+ description: `${appName} web funnel`,
78
+ }))) {
79
+ updated.push('funnel.config.json');
80
+ }
81
+ // 5. Update HTML title if index.html exists
56
82
  const indexHtmlPath = path.join(dir, 'index.html');
57
83
  if (await replaceInFile(indexHtmlPath, [
58
84
  { pattern: /<title>[^<]*<\/title>/, replacement: `<title>${appName}</title>` },
@@ -0,0 +1,71 @@
1
+ import { type FunnelContractDiagnostic, type StepContractV2MigrationRule as SemanticMigrationRule } from '@funnelsgrove/runtime';
2
+ export declare const STEP_CONTRACT_V2_REPORT_PATH = ".funnelsgrove/migrations/step-contract-v2-report.json";
3
+ export type StepContractMigrationTargetVersion = 2 | 3;
4
+ export declare const stepContractMigrationReportPath: (targetVersion: StepContractMigrationTargetVersion) => string;
5
+ export type StepContractMigrationMode = 'check' | 'apply';
6
+ export type StepContractMigrationFlags = {
7
+ check?: boolean;
8
+ apply?: boolean;
9
+ };
10
+ export type StepContractMigrationRule = SemanticMigrationRule | 'add-name-from-id+restore-reserved-step' | 'upgrade-purchase-terminal' | 'set-step-contract-version';
11
+ export type StepContractMigrationOperation = {
12
+ path: string;
13
+ start: number;
14
+ end: number;
15
+ beforeHash: string;
16
+ afterHash: string;
17
+ rule: StepContractMigrationRule;
18
+ };
19
+ export type StepContractMigrationReport = {
20
+ schemaVersion: 1;
21
+ fromVersion: 1 | 2 | null;
22
+ toVersion: StepContractMigrationTargetVersion;
23
+ checkedAt: string;
24
+ operations: readonly StepContractMigrationOperation[];
25
+ protectedChanges: readonly StepContractMigrationOperation[];
26
+ diagnostics: readonly FunnelContractDiagnostic[];
27
+ valid: boolean;
28
+ };
29
+ export type StepContractMigrationCheckOptions = {
30
+ projectDir: string;
31
+ checkedAt?: string;
32
+ managedBundleAvailable: boolean;
33
+ targetVersion?: StepContractMigrationTargetVersion;
34
+ };
35
+ export type StepContractMigrationCheckResult = {
36
+ reportPath: string;
37
+ report: StepContractMigrationReport;
38
+ };
39
+ export type StepContractMigrationValidationResult = {
40
+ valid: boolean;
41
+ diagnostics: readonly FunnelContractDiagnostic[];
42
+ };
43
+ export type InstallManagedCandidateContext = {
44
+ candidateDir: string;
45
+ report: StepContractMigrationReport;
46
+ };
47
+ export type ValidateMigrationCandidateContext = {
48
+ candidateDir: string;
49
+ phase: 'shadow' | 'committed';
50
+ authoringContractVersion: StepContractMigrationTargetVersion;
51
+ };
52
+ export type StepContractMigrationFaultPoint = 'after-report' | 'after-shadow-created' | 'after-source-edits' | 'after-managed-install' | 'after-shadow-validation' | 'before-stage-file' | 'after-stage-file' | 'after-journal-staged' | 'before-commit-file' | 'after-commit-file' | 'after-manifest-commit' | 'before-committed-validation' | 'after-committed-validation';
53
+ export type StepContractMigrationFaultContext = {
54
+ path?: string;
55
+ };
56
+ export type StepContractMigrationApplyOptions = StepContractMigrationCheckOptions & {
57
+ installManagedCandidate: (context: InstallManagedCandidateContext) => Promise<void>;
58
+ validateCandidate: (context: ValidateMigrationCandidateContext) => Promise<StepContractMigrationValidationResult>;
59
+ faultInjector?: (point: StepContractMigrationFaultPoint, context: StepContractMigrationFaultContext) => void | Promise<void>;
60
+ };
61
+ export type StepContractMigrationApplyResult = StepContractMigrationCheckResult & {
62
+ applied: true;
63
+ };
64
+ export declare class StepContractMigrationError extends Error {
65
+ readonly report?: StepContractMigrationReport;
66
+ constructor(message: string, report?: StepContractMigrationReport);
67
+ }
68
+ export declare const resolveStepContractMigrationMode: (flags: StepContractMigrationFlags) => StepContractMigrationMode;
69
+ export declare const checkStepContractV2Migration: (options: StepContractMigrationCheckOptions) => Promise<StepContractMigrationCheckResult>;
70
+ export declare const applyStepContractV2Migration: (options: StepContractMigrationApplyOptions) => Promise<StepContractMigrationApplyResult>;
71
+ export declare const recoverStepContractV2Migration: (projectDirectory: string, targetVersion?: StepContractMigrationTargetVersion) => Promise<"none" | "restored">;