@funnelsgrove/runtime 0.1.37 → 0.1.39
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.
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import type { RuntimeMode } from '../services/runtime-mode.service.js';
|
|
2
2
|
export type GeneratedOfferSetItemKind = 'plan' | 'upsell' | 'downsell';
|
|
3
|
+
export type GeneratedOfferSetPlanModeConfig = {
|
|
4
|
+
readonly providerPlanId?: string | null;
|
|
5
|
+
readonly priceLabel?: string | null;
|
|
6
|
+
readonly perDayAmount?: string | null;
|
|
7
|
+
readonly perDayLabel?: string | null;
|
|
8
|
+
readonly amountCents?: number | null;
|
|
9
|
+
readonly checkoutSummaryLabel?: string | null;
|
|
10
|
+
readonly billingInterval?: string | null;
|
|
11
|
+
readonly billingIntervalCount?: number | null;
|
|
12
|
+
};
|
|
3
13
|
export type GeneratedOfferSetPlanItem = {
|
|
4
14
|
readonly funnelPlanKey: string;
|
|
15
|
+
readonly runtimePlanKey?: string | null;
|
|
5
16
|
readonly projectBillingPlanId?: string | null;
|
|
6
17
|
readonly testProviderPlanId?: string | null;
|
|
7
18
|
readonly liveProviderPlanId?: string | null;
|
|
19
|
+
readonly title?: string | null;
|
|
20
|
+
readonly description?: string | null;
|
|
21
|
+
readonly priceLabel?: string | null;
|
|
22
|
+
readonly perDayAmount?: string | null;
|
|
23
|
+
readonly perDayLabel?: string | null;
|
|
24
|
+
readonly amountCents?: number | null;
|
|
25
|
+
readonly checkoutSummaryLabel?: string | null;
|
|
26
|
+
readonly billingInterval?: string | null;
|
|
27
|
+
readonly billingIntervalCount?: number | null;
|
|
28
|
+
readonly providerMappingsByMode?: Partial<Record<RuntimeMode, GeneratedOfferSetPlanModeConfig>> | null;
|
|
8
29
|
readonly analyticsPurchaseValue?: number | null;
|
|
9
30
|
readonly position?: number | null;
|
|
10
31
|
readonly isDefault?: boolean | null;
|
|
@@ -44,6 +65,14 @@ export type OfferSetRuntimePlanOverrides = {
|
|
|
44
65
|
providerPlanId?: string;
|
|
45
66
|
analyticsPurchaseValue?: number;
|
|
46
67
|
title?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
priceLabel?: string;
|
|
70
|
+
perDayAmount?: string;
|
|
71
|
+
perDayLabel?: string;
|
|
72
|
+
amountCents?: number;
|
|
73
|
+
checkoutSummaryLabel?: string;
|
|
74
|
+
billingInterval?: string;
|
|
75
|
+
billingIntervalCount?: number;
|
|
47
76
|
featuredTag?: string;
|
|
48
77
|
oldPriceLabel?: string;
|
|
49
78
|
isDefault?: boolean;
|
|
@@ -51,7 +80,7 @@ export type OfferSetRuntimePlanOverrides = {
|
|
|
51
80
|
export type OfferSetRuntimeInput<TPlan extends Record<string, unknown>> = {
|
|
52
81
|
offerSets: readonly GeneratedOfferSet[];
|
|
53
82
|
offerSetKey?: string;
|
|
54
|
-
baseCatalogsByMode
|
|
83
|
+
baseCatalogsByMode?: Partial<Record<RuntimeMode, Record<string, TPlan>>>;
|
|
55
84
|
fallbackDiscounts?: readonly OfferSetRuntimeDiscount[];
|
|
56
85
|
};
|
|
57
86
|
export type OfferSetRuntimeResult<TPlan extends Record<string, unknown>> = {
|
|
@@ -68,4 +97,4 @@ export type OfferSetRuntimeResult<TPlan extends Record<string, unknown>> = {
|
|
|
68
97
|
export declare const findGeneratedOfferSet: (offerSets: readonly GeneratedOfferSet[], offerSetKey?: string) => GeneratedOfferSet | null;
|
|
69
98
|
export declare const applyGeneratedOfferSetToPlanCatalog: <TPlan extends Record<string, unknown>>(baseCatalog: Record<string, TPlan>, mode: RuntimeMode, offerSet: GeneratedOfferSet | null) => Record<string, TPlan & OfferSetRuntimePlanOverrides>;
|
|
70
99
|
export declare const buildGeneratedOfferSetDiscountList: (offerSet: GeneratedOfferSet | null, mode: RuntimeMode, fallbackDiscounts?: readonly OfferSetRuntimeDiscount[]) => readonly OfferSetRuntimeDiscount[];
|
|
71
|
-
export declare const resolveGeneratedOfferSetRuntime: <TPlan extends Record<string, unknown>>(input: OfferSetRuntimeInput<TPlan>) => OfferSetRuntimeResult<TPlan>;
|
|
100
|
+
export declare const resolveGeneratedOfferSetRuntime: <TPlan extends Record<string, unknown> = Record<string, unknown>>(input: OfferSetRuntimeInput<TPlan>) => OfferSetRuntimeResult<TPlan>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const isRecord = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value));
|
|
2
2
|
const readString = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
|
|
3
|
+
const normalizeCatalogLookupKey = (value) => (value
|
|
4
|
+
.trim()
|
|
5
|
+
.toLowerCase()
|
|
6
|
+
.replace(/[^a-z0-9]+/g, ''));
|
|
3
7
|
const readFiniteNumber = (value) => {
|
|
4
8
|
const numeric = typeof value === 'number'
|
|
5
9
|
? value
|
|
@@ -8,6 +12,24 @@ const readFiniteNumber = (value) => {
|
|
|
8
12
|
: null;
|
|
9
13
|
return numeric !== null && Number.isFinite(numeric) ? numeric : null;
|
|
10
14
|
};
|
|
15
|
+
const readStringField = (record, keys) => {
|
|
16
|
+
for (const key of keys) {
|
|
17
|
+
const value = readString(record[key]);
|
|
18
|
+
if (value) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
const readFiniteNumberField = (record, keys) => {
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const value = readFiniteNumber(record[key]);
|
|
27
|
+
if (value !== null) {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
11
33
|
const readPositiveInteger = (value) => {
|
|
12
34
|
const numeric = readFiniteNumber(value);
|
|
13
35
|
return numeric !== null && numeric > 0 ? Math.round(numeric) : null;
|
|
@@ -32,9 +54,87 @@ export const findGeneratedOfferSet = (offerSets, offerSetKey) => {
|
|
|
32
54
|
const activeOfferSets = offerSets.filter((offerSet) => offerSet.isActive !== false);
|
|
33
55
|
return ((_b = (_a = activeOfferSets.find((offerSet) => offerSet.key === offerSetKey)) !== null && _a !== void 0 ? _a : activeOfferSets[0]) !== null && _b !== void 0 ? _b : null);
|
|
34
56
|
};
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
const resolveCatalogKey = (baseCatalog, funnelPlanKey) => {
|
|
58
|
+
var _a;
|
|
59
|
+
const key = funnelPlanKey.trim();
|
|
60
|
+
if (!key) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
if (Object.prototype.hasOwnProperty.call(baseCatalog, key)) {
|
|
64
|
+
return key;
|
|
65
|
+
}
|
|
66
|
+
const normalizedKey = normalizeCatalogLookupKey(key);
|
|
67
|
+
return (_a = Object.keys(baseCatalog).find((catalogKey) => (normalizeCatalogLookupKey(catalogKey) === normalizedKey))) !== null && _a !== void 0 ? _a : null;
|
|
68
|
+
};
|
|
69
|
+
const readOfferItemMetadata = (item) => (isRecord(item.metadata) ? item.metadata : {});
|
|
70
|
+
const readOfferItemExplicitRuntimePlanKey = (item) => {
|
|
71
|
+
var _a;
|
|
72
|
+
const metadata = readOfferItemMetadata(item);
|
|
73
|
+
return ((_a = readString(item.runtimePlanKey)) !== null && _a !== void 0 ? _a : readStringField(metadata, ['runtimePlanKey', 'planKey', 'billingPlanKey']));
|
|
74
|
+
};
|
|
75
|
+
const readGeneratedModeConfig = (item, mode) => {
|
|
76
|
+
const directMappings = isRecord(item.providerMappingsByMode) ? item.providerMappingsByMode : {};
|
|
77
|
+
const directModeConfig = directMappings[mode];
|
|
78
|
+
if (isRecord(directModeConfig)) {
|
|
79
|
+
return directModeConfig;
|
|
80
|
+
}
|
|
81
|
+
const metadata = readOfferItemMetadata(item);
|
|
82
|
+
const metadataMappings = isRecord(metadata.providerMappingsByMode) ? metadata.providerMappingsByMode : {};
|
|
83
|
+
const metadataModeConfig = metadataMappings[mode];
|
|
84
|
+
return isRecord(metadataModeConfig) ? metadataModeConfig : {};
|
|
85
|
+
};
|
|
86
|
+
const readProviderPlanId = (item, mode) => {
|
|
87
|
+
var _a;
|
|
88
|
+
const modeConfig = readGeneratedModeConfig(item, mode);
|
|
89
|
+
return ((_a = readString(modeConfig.providerPlanId)) !== null && _a !== void 0 ? _a : (mode === 'test'
|
|
90
|
+
? readString(item.testProviderPlanId)
|
|
91
|
+
: readString(item.liveProviderPlanId)));
|
|
92
|
+
};
|
|
93
|
+
const resolveOfferItemRuntimeKey = (item, baseCatalogsByMode) => {
|
|
94
|
+
var _a, _b, _c, _d, _e;
|
|
95
|
+
if (!item) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const explicitKey = readOfferItemExplicitRuntimePlanKey(item);
|
|
99
|
+
return ((_e = (_d = (_c = (_b = (_a = (explicitKey ? resolveCatalogKey(baseCatalogsByMode.test, explicitKey) : null)) !== null && _a !== void 0 ? _a : (explicitKey ? resolveCatalogKey(baseCatalogsByMode.live, explicitKey) : null)) !== null && _b !== void 0 ? _b : resolveCatalogKey(baseCatalogsByMode.test, item.funnelPlanKey)) !== null && _c !== void 0 ? _c : resolveCatalogKey(baseCatalogsByMode.live, item.funnelPlanKey)) !== null && _d !== void 0 ? _d : explicitKey) !== null && _e !== void 0 ? _e : readString(item.funnelPlanKey));
|
|
100
|
+
};
|
|
101
|
+
const resolveOfferItemCatalogKey = (item, baseCatalog) => {
|
|
102
|
+
var _a, _b, _c;
|
|
103
|
+
const explicitKey = readOfferItemExplicitRuntimePlanKey(item);
|
|
104
|
+
return ((_c = (_b = (_a = (explicitKey ? resolveCatalogKey(baseCatalog, explicitKey) : null)) !== null && _a !== void 0 ? _a : resolveCatalogKey(baseCatalog, item.funnelPlanKey)) !== null && _b !== void 0 ? _b : explicitKey) !== null && _c !== void 0 ? _c : readString(item.funnelPlanKey));
|
|
105
|
+
};
|
|
106
|
+
const readGeneratedPlanStringValue = (item, mode, itemKeys, metadataKeys = itemKeys) => {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
const modeConfig = readGeneratedModeConfig(item, mode);
|
|
109
|
+
const metadata = readOfferItemMetadata(item);
|
|
110
|
+
return ((_b = (_a = readStringField(modeConfig, metadataKeys)) !== null && _a !== void 0 ? _a : itemKeys.reduce((result, key) => result !== null && result !== void 0 ? result : readString(item[key]), null)) !== null && _b !== void 0 ? _b : readStringField(metadata, metadataKeys));
|
|
111
|
+
};
|
|
112
|
+
const readGeneratedPlanNumberValue = (item, mode, itemKeys, metadataKeys = itemKeys) => {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
const modeConfig = readGeneratedModeConfig(item, mode);
|
|
115
|
+
const metadata = readOfferItemMetadata(item);
|
|
116
|
+
const itemValue = itemKeys.reduce((result, key) => (result !== null && result !== void 0 ? result : readFiniteNumber(item[key])), null);
|
|
117
|
+
return ((_b = (_a = readFiniteNumberField(modeConfig, metadataKeys)) !== null && _a !== void 0 ? _a : itemValue) !== null && _b !== void 0 ? _b : readFiniteNumberField(metadata, metadataKeys));
|
|
118
|
+
};
|
|
119
|
+
const buildGeneratedPlanOverrides = (item, mode, isDefault) => {
|
|
120
|
+
var _a, _b, _c, _d, _e, _f;
|
|
121
|
+
const metadata = readOfferItemMetadata(item);
|
|
122
|
+
const providerPlanId = readProviderPlanId(item, mode);
|
|
123
|
+
const projectPlanId = (_a = readString(item.projectBillingPlanId)) !== null && _a !== void 0 ? _a : readStringField(metadata, ['projectBillingPlanId', 'projectPlanId', 'project_billing_plan_id']);
|
|
124
|
+
const title = (_c = (_b = readString(item.title)) !== null && _b !== void 0 ? _b : readString(item.displayNameOverride)) !== null && _c !== void 0 ? _c : readStringField(metadata, ['title', 'displayName', 'publicPlanName', 'public_plan_name']);
|
|
125
|
+
const description = readGeneratedPlanStringValue(item, mode, ['description']);
|
|
126
|
+
const priceLabel = readGeneratedPlanStringValue(item, mode, ['priceLabel']);
|
|
127
|
+
const perDayAmount = readGeneratedPlanStringValue(item, mode, ['perDayAmount']);
|
|
128
|
+
const perDayLabel = readGeneratedPlanStringValue(item, mode, ['perDayLabel']);
|
|
129
|
+
const checkoutSummaryLabel = readGeneratedPlanStringValue(item, mode, ['checkoutSummaryLabel']);
|
|
130
|
+
const billingInterval = readGeneratedPlanStringValue(item, mode, ['billingInterval']);
|
|
131
|
+
const billingIntervalCount = readGeneratedPlanNumberValue(item, mode, ['billingIntervalCount']);
|
|
132
|
+
const amountCents = readGeneratedPlanNumberValue(item, mode, ['amountCents']);
|
|
133
|
+
const analyticsPurchaseValue = (_d = readFiniteNumber(item.analyticsPurchaseValue)) !== null && _d !== void 0 ? _d : readFiniteNumberField(metadata, ['analyticsPurchaseValue']);
|
|
134
|
+
const featuredTag = (_e = readString(item.featuredTag)) !== null && _e !== void 0 ? _e : readStringField(metadata, ['featuredTag']);
|
|
135
|
+
const oldPriceLabel = (_f = readString(item.oldPriceLabel)) !== null && _f !== void 0 ? _f : readStringField(metadata, ['oldPriceLabel']);
|
|
136
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (projectPlanId ? { projectPlanId } : {})), (providerPlanId ? { providerPlanId } : {})), (analyticsPurchaseValue !== null ? { analyticsPurchaseValue } : {})), (title ? { title } : {})), (description ? { description } : {})), (priceLabel ? { priceLabel } : {})), (perDayAmount ? { perDayAmount } : {})), (perDayLabel ? { perDayLabel } : {})), (amountCents !== null ? { amountCents: Math.max(0, Math.round(amountCents)) } : {})), (checkoutSummaryLabel ? { checkoutSummaryLabel } : {})), (billingInterval ? { billingInterval } : {})), (billingIntervalCount !== null ? { billingIntervalCount: Math.max(1, Math.round(billingIntervalCount)) } : {})), (featuredTag ? { featuredTag } : {})), (oldPriceLabel ? { oldPriceLabel } : {})), (isDefault ? { isDefault: true } : {}));
|
|
137
|
+
};
|
|
38
138
|
export const applyGeneratedOfferSetToPlanCatalog = (baseCatalog, mode, offerSet) => {
|
|
39
139
|
var _a, _b, _c;
|
|
40
140
|
if (!offerSet) {
|
|
@@ -46,23 +146,17 @@ export const applyGeneratedOfferSetToPlanCatalog = (baseCatalog, mode, offerSet)
|
|
|
46
146
|
...getSortedItemsByKind(offerSet, 'upsell'),
|
|
47
147
|
...getSortedItemsByKind(offerSet, 'downsell'),
|
|
48
148
|
];
|
|
49
|
-
const defaultPlanKey = ((_a = planItems.find((item) => item.isDefault === true)) === null || _a === void 0 ? void 0 : _a.funnelPlanKey) ||
|
|
50
|
-
((_b = planItems[0]) === null || _b === void 0 ? void 0 : _b.funnelPlanKey) ||
|
|
51
|
-
'';
|
|
52
149
|
const nextCatalog = Object.assign({}, baseCatalog);
|
|
150
|
+
const defaultPlanItem = (_a = planItems.find((item) => item.isDefault === true)) !== null && _a !== void 0 ? _a : planItems[0];
|
|
151
|
+
const defaultPlanKey = defaultPlanItem
|
|
152
|
+
? (_b = resolveOfferItemCatalogKey(defaultPlanItem, nextCatalog)) !== null && _b !== void 0 ? _b : defaultPlanItem.funnelPlanKey
|
|
153
|
+
: '';
|
|
53
154
|
for (const item of catalogItems) {
|
|
54
|
-
const key = item
|
|
55
|
-
|
|
56
|
-
if (!key || !basePlan) {
|
|
155
|
+
const key = resolveOfferItemCatalogKey(item, nextCatalog);
|
|
156
|
+
if (!key) {
|
|
57
157
|
continue;
|
|
58
158
|
}
|
|
59
|
-
|
|
60
|
-
const displayNameOverride = readString(item.displayNameOverride);
|
|
61
|
-
const featuredTag = readString(item.featuredTag);
|
|
62
|
-
const oldPriceLabel = readString(item.oldPriceLabel);
|
|
63
|
-
nextCatalog[key] = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, basePlan), (readString(item.projectBillingPlanId) ? { projectPlanId: (_c = readString(item.projectBillingPlanId)) !== null && _c !== void 0 ? _c : undefined } : {})), (providerPlanId ? { providerPlanId } : {})), (typeof item.analyticsPurchaseValue === 'number'
|
|
64
|
-
? { analyticsPurchaseValue: item.analyticsPurchaseValue }
|
|
65
|
-
: {})), (displayNameOverride ? { title: displayNameOverride } : {})), (featuredTag ? { featuredTag } : {})), (oldPriceLabel ? { oldPriceLabel } : {})), { isDefault: getOfferSetItemKind(item) === 'plan' && key === defaultPlanKey || undefined });
|
|
159
|
+
nextCatalog[key] = Object.assign(Object.assign({}, ((_c = nextCatalog[key]) !== null && _c !== void 0 ? _c : {})), buildGeneratedPlanOverrides(item, mode, getOfferSetItemKind(item) === 'plan' && key === defaultPlanKey));
|
|
66
160
|
}
|
|
67
161
|
return nextCatalog;
|
|
68
162
|
};
|
|
@@ -130,19 +224,28 @@ export const buildGeneratedOfferSetDiscountList = (offerSet, mode, fallbackDisco
|
|
|
130
224
|
];
|
|
131
225
|
};
|
|
132
226
|
export const resolveGeneratedOfferSetRuntime = (input) => {
|
|
133
|
-
var _a, _b, _c, _d;
|
|
227
|
+
var _a, _b, _c, _d, _e, _f;
|
|
134
228
|
const offerSet = findGeneratedOfferSet(input.offerSets, input.offerSetKey);
|
|
229
|
+
const baseCatalogsByMode = {
|
|
230
|
+
test: (_b = (_a = input.baseCatalogsByMode) === null || _a === void 0 ? void 0 : _a.test) !== null && _b !== void 0 ? _b : {},
|
|
231
|
+
live: (_d = (_c = input.baseCatalogsByMode) === null || _c === void 0 ? void 0 : _c.live) !== null && _d !== void 0 ? _d : {},
|
|
232
|
+
};
|
|
135
233
|
const planItems = getSortedItemsByKind(offerSet, 'plan');
|
|
136
234
|
const upsellItems = getSortedItemsByKind(offerSet, 'upsell');
|
|
137
235
|
const downsellItems = getSortedItemsByKind(offerSet, 'downsell');
|
|
138
|
-
const planKeys = planItems
|
|
139
|
-
|
|
236
|
+
const planKeys = planItems
|
|
237
|
+
.map((item) => resolveOfferItemRuntimeKey(item, baseCatalogsByMode))
|
|
238
|
+
.filter((key) => Boolean(key));
|
|
239
|
+
const upsellPlanKeys = upsellItems
|
|
240
|
+
.map((item) => resolveOfferItemRuntimeKey(item, baseCatalogsByMode))
|
|
241
|
+
.filter((key) => Boolean(key));
|
|
140
242
|
const downsellPlanKeys = (downsellItems.length > 0 ? downsellItems : planItems)
|
|
141
|
-
.map((item) => item
|
|
142
|
-
|
|
243
|
+
.map((item) => resolveOfferItemRuntimeKey(item, baseCatalogsByMode))
|
|
244
|
+
.filter((key) => Boolean(key));
|
|
245
|
+
const defaultPlanKey = (_f = resolveOfferItemRuntimeKey((_e = planItems.find((item) => item.isDefault === true)) !== null && _e !== void 0 ? _e : planItems[0], baseCatalogsByMode)) !== null && _f !== void 0 ? _f : '';
|
|
143
246
|
const plansByMode = {
|
|
144
|
-
test: applyGeneratedOfferSetToPlanCatalog(
|
|
145
|
-
live: applyGeneratedOfferSetToPlanCatalog(
|
|
247
|
+
test: applyGeneratedOfferSetToPlanCatalog(baseCatalogsByMode.test, 'test', offerSet),
|
|
248
|
+
live: applyGeneratedOfferSetToPlanCatalog(baseCatalogsByMode.live, 'live', offerSet),
|
|
146
249
|
};
|
|
147
250
|
const discountListsByMode = {
|
|
148
251
|
test: buildGeneratedOfferSetDiscountList(offerSet, 'test', input.fallbackDiscounts),
|