@funnelsgrove/cli 0.1.67 → 0.1.71

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 (33) hide show
  1. package/README.md +6 -3
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +150 -4
  4. package/dist/localSync.d.ts +37 -0
  5. package/dist/localSync.js +130 -0
  6. package/dist/sourceRebase.d.ts +2 -0
  7. package/dist/sourceRebase.js +66 -0
  8. package/funnel-contract-compatibility.json +46 -46
  9. package/package.json +4 -4
  10. package/template_docs/.funnelsgrove-docs.json +9 -9
  11. package/template_docs/docs/funnelsgrove/contracts/analytics-events.md +2 -1
  12. package/template_docs/docs/funnelsgrove/contracts/payments.md +6 -1
  13. package/template_docs/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
  14. package/template_docs/docs/funnelsgrove/qa/analytics.md +4 -0
  15. package/template_docs/docs/funnelsgrove/qa/paywall-checkout.md +3 -0
  16. package/template_docs/docs/funnelsgrove/steps/paywall_offer.md +4 -2
  17. package/template_docs/docs/funnelsgrove/steps/upsell_offer.md +3 -2
  18. package/template_docs/funnel-docs.config.json +1 -1
  19. package/template_scaffold/.funnelsgrove-docs.json +9 -9
  20. package/template_scaffold/.funnelsgrove-scaffold.json +16 -16
  21. package/template_scaffold/docs/funnelsgrove/contracts/analytics-events.md +2 -1
  22. package/template_scaffold/docs/funnelsgrove/contracts/payments.md +6 -1
  23. package/template_scaffold/docs/funnelsgrove/migrations/step-contract-v3.md +1 -1
  24. package/template_scaffold/docs/funnelsgrove/qa/analytics.md +4 -0
  25. package/template_scaffold/docs/funnelsgrove/qa/paywall-checkout.md +3 -0
  26. package/template_scaffold/docs/funnelsgrove/steps/paywall_offer.md +4 -2
  27. package/template_scaffold/docs/funnelsgrove/steps/upsell_offer.md +3 -2
  28. package/template_scaffold/funnel-docs.config.json +1 -1
  29. package/template_scaffold/package-lock.json +15 -15
  30. package/template_scaffold/package.json +3 -3
  31. package/template_scaffold/src/steps/step-32-paywall.tsx +89 -106
  32. package/template_scaffold/tests/funnel-agent-docs.test.ts +1 -1
  33. package/template_scaffold/tests/src/steps/step-32-paywall.test.ts +37 -51
