UNPKG

scrivito

Version:

Scrivito is a professional, yet easy to use SaaS Enterprise Content Management Service, built for digital agencies and medium to large businesses. It is completely maintenance-free, cost-effective, and has unprecedented performance and security.

414 lines (350 loc) 11.8 kB
// @rewire import { browserAnalyticsProvider, nodeAnalyticsProvider, } from 'scrivito_sdk/app_support/analytics_provider'; import { configureAssetUrlBase } from 'scrivito_sdk/app_support/asset_url_base'; import { skipContentTagsForEmptyAttributes } from 'scrivito_sdk/app_support/content_tags_for_empty_attributes'; import { currentAppSpace } from 'scrivito_sdk/app_support/current_app_space'; import { currentSiteId } from 'scrivito_sdk/app_support/current_page'; import { getIamTokenFetcher, getLoginHandler, isUserLoggedIn, } from 'scrivito_sdk/app_support/current_user'; import { setExtensionsUrl } from 'scrivito_sdk/app_support/extensions_url'; import { setForcedEditorLanguage } from 'scrivito_sdk/app_support/forced_editor_language'; import { setInitialContentDumpUrl } from 'scrivito_sdk/app_support/initial_content_dump_url'; import { loadEditingAssets } from 'scrivito_sdk/app_support/load_editing_assets'; import { initializeLoggedInState } from 'scrivito_sdk/app_support/logged_in_state'; import { isRunningInBrowser, nodeAdapter, } from 'scrivito_sdk/app_support/node_adapter'; import { initRouting } from 'scrivito_sdk/app_support/routing'; import { SiteMappingConfiguration } from 'scrivito_sdk/app_support/site_mapping'; import { setTreatLocalhostLike } from 'scrivito_sdk/app_support/treat_localhost_like'; import { UiAdapterClient, uiAdapter, } from 'scrivito_sdk/app_support/ui_adapter'; import { SiteIdForObjCallback, getUnstableSelectedSiteId, setUnstableMultiSiteMode, useUnstableMultiSiteMode, } from 'scrivito_sdk/app_support/unstable_multi_site_mode'; import { getVisitorAuthenticationProvider } from 'scrivito_sdk/app_support/visitor_authentication'; import { toggleWidgetHighlighting } from 'scrivito_sdk/app_support/widget_highlighting'; import { Priority, clientConfig, cmsRestApi, getTokenProvider, } from 'scrivito_sdk/client'; import { Deferred, ScrivitoError, cdnAssetUrlBase, currentOrigin, logError, onReset, setConfiguredTenant, setTimeout, throwInvalidArgumentsError, } from 'scrivito_sdk/common'; import { IN_MEMORY_TENANT, configureForLazyWidgets, disableObjReplication, useInMemoryTenant, } from 'scrivito_sdk/data'; import { activateDataIntegration } from 'scrivito_sdk/data_integration'; import { load } from 'scrivito_sdk/loadable'; import { getRootObjFrom, restrictToSite, setWantsAutoAttributeConversion, } from 'scrivito_sdk/models'; import { Obj, enableStrictSearchOperators, setCurrentSiteIdHandler, unwrapAppClass, } from 'scrivito_sdk/realm'; import type { ForcedEditorLanguage } from 'scrivito_sdk/ui_interface/app_adapter'; import { ConstraintsValidationCallback, setConstraintsValidationCallback, } from './constraints_validation_callback'; export interface IamApiKey { clientId: string; clientSecret: string; } export interface Configuration { tenant: string; adoptUi?: boolean | string; autoConvertAttributes?: boolean; baseUrlForSite?: SiteMappingConfiguration['baseUrlForSite']; endpoint?: string; constraintsValidation?: ConstraintsValidationCallback; homepage?: () => Obj | null; origin?: string; routingBasePath?: string; siteForUrl?: SiteMappingConfiguration['siteForUrl']; visitorAuthentication?: boolean; apiKey?: string | IamApiKey; priority?: Priority; editorLanguage?: ForcedEditorLanguage; extensionsUrl?: string; strictSearchOperators?: boolean; optimizedWidgetLoading?: boolean; contentTagsForEmptyAttributes?: boolean; iamAuthLocation?: string; treatLocalhostLike?: string; activateDataIntegration?: boolean; /** @internal */ unstable?: { assetUrlBase?: string; getSiteIdForObj?: SiteIdForObjCallback; trustedUiOrigins?: string[]; initialContentDumpUrl?: string; allowOfflineMode?: boolean; widgetHighlighting?: boolean; }; } let configDeferred = new Deferred<Configuration>(); /** @public */ export function configure(configuration: Configuration): void; /** @internal */ export function configure(configuration: Readonly<Configuration>): void { checkConfigure(configuration); const routingConfiguration = getCheckedRoutingConfiguration(configuration); setConfiguration(configuration); const unofficialConfiguration = configuration.unstable; const getUnstableSiteIdForObj = unofficialConfiguration?.getSiteIdForObj; if (getUnstableSiteIdForObj) { setUnstableMultiSiteMode(getUnstableSiteIdForObj); } if (configuration.tenant === IN_MEMORY_TENANT) { useInMemoryTenant(); setCurrentSiteIdHandler(() => IN_MEMORY_TENANT); disableObjReplication(); } else { const tenant = configuration.tenant; setConfiguredTenant(tenant); if (isRunningInBrowser()) initializeLoggedInState(); configureAssetUrlBase( unofficialConfiguration?.assetUrlBase ?? cdnAssetUrlBase() ); clientConfig.set({ iamAuthLocation: getIamAuthLocation(configuration.iamAuthLocation), iamTokenFetcher: getIamTokenFetcher(), loginHandler: getLoginHandler(), }); const treatLocalhostLike = configuration.treatLocalhostLike; if (treatLocalhostLike) setTreatLocalhostLike(treatLocalhostLike); if (uiAdapter) { configureWithUi(tenant, uiAdapter); } else { configureWithoutUi(configuration); } } initRouting(routingConfiguration); configureConstraintsValidationCallback(configuration); if (configuration.contentTagsForEmptyAttributes === false) { skipContentTagsForEmptyAttributes(); } if (configuration.strictSearchOperators) enableStrictSearchOperators(); if (configuration.activateDataIntegration) activateDataIntegration(); setWantsAutoAttributeConversion(!!configuration.autoConvertAttributes); setForcedEditorLanguage(configuration.editorLanguage || null); setExtensionsUrl(configuration.extensionsUrl || undefined); if (unofficialConfiguration?.initialContentDumpUrl) { setInitialContentDumpUrl(unofficialConfiguration.initialContentDumpUrl); } if (unofficialConfiguration?.widgetHighlighting) { toggleWidgetHighlighting(true); } } export function getConfiguration(): Promise<Configuration> { return configDeferred.promise; } // exported for test purpose only export function setConfiguration(configuration: Configuration): void { if (!configDeferred.isPending()) { throw new ScrivitoError('Scrivito.configure has already been called.'); } configDeferred.resolve(configuration); } // For test purpose only export function resetConfiguration(): void { configDeferred = new Deferred(); clientConfig.reset(); } function checkConfigure(configuration: Configuration) { if (!configuration.tenant) { throwInvalidConfigurationError("The param 'tenant' is required."); } if (configuration.apiKey && isRunningInBrowser()) { throwInvalidConfigurationError( "The option 'apiKey' is only available under Node.js." ); } if (configuration.origin && !isOrigin(configuration.origin)) { throwInvalidConfigurationError( "The option 'origin' is must be a valid origin string." ); } if ( configuration.adoptUi && !( typeof configuration.adoptUi === 'boolean' || isOrigin(configuration.adoptUi) ) ) { throwInvalidConfigurationError( "The option 'adoptUi' is must be an origin string." ); } } function throwInvalidConfigurationError(message: string) { throwInvalidArgumentsError('configure', message, { docPermalink: 'js-sdk/configure', }); } function isOrigin(origin: string) { try { return new URL(origin).origin === origin; } catch { return false; } } function configureWithUi(tenant: string, uiAdapterClient: UiAdapterClient) { uiAdapterClient.configureTenant({ tenant, }); if (useUnstableMultiSiteMode()) warnIfNoSiteIdSelection(); setAppAdapter(uiAdapterClient); loadEditingAssets(); } function configureWithoutUi({ endpoint, optimizedWidgetLoading, visitorAuthentication, priority, apiKey, }: Configuration) { if (optimizedWidgetLoading) configureForLazyWidgets(true); configureCmsRestApi({ endpoint, visitorAuthentication, priority, apiKey, }); } function configureCmsRestApi({ endpoint: configuredEndpoint, visitorAuthentication, priority, apiKey, }: { endpoint?: string; visitorAuthentication?: boolean; priority?: Priority; apiKey?: string | IamApiKey; }) { const endpoint = configuredEndpoint || 'api.scrivito.com'; cmsRestApi.init({ apiBaseUrl: endpoint.startsWith('http') ? endpoint : `https://${endpoint}`, authorizationProvider: getCmsAuthProvider(apiKey, visitorAuthentication), analyticsProvider: isRunningInBrowser() ? browserAnalyticsProvider : nodeAnalyticsProvider, priority, }); } function getCmsAuthProvider( apiKey?: string | IamApiKey, visitorAuthentication?: boolean ) { if (nodeAdapter && apiKey) { return new nodeAdapter.ApiKeyAuthorizationProvider(apiKey); } if (isRunningInBrowser() && isUserLoggedIn()) { return getTokenProvider({ audience: 'https://api.justrelate.com' }); } return getVisitorAuthenticationProvider(visitorAuthentication); } function getCheckedRoutingConfiguration({ homepage, origin, routingBasePath, baseUrlForSite, siteForUrl, }: Configuration) { const homepageCallback = homepage ? () => unwrapAppClass(homepage()) : () => getRootObjFrom(currentAppSpace().and(restrictToSite('default'))); if (baseUrlForSite && siteForUrl) { if (routingBasePath || origin) { const presentKey = routingBasePath ? 'routingBasePath' : 'origin'; throwInvalidConfigurationError( `The '${presentKey}' cannot be combined with the "baseUrlForSite" option` ); } return { homepageCallback, baseUrlForSite, siteForUrl, }; } if (baseUrlForSite || siteForUrl) { const presentKey = siteForUrl ? 'siteForUrl' : 'baseUrlForSite'; const missingKey = siteForUrl ? 'baseUrlForSite' : 'siteForUrl'; throwInvalidConfigurationError( `Unexpected value for argument 'configuration': a value for '${missingKey}' is required if '${presentKey}' is present.` ); } if (origin !== undefined && !isOrigin(origin)) { throwInvalidConfigurationError( `Unexpected value: '${origin}' is not a valid origin.` ); } return { homepageCallback, origin, routingBasePath }; } function configureConstraintsValidationCallback(configuration: Configuration) { const constraintsValidationCallback = configuration.constraintsValidation; if (constraintsValidationCallback) { setConstraintsValidationCallback(constraintsValidationCallback); } } async function setAppAdapter(uiAdapterClient: UiAdapterClient) { const { startAppAdapter } = await importUiInterface(); const port = startAppAdapter(); uiAdapterClient.setAppAdapter(port); } /** exported for test purposes only */ export function importUiInterface() { return import('scrivito_sdk/ui_interface'); } async function warnIfNoSiteIdSelection() { const timeout = setTimeout(async () => { const siteId = await load(currentSiteId); if (siteId === 'default') { logError( 'Warning: No site ID was selected within 30 seconds.' + ' In the multi-site mode a site ID must be selected before Scrivito can render content.' + ' Forgot to use Scrivito.unstable_selectSiteId?' ); } }, 30000); await load(getUnstableSelectedSiteId); clearTimeout(timeout); } function getIamAuthLocation(iamAuthLocation: string | undefined) { if (typeof iamAuthLocation === 'string') return iamAuthLocation; const origin = currentOrigin(); return origin ? `${origin}/auth` : undefined; } onReset(resetConfiguration);