meta.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. import { User, Features, FeatureUsage } from '../../../types/user'
  2. import { User as MinimalUser } from '../../../types/admin/user'
  3. import { User as ManagedUser } from '../../../types/group-management/user'
  4. import { UserSettings } from '../../../types/user-settings'
  5. import { OAuthProviders } from '../../../types/oauth-providers'
  6. import { ExposedSettings } from '../../../types/exposed-settings'
  7. import {
  8. type ImageName,
  9. OverallThemeMeta,
  10. ProjectCompiler,
  11. type SpellCheckLanguage,
  12. } from '../../../types/project-settings'
  13. import { CurrencyCode } from '../../../types/subscription/currency'
  14. import { PricingFormState } from '../../../types/subscription/payment-context-value'
  15. import { LocalIndividualPlans, Plan } from '../../../types/subscription/plan'
  16. import { Affiliation } from '../../../types/affiliation'
  17. import type { PortalTemplate } from '../../../types/portal-template'
  18. import { UserEmailData } from '../../../types/user-email'
  19. import {
  20. GroupsAndEnterpriseBannerVariant,
  21. Institution as InstitutionType,
  22. Notification as NotificationType,
  23. PendingGroupSubscriptionEnrollment,
  24. USGovBannerVariant,
  25. } from '../../../types/project/dashboard/notification'
  26. import { Survey } from '../../../types/project/dashboard/survey'
  27. import { GetProjectsResponseBody } from '../../../types/project/dashboard/api'
  28. import { Tag } from '../../../app/src/Features/Tags/types'
  29. import { Institution } from '../../../types/institution'
  30. import {
  31. GroupPolicy,
  32. GroupSubscription,
  33. ManagedGroupSubscription,
  34. MemberGroupSubscription,
  35. StripePaymentProviderService,
  36. } from '../../../types/subscription/dashboard/subscription'
  37. import { SplitTestInfo } from '../../../types/split-test'
  38. import { ValidationStatus } from '../../../types/group-management/validation'
  39. import { ManagedInstitution } from '../../../types/subscription/dashboard/managed-institution'
  40. import { OnboardingFormData } from '../../../types/onboarding'
  41. import { GroupSSOTestResult } from '../../../modules/group-settings/frontend/js/utils/types'
  42. import {
  43. AccessToken,
  44. InstitutionLink,
  45. SAMLError,
  46. } from '../../../types/settings-page'
  47. import { SuggestedLanguage } from '../../../types/system-message'
  48. import type { TeamInvite } from '../../../types/team-invite'
  49. import {
  50. GroupPlans,
  51. GroupPlansData,
  52. } from '../../../types/subscription/dashboard/group-plans'
  53. import {
  54. GroupSSOLinkingStatus,
  55. SSOConfig,
  56. } from '../../../types/subscription/sso'
  57. import { PasswordStrengthOptions } from '../../../types/password-strength-options'
  58. import { Subscription as ProjectDashboardSubscription } from '../../../types/project/dashboard/subscription'
  59. import { ThirdPartyIds } from '../../../types/third-party-ids'
  60. import { Publisher } from '../../../types/subscription/dashboard/publisher'
  61. import { SubscriptionChangePreview } from '../../../types/subscription/subscription-change-preview'
  62. import { SubscriptionCreationPreview } from '../../../types/subscription/subscription-creation-preview'
  63. import { DefaultNavbarMetadata } from '@/shared/components/types/default-navbar-metadata'
  64. import { FooterMetadata } from '@/shared/components/types/footer-metadata'
  65. import type { ScriptLogType } from '../../../modules/admin-panel/frontend/js/features/script-logs/script-log'
  66. import { Subscription as AdminSubscription } from '../../../types/admin/subscription'
  67. import { AdminCapability } from '../../../types/admin-capabilities'
  68. import { AlgoliaConfig } from '../../../modules/algolia-search/frontend/js/types'
  69. import { WritefullPublicEnv } from '@wf/domain/writefull-public-env'
  70. import { UserNotificationPreferences } from '../../../types/notifications'
  71. import { SharingPermissions } from '../../../modules/sharing-permissions/app/src/types'
  72. import { FullHistoryFailure } from '@ol-types/history/projectHistory'
  73. export interface Meta {
  74. 'ol-ExposedSettings': ExposedSettings
  75. 'ol-addonPrices': Record<
  76. string,
  77. { annual: string; monthly: string; annualDividedByTwelve: string }
  78. >
  79. 'ol-adminCapabilities': AdminCapability[]
  80. 'ol-adminSubscription': AdminSubscription
  81. 'ol-adminUserExists': boolean
  82. 'ol-aiAssistViaWritefullSource': string
  83. 'ol-algolia': AlgoliaConfig | undefined
  84. 'ol-allInReconfirmNotificationPeriods': UserEmailData[]
  85. 'ol-allowedExperiments': string[]
  86. 'ol-anonymous': boolean
  87. 'ol-baseAssetPath': string
  88. 'ol-brandVariation': Record<string, any>
  89. 'ol-canUseAddSeatsFeature': boolean
  90. 'ol-canUseClsiCache': boolean
  91. 'ol-canUseFlexibleLicensing': boolean
  92. // dynamic keys based on permissions
  93. 'ol-cannot-add-secondary-email': boolean
  94. 'ol-cannot-change-password': boolean
  95. 'ol-cannot-delete-own-account': boolean
  96. 'ol-cannot-join-subscription': boolean
  97. 'ol-cannot-leave-group-subscription': boolean
  98. 'ol-cannot-link-google-sso': boolean
  99. 'ol-cannot-link-other-third-party-sso': boolean
  100. 'ol-cannot-reactivate-subscription': boolean
  101. 'ol-cannot-use-ai': boolean
  102. 'ol-capabilities': Array<'dropbox' | 'chat' | 'use-ai' | 'link-sharing'>
  103. 'ol-compileSettings': {
  104. compileTimeout: number
  105. }
  106. 'ol-compilesUserContentDomain': string
  107. 'ol-countryCode': PricingFormState['country']
  108. 'ol-couponCode': PricingFormState['coupon']
  109. 'ol-createNewUserViaDomainCapture': boolean
  110. 'ol-createdAt': Date
  111. 'ol-csrfToken': string
  112. 'ol-currentInstitutionsWithLicence': Institution[]
  113. 'ol-currentManagedUserAdminEmail': string
  114. 'ol-currentPlanCode': string | null
  115. 'ol-currentUrl': string
  116. 'ol-customerIoEnabled': boolean
  117. 'ol-debugPdfDetach': boolean
  118. 'ol-defaultLatexCompiler': ProjectCompiler
  119. 'ol-detachRole': 'detached' | 'detacher' | ''
  120. 'ol-dictionariesRoot': 'string'
  121. 'ol-domainCaptureEnabled': boolean | undefined
  122. 'ol-domainCaptureTestURL': string | undefined
  123. 'ol-domainVerificationGracePeriodDays': number
  124. 'ol-domainVerificationUIEnabled': boolean
  125. 'ol-dropbox': { error: boolean; registered: boolean }
  126. 'ol-editorThemes': { name: string; dark: boolean }[]
  127. 'ol-email': string
  128. 'ol-emailAddressLimit': number
  129. 'ol-error': { name: string } | undefined
  130. 'ol-errorType': string | undefined
  131. 'ol-expired': boolean
  132. 'ol-featureUsage': FeatureUsage
  133. 'ol-features': Features
  134. 'ol-footer': FooterMetadata
  135. 'ol-galleryTagName': string
  136. 'ol-gitBridgeEnabled': boolean
  137. 'ol-gitBridgePublicBaseUrl': string
  138. 'ol-github': { enabled: boolean; error: boolean }
  139. 'ol-groupAuditLogs': []
  140. 'ol-groupDomains': []
  141. 'ol-groupId': string
  142. 'ol-groupName': string
  143. 'ol-groupPlans': GroupPlans
  144. 'ol-groupPlansData': GroupPlansData
  145. 'ol-groupPolicy': GroupPolicy
  146. 'ol-groupSSOActive': boolean
  147. 'ol-groupSSOConfig'?: SSOConfig
  148. 'ol-groupSSOTestResult': GroupSSOTestResult
  149. 'ol-groupSettingsAdvertisedFor': string[]
  150. 'ol-groupSettingsEnabledFor': string[]
  151. 'ol-groupSize': number
  152. 'ol-groupSsoSetupSuccess': boolean
  153. 'ol-groupSubscriptionsPendingEnrollment': PendingGroupSubscriptionEnrollment[]
  154. 'ol-groupsAndEnterpriseBannerVariant': GroupsAndEnterpriseBannerVariant
  155. 'ol-hasAiAssistViaWritefull': boolean
  156. 'ol-hasAiFreeTier': boolean
  157. 'ol-hasGroupSSOFeature': boolean
  158. 'ol-hasIndividualPaidSubscription': boolean
  159. 'ol-hasManagedUsersFeature': boolean
  160. 'ol-hasModifyGroupManagerAccess': boolean
  161. 'ol-hasPassword': boolean
  162. 'ol-hasSplitTestWriteAccess': boolean
  163. 'ol-hasSubscription': boolean
  164. 'ol-hasTrackChangesFeature': boolean
  165. 'ol-hasUnlimitedAi': boolean
  166. 'ol-hasWriteAccess': boolean
  167. 'ol-hideLinkingWidgets': boolean // CI only
  168. 'ol-historyBlobStats': {
  169. projectId: string
  170. textBlobsBytes: number
  171. binaryBlobsBytes: number
  172. totalBytes: number
  173. nTextBlobs: number
  174. nBinaryBlobs: number
  175. owned?: boolean
  176. }[]
  177. 'ol-i18n': { currentLangCode: string }
  178. 'ol-imageNames': ImageName[]
  179. 'ol-inactiveTutorials': string[]
  180. 'ol-institutionEmail': string | undefined
  181. 'ol-institutionEmailNonCanonical': string | undefined
  182. 'ol-institutionLinked': InstitutionLink | undefined
  183. 'ol-inviteToken': string
  184. 'ol-inviterName': string
  185. 'ol-isCollectionMethodManual': boolean
  186. 'ol-isExternalAuthenticationSystemUsed': boolean
  187. 'ol-isManagedAccount': boolean
  188. 'ol-isManagedGroupAdmin': boolean
  189. 'ol-isProfessional': boolean
  190. 'ol-isRegisteredViaGoogle': boolean
  191. 'ol-isRestrictedTokenMember': boolean
  192. 'ol-isSaas': boolean
  193. 'ol-isUpgrade': boolean
  194. 'ol-isUserGroupManager': boolean
  195. 'ol-itm_campaign': string
  196. 'ol-itm_content': string
  197. 'ol-itm_referrer': string
  198. 'ol-joinedGroupName': string
  199. 'ol-labs': boolean
  200. 'ol-labsExperiments': Array<{
  201. name: string
  202. title: string
  203. description: string
  204. icon: string
  205. surveyLink: string
  206. isFull: boolean
  207. optedIn: boolean
  208. versionCreatedAt: string | null
  209. }>
  210. 'ol-languages': SpellCheckLanguage[]
  211. 'ol-learnedWords': string[]
  212. 'ol-legacyEditorThemes': { name: string; dark: boolean }[]
  213. 'ol-licenseQuantity'?: number
  214. 'ol-loadingText': string
  215. 'ol-localIndividualPlans': LocalIndividualPlans
  216. 'ol-managedGroupSubscriptions': ManagedGroupSubscription[]
  217. 'ol-managedInstitutions': ManagedInstitution[]
  218. 'ol-managedPublishers': Publisher[]
  219. 'ol-managedUsersActive': boolean
  220. 'ol-managedUsersEnabled': boolean
  221. 'ol-managers': MinimalUser[]
  222. 'ol-mathJaxPath': string
  223. 'ol-maxDocLength': number
  224. 'ol-maxReconnectGracefullyIntervalMs': number
  225. 'ol-memberGroupSubscriptions': MemberGroupSubscription[]
  226. 'ol-memberOfSSOEnabledGroups': GroupSSOLinkingStatus[]
  227. 'ol-members': MinimalUser[]
  228. 'ol-multiple-group-subscriptions': Array<
  229. Pick<GroupSubscription, 'teamName'> & { id: string; email: string }
  230. >
  231. 'ol-navbar': DefaultNavbarMetadata
  232. 'ol-newsletter-subscribed': boolean
  233. 'ol-no-single-dollar': boolean
  234. 'ol-notifications': NotificationType[]
  235. 'ol-notificationsInstitution': InstitutionType[]
  236. 'ol-oauthProviders': OAuthProviders
  237. 'ol-odcData': OnboardingFormData
  238. 'ol-otMigrationStage': number
  239. 'ol-overallThemes': OverallThemeMeta[]
  240. 'ol-ownerIsManaged': boolean
  241. 'ol-pages': number
  242. 'ol-passwordStrengthOptions': PasswordStrengthOptions
  243. 'ol-paywallPlans': { [key: string]: string }
  244. 'ol-personalAccessTokens': AccessToken[] | undefined
  245. 'ol-plan': Plan
  246. 'ol-planCode': string
  247. 'ol-planCodesChangingAtTermEnd': string[] | undefined
  248. 'ol-plans': Plan[]
  249. 'ol-portalTemplates': PortalTemplate[]
  250. 'ol-postCheckoutRedirect': string
  251. 'ol-postUrl': string
  252. 'ol-prefetchedProjectsBlob': GetProjectsResponseBody | undefined
  253. 'ol-preventCompileOnLoad'?: boolean
  254. 'ol-primaryEmail': { email: string; confirmed: boolean }
  255. 'ol-project': any // TODO
  256. 'ol-projectEntityCounts': { files: number; docs: number }
  257. 'ol-projectHistoryFailures': FullHistoryFailure[]
  258. 'ol-projectName': string
  259. 'ol-projectSyncSuccessMessage': string
  260. 'ol-projectTags': Tag[]
  261. 'ol-project_id': string
  262. 'ol-purchaseReferrer': string
  263. 'ol-recommendedCurrency': CurrencyCode
  264. 'ol-reconfirmationRemoveEmail': string
  265. 'ol-reconfirmedViaSAML': string
  266. 'ol-recurlyAccount':
  267. | {
  268. code: string
  269. error?: undefined
  270. }
  271. | {
  272. error: boolean
  273. code?: undefined
  274. }
  275. | undefined
  276. 'ol-recurlyApiKey': string
  277. 'ol-recurlySubdomain': string
  278. 'ol-ro-mirror-on-client-no-local-storage': boolean
  279. 'ol-samlError': SAMLError | undefined
  280. 'ol-script-log': ScriptLogType
  281. 'ol-script-logs': ScriptLogType[]
  282. 'ol-settingsGroupSSO': { enabled: boolean } | undefined
  283. 'ol-settingsPlans': Plan[]
  284. 'ol-sharingPermissions': SharingPermissions
  285. 'ol-shouldAllowEditingDetails': boolean
  286. 'ol-shouldLoadHotjar': boolean
  287. 'ol-showAiFeatures': boolean
  288. 'ol-showAiFeaturesDisabled': boolean
  289. 'ol-showCouponField': boolean
  290. 'ol-showGroupDiscount': boolean
  291. 'ol-showGroupsAndEnterpriseBanner': boolean
  292. 'ol-showInrGeoBanner': boolean
  293. 'ol-showLATAMBanner': boolean
  294. 'ol-showSupport': boolean
  295. 'ol-showSymbolPalette': boolean
  296. 'ol-showTemplatesServerPro': boolean
  297. 'ol-showUSGovBanner': boolean
  298. 'ol-showUpgradePrompt': boolean
  299. 'ol-splitTestInfo': { [name: string]: SplitTestInfo }
  300. 'ol-splitTestName': string
  301. 'ol-splitTestVariants': { [name: string]: string }
  302. 'ol-ssoDisabled': boolean
  303. 'ol-ssoErrorMessage': string
  304. 'ol-ssoInitPath': string
  305. 'ol-standardPlanPricing': {
  306. monthly?: string
  307. annual?: string
  308. monthlyTimesTwelve?: string
  309. }
  310. 'ol-stripeCustomerData': Array<{
  311. customerId: string
  312. subscriptionId: string
  313. subscriptionState: string | null
  314. paymentProviderService: StripePaymentProviderService | null
  315. managementUrl: string
  316. segment?: string | null
  317. error?: string
  318. }>
  319. 'ol-stripePublicKeyUK': string
  320. 'ol-stripePublicKeyUS': string
  321. 'ol-subscription': any // TODO: mixed types, split into two fields
  322. 'ol-subscriptionChangePreview': SubscriptionChangePreview
  323. 'ol-subscriptionCreationPreview': SubscriptionCreationPreview
  324. 'ol-subscriptionFeatures': {
  325. managedUsers?: boolean
  326. groupSSO?: boolean
  327. domainCapture?: boolean
  328. }
  329. 'ol-subscriptionId': string
  330. 'ol-subscriptionPaymentErrorCode': string | null
  331. 'ol-suggestedLanguage': SuggestedLanguage | undefined
  332. 'ol-survey': Survey | undefined
  333. 'ol-symbolPaletteAvailable': boolean
  334. 'ol-tags': Tag[]
  335. 'ol-teamInvites': TeamInvite[]
  336. 'ol-thirdPartyIds': ThirdPartyIds
  337. 'ol-totalLicenses': number
  338. 'ol-translationIoNotLoaded': string
  339. 'ol-translationLoadErrorMessage': string
  340. 'ol-translationMaintenance': string
  341. 'ol-translationUnableToJoin': string
  342. 'ol-trialDisabledReason': string | undefined
  343. 'ol-usGovBannerVariant': USGovBannerVariant
  344. 'ol-useShareJsHash': boolean
  345. 'ol-user': User
  346. 'ol-userAffiliations': Affiliation[]
  347. 'ol-userCanExtendTrial': boolean
  348. 'ol-userCanNotStartRequestedTrial': boolean
  349. 'ol-userEmails': UserEmailData[]
  350. 'ol-userNotificationPreferences': UserNotificationPreferences
  351. 'ol-userSettings': UserSettings
  352. 'ol-user_id': string | undefined
  353. 'ol-users': ManagedUser[]
  354. 'ol-usersBestSubscription': ProjectDashboardSubscription | undefined
  355. 'ol-usersEmail': string | undefined
  356. 'ol-usersSubscription': { personal: boolean; group: boolean }
  357. 'ol-validationStatus': ValidationStatus
  358. 'ol-viaDomainCapture': boolean
  359. 'ol-wikiEnabled': boolean
  360. 'ol-writefullEnabled': boolean
  361. 'ol-writefullEnv': WritefullPublicEnv
  362. 'ol-wsUrl': string
  363. }
  364. type DeepPartial<T> =
  365. T extends Record<string, any> ? { [P in keyof T]?: DeepPartial<T[P]> } : T
  366. export type PartialMeta = DeepPartial<Meta>
  367. export type MetaAttributesCache<
  368. K extends keyof PartialMeta = keyof PartialMeta,
  369. > = Map<K, PartialMeta[K]>
  370. export type MetaTag = {
  371. [K in keyof Meta]: {
  372. name: K
  373. value: Meta[K]
  374. }
  375. }[keyof Meta]
  376. // cache for parsed values
  377. window.metaAttributesCache = window.metaAttributesCache || new Map()
  378. export default function getMeta<T extends keyof Meta>(name: T): Meta[T] {
  379. if (window.metaAttributesCache.has(name)) {
  380. return window.metaAttributesCache.get(name)
  381. }
  382. const element = document.head.querySelector(
  383. `meta[name="${name}"]`
  384. ) as HTMLMetaElement
  385. if (!element) {
  386. return undefined!
  387. }
  388. const plainTextValue = element.content
  389. let value
  390. switch (element.dataset.type) {
  391. case 'boolean':
  392. // in pug: content=false -> no content field
  393. // in pug: content=true -> empty content field
  394. value = element.hasAttribute('content')
  395. break
  396. case 'json':
  397. case 'number':
  398. if (!plainTextValue) {
  399. // JSON.parse('') throws
  400. value = undefined
  401. } else {
  402. value = JSON.parse(plainTextValue)
  403. }
  404. break
  405. default:
  406. value = plainTextValue
  407. }
  408. window.metaAttributesCache.set(name, value)
  409. return value
  410. }