@funnelsgrove/runtime 0.1.43 → 0.1.45
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.
- package/README.md +44 -39
- package/dist/index.js +5 -0
- package/dist/runtime/use-funnel-flow-controller.js +9 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,60 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
Shared funnel runtime contracts and helpers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read this file before editing shared funnel mechanics. Deeper runtime behavior is
|
|
6
|
+
documented in [`docs/product-specs/funnel-template-runtime.md`](../../docs/product-specs/funnel-template-runtime.md).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- The build normalizes generated relative ESM imports to explicit `.js` files for published package consumers.
|
|
8
|
+
## Owns funnel mechanics
|
|
9
|
+
|
|
10
|
+
`@funnelsgrove/runtime` owns the reusable mechanics that every generated or published funnel depends on: manifests, routing, env resolution, funnel state, preview/runtime detection, and generic runtime UI. Funnels bring their own copy, visual design, billing catalog, and step JSX.
|
|
11
11
|
|
|
12
12
|
## Use This Package For
|
|
13
13
|
|
|
14
14
|
- manifest types and validation
|
|
15
15
|
- route resolution and entry-point handling
|
|
16
|
+
- runtime mode and public env/config resolution
|
|
16
17
|
- experiment assignment helpers
|
|
17
18
|
- preview-bridge parsing and preview/runtime detection
|
|
18
19
|
- published theme contract and CSS variable helpers
|
|
19
|
-
- funnel context,
|
|
20
|
-
-
|
|
20
|
+
- funnel context, flow controller, and generic runtime UI primitives
|
|
21
|
+
- funnel-scoped state/storage helpers
|
|
21
22
|
- browser-safe API client helpers used by funnels
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
- subscription handoff link rendering for iOS, Android, and desktop/web fallbacks from runtime env
|
|
43
|
-
- subscription handoff browser-only values are resolved after mount so published success routes hydrate without text mismatches
|
|
44
|
-
- public subscription summaries with provider plan and period metadata for funnel-local management screens
|
|
45
|
-
- a bottom-right `RuntimeDevInfoBox` that funnels can mount in test mode to inspect public runtime config with one canonical env/config name per row, copy the current funnel user id/email, switch to live mode, and clear funnel-scoped paywall state
|
|
23
|
+
- subscription handoff and subscription management screens whose copy stays funnel-local
|
|
24
|
+
- test-mode developer info surfaces for funnel preview tooling
|
|
25
|
+
|
|
26
|
+
## Do Not Use This Package For
|
|
27
|
+
|
|
28
|
+
- funnel-specific step JSX
|
|
29
|
+
- brand/theme assets
|
|
30
|
+
- billing catalog data
|
|
31
|
+
- analytics transport
|
|
32
|
+
- Stripe checkout UI
|
|
33
|
+
- funnel-specific developer widget copy
|
|
34
|
+
- direct provider SDK calls
|
|
35
|
+
|
|
36
|
+
## Public Surfaces
|
|
37
|
+
|
|
38
|
+
- `config/*`: manifest, theme, font, env, and builder-preview contracts.
|
|
39
|
+
- `runtime/*`: flow, routing, attribution, feature flags, preview bridge, URL attributes, and manifest validation.
|
|
40
|
+
- `services/*`: API client, runtime mode, public env, project env, logger, and funnel state helpers.
|
|
41
|
+
- `components/*`: generic runtime context, subscription handoff/management, editor panel, dev info, and base controls.
|
|
42
|
+
- `content/*` and `steps/types`: shared step content and taxonomy contracts.
|
|
46
43
|
|
|
47
44
|
## Storage Conventions
|
|
48
45
|
|
|
49
46
|
- runtime user ids stay funnel-scoped under `funnel:<funnelId>:user-id`
|
|
50
47
|
- runtime-generated user ids use the `u_` prefix
|
|
51
48
|
- paywall state is stored through runtime helpers under `fg_state:<funnelId>`
|
|
52
|
-
- developer reset controls should clear paywall state through `clearPaywallStateValue`, including
|
|
49
|
+
- developer reset controls should clear paywall state through `clearPaywallStateValue`, including funnel-provided compatibility keys when a live funnel still needs them
|
|
53
50
|
|
|
54
|
-
##
|
|
51
|
+
## Engineering Rules
|
|
55
52
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
- Do not import `@funnelsgrove/payments` or `@funnelsgrove/analytics` here for checkout or transport orchestration.
|
|
54
|
+
- Runtime UI must be generic and copy-injectable.
|
|
55
|
+
- Browser-only values must resolve after mount when SSR hydration can be affected.
|
|
56
|
+
- Keep env mapping centralized in `config/env.config.ts`.
|
|
57
|
+
|
|
58
|
+
## Build, Test, Publish
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run test:run --workspace @funnelsgrove/runtime
|
|
62
|
+
npm run build --workspace @funnelsgrove/runtime
|
|
63
|
+
npm publish --workspace @funnelsgrove/runtime --access public
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The build normalizes generated relative ESM imports to explicit `.js` files for published package consumers.
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
// Shared config contracts.
|
|
1
2
|
export * from './config/builder-preview.protocol.js';
|
|
2
3
|
export * from './config/env.config.js';
|
|
3
4
|
export * from './config/funnel.manifest.types.js';
|
|
4
5
|
export * from './config/funnel.experiments.types.js';
|
|
5
6
|
export * from './config/funnel-theme.js';
|
|
6
7
|
export * from './config/font-config.js';
|
|
8
|
+
// Structured content contracts.
|
|
7
9
|
export * from './content/step-content.js';
|
|
10
|
+
// Runtime mechanics.
|
|
8
11
|
export * from './runtime/experiment-assignment.js';
|
|
9
12
|
export * from './runtime/browser-helpers.js';
|
|
10
13
|
export * from './runtime/funnel-flow.js';
|
|
@@ -20,6 +23,7 @@ export * from './runtime/route-resolver.js';
|
|
|
20
23
|
export * from './runtime/subscription-handoff.js';
|
|
21
24
|
export * from './runtime/url-user-attributes.js';
|
|
22
25
|
export * from './runtime/use-url-user-attributes-sync.js';
|
|
26
|
+
// Browser-safe services.
|
|
23
27
|
export * from './services/api.service.js';
|
|
24
28
|
export * from './services/funnel-state.service.js';
|
|
25
29
|
export * from './services/logger.js';
|
|
@@ -28,6 +32,7 @@ export * from './services/project-env.js';
|
|
|
28
32
|
export * from './services/public-env.js';
|
|
29
33
|
export * from './services/runtime-api.config.js';
|
|
30
34
|
export * from './services/runtime-mode.service.js';
|
|
35
|
+
// SDK and generic runtime UI.
|
|
31
36
|
export * from './sdk/userAnswers.js';
|
|
32
37
|
export * from './components/FunnelContext.js';
|
|
33
38
|
export * from './components/FunnelEditorPanel.js';
|
|
@@ -11,7 +11,7 @@ import { resolveExperimentAssignment } from './experiment-assignment.js';
|
|
|
11
11
|
import { collectCurrentFunnelAttribution } from './funnel-attribution.js';
|
|
12
12
|
import { resolveUrlUserAttributes } from './url-user-attributes.js';
|
|
13
13
|
import { bootstrapPostHog, identifyPostHog, isPostHogReady, resolveExperimentVariant, } from './posthog-flags.js';
|
|
14
|
-
import { isEditorEnabled, useRuntimeMode } from '../services/runtime-mode.service.js';
|
|
14
|
+
import { getRuntimeMode, isEditorEnabled, useRuntimeMode } from '../services/runtime-mode.service.js';
|
|
15
15
|
import { isPreviewFrameRuntime } from '../services/preview-frame.service.js';
|
|
16
16
|
const FIRST_STEP_ENGAGEMENT_THRESHOLD_MS = 1000;
|
|
17
17
|
export function computeRenderSuspended(input) {
|
|
@@ -325,9 +325,10 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
325
325
|
const startedAt = stepStartedAtByIdRef.current[meta.id] || record.completedAt;
|
|
326
326
|
dispatchWindowCustomEvent('funnel:step-completed', record);
|
|
327
327
|
if (!isPreviewRuntime) {
|
|
328
|
+
const analyticsRuntimeMode = getRuntimeMode();
|
|
328
329
|
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepCompleted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
329
330
|
userId: currentUserIdRef.current,
|
|
330
|
-
environment:
|
|
331
|
+
environment: analyticsRuntimeMode,
|
|
331
332
|
stepId: record.stepId,
|
|
332
333
|
stepName: record.stepName,
|
|
333
334
|
stepType,
|
|
@@ -358,7 +359,7 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
358
359
|
}
|
|
359
360
|
return updatedUser;
|
|
360
361
|
});
|
|
361
|
-
}, [analytics, isPreviewRuntime,
|
|
362
|
+
}, [analytics, isPreviewRuntime, stepById]);
|
|
362
363
|
const completeStep = useCallback((stepId, choices) => {
|
|
363
364
|
recordStepCompletion(stepId, choices);
|
|
364
365
|
}, [recordStepCompletion]);
|
|
@@ -383,10 +384,11 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
383
384
|
const stepName = (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name) || (renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title) || renderedStepId;
|
|
384
385
|
const stepType = renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type;
|
|
385
386
|
stepStartedAtByIdRef.current[renderedStepId] = startedAt;
|
|
387
|
+
const analyticsRuntimeMode = getRuntimeMode();
|
|
386
388
|
if (!isPreviewRuntime) {
|
|
387
389
|
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepStarted) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
388
390
|
userId: currentUserIdRef.current,
|
|
389
|
-
environment:
|
|
391
|
+
environment: analyticsRuntimeMode,
|
|
390
392
|
stepId: renderedStepId,
|
|
391
393
|
stepName,
|
|
392
394
|
stepType,
|
|
@@ -397,7 +399,7 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
397
399
|
firstStepViewedTrackedRef.current = true;
|
|
398
400
|
(_b = analytics === null || analytics === void 0 ? void 0 : analytics.trackFunnelStarted) === null || _b === void 0 ? void 0 : _b.call(analytics, {
|
|
399
401
|
userId: currentUserIdRef.current,
|
|
400
|
-
environment:
|
|
402
|
+
environment: analyticsRuntimeMode,
|
|
401
403
|
stepId: renderedStepId,
|
|
402
404
|
stepName,
|
|
403
405
|
stepType,
|
|
@@ -406,7 +408,7 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
406
408
|
});
|
|
407
409
|
(_c = analytics === null || analytics === void 0 ? void 0 : analytics.trackFirstStepViewed) === null || _c === void 0 ? void 0 : _c.call(analytics, {
|
|
408
410
|
userId: currentUserIdRef.current,
|
|
409
|
-
environment:
|
|
411
|
+
environment: analyticsRuntimeMode,
|
|
410
412
|
stepId: renderedStepId,
|
|
411
413
|
stepName,
|
|
412
414
|
stepType,
|
|
@@ -435,7 +437,7 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
435
437
|
const engagedAt = new Date().toISOString();
|
|
436
438
|
(_a = analytics === null || analytics === void 0 ? void 0 : analytics.trackStepEngaged) === null || _a === void 0 ? void 0 : _a.call(analytics, {
|
|
437
439
|
userId: currentUserIdRef.current,
|
|
438
|
-
environment:
|
|
440
|
+
environment: analyticsRuntimeMode,
|
|
439
441
|
stepId: engagedStepId,
|
|
440
442
|
stepName: engagedStepName,
|
|
441
443
|
stepType: engagedStepType,
|
|
@@ -458,7 +460,6 @@ export function useFunnelFlowController({ analytics, initialStepId, initialAttri
|
|
|
458
460
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.name,
|
|
459
461
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.title,
|
|
460
462
|
renderedStepMeta === null || renderedStepMeta === void 0 ? void 0 : renderedStepMeta.type,
|
|
461
|
-
runtimeMode,
|
|
462
463
|
safeActiveStepId,
|
|
463
464
|
]);
|
|
464
465
|
useEffect(() => {
|