@@ -43,8 +43,7 @@ describe('ClaimBee paywall source', () => {
43
43
  );
44
44
 
45
45
  expect(stepSource).toContain('SharedCheckoutSpecialOfferDialog');
46
- expect(stepSource).toContain('ApplePaySubscriptionCheckoutSlot');
47
- expect(stepSource).toContain('GooglePaySubscriptionCheckoutSlot');
46
+ expect(stepSource).toContain('StripeSubscriptionWalletSurface');
48
47
  expect(stepSource).toContain('SharedStripeCheckoutV2Dialog');
49
48
  expect(stepSource).not.toContain('StripeExpressCheckoutElement');
50
49
  expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
@@ -67,8 +66,8 @@ describe('ClaimBee paywall source', () => {
67
66
  expect(stepSource).toContain('imageSrc={content.checkout.specialOffer.image.src}');
68
67
  expect(stepSource).toContain("className='paywall-v2-scroll-cta paywall-v2-vibe-cta'");
69
68
  expect(stepSource).toContain("className='paywall-v2-card-button paywall-v2-vibe-cta'");
70
- expect(stepSource).toContain("className='paywall-v2-apple-pay-button'");
71
- expect(stepSource).toContain("className='paywall-v2-google-pay-button'");
69
+ expect(stepSource).toContain("applePay: 'paywall-v2-apple-pay-button'");
70
+ expect(stepSource).toContain("googlePay: 'paywall-v2-google-pay-button'");
72
71
  expect(stepSource).not.toContain('Continue with other payment options');
73
72
  });
74
73
 
@@ -172,7 +171,7 @@ describe('ClaimBee paywall source', () => {
172
171
  expect(stepSource).toContain('useStripeSubscriptionCheckoutSession');
173
172
  expect(stepSource).toContain('checkoutSession.activeClientSecret');
174
173
  expect(stepSource).toContain('<SharedStripeCheckoutV2Dialog');
175
- expect(stepSource).toContain('<ApplePaySubscriptionCheckoutSlot');
174
+ expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
176
175
  expect(stepSource).not.toContain('<StripeExpressCheckoutElement');
177
176
  expect(stepSource).not.toContain('createStripePaymentIntent');
178
177
  expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
@@ -183,23 +182,25 @@ describe('ClaimBee paywall source', () => {
183
182
  expect(stepSource).toContain("aria-label='Loading checkout'");
184
183
  });
185
184
 
186
- it('claims paywall completion only when payment information is submitted', () => {
185
+ it('claims paywall completion only after verified checkout success', () => {
187
186
  const stepSource = readFileSync(
188
187
  path.resolve(__dirname, '../../../src/steps/step-32-paywall.tsx'),
189
188
  'utf8',
190
189
  );
191
- const paymentInfoHandler = stepSource.slice(
192
- stepSource.indexOf('const trackPaymentInfoSubmitted = () => {'),
190
+ const completionHandler = stepSource.slice(
191
+ stepSource.indexOf('const handleCheckoutCompleted = () => {'),
193
192
  stepSource.indexOf('const preparePaywallWalletCheckout'),
194
193
  );
195
194
  const checkoutStartHandler = stepSource.slice(
196
195
  stepSource.indexOf('const startCheckout = async () => {'),
197
- stepSource.indexOf('const trackPaymentInfoSubmitted'),
196
+ stepSource.indexOf('const handleCheckoutCompleted'),
198
197
  );
199
198
 
200
199
  expect(stepSource).toContain('const { user, setUser, attributes, featureFlags, completeStep } = useFunnel();');
201
- expect(paymentInfoHandler).toContain('completeStep(analyticsStepId, {');
202
- expect(paymentInfoHandler).toContain('planId: selectedDisplayPlan.id');
200
+ expect(completionHandler).toContain('completeStep(analyticsStepId, {');
201
+ expect(completionHandler).toContain('planId: selectedDisplayPlan.id');
202
+ expect(stepSource).toContain('onSuccess={handleCheckoutCompleted}');
203
+ expect(stepSource).not.toContain('onPaymentInfoSubmitted=');
203
204
  expect(checkoutStartHandler).not.toContain('completeStep(');
204
205
  });
205
206
 
@@ -315,8 +316,7 @@ describe('ClaimBee paywall source', () => {
315
316
  expect(plansIndex).toBeGreaterThan(-1);
316
317
  expect(paymentStackIndex).toBeGreaterThan(plansIndex);
317
318
  expect(featureGridIndex).toBeGreaterThan(paymentStackIndex);
318
- expect(stepSource).toContain('ApplePaySubscriptionCheckoutSlot');
319
- expect(stepSource).toContain('GooglePaySubscriptionCheckoutSlot');
319
+ expect(stepSource).toContain('StripeSubscriptionWalletSurface');
320
320
  expect(stepSource).toContain('.paywall-v2-apple-pay-button,');
321
321
  expect(stepSource).toContain('.paywall-v2-google-pay-button {');
322
322
  expect(stepSource).toContain('min-height: 64px;');
@@ -352,12 +352,13 @@ describe('ClaimBee paywall source', () => {
352
352
  'utf8',
353
353
  );
354
354
 
355
- expect(stepSource).toContain("<div className='paywall-v2-apple-pay-shell'>");
356
- expect(stepSource).toContain("<div className='paywall-v2-google-pay-shell'>");
357
- expect(stepSource).toContain('<ApplePaySubscriptionCheckoutSlot');
358
- expect(stepSource).toContain('<GooglePaySubscriptionCheckoutSlot');
355
+ expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
356
+ expect(stepSource).toContain("surfaceId={PAYWALL_WALLET_SURFACE_ID}");
357
+ expect(stepSource).toContain('checkout={walletCheckoutInput}');
358
+ expect(stepSource).toContain('resolveMethodClassName={resolvePaywallWalletMethodClassName}');
359
+ expect(stepSource).toContain('resolveMethodWrapperClassName={resolvePaywallWalletMethodWrapperClassName}');
359
360
  expect(stepSource).toContain('onUnavailableClick={openCardCheckoutFromUnavailableWallet}');
360
- expect(stepSource).toContain('session={checkoutSession}');
361
+ expect(stepSource).not.toContain('session={checkoutSession}');
361
362
  expect(stepSource).not.toContain('onClick={() => void startWalletCheckout()}');
362
363
  expect(stepSource).not.toContain('StripeExpressCheckoutElement');
363
364
  expect(stepSource).not.toContain('createStripeSubscriptionCheckout');
@@ -372,14 +373,13 @@ describe('ClaimBee paywall source', () => {
372
373
  'utf8',
373
374
  );
374
375
 
375
- expect(stepSource).toContain('type PaywallWalletAvailabilityState = {');
376
- expect(stepSource).toContain('const [paywallWalletAvailability, setPaywallWalletAvailability] = useState<PaywallWalletAvailabilityState>');
377
- expect(stepSource).toContain("updatePaywallWalletAvailability('applePay', available)");
378
- expect(stepSource).toContain("updatePaywallWalletAvailability('googlePay', available)");
379
- expect(stepSource).toContain('const checkoutInitialWalletAvailable = walletPaymentMethods.some(');
376
+ expect(stepSource).toContain('type PaywallWalletAvailabilityState = Partial<Record<string, boolean>>;');
377
+ expect(stepSource).toContain('useState<PaywallWalletAvailabilityState>({});');
378
+ expect(stepSource).toContain('const handleWalletSurfaceAvailabilityChange = (');
379
+ expect(stepSource).toContain('availability: StripeSubscriptionWalletSurfaceAvailability');
380
+ expect(stepSource).toContain('paywallWalletAvailability[PAYWALL_WALLET_SURFACE_ID] ?? null');
380
381
  expect(stepSource).toContain('initialWalletAvailable={checkoutInitialWalletAvailable}');
381
- expect(stepSource).toContain('onAvailabilityChange={handleApplePayAvailabilityChange}');
382
- expect(stepSource).toContain('onAvailabilityChange={handleGooglePayAvailabilityChange}');
382
+ expect(stepSource).toContain('handleWalletSurfaceAvailabilityChange(PAYWALL_WALLET_SURFACE_ID, availability)');
383
383
  });
384
384
 
385
385
  it('prepares the real Apple Pay element on load instead of waiting for the placeholder tap', () => {
@@ -389,7 +389,7 @@ describe('ClaimBee paywall source', () => {
389
389
  );
390
390
 
391
391
  expect(stepSource).toContain('useStripeSubscriptionCheckoutSession');
392
- expect(stepSource).toContain('ApplePaySubscriptionCheckoutSlot');
392
+ expect(stepSource).toContain('StripeSubscriptionWalletSurface');
393
393
  expect(stepSource).toContain('checkoutSession.prepareCardCheckout');
394
394
  expect(stepSource).toContain('checkoutSession.activeClientSecret');
395
395
  expect(stepSource).not.toContain('const autoWalletIntentKeyRef = useRef<string | null>(null);');
@@ -410,8 +410,8 @@ describe('ClaimBee paywall source', () => {
410
410
  expect(stepSource).toContain('couponId: checkoutPromoActive ? selectedDisplayPlan?.couponId ?? null : null');
411
411
  expect(stepSource).toContain('summaryLabel: checkoutSummaryLabel');
412
412
  expect(stepSource).toContain('checkoutSession.activeClientSecret');
413
- expect(stepSource).toContain('<ApplePaySubscriptionCheckoutSlot');
414
- expect(stepSource).toContain('<GooglePaySubscriptionCheckoutSlot');
413
+ expect(stepSource).toContain('<StripeSubscriptionWalletSurface');
414
+ expect(stepSource).toContain('const walletCheckoutInput = useMemo<StripeSubscriptionWalletSurfaceCheckoutInput>');
415
415
  expect(stepSource).not.toContain('StripeExpressCheckoutElement');
416
416
  expect(stepSource).not.toContain('preparePaywallWalletSubscription');
417
417
  expect(stepSource).not.toContain('const handlePaywallApplePayPlaceholderClick = () => {');
@@ -424,9 +424,8 @@ describe('ClaimBee paywall source', () => {
424
424
  'utf8',
425
425
  );
426
426
 
427
- expect(stepSource).toContain('const trackPaymentInfoSubmitted = () => {');
427
+ expect(stepSource).not.toContain('publicAnalyticsSdk');
428
428
  expect(stepSource).not.toContain('publicAnalyticsSdk.trackCheckoutStarted({');
429
- expect(stepSource).toContain('publicAnalyticsSdk.trackPaymentInfoSubmitted({');
430
429
  expect(stepSource).toContain('stepType: stepPaywall.type');
431
430
  expect(stepSource).toContain('value: selectedDisplayPlan.analyticsPurchaseValue');
432
431
  expect(stepSource).toContain("currency: 'USD'");
@@ -434,16 +433,16 @@ describe('ClaimBee paywall source', () => {
434
433
  expect(stepSource).toContain('const checkoutAnalyticsContext = useMemo(');
435
434
  expect(stepSource).toContain('checkoutAnalytics={checkoutAnalyticsContext}');
436
435
  expect(stepSource).toContain('checkoutSessionId={checkoutSession.checkoutSessionId}');
437
- expect(stepSource).toContain('onPaymentInfoSubmitted={trackPaymentInfoSubmitted}');
436
+ expect(stepSource).not.toContain('onPaymentInfoSubmitted=');
438
437
  expect(stepSource).toContain("await checkoutSession.prepareCardCheckout({ checkoutStartSource: 'checkout_render' });");
439
- expect(stepSource).not.toContain('const trackCheckoutCompleted = () => {');
438
+ expect(stepSource).toContain('const handleCheckoutCompleted = () => {');
440
439
  expect(stepSource).not.toContain('publicAnalyticsSdk.trackCheckoutCompleted({');
441
- expect(stepSource).not.toContain('onSuccess={trackCheckoutCompleted}');
440
+ expect(stepSource).toContain('onSuccess={handleCheckoutCompleted}');
442
441
  expect(stepSource).not.toContain("eventType: 'checkout_started'");
443
442
  expect(stepSource).not.toContain("eventType: 'checkout_completed'");
444
443
  });
445
444
 
446
- it('emits payment-info analytics only after a stable Checkout Session exists', () => {
445
+ it('delegates payment-info analytics to shared checkout components with session context', () => {
447
446
  const stepSource = readFileSync(
448
447
  path.resolve(__dirname, '../../../src/steps/step-32-paywall.tsx'),
449
448
  'utf8',
@@ -456,25 +455,12 @@ describe('ClaimBee paywall source', () => {
456
455
  stepSource.indexOf('const checkoutAnalyticsContext = useMemo(() => {'),
457
456
  stepSource.indexOf('const successReturnUrl = useMemo(() => {'),
458
457
  );
459
- const paymentInfoHandler = stepSource.slice(
460
- stepSource.indexOf('const trackPaymentInfoSubmitted = () => {'),
461
- stepSource.indexOf('const preparePaywallWalletCheckout'),
462
- );
463
-
464
458
  expect(analyticsMetadata).toContain("currency: 'USD'");
465
459
  expect(checkoutAnalyticsContext).toContain('metadata: checkoutAnalyticsMetadata');
466
- expect(paymentInfoHandler).toContain(
467
- 'const checkoutSessionId = checkoutSession.checkoutSessionId?.trim();',
468
- );
469
- expect(paymentInfoHandler).toContain('if (!checkoutSessionId) {');
470
- expect(paymentInfoHandler.indexOf('if (!checkoutSessionId) {')).toBeLessThan(
471
- paymentInfoHandler.indexOf('completeStep(analyticsStepId, {'),
472
- );
473
- expect(paymentInfoHandler).toContain(
474
- 'eventId: `add_payment_info:${checkoutSessionId}`',
475
- );
476
- expect(paymentInfoHandler).toContain('metadata: checkoutAnalyticsMetadata');
477
- expect(paymentInfoHandler).toContain('void publicAnalyticsSdk.flush().catch(() => 0);');
460
+ expect(stepSource.match(/checkoutAnalytics=\{checkoutAnalyticsContext\}/g)).toHaveLength(2);
461
+ expect(stepSource).toContain('checkoutSessionId={checkoutSession.checkoutSessionId}');
462
+ expect(stepSource).not.toContain('trackPaymentInfoSubmitted');
463
+ expect(stepSource).not.toContain('add_payment_info:');
478
464
  });
479
465
 
480
466
  it('uses a local three-plan catalog so the paywall shows weekly, monthly, and yearly offers', () => {