fix: Enhance the Robustness of Embed Snippets for Multiple Embeds on a Single Page (#11512)

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
Hariom Balhara 2023-12-16 00:21:51 +05:30 committed by GitHub
parent fcb443a8eb
commit a9fcd900cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 31 deletions

View File

@ -322,6 +322,7 @@ function EventTypeSingleLayout({
StartIcon={Code} StartIcon={Code}
color="secondary" color="secondary"
variant="icon" variant="icon"
namespace={eventType.slug}
tooltip={t("embed")} tooltip={t("embed")}
tooltipSide="bottom" tooltipSide="bottom"
tooltipOffset={4} tooltipOffset={4}

View File

@ -507,6 +507,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{!isManagedEventType && ( {!isManagedEventType && (
<DropdownMenuItem className="outline-none"> <DropdownMenuItem className="outline-none">
<EventTypeEmbedButton <EventTypeEmbedButton
namespace={type.slug}
as={DropdownItem} as={DropdownItem}
type="button" type="button"
StartIcon={Code} StartIcon={Code}

View File

@ -12,6 +12,7 @@ import { CAL_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery"; import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
import slugify from "@calcom/lib/slugify";
import { trpc } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react";
import type { ButtonProps } from "@calcom/ui"; import type { ButtonProps } from "@calcom/ui";
import { import {
@ -434,6 +435,8 @@ export const FormAction = forwardRef(function FormAction<T extends typeof Button
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore //@ts-ignore
embedUrl: embedLink, embedUrl: embedLink,
// We are okay with namespace clashing here if just in case names clash
namespace: slugify((routingForm?.name || "").substring(0, 5)),
}, },
edit: { edit: {
href: `${appUrl}/form-edit/${routingForm?.id}`, href: `${appUrl}/form-edit/${routingForm?.id}`,

View File

@ -13,12 +13,13 @@ type CalProps = {
debug?: boolean; debug?: boolean;
uiDebug?: boolean; uiDebug?: boolean;
}; };
namespace?: string;
config?: PrefillAndIframeAttrsConfig; config?: PrefillAndIframeAttrsConfig;
embedJsUrl?: string; embedJsUrl?: string;
} & React.HTMLAttributes<HTMLDivElement>; } & React.HTMLAttributes<HTMLDivElement>;
const Cal = function Cal(props: CalProps) { const Cal = function Cal(props: CalProps) {
const { calLink, calOrigin, config, initConfig = {}, embedJsUrl, ...restProps } = props; const { calLink, calOrigin, namespace = "", config, initConfig = {}, embedJsUrl, ...restProps } = props;
if (!calLink) { if (!calLink) {
throw new Error("calLink is required"); throw new Error("calLink is required");
} }
@ -31,16 +32,28 @@ const Cal = function Cal(props: CalProps) {
} }
initializedRef.current = true; initializedRef.current = true;
const element = ref.current; const element = ref.current;
Cal("init", { if (namespace) {
...initConfig, Cal("init", namespace, {
origin: calOrigin, ...initConfig,
}); origin: calOrigin,
Cal("inline", { });
elementOrSelector: element, Cal.ns[namespace]("inline", {
calLink, elementOrSelector: element,
config, calLink,
}); config,
}, [Cal, calLink, config, calOrigin, initConfig]); });
} else {
Cal("init", {
...initConfig,
origin: calOrigin,
});
Cal("inline", {
elementOrSelector: element,
calLink,
config,
});
}
}, [Cal, calLink, config, namespace, calOrigin, initConfig]);
if (!Cal) { if (!Cal) {
return null; return null;

View File

@ -498,12 +498,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
embedType, embedType,
embedUrl, embedUrl,
tabs, tabs,
namespace,
eventTypeHideOptionDisabled, eventTypeHideOptionDisabled,
types, types,
}: { }: {
embedType: EmbedType; embedType: EmbedType;
embedUrl: string; embedUrl: string;
tabs: EmbedTabs; tabs: EmbedTabs;
namespace: string;
eventTypeHideOptionDisabled: boolean; eventTypeHideOptionDisabled: boolean;
types: EmbedTypes; types: EmbedTypes;
}) => { }) => {
@ -1009,6 +1011,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
<div className="flex h-[55vh] flex-grow flex-col"> <div className="flex h-[55vh] flex-grow flex-col">
{tab.type === "code" ? ( {tab.type === "code" ? (
<tab.Component <tab.Component
namespace={namespace}
embedType={embedType} embedType={embedType}
calLink={calLink} calLink={calLink}
previewState={previewState} previewState={previewState}
@ -1016,6 +1019,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
/> />
) : ( ) : (
<tab.Component <tab.Component
namespace={namespace}
embedType={embedType} embedType={embedType}
calLink={calLink} calLink={calLink}
previewState={previewState} previewState={previewState}
@ -1097,7 +1101,8 @@ export const EmbedDialog = ({
eventTypeHideOptionDisabled: boolean; eventTypeHideOptionDisabled: boolean;
}) => { }) => {
const searchParams = useCompatSearchParams(); const searchParams = useCompatSearchParams();
const embedUrl = searchParams?.get("embedUrl") as string; const embedUrl = (searchParams?.get("embedUrl") || "") as string;
const namespace = (searchParams?.get("namespace") || "") as string;
return ( return (
<Dialog name="embed" clearQueryParamsOnClose={queryParamsForDialog}> <Dialog name="embed" clearQueryParamsOnClose={queryParamsForDialog}>
{!searchParams?.get("embedType") ? ( {!searchParams?.get("embedType") ? (
@ -1106,6 +1111,7 @@ export const EmbedDialog = ({
<EmbedTypeCodeAndPreviewDialogContent <EmbedTypeCodeAndPreviewDialogContent
embedType={searchParams?.get("embedType") as EmbedType} embedType={searchParams?.get("embedType") as EmbedType}
embedUrl={embedUrl} embedUrl={embedUrl}
namespace={namespace}
tabs={tabs} tabs={tabs}
types={types} types={types}
eventTypeHideOptionDisabled={eventTypeHideOptionDisabled} eventTypeHideOptionDisabled={eventTypeHideOptionDisabled}
@ -1117,6 +1123,7 @@ export const EmbedDialog = ({
type EmbedButtonProps<T> = { type EmbedButtonProps<T> = {
embedUrl: string; embedUrl: string;
namespace: string;
children?: React.ReactNode; children?: React.ReactNode;
className?: string; className?: string;
as?: T; as?: T;
@ -1129,6 +1136,7 @@ export const EmbedButton = <T extends React.ElementType>({
className = "", className = "",
as, as,
eventId, eventId,
namespace,
...props ...props
}: EmbedButtonProps<T> & React.ComponentPropsWithoutRef<T>) => { }: EmbedButtonProps<T> & React.ComponentPropsWithoutRef<T>) => {
const { goto } = useRouterHelpers(); const { goto } = useRouterHelpers();
@ -1137,6 +1145,7 @@ export const EmbedButton = <T extends React.ElementType>({
goto({ goto({
dialog: "embed", dialog: "embed",
eventId: eventId ? eventId.toString() : "", eventId: eventId ? eventId.toString() : "",
namespace,
embedUrl, embedUrl,
}); });
}; };

View File

@ -2,6 +2,7 @@ import { CAL_URL, IS_SELF_HOSTED, WEBAPP_URL } from "@calcom/lib/constants";
import type { PreviewState } from "../types"; import type { PreviewState } from "../types";
import { embedLibUrl } from "./constants"; import { embedLibUrl } from "./constants";
import { getApiName } from "./getApiName";
import { getDimension } from "./getDimension"; import { getDimension } from "./getDimension";
export const doWeNeedCalOriginProp = (embedCalOrigin: string) => { export const doWeNeedCalOriginProp = (embedCalOrigin: string) => {
@ -18,14 +19,17 @@ export const Codes = {
uiInstructionCode, uiInstructionCode,
previewState, previewState,
embedCalOrigin, embedCalOrigin,
namespace,
}: { }: {
calLink: string; calLink: string;
uiInstructionCode: string; uiInstructionCode: string;
previewState: PreviewState; previewState: PreviewState;
embedCalOrigin: string; embedCalOrigin: string;
namespace: string;
}) => { }) => {
const width = getDimension(previewState.inline.width); const width = getDimension(previewState.inline.width);
const height = getDimension(previewState.inline.height); const height = getDimension(previewState.inline.height);
const namespaceProp = `${namespace ? `namespace="${namespace}"` : ""}`;
return code` return code`
import Cal, { getCalApi } from "@calcom/embed-react"; import Cal, { getCalApi } from "@calcom/embed-react";
import { useEffect } from "react"; import { useEffect } from "react";
@ -36,21 +40,23 @@ export const Codes = {
${uiInstructionCode} ${uiInstructionCode}
})(); })();
}, []) }, [])
return <Cal return <Cal ${namespaceProp}
calLink="${calLink}" calLink="${calLink}"
style={{width:"${width}",height:"${height}",overflow:"scroll"}} style={{width:"${width}",height:"${height}",overflow:"scroll"}}
${previewState.layout ? `config={{layout: '${previewState.layout}'}}` : ""} ${previewState.layout ? `config={{layout: '${previewState.layout}'}}` : ""}
${doWeNeedCalOriginProp(embedCalOrigin) ? ` calOrigin="${embedCalOrigin}"` : ""} ${doWeNeedCalOriginProp(embedCalOrigin) ? ` calOrigin="${embedCalOrigin}"` : ""}
${IS_SELF_HOSTED ? `calJsUrl="${embedLibUrl}"` : ""} ${IS_SELF_HOSTED ? `embedJsUrl="${embedLibUrl}"` : ""}
/>; />;
};`; };`;
}, },
"floating-popup": ({ "floating-popup": ({
floatingButtonArg, floatingButtonArg,
uiInstructionCode, uiInstructionCode,
namespace,
}: { }: {
floatingButtonArg: string; floatingButtonArg: string;
uiInstructionCode: string; uiInstructionCode: string;
namespace: string;
}) => { }) => {
return code` return code`
import { getCalApi } from "@calcom/embed-react"; import { getCalApi } from "@calcom/embed-react";
@ -59,7 +65,7 @@ export const Codes = {
useEffect(()=>{ useEffect(()=>{
(async function () { (async function () {
const cal = await getCalApi(${IS_SELF_HOSTED ? `"${embedLibUrl}"` : ""}); const cal = await getCalApi(${IS_SELF_HOSTED ? `"${embedLibUrl}"` : ""});
cal("floatingButton", ${floatingButtonArg}); ${getApiName({ namespace, mainApiName: "cal" })}("floatingButton", ${floatingButtonArg});
${uiInstructionCode} ${uiInstructionCode}
})(); })();
}, []) }, [])
@ -70,11 +76,13 @@ export const Codes = {
uiInstructionCode, uiInstructionCode,
previewState, previewState,
embedCalOrigin, embedCalOrigin,
namespace,
}: { }: {
calLink: string; calLink: string;
uiInstructionCode: string; uiInstructionCode: string;
previewState: PreviewState; previewState: PreviewState;
embedCalOrigin: string; embedCalOrigin: string;
namespace: string;
}) => { }) => {
return code` return code`
import { getCalApi } from "@calcom/embed-react"; import { getCalApi } from "@calcom/embed-react";
@ -86,7 +94,7 @@ export const Codes = {
${uiInstructionCode} ${uiInstructionCode}
})(); })();
}, []) }, [])
return <button return <button data-cal-namespace="${namespace}"
data-cal-link="${calLink}" data-cal-link="${calLink}"
${doWeNeedCalOriginProp(embedCalOrigin) ? ` data-cal-origin="${embedCalOrigin}"` : ""} ${doWeNeedCalOriginProp(embedCalOrigin) ? ` data-cal-origin="${embedCalOrigin}"` : ""}
${`data-cal-config='${JSON.stringify({ ${`data-cal-config='${JSON.stringify({
@ -101,12 +109,14 @@ export const Codes = {
calLink, calLink,
uiInstructionCode, uiInstructionCode,
previewState, previewState,
namespace,
}: { }: {
calLink: string; calLink: string;
uiInstructionCode: string; uiInstructionCode: string;
previewState: PreviewState; previewState: PreviewState;
namespace: string;
}) => { }) => {
return code`Cal("inline", { return code`${getApiName({ namespace })}("inline", {
elementOrSelector:"#my-cal-inline", elementOrSelector:"#my-cal-inline",
calLink: "${calLink}", calLink: "${calLink}",
layout: "${previewState.layout}" layout: "${previewState.layout}"
@ -118,25 +128,30 @@ export const Codes = {
"floating-popup": ({ "floating-popup": ({
floatingButtonArg, floatingButtonArg,
uiInstructionCode, uiInstructionCode,
namespace,
}: { }: {
floatingButtonArg: string; floatingButtonArg: string;
uiInstructionCode: string; uiInstructionCode: string;
namespace: string;
}) => { }) => {
return code`Cal("floatingButton", ${floatingButtonArg}); return code`${getApiName({ namespace, mainApiName: "Cal" })}("floatingButton", ${floatingButtonArg});
${uiInstructionCode}`; ${uiInstructionCode}`;
}, },
"element-click": ({ "element-click": ({
calLink, calLink,
uiInstructionCode, uiInstructionCode,
previewState, previewState,
namespace,
}: { }: {
calLink: string; calLink: string;
uiInstructionCode: string; uiInstructionCode: string;
previewState: PreviewState; previewState: PreviewState;
namespace: string;
}) => { }) => {
return code` return code`
// Important: Please add following attributes to the element you want to open Cal on click // Important: Please add the following attributes to the element that should trigger the calendar to open upon clicking.
// \`data-cal-link="${calLink}"\` // \`data-cal-link="${calLink}"\`
// data-cal-namespace="${namespace}"
// \`data-cal-config='${JSON.stringify({ // \`data-cal-config='${JSON.stringify({
layout: previewState.layout, layout: previewState.layout,
})}'\` })}'\`

View File

@ -11,6 +11,7 @@ import { Code, Trello } from "@calcom/ui/components/icon";
import type { EmbedType, PreviewState, EmbedFramework } from "../types"; import type { EmbedType, PreviewState, EmbedFramework } from "../types";
import { Codes, doWeNeedCalOriginProp } from "./EmbedCodes"; import { Codes, doWeNeedCalOriginProp } from "./EmbedCodes";
import { EMBED_PREVIEW_HTML_URL, embedLibUrl } from "./constants"; import { EMBED_PREVIEW_HTML_URL, embedLibUrl } from "./constants";
import { getApiName } from "./getApiName";
import { getDimension } from "./getDimension"; import { getDimension } from "./getDimension";
import { useEmbedCalOrigin } from "./hooks"; import { useEmbedCalOrigin } from "./hooks";
@ -22,10 +23,10 @@ export const tabs = [
type: "code", type: "code",
Component: forwardRef< Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null, HTMLTextAreaElement | HTMLIFrameElement | null,
{ embedType: EmbedType; calLink: string; previewState: PreviewState } { embedType: EmbedType; calLink: string; previewState: PreviewState; namespace: string }
>(function EmbedHtml({ embedType, calLink, previewState }, ref) { >(function EmbedHtml({ embedType, calLink, previewState, namespace }, ref) {
const { t } = useLocale(); const { t } = useLocale();
const embedSnippetString = useGetEmbedSnippetString(); const embedSnippetString = useGetEmbedSnippetString(namespace);
const embedCalOrigin = useEmbedCalOrigin(); const embedCalOrigin = useEmbedCalOrigin();
if (ref instanceof Function || !ref) { if (ref instanceof Function || !ref) {
return null; return null;
@ -55,7 +56,14 @@ export const tabs = [
: "" : ""
}<script type="text/javascript"> }<script type="text/javascript">
${embedSnippetString} ${embedSnippetString}
${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previewState, embedCalOrigin })} ${getEmbedTypeSpecificString({
embedFramework: "HTML",
embedType,
calLink,
previewState,
embedCalOrigin,
namespace,
})}
</script> </script>
<!-- Cal ${embedType} embed code ends -->`} <!-- Cal ${embedType} embed code ends -->`}
/> />
@ -71,8 +79,8 @@ export const tabs = [
type: "code", type: "code",
Component: forwardRef< Component: forwardRef<
HTMLTextAreaElement | HTMLIFrameElement | null, HTMLTextAreaElement | HTMLIFrameElement | null,
{ embedType: EmbedType; calLink: string; previewState: PreviewState } { embedType: EmbedType; calLink: string; previewState: PreviewState; namespace: string }
>(function EmbedReact({ embedType, calLink, previewState }, ref) { >(function EmbedReact({ embedType, calLink, previewState, namespace }, ref) {
const { t } = useLocale(); const { t } = useLocale();
const embedCalOrigin = useEmbedCalOrigin(); const embedCalOrigin = useEmbedCalOrigin();
@ -99,7 +107,14 @@ export const tabs = [
/* If you are using npm */ /* If you are using npm */
// npm install @calcom/embed-react // npm install @calcom/embed-react
${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, previewState, embedCalOrigin })} ${getEmbedTypeSpecificString({
embedFramework: "react",
embedType,
calLink,
previewState,
embedCalOrigin,
namespace,
})}
`} `}
/> />
</> </>
@ -113,7 +128,7 @@ export const tabs = [
type: "iframe", type: "iframe",
Component: forwardRef< Component: forwardRef<
HTMLIFrameElement | HTMLTextAreaElement | null, HTMLIFrameElement | HTMLTextAreaElement | null,
{ calLink: string; embedType: EmbedType; previewState: PreviewState } { calLink: string; embedType: EmbedType; previewState: PreviewState; namespace: string }
>(function Preview({ calLink, embedType }, ref) { >(function Preview({ calLink, embedType }, ref) {
const bookerUrl = useBookerUrl(); const bookerUrl = useBookerUrl();
const iframeSrc = `${EMBED_PREVIEW_HTML_URL}?embedType=${embedType}&calLink=${calLink}&embedLibUrl=${embedLibUrl}&bookerUrl=${bookerUrl}`; const iframeSrc = `${EMBED_PREVIEW_HTML_URL}?embedType=${embedType}&calLink=${calLink}&embedLibUrl=${embedLibUrl}&bookerUrl=${bookerUrl}`;
@ -144,12 +159,14 @@ const getEmbedTypeSpecificString = ({
calLink, calLink,
embedCalOrigin, embedCalOrigin,
previewState, previewState,
namespace,
}: { }: {
embedFramework: EmbedFramework; embedFramework: EmbedFramework;
embedType: EmbedType; embedType: EmbedType;
calLink: string; calLink: string;
previewState: PreviewState; previewState: PreviewState;
embedCalOrigin: string; embedCalOrigin: string;
namespace: string;
}) => { }) => {
const frameworkCodes = Codes[embedFramework]; const frameworkCodes = Codes[embedFramework];
if (!frameworkCodes) { if (!frameworkCodes) {
@ -165,7 +182,7 @@ const getEmbedTypeSpecificString = ({
}; };
if (embedFramework === "react") { if (embedFramework === "react") {
uiInstructionStringArg = { uiInstructionStringArg = {
apiName: "cal", apiName: getApiName({ namespace, mainApiName: "cal" }),
theme: previewState.theme, theme: previewState.theme,
brandColor: previewState.palette.brandColor, brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails, hideEventTypeDetails: previewState.hideEventTypeDetails,
@ -173,7 +190,7 @@ const getEmbedTypeSpecificString = ({
}; };
} else { } else {
uiInstructionStringArg = { uiInstructionStringArg = {
apiName: "Cal", apiName: getApiName({ namespace, mainApiName: "Cal" }),
theme: previewState.theme, theme: previewState.theme,
brandColor: previewState.palette.brandColor, brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails, hideEventTypeDetails: previewState.hideEventTypeDetails,
@ -189,6 +206,7 @@ const getEmbedTypeSpecificString = ({
uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg), uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg),
previewState, previewState,
embedCalOrigin, embedCalOrigin,
namespace,
}); });
} else if (embedType === "floating-popup") { } else if (embedType === "floating-popup") {
const floatingButtonArg = { const floatingButtonArg = {
@ -197,11 +215,13 @@ const getEmbedTypeSpecificString = ({
...previewState.floatingPopup, ...previewState.floatingPopup,
}; };
return frameworkCodes[embedType]({ return frameworkCodes[embedType]({
namespace,
floatingButtonArg: JSON.stringify(floatingButtonArg), floatingButtonArg: JSON.stringify(floatingButtonArg),
uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg), uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg),
}); });
} else if (embedType === "element-click") { } else if (embedType === "element-click") {
return frameworkCodes[embedType]({ return frameworkCodes[embedType]({
namespace,
calLink, calLink,
uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg), uiInstructionCode: getEmbedUIInstructionString(uiInstructionStringArg),
previewState, previewState,
@ -253,10 +273,10 @@ const getInstructionString = ({
return `${apiName}("${instructionName}", ${JSON.stringify(instructionArg)});`; return `${apiName}("${instructionName}", ${JSON.stringify(instructionArg)});`;
}; };
function useGetEmbedSnippetString() { function useGetEmbedSnippetString(namespace: string | null) {
const bookerUrl = useBookerUrl(); const bookerUrl = useBookerUrl();
// TODO: Import this string from @calcom/embed-snippet // TODO: Import this string from @calcom/embed-snippet
return `(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${embedLibUrl}", "init"); return `(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${embedLibUrl}", "init");
Cal("init", {origin:"${bookerUrl}"}); Cal("init", ${namespace ? `"${namespace}",` : ""} {origin:"${bookerUrl}"});
`; `;
} }

View File

@ -0,0 +1,14 @@
export function getApiName({
namespace,
mainApiName = "Cal",
}: {
namespace: string | null;
mainApiName?: string;
}) {
if (!namespace) {
return mainApiName;
}
const isAValidVariableName = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(namespace);
// Try to use dot notation if possible because it's more readable otherwise use bracket notation
return isAValidVariableName ? `${mainApiName}.ns.${namespace}` : `${mainApiName}.ns["${namespace}"]`;
}