@funnelsgrove/runtime 0.7.0 → 0.7.1

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 CHANGED
@@ -37,6 +37,7 @@ documented in [`docs/product-specs/funnel-template-runtime.md`](../../docs/produ
37
37
 
38
38
  - `config/*`: manifest, theme, font, env, and builder-preview contracts.
39
39
  - `runtime/*`: flow, routing, attribution, feature flags, preview bridge, URL attributes, and manifest validation.
40
+ - `runtime/published-runtime-resolution`: running-experiment normalization and immutable offer-set selection from pricing assignments.
40
41
  - `services/*`: API client, runtime mode, public env, project env, logger, and funnel state helpers.
41
42
  - `components/*`: generic runtime context, subscription handoff/management, editor panel, dev info, and base controls.
42
43
  - `content/*` and `steps/types`: shared step content and taxonomy contracts.
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export type { FunnelStepChoices } from './runtime/use-step-choices.js';
23
23
  export * from './runtime/preview-bridge.js';
24
24
  export * from './runtime/preview-definition-overrides.js';
25
25
  export * from './runtime/offer-set-runtime.js';
26
+ export * from './runtime/published-runtime-resolution.js';
26
27
  export * from './runtime/route-resolver.js';
27
28
  export * from './runtime/submit-email-capture.js';
28
29
  export * from './runtime/subscription-handoff.js';
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ export { useStepChoices } from './runtime/use-step-choices.js';
24
24
  export * from './runtime/preview-bridge.js';
25
25
  export * from './runtime/preview-definition-overrides.js';
26
26
  export * from './runtime/offer-set-runtime.js';
27
+ export * from './runtime/published-runtime-resolution.js';
27
28
  export * from './runtime/route-resolver.js';
28
29
  export * from './runtime/submit-email-capture.js';
29
30
  export * from './runtime/subscription-handoff.js';
@@ -0,0 +1,23 @@
1
+ import type { FunnelExperimentDefinition, FunnelPricingExperimentDefinition } from '../config/funnel.experiments.types.js';
2
+ import type { FunnelManifestExperiment } from '../config/funnel.manifest.types.js';
3
+ import type { ResolvedFunnelRuntimeConfig } from '../services/funnel-runtime-config.js';
4
+ import type { GeneratedOfferSet } from './offer-set-runtime.js';
5
+ export type ResolvedPublishedExperiments = {
6
+ experiments: readonly FunnelExperimentDefinition[];
7
+ pricingExperiments: readonly FunnelPricingExperimentDefinition[];
8
+ flowExperiments: readonly FunnelManifestExperiment[];
9
+ };
10
+ export type PublishedOfferSetSelection = {
11
+ offerSetId: string;
12
+ offerSet: GeneratedOfferSet | null;
13
+ experimentId: string | null;
14
+ variantKey: string | null;
15
+ };
16
+ export declare const resolvePublishedRuntimeExperiments: (config: ResolvedFunnelRuntimeConfig | null) => ResolvedPublishedExperiments;
17
+ export declare const resolvePublishedOfferSetSelection: ({ offerSets, pricingExperiments, featureFlags, search, defaultOfferSetId, }: {
18
+ offerSets: readonly GeneratedOfferSet[];
19
+ pricingExperiments: readonly FunnelPricingExperimentDefinition[];
20
+ featureFlags: Readonly<Record<string, string>>;
21
+ search?: string;
22
+ defaultOfferSetId?: string;
23
+ }) => PublishedOfferSetSelection;
@@ -0,0 +1,72 @@
1
+ import { toManifestExperiments } from '../config/funnel.experiments.types.js';
2
+ const getPricingExperimentVariants = (experiment) => {
3
+ var _a;
4
+ return ((_a = experiment.variants) === null || _a === void 0 ? void 0 : _a.length)
5
+ ? experiment.variants
6
+ : [experiment.control, experiment.variant];
7
+ };
8
+ export const resolvePublishedRuntimeExperiments = (config) => {
9
+ var _a;
10
+ const experiments = (_a = config === null || config === void 0 ? void 0 : config.experiments.filter((experiment) => { var _a; return ((_a = experiment.status) !== null && _a !== void 0 ? _a : 'running') === 'running'; })) !== null && _a !== void 0 ? _a : [];
11
+ return {
12
+ experiments,
13
+ pricingExperiments: experiments.filter((experiment) => experiment.type === 'pricing'),
14
+ flowExperiments: toManifestExperiments(experiments),
15
+ };
16
+ };
17
+ export const resolvePublishedOfferSetSelection = ({ offerSets, pricingExperiments, featureFlags, search = '', defaultOfferSetId, }) => {
18
+ var _a, _b, _c, _d, _e;
19
+ const activeOfferSets = offerSets.filter((offerSet) => {
20
+ var _a;
21
+ return (offerSet.isActive !== false && Boolean((_a = offerSet.id) === null || _a === void 0 ? void 0 : _a.trim()));
22
+ });
23
+ const activeOfferSetsById = new Map(activeOfferSets.map((offerSet) => [offerSet.id.trim(), offerSet]));
24
+ const params = new URLSearchParams(search);
25
+ const forcedExperimentId = params.get('mode') === 'test'
26
+ ? ((_a = params.get('experiment')) === null || _a === void 0 ? void 0 : _a.trim()) || null
27
+ : null;
28
+ const forcedVariantKey = forcedExperimentId
29
+ ? ((_b = params.get('variant')) === null || _b === void 0 ? void 0 : _b.trim()) || null
30
+ : null;
31
+ const resolveExperimentSelection = (experiment, variantKey) => {
32
+ if (!variantKey) {
33
+ return null;
34
+ }
35
+ const variant = getPricingExperimentVariants(experiment)
36
+ .find((candidate) => candidate.variantKey === variantKey);
37
+ const offerSet = variant ? activeOfferSetsById.get(variant.offerSetId) : null;
38
+ return variant && offerSet
39
+ ? {
40
+ offerSetId: offerSet.id,
41
+ offerSet,
42
+ experimentId: experiment.id,
43
+ variantKey,
44
+ }
45
+ : null;
46
+ };
47
+ if (forcedExperimentId) {
48
+ const forcedExperiment = pricingExperiments.find((experiment) => experiment.id === forcedExperimentId);
49
+ const forcedSelection = forcedExperiment
50
+ ? resolveExperimentSelection(forcedExperiment, forcedVariantKey)
51
+ : null;
52
+ if (forcedSelection) {
53
+ return forcedSelection;
54
+ }
55
+ }
56
+ for (const experiment of pricingExperiments) {
57
+ const selection = resolveExperimentSelection(experiment, ((_c = featureFlags[experiment.id]) === null || _c === void 0 ? void 0 : _c.trim()) || null);
58
+ if (selection) {
59
+ return selection;
60
+ }
61
+ }
62
+ const defaultOfferSet = defaultOfferSetId
63
+ ? activeOfferSetsById.get(defaultOfferSetId.trim())
64
+ : null;
65
+ const offerSet = (_d = defaultOfferSet !== null && defaultOfferSet !== void 0 ? defaultOfferSet : activeOfferSets[0]) !== null && _d !== void 0 ? _d : null;
66
+ return {
67
+ offerSetId: (_e = offerSet === null || offerSet === void 0 ? void 0 : offerSet.id) !== null && _e !== void 0 ? _e : '',
68
+ offerSet,
69
+ experimentId: null,
70
+ variantKey: null,
71
+ };
72
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/runtime",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",