@openedx/frontend-base 1.0.0-alpha.23 → 1.0.0-alpha.25

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.
@@ -178,13 +178,22 @@ export declare function removeActiveWidgetRole(role: string): void;
178
178
  export declare function getActiveWidgetRoles(): string[];
179
179
  export declare function getActiveRoles(): string[];
180
180
  /**
181
- * Collects all `provides` entries from registered apps that match the given key.
181
+ * Collects all `provides` entries from registered apps that match the given identifier.
182
182
  * This enables inter-app data sharing without frontend-base needing to understand the data shape.
183
183
  *
184
- * @param key - The namespaced identifier for the provided data.
185
- * @returns An array of provided data objects from all apps that declared data for this key.
184
+ * @param id - The namespaced provides identifier.
185
+ * @returns An array of provided data from all apps that declared data for this identifier.
186
186
  */
187
- export declare function getProvidedData(key: string): unknown[];
187
+ export declare function getProvides(id: string): unknown[];
188
+ /**
189
+ * Collects and flattens all `provides` entries for the given identifier
190
+ * as strings. Each entry can be a single string or a string array; entries
191
+ * of other types are silently skipped.
192
+ *
193
+ * @param id - The namespaced provides identifier.
194
+ * @returns A flat array of strings from all apps that declared data for this identifier.
195
+ */
196
+ export declare function getProvidesAsStrings(id: string): string[];
188
197
  /**
189
198
  * Get an external link URL based on the URL provided. If the passed in URL is overridden in the
190
199
  * `externalLinkUrlOverrides` object, it will return the overridden URL. Otherwise, it will return
@@ -298,24 +298,37 @@ export function getActiveRoles() {
298
298
  return [...getActiveRouteRoles(), ...getActiveWidgetRoles()];
299
299
  }
300
300
  /**
301
- * Collects all `provides` entries from registered apps that match the given key.
301
+ * Collects all `provides` entries from registered apps that match the given identifier.
302
302
  * This enables inter-app data sharing without frontend-base needing to understand the data shape.
303
303
  *
304
- * @param key - The namespaced identifier for the provided data.
305
- * @returns An array of provided data objects from all apps that declared data for this key.
304
+ * @param id - The namespaced provides identifier.
305
+ * @returns An array of provided data from all apps that declared data for this identifier.
306
306
  */
307
- export function getProvidedData(key) {
307
+ export function getProvides(id) {
308
308
  const { apps } = getSiteConfig();
309
309
  if (!apps)
310
310
  return [];
311
311
  const results = [];
312
312
  for (const app of apps) {
313
- if (app.provides && app.provides[key] !== undefined) {
314
- results.push(app.provides[key]);
313
+ if (app.provides && app.provides[id] !== undefined) {
314
+ results.push(app.provides[id]);
315
315
  }
316
316
  }
317
317
  return results;
318
318
  }
319
+ /**
320
+ * Collects and flattens all `provides` entries for the given identifier
321
+ * as strings. Each entry can be a single string or a string array; entries
322
+ * of other types are silently skipped.
323
+ *
324
+ * @param id - The namespaced provides identifier.
325
+ * @returns A flat array of strings from all apps that declared data for this identifier.
326
+ */
327
+ export function getProvidesAsStrings(id) {
328
+ return getProvides(id)
329
+ .filter((data) => typeof data === 'string' || Array.isArray(data))
330
+ .flat();
331
+ }
319
332
  /**
320
333
  * Get an external link URL based on the URL provided. If the passed in URL is overridden in the
321
334
  * `externalLinkUrlOverrides` object, it will return the overridden URL. Otherwise, it will return
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../runtime/config/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * #### Import members from **@edx/frontend-base**\n *\n * The configuration module provides utilities for working with an application's configuration\n * document (SiteConfig). Configuration variables can be supplied to the\n * application in three different ways. They are applied in the following order:\n *\n * - Site Configuration File (site.config.tsx)\n * - Initialization Config Handler\n * - Runtime Configuration\n *\n * Last one in wins, and are deep merged together. Variables with the same name defined via the\n * later methods will override any defined using an earlier method. i.e., if a variable is defined\n * in Runtime Configuration, that will override the same variable defined in either of the earlier\n * methods. Configuration defined in a JS file will override any default values below.\n *\n * ##### Site Configuration File\n *\n * Configuration variables can be supplied in a file named site.config.tsx. This file must\n * export either an Object containing configuration variables or a function. The function must\n * return an Object containing configuration variables or, alternately, a promise which resolves to\n * an Object.\n *\n * Using a function or async function allows the configuration to be resolved at runtime (because\n * the function will be executed at runtime). This is not common, and the capability is included\n * for the sake of flexibility.\n *\n * The Site Configuration File is well-suited to extensibility use cases or component overrides,\n * in that the configuration file can depend on any installed JavaScript module. It is also the\n * preferred way of doing build-time configuration if runtime configuration isn't used by your\n * deployment of the platform.\n *\n * Exporting a config object:\n * ```\n * const siteConfig = {\n * lmsBaseUrl: 'http://localhost:18000'\n * };\n *\n * export default siteConfig;\n * ```\n *\n * Exporting a function that returns an object:\n * ```\n * function getSiteConfig() {\n * return {\n * lmsBaseUrl: 'http://localhost:18000'\n * };\n * }\n * ```\n *\n * Exporting a function that returns a promise that resolves to an object:\n * ```\n * function getAsyncSiteConfig() {\n * return new Promise((resolve, reject) => {\n * resolve({\n * lmsBaseUrl: 'http://localhost:18000'\n * });\n * });\n * }\n *\n * export default getAsyncSiteConfig;\n * ```\n *\n * ##### Initialization Config Handler\n *\n * The configuration document can be extended by\n * applications at run-time using a `config` initialization handler. Please see the Initialization\n * documentation for more information on handlers and initialization phases.\n *\n * ```\n * initialize({\n * handlers: {\n * config: () => {\n * mergeSiteConfig({\n * CUSTOM_VARIABLE: 'custom value',\n * lmsBaseUrl: 'http://localhost:18001' // You can override variables, but this is uncommon.\n * }, 'App config override handler');\n * },\n * },\n * });\n * ```\n *\n * ##### Runtime Configuration\n *\n * Configuration variables can also be supplied using the \"runtime configuration\" method, taking\n * advantage of the Micro-frontend Config API in edx-platform. More information on this API can be\n * found in the ADR which introduced it:\n *\n * https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/mfe_config_api/docs/decisions/0001-mfe-config-api.rst\n *\n * The runtime configuration method can be enabled by supplying a runtimeConfigJsonUrl via one of the other\n * two configuration methods above.\n *\n * Runtime configuration is particularly useful if you need to supply different configurations to\n * a single deployment of a micro-frontend, for instance. It is also a perfectly valid alternative\n * to build-time configuration, though it introduces an additional API call to edx-platform on MFE\n * initialization.\n *\n *\n * @module Config\n */\n\nimport keyBy from 'lodash.keyby';\nimport merge from 'lodash.merge';\nimport {\n AppConfig,\n EnvironmentTypes,\n SiteConfig\n} from '../../types';\nimport { ACTIVE_ROLES_CHANGED, CONFIG_CHANGED } from '../constants';\nimport { publish } from '../subscriptions';\n\nlet siteConfig: SiteConfig = {\n // Required\n siteId: '',\n baseUrl: '',\n siteName: '',\n loginUrl: '',\n logoutUrl: '',\n lmsBaseUrl: '',\n\n // Optional\n environment: EnvironmentTypes.PRODUCTION,\n apps: [],\n externalRoutes: [],\n externalLinkUrlOverrides: [],\n runtimeConfigJsonUrl: null,\n theme: {},\n accessTokenCookieName: 'edx-jwt-cookie-header-payload',\n csrfTokenApiPath: '/csrf/api/v1/token',\n ignoredErrorRegex: null,\n languagePreferenceCookieName: 'openedx-language-preference',\n refreshAccessTokenApiPath: '/login_refresh',\n userInfoCookieName: 'edx-user-info',\n segmentKey: null,\n};\n\n/**\n * Getter for the application configuration document. This is synchronous and merely returns a\n * reference to an existing object, and is thus safe to call as often as desired.\n *\n * Example:\n *\n * ```\n * import { getSiteConfig } from '@openedx/frontend-base';\n *\n * const {\n * lmsBaseUrl,\n * } = getSiteConfig();\n * ```\n *\n * @returns {SiteConfig}\n */\nexport function getSiteConfig() {\n return siteConfig;\n}\n\n/**\n * Replaces the existing SiteConfig. This is not commonly used, but can be helpful for tests.\n *\n * Example:\n *\n * ```\n * import { setSiteConfig } from '@openedx/frontend-base';\n *\n * setSiteConfig({\n * lmsBaseUrl, // This is overriding the ENTIRE document - this is not merged in!\n * });\n * ```\n *\n * @param newConfig A replacement SiteConfig which will completely override the current SiteConfig.\n */\nexport function setSiteConfig(newSiteConfig: SiteConfig) {\n siteConfig = newSiteConfig;\n publish(CONFIG_CHANGED);\n}\n\ninterface MergeSiteConfigOptions {\n limitAppMergeToConfig?: boolean,\n}\n\n/**\n * Merges additional configuration values into the site config returned by `getSiteConfig`. Will\n * override any values that exist with the same keys.\n *\n * ```\n * mergeSiteConfig({\n * NEW_KEY: 'new value',\n * OTHER_NEW_KEY: 'other new value',\n * });\n *\n * This function uses lodash.merge internally to merge configuration objects\n * which means they will be merged recursively. See https://lodash.com/docs/latest#merge for\n * documentation on the exact behavior.\n *\n * Apps are merged by appId rather than array index. By default, apps in the incoming config\n * that don't exist in the current config will be added.\n *\n * When `limitAppMergeToConfig` is true:\n * - All non-app parts of the config are still merged normally\n * - Only the `config` property of each existing app is merged\n * - Apps in the incoming config that don't exist in the current config are ignored\n *\n * @param {Object} newSiteConfig\n * @param {Object} options\n * @param {boolean} options.limitAppMergeToConfig - Limit app merging to only the config property of existing apps\n */\nexport function mergeSiteConfig(\n newSiteConfig: Partial<SiteConfig>,\n options: MergeSiteConfigOptions = {}\n) {\n const { limitAppMergeToConfig = false } = options;\n const { apps: newApps, ...restOfNewConfig } = newSiteConfig;\n\n // lodash merge the top-level (non-app) part\n siteConfig = merge(siteConfig, restOfNewConfig);\n\n // if we don't have new apps, we're done\n if (!newApps?.length) {\n publish(CONFIG_CHANGED);\n return;\n }\n\n // if we're doing a full merge, merge the objects\n if (!limitAppMergeToConfig) {\n siteConfig.apps = Object.values(merge(\n keyBy(siteConfig.apps || [], 'appId'),\n keyBy(newApps, 'appId')\n ));\n publish(CONFIG_CHANGED);\n return;\n }\n\n // we're doing a config-only merge, if we don't\n // have apps already, we can't update their configs\n if (!siteConfig.apps?.length) {\n publish(CONFIG_CHANGED);\n return;\n }\n\n // handle config-only merging\n const newAppsById = keyBy(newApps, 'appId');\n for (const app of siteConfig.apps) {\n const newApp = newAppsById[app.appId];\n if (newApp?.config) {\n app.config = merge(app.config, newApp.config);\n }\n }\n\n publish(CONFIG_CHANGED);\n}\n\nconst appConfigs: Record<string, AppConfig> = {};\n\n/**\n * addAppConfigs finds any AppConfig objects in the apps in SiteConfig and makes their config\n * available to be used by Apps via getAppConfig(appId) or useAppConfig() functions. This is\n * used at initialization time to process any AppConfigs bundled with the site.\n */\nexport function addAppConfigs() {\n const { apps } = getSiteConfig();\n if (!apps) return;\n\n for (const app of apps) {\n const { appId, config } = app;\n if (config !== undefined) {\n appConfigs[appId] = config;\n }\n }\n\n publish(CONFIG_CHANGED);\n}\n\nexport function getAppConfig(id: string) {\n const { commonAppConfig } = getSiteConfig();\n if (commonAppConfig === undefined) {\n return appConfigs[id];\n }\n return merge({}, commonAppConfig, appConfigs[id]);\n}\n\nexport function mergeAppConfig(id: string, newAppConfig: AppConfig) {\n appConfigs[id] = merge(appConfigs[id], newAppConfig);\n publish(CONFIG_CHANGED);\n}\n\nlet activeRouteRoles: string[] = [];\n\nexport function setActiveRouteRoles(roles: string[]) {\n activeRouteRoles = roles;\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function getActiveRouteRoles() {\n return activeRouteRoles;\n}\n\nconst activeWidgetRoles: Record<string, number> = {};\n\nexport function addActiveWidgetRole(role: string) {\n activeWidgetRoles[role] ??= 0;\n activeWidgetRoles[role] += 1;\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function removeActiveWidgetRole(role: string) {\n if (activeWidgetRoles[role] !== undefined) {\n activeWidgetRoles[role] -= 1;\n }\n if (activeWidgetRoles[role] < 1) {\n delete activeWidgetRoles[role];\n }\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function getActiveWidgetRoles() {\n return Object.entries(activeWidgetRoles)\n .filter(([, count]: [role: string, count: number]) => count !== undefined && count > 0)\n .map(([role]: [role: string, count: number]) => role);\n}\n\n// Gets all active roles from the route roles and widget roles.\nexport function getActiveRoles() {\n return [...getActiveRouteRoles(), ...getActiveWidgetRoles()];\n}\n\n/**\n * Collects all `provides` entries from registered apps that match the given key.\n * This enables inter-app data sharing without frontend-base needing to understand the data shape.\n *\n * @param key - The namespaced identifier for the provided data.\n * @returns An array of provided data objects from all apps that declared data for this key.\n */\nexport function getProvidedData(key: string): unknown[] {\n const { apps } = getSiteConfig();\n if (!apps) return [];\n\n const results: unknown[] = [];\n for (const app of apps) {\n if (app.provides && app.provides[key] !== undefined) {\n results.push(app.provides[key]);\n }\n }\n return results;\n}\n\n/**\n * Get an external link URL based on the URL provided. If the passed in URL is overridden in the\n * `externalLinkUrlOverrides` object, it will return the overridden URL. Otherwise, it will return\n * the provided URL.\n *\n *\n * @param {string} url - The default URL.\n * @returns {string} - The external link URL. Defaults to the input URL if not found in the\n * `externalLinkUrlOverrides` object. If the input URL is invalid, '#' is returned.\n *\n * @example\n * import { getExternalLinkUrl } from '@openedx/frontend-base';\n *\n * <Hyperlink\n * destination={getExternalLinkUrl(data.helpLink)}\n * target=\"_blank\"\n * >\n */\nexport function getExternalLinkUrl(url: string): string {\n // Guard against whitespace-only strings\n if (typeof url !== 'string' || !url.trim()) {\n return '#';\n }\n\n const overriddenLinkUrls = getSiteConfig().externalLinkUrlOverrides ?? {};\n return overriddenLinkUrls[url] ?? url;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../runtime/config/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoGG;;;;;;;;;;;;AAEH,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,KAAK,MAAM,cAAc,CAAC;AACjC,OAAO,EAEL,gBAAgB,EAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,IAAI,UAAU,GAAe;IAC3B,WAAW;IACX,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,SAAS,EAAE,EAAE;IACb,UAAU,EAAE,EAAE;IAEd,WAAW;IACX,WAAW,EAAE,gBAAgB,CAAC,UAAU;IACxC,IAAI,EAAE,EAAE;IACR,cAAc,EAAE,EAAE;IAClB,wBAAwB,EAAE,EAAE;IAC5B,oBAAoB,EAAE,IAAI;IAC1B,KAAK,EAAE,EAAE;IACT,qBAAqB,EAAE,+BAA+B;IACtD,gBAAgB,EAAE,oBAAoB;IACtC,iBAAiB,EAAE,IAAI;IACvB,4BAA4B,EAAE,6BAA6B;IAC3D,yBAAyB,EAAE,gBAAgB;IAC3C,kBAAkB,EAAE,eAAe;IACnC,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;IAeI;AACJ,MAAM,UAAU,aAAa;IAC3B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,aAAyB;IACrD,UAAU,GAAG,aAAa,CAAC;IAC3B,OAAO,CAAC,cAAc,CAAC,CAAC;AAC1B,CAAC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,eAAe,CAC7B,aAAkC,EAClC,UAAkC,EAAE;;IAEpC,MAAM,EAAE,qBAAqB,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAClD,MAAM,EAAE,IAAI,EAAE,OAAO,KAAyB,aAAa,EAAjC,eAAe,UAAK,aAAa,EAArD,QAAqC,CAAgB,CAAC;IAE5D,4CAA4C;IAC5C,UAAU,GAAG,KAAK,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAEhD,wCAAwC;IACxC,IAAI,CAAC,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,CAAA,EAAE,CAAC;QACrB,OAAO,CAAC,cAAc,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IAED,iDAAiD;IACjD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CACnC,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,EACrC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CACxB,CAAC,CAAC;QACH,OAAO,CAAC,cAAc,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IAED,+CAA+C;IAC/C,mDAAmD;IACnD,IAAI,CAAC,CAAA,MAAA,UAAU,CAAC,IAAI,0CAAE,MAAM,CAAA,EAAE,CAAC;QAC7B,OAAO,CAAC,cAAc,CAAC,CAAC;QACxB,OAAO;IACT,CAAC;IAED,6BAA6B;IAC7B,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,EAAE,CAAC;YACnB,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,cAAc,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,GAA8B,EAAE,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,UAAU,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,cAAc,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,MAAM,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAAC;IAC5C,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,UAAU,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC,EAAE,EAAE,eAAe,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAU,EAAE,YAAuB;IAChE,UAAU,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;IACrD,OAAO,CAAC,cAAc,CAAC,CAAC;AAC1B,CAAC;AAED,IAAI,gBAAgB,GAAa,EAAE,CAAC;AAEpC,MAAM,UAAU,mBAAmB,CAAC,KAAe;IACjD,gBAAgB,GAAG,KAAK,CAAC;IACzB,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,iBAAiB,GAA2B,EAAE,CAAC;AAErD,MAAM,UAAU,mBAAmB,CAAC,IAAY;;IAC9C,MAAA,iBAAiB,CAAC,IAAI,qCAAtB,iBAAiB,CAAC,IAAI,IAAM,CAAC,EAAC;IAC9B,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QAC1C,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC;SACrC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAgC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;SACtF,GAAG,CAAC,CAAC,CAAC,IAAI,CAAgC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AAC1D,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,mBAAmB,EAAE,EAAE,GAAG,oBAAoB,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE,CAAC;IACjC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAErB,MAAM,OAAO,GAAc,EAAE,CAAC;IAC9B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAU;IAC7C,OAAO,WAAW,CAAC,EAAE,CAAC;SACnB,MAAM,CAAC,CAAC,IAAI,EAA6B,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;SAC5F,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;;IAC5C,wCAAwC;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3C,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,kBAAkB,GAAG,MAAA,aAAa,EAAE,CAAC,wBAAwB,mCAAI,EAAE,CAAC;IAC1E,OAAO,MAAA,kBAAkB,CAAC,GAAG,CAAC,mCAAI,GAAG,CAAC;AACxC,CAAC","sourcesContent":["/**\n * #### Import members from **@edx/frontend-base**\n *\n * The configuration module provides utilities for working with an application's configuration\n * document (SiteConfig). Configuration variables can be supplied to the\n * application in three different ways. They are applied in the following order:\n *\n * - Site Configuration File (site.config.tsx)\n * - Initialization Config Handler\n * - Runtime Configuration\n *\n * Last one in wins, and are deep merged together. Variables with the same name defined via the\n * later methods will override any defined using an earlier method. i.e., if a variable is defined\n * in Runtime Configuration, that will override the same variable defined in either of the earlier\n * methods. Configuration defined in a JS file will override any default values below.\n *\n * ##### Site Configuration File\n *\n * Configuration variables can be supplied in a file named site.config.tsx. This file must\n * export either an Object containing configuration variables or a function. The function must\n * return an Object containing configuration variables or, alternately, a promise which resolves to\n * an Object.\n *\n * Using a function or async function allows the configuration to be resolved at runtime (because\n * the function will be executed at runtime). This is not common, and the capability is included\n * for the sake of flexibility.\n *\n * The Site Configuration File is well-suited to extensibility use cases or component overrides,\n * in that the configuration file can depend on any installed JavaScript module. It is also the\n * preferred way of doing build-time configuration if runtime configuration isn't used by your\n * deployment of the platform.\n *\n * Exporting a config object:\n * ```\n * const siteConfig = {\n * lmsBaseUrl: 'http://localhost:18000'\n * };\n *\n * export default siteConfig;\n * ```\n *\n * Exporting a function that returns an object:\n * ```\n * function getSiteConfig() {\n * return {\n * lmsBaseUrl: 'http://localhost:18000'\n * };\n * }\n * ```\n *\n * Exporting a function that returns a promise that resolves to an object:\n * ```\n * function getAsyncSiteConfig() {\n * return new Promise((resolve, reject) => {\n * resolve({\n * lmsBaseUrl: 'http://localhost:18000'\n * });\n * });\n * }\n *\n * export default getAsyncSiteConfig;\n * ```\n *\n * ##### Initialization Config Handler\n *\n * The configuration document can be extended by\n * applications at run-time using a `config` initialization handler. Please see the Initialization\n * documentation for more information on handlers and initialization phases.\n *\n * ```\n * initialize({\n * handlers: {\n * config: () => {\n * mergeSiteConfig({\n * CUSTOM_VARIABLE: 'custom value',\n * lmsBaseUrl: 'http://localhost:18001' // You can override variables, but this is uncommon.\n * }, 'App config override handler');\n * },\n * },\n * });\n * ```\n *\n * ##### Runtime Configuration\n *\n * Configuration variables can also be supplied using the \"runtime configuration\" method, taking\n * advantage of the Micro-frontend Config API in edx-platform. More information on this API can be\n * found in the ADR which introduced it:\n *\n * https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/mfe_config_api/docs/decisions/0001-mfe-config-api.rst\n *\n * The runtime configuration method can be enabled by supplying a runtimeConfigJsonUrl via one of the other\n * two configuration methods above.\n *\n * Runtime configuration is particularly useful if you need to supply different configurations to\n * a single deployment of a micro-frontend, for instance. It is also a perfectly valid alternative\n * to build-time configuration, though it introduces an additional API call to edx-platform on MFE\n * initialization.\n *\n *\n * @module Config\n */\n\nimport keyBy from 'lodash.keyby';\nimport merge from 'lodash.merge';\nimport {\n AppConfig,\n EnvironmentTypes,\n SiteConfig\n} from '../../types';\nimport { ACTIVE_ROLES_CHANGED, CONFIG_CHANGED } from '../constants';\nimport { publish } from '../subscriptions';\n\nlet siteConfig: SiteConfig = {\n // Required\n siteId: '',\n baseUrl: '',\n siteName: '',\n loginUrl: '',\n logoutUrl: '',\n lmsBaseUrl: '',\n\n // Optional\n environment: EnvironmentTypes.PRODUCTION,\n apps: [],\n externalRoutes: [],\n externalLinkUrlOverrides: [],\n runtimeConfigJsonUrl: null,\n theme: {},\n accessTokenCookieName: 'edx-jwt-cookie-header-payload',\n csrfTokenApiPath: '/csrf/api/v1/token',\n ignoredErrorRegex: null,\n languagePreferenceCookieName: 'openedx-language-preference',\n refreshAccessTokenApiPath: '/login_refresh',\n userInfoCookieName: 'edx-user-info',\n segmentKey: null,\n};\n\n/**\n * Getter for the application configuration document. This is synchronous and merely returns a\n * reference to an existing object, and is thus safe to call as often as desired.\n *\n * Example:\n *\n * ```\n * import { getSiteConfig } from '@openedx/frontend-base';\n *\n * const {\n * lmsBaseUrl,\n * } = getSiteConfig();\n * ```\n *\n * @returns {SiteConfig}\n */\nexport function getSiteConfig() {\n return siteConfig;\n}\n\n/**\n * Replaces the existing SiteConfig. This is not commonly used, but can be helpful for tests.\n *\n * Example:\n *\n * ```\n * import { setSiteConfig } from '@openedx/frontend-base';\n *\n * setSiteConfig({\n * lmsBaseUrl, // This is overriding the ENTIRE document - this is not merged in!\n * });\n * ```\n *\n * @param newConfig A replacement SiteConfig which will completely override the current SiteConfig.\n */\nexport function setSiteConfig(newSiteConfig: SiteConfig) {\n siteConfig = newSiteConfig;\n publish(CONFIG_CHANGED);\n}\n\ninterface MergeSiteConfigOptions {\n limitAppMergeToConfig?: boolean,\n}\n\n/**\n * Merges additional configuration values into the site config returned by `getSiteConfig`. Will\n * override any values that exist with the same keys.\n *\n * ```\n * mergeSiteConfig({\n * NEW_KEY: 'new value',\n * OTHER_NEW_KEY: 'other new value',\n * });\n *\n * This function uses lodash.merge internally to merge configuration objects\n * which means they will be merged recursively. See https://lodash.com/docs/latest#merge for\n * documentation on the exact behavior.\n *\n * Apps are merged by appId rather than array index. By default, apps in the incoming config\n * that don't exist in the current config will be added.\n *\n * When `limitAppMergeToConfig` is true:\n * - All non-app parts of the config are still merged normally\n * - Only the `config` property of each existing app is merged\n * - Apps in the incoming config that don't exist in the current config are ignored\n *\n * @param {Object} newSiteConfig\n * @param {Object} options\n * @param {boolean} options.limitAppMergeToConfig - Limit app merging to only the config property of existing apps\n */\nexport function mergeSiteConfig(\n newSiteConfig: Partial<SiteConfig>,\n options: MergeSiteConfigOptions = {}\n) {\n const { limitAppMergeToConfig = false } = options;\n const { apps: newApps, ...restOfNewConfig } = newSiteConfig;\n\n // lodash merge the top-level (non-app) part\n siteConfig = merge(siteConfig, restOfNewConfig);\n\n // if we don't have new apps, we're done\n if (!newApps?.length) {\n publish(CONFIG_CHANGED);\n return;\n }\n\n // if we're doing a full merge, merge the objects\n if (!limitAppMergeToConfig) {\n siteConfig.apps = Object.values(merge(\n keyBy(siteConfig.apps || [], 'appId'),\n keyBy(newApps, 'appId')\n ));\n publish(CONFIG_CHANGED);\n return;\n }\n\n // we're doing a config-only merge, if we don't\n // have apps already, we can't update their configs\n if (!siteConfig.apps?.length) {\n publish(CONFIG_CHANGED);\n return;\n }\n\n // handle config-only merging\n const newAppsById = keyBy(newApps, 'appId');\n for (const app of siteConfig.apps) {\n const newApp = newAppsById[app.appId];\n if (newApp?.config) {\n app.config = merge(app.config, newApp.config);\n }\n }\n\n publish(CONFIG_CHANGED);\n}\n\nconst appConfigs: Record<string, AppConfig> = {};\n\n/**\n * addAppConfigs finds any AppConfig objects in the apps in SiteConfig and makes their config\n * available to be used by Apps via getAppConfig(appId) or useAppConfig() functions. This is\n * used at initialization time to process any AppConfigs bundled with the site.\n */\nexport function addAppConfigs() {\n const { apps } = getSiteConfig();\n if (!apps) return;\n\n for (const app of apps) {\n const { appId, config } = app;\n if (config !== undefined) {\n appConfigs[appId] = config;\n }\n }\n\n publish(CONFIG_CHANGED);\n}\n\nexport function getAppConfig(id: string) {\n const { commonAppConfig } = getSiteConfig();\n if (commonAppConfig === undefined) {\n return appConfigs[id];\n }\n return merge({}, commonAppConfig, appConfigs[id]);\n}\n\nexport function mergeAppConfig(id: string, newAppConfig: AppConfig) {\n appConfigs[id] = merge(appConfigs[id], newAppConfig);\n publish(CONFIG_CHANGED);\n}\n\nlet activeRouteRoles: string[] = [];\n\nexport function setActiveRouteRoles(roles: string[]) {\n activeRouteRoles = roles;\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function getActiveRouteRoles() {\n return activeRouteRoles;\n}\n\nconst activeWidgetRoles: Record<string, number> = {};\n\nexport function addActiveWidgetRole(role: string) {\n activeWidgetRoles[role] ??= 0;\n activeWidgetRoles[role] += 1;\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function removeActiveWidgetRole(role: string) {\n if (activeWidgetRoles[role] !== undefined) {\n activeWidgetRoles[role] -= 1;\n }\n if (activeWidgetRoles[role] < 1) {\n delete activeWidgetRoles[role];\n }\n publish(ACTIVE_ROLES_CHANGED);\n}\n\nexport function getActiveWidgetRoles() {\n return Object.entries(activeWidgetRoles)\n .filter(([, count]: [role: string, count: number]) => count !== undefined && count > 0)\n .map(([role]: [role: string, count: number]) => role);\n}\n\n// Gets all active roles from the route roles and widget roles.\nexport function getActiveRoles() {\n return [...getActiveRouteRoles(), ...getActiveWidgetRoles()];\n}\n\n/**\n * Collects all `provides` entries from registered apps that match the given identifier.\n * This enables inter-app data sharing without frontend-base needing to understand the data shape.\n *\n * @param id - The namespaced provides identifier.\n * @returns An array of provided data from all apps that declared data for this identifier.\n */\nexport function getProvides(id: string): unknown[] {\n const { apps } = getSiteConfig();\n if (!apps) return [];\n\n const results: unknown[] = [];\n for (const app of apps) {\n if (app.provides && app.provides[id] !== undefined) {\n results.push(app.provides[id]);\n }\n }\n return results;\n}\n\n/**\n * Collects and flattens all `provides` entries for the given identifier\n * as strings. Each entry can be a single string or a string array; entries\n * of other types are silently skipped.\n *\n * @param id - The namespaced provides identifier.\n * @returns A flat array of strings from all apps that declared data for this identifier.\n */\nexport function getProvidesAsStrings(id: string): string[] {\n return getProvides(id)\n .filter((data): data is string | string[] => typeof data === 'string' || Array.isArray(data))\n .flat();\n}\n\n/**\n * Get an external link URL based on the URL provided. If the passed in URL is overridden in the\n * `externalLinkUrlOverrides` object, it will return the overridden URL. Otherwise, it will return\n * the provided URL.\n *\n *\n * @param {string} url - The default URL.\n * @returns {string} - The external link URL. Defaults to the input URL if not found in the\n * `externalLinkUrlOverrides` object. If the input URL is invalid, '#' is returned.\n *\n * @example\n * import { getExternalLinkUrl } from '@openedx/frontend-base';\n *\n * <Hyperlink\n * destination={getExternalLinkUrl(data.helpLink)}\n * target=\"_blank\"\n * >\n */\nexport function getExternalLinkUrl(url: string): string {\n // Guard against whitespace-only strings\n if (typeof url !== 'string' || !url.trim()) {\n return '#';\n }\n\n const overriddenLinkUrls = getSiteConfig().externalLinkUrlOverrides ?? {};\n return overriddenLinkUrls[url] ?? url;\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  export { configureAnalytics, getAnalyticsService, identifyAnonymousUser, identifyAuthenticatedUser, MockAnalyticsService, resetAnalyticsService, SegmentAnalyticsService, sendPageEvent, sendTrackEvent, sendTrackingLogEvent } from './analytics';
2
2
  export { AUTHENTICATED_USER_CHANGED, AUTHENTICATED_USER_TOPIC, AxiosJwtAuthService, configureAuth, ensureAuthenticatedUser, fetchAuthenticatedUser, getAuthenticatedHttpClient, getAuthenticatedUser, getAuthService, getHttpClient, getLoginRedirectUrl, getLogoutRedirectUrl, hydrateAuthenticatedUser, MockAuthService, redirectToLogin, redirectToLogout, setAuthenticatedUser } from './auth';
3
- export { getSiteConfig, setSiteConfig, mergeSiteConfig, addAppConfigs, getAppConfig, mergeAppConfig, setActiveRouteRoles, getActiveRouteRoles, addActiveWidgetRole, removeActiveWidgetRole, getActiveWidgetRoles, getActiveRoles, getExternalLinkUrl, getProvidedData } from './config';
3
+ export { getSiteConfig, setSiteConfig, mergeSiteConfig, addAppConfigs, getAppConfig, mergeAppConfig, setActiveRouteRoles, getActiveRouteRoles, addActiveWidgetRole, removeActiveWidgetRole, getActiveWidgetRoles, getActiveRoles, getExternalLinkUrl, getProvides, getProvidesAsStrings } from './config';
4
4
  export * from './constants';
5
5
  export { configureI18n, createIntl, defineMessages, FormattedDate, FormattedMessage, FormattedNumber, FormattedPlural, FormattedRelativeTime, FormattedTime, getLocale, getLocalizedLanguageName, getMessages, getPrimaryLanguageSubtag, getSupportedLanguageList, handleRtl, injectIntl, IntlProvider, intlShape, isRtl, LOCALE_CHANGED, LOCALE_TOPIC, mergeMessages, updateLocale, useIntl } from './i18n';
6
6
  export { auth, getBasename, initError, initialize } from './initialize';
@@ -1,6 +1,6 @@
1
1
  export { configureAnalytics, getAnalyticsService, identifyAnonymousUser, identifyAuthenticatedUser, MockAnalyticsService, resetAnalyticsService, SegmentAnalyticsService, sendPageEvent, sendTrackEvent, sendTrackingLogEvent } from './analytics';
2
2
  export { AUTHENTICATED_USER_CHANGED, AUTHENTICATED_USER_TOPIC, AxiosJwtAuthService, configureAuth, ensureAuthenticatedUser, fetchAuthenticatedUser, getAuthenticatedHttpClient, getAuthenticatedUser, getAuthService, getHttpClient, getLoginRedirectUrl, getLogoutRedirectUrl, hydrateAuthenticatedUser, MockAuthService, redirectToLogin, redirectToLogout, setAuthenticatedUser } from './auth';
3
- export { getSiteConfig, setSiteConfig, mergeSiteConfig, addAppConfigs, getAppConfig, mergeAppConfig, setActiveRouteRoles, getActiveRouteRoles, addActiveWidgetRole, removeActiveWidgetRole, getActiveWidgetRoles, getActiveRoles, getExternalLinkUrl, getProvidedData } from './config';
3
+ export { getSiteConfig, setSiteConfig, mergeSiteConfig, addAppConfigs, getAppConfig, mergeAppConfig, setActiveRouteRoles, getActiveRouteRoles, addActiveWidgetRole, removeActiveWidgetRole, getActiveWidgetRoles, getActiveRoles, getExternalLinkUrl, getProvides, getProvidesAsStrings } from './config';
4
4
  export * from './constants';
5
5
  export { configureI18n, createIntl, defineMessages, FormattedDate, FormattedMessage, FormattedNumber, FormattedPlural, FormattedRelativeTime, FormattedTime, getLocale, getLocalizedLanguageName, getMessages, getPrimaryLanguageSubtag, getSupportedLanguageList, handleRtl, injectIntl, IntlProvider, intlShape, isRtl, LOCALE_CHANGED, LOCALE_TOPIC, mergeMessages, updateLocale, useIntl } from './i18n';
6
6
  export { auth, getBasename, initError, initialize } from './initialize';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,aAAa,EACb,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,eAAe,EAChB,MAAM,UAAU,CAAC;AAElB,cAAc,aAAa,CAAC;AAE5B,OAAO,EACL,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,wBAAwB,EACxB,WAAW,EACX,wBAAwB,EACxB,wBAAwB,EACxB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,SAAS,EACT,KAAK,EACL,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,OAAO,EACR,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,OAAO,EACP,aAAa,EACb,SAAS,EACT,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,qBAAqB,EACrB,OAAO,EACP,SAAS,EACT,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,cAAc,SAAS,CAAC","sourcesContent":["export {\n configureAnalytics,\n getAnalyticsService,\n identifyAnonymousUser,\n identifyAuthenticatedUser,\n MockAnalyticsService,\n resetAnalyticsService,\n SegmentAnalyticsService,\n sendPageEvent,\n sendTrackEvent,\n sendTrackingLogEvent\n} from './analytics';\n\nexport {\n AUTHENTICATED_USER_CHANGED,\n AUTHENTICATED_USER_TOPIC,\n AxiosJwtAuthService,\n configureAuth,\n ensureAuthenticatedUser,\n fetchAuthenticatedUser,\n getAuthenticatedHttpClient,\n getAuthenticatedUser,\n getAuthService,\n getHttpClient,\n getLoginRedirectUrl,\n getLogoutRedirectUrl,\n hydrateAuthenticatedUser,\n MockAuthService,\n redirectToLogin,\n redirectToLogout,\n setAuthenticatedUser\n} from './auth';\n\nexport {\n getSiteConfig,\n setSiteConfig,\n mergeSiteConfig,\n addAppConfigs,\n getAppConfig,\n mergeAppConfig,\n setActiveRouteRoles,\n getActiveRouteRoles,\n addActiveWidgetRole,\n removeActiveWidgetRole,\n getActiveWidgetRoles,\n getActiveRoles,\n getExternalLinkUrl,\n getProvidedData\n} from './config';\n\nexport * from './constants';\n\nexport {\n configureI18n,\n createIntl,\n defineMessages,\n FormattedDate,\n FormattedMessage,\n FormattedNumber,\n FormattedPlural,\n FormattedRelativeTime,\n FormattedTime,\n getLocale,\n getLocalizedLanguageName,\n getMessages,\n getPrimaryLanguageSubtag,\n getSupportedLanguageList,\n handleRtl,\n injectIntl,\n IntlProvider,\n intlShape,\n isRtl,\n LOCALE_CHANGED,\n LOCALE_TOPIC,\n mergeMessages,\n updateLocale,\n useIntl\n} from './i18n';\n\nexport {\n auth,\n getBasename,\n initError,\n initialize\n} from './initialize';\n\nexport {\n configureLogging,\n getLoggingService,\n logError,\n logInfo,\n MockLoggingService,\n NewRelicLoggingService,\n resetLoggingService\n} from './logging';\n\nexport {\n CurrentAppContext,\n CurrentAppProvider,\n SiteContext,\n SiteProvider,\n AuthenticatedPageRoute,\n Divider,\n ErrorBoundary,\n ErrorPage,\n LoginRedirect,\n PageWrap,\n useSiteEvent,\n useAuthenticatedUser,\n useSiteConfig,\n useAppConfig\n} from './react';\n\nexport {\n authenticatedLoader,\n getUrlByRouteRole,\n isRoleRouteObject\n} from './routing';\n\nexport {\n clearAllSubscriptions,\n publish,\n subscribe,\n unsubscribe\n} from './subscriptions';\n\nexport {\n initializeMockApp,\n mockMessages\n} from './testing';\n\nexport {\n camelCaseObject,\n convertKeyNames,\n getQueryParameters,\n isValidVariableName,\n modifyObjectKeys,\n snakeCaseObject\n} from './utils';\n\nexport * from './slots';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,aAAa,EACb,uBAAuB,EACvB,sBAAsB,EACtB,0BAA0B,EAC1B,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,aAAa,EACb,aAAa,EACb,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACrB,MAAM,UAAU,CAAC;AAElB,cAAc,aAAa,CAAC;AAE5B,OAAO,EACL,aAAa,EACb,UAAU,EACV,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,wBAAwB,EACxB,WAAW,EACX,wBAAwB,EACxB,wBAAwB,EACxB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,SAAS,EACT,KAAK,EACL,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,OAAO,EACR,MAAM,QAAQ,CAAC;AAEhB,OAAO,EACL,IAAI,EACJ,WAAW,EACX,SAAS,EACT,UAAU,EACX,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,OAAO,EACP,aAAa,EACb,SAAS,EACT,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,YAAY,EACb,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,qBAAqB,EACrB,OAAO,EACP,SAAS,EACT,WAAW,EACZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,YAAY,EACb,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,cAAc,SAAS,CAAC","sourcesContent":["export {\n configureAnalytics,\n getAnalyticsService,\n identifyAnonymousUser,\n identifyAuthenticatedUser,\n MockAnalyticsService,\n resetAnalyticsService,\n SegmentAnalyticsService,\n sendPageEvent,\n sendTrackEvent,\n sendTrackingLogEvent\n} from './analytics';\n\nexport {\n AUTHENTICATED_USER_CHANGED,\n AUTHENTICATED_USER_TOPIC,\n AxiosJwtAuthService,\n configureAuth,\n ensureAuthenticatedUser,\n fetchAuthenticatedUser,\n getAuthenticatedHttpClient,\n getAuthenticatedUser,\n getAuthService,\n getHttpClient,\n getLoginRedirectUrl,\n getLogoutRedirectUrl,\n hydrateAuthenticatedUser,\n MockAuthService,\n redirectToLogin,\n redirectToLogout,\n setAuthenticatedUser\n} from './auth';\n\nexport {\n getSiteConfig,\n setSiteConfig,\n mergeSiteConfig,\n addAppConfigs,\n getAppConfig,\n mergeAppConfig,\n setActiveRouteRoles,\n getActiveRouteRoles,\n addActiveWidgetRole,\n removeActiveWidgetRole,\n getActiveWidgetRoles,\n getActiveRoles,\n getExternalLinkUrl,\n getProvides,\n getProvidesAsStrings\n} from './config';\n\nexport * from './constants';\n\nexport {\n configureI18n,\n createIntl,\n defineMessages,\n FormattedDate,\n FormattedMessage,\n FormattedNumber,\n FormattedPlural,\n FormattedRelativeTime,\n FormattedTime,\n getLocale,\n getLocalizedLanguageName,\n getMessages,\n getPrimaryLanguageSubtag,\n getSupportedLanguageList,\n handleRtl,\n injectIntl,\n IntlProvider,\n intlShape,\n isRtl,\n LOCALE_CHANGED,\n LOCALE_TOPIC,\n mergeMessages,\n updateLocale,\n useIntl\n} from './i18n';\n\nexport {\n auth,\n getBasename,\n initError,\n initialize\n} from './initialize';\n\nexport {\n configureLogging,\n getLoggingService,\n logError,\n logInfo,\n MockLoggingService,\n NewRelicLoggingService,\n resetLoggingService\n} from './logging';\n\nexport {\n CurrentAppContext,\n CurrentAppProvider,\n SiteContext,\n SiteProvider,\n AuthenticatedPageRoute,\n Divider,\n ErrorBoundary,\n ErrorPage,\n LoginRedirect,\n PageWrap,\n useSiteEvent,\n useAuthenticatedUser,\n useSiteConfig,\n useAppConfig\n} from './react';\n\nexport {\n authenticatedLoader,\n getUrlByRouteRole,\n isRoleRouteObject\n} from './routing';\n\nexport {\n clearAllSubscriptions,\n publish,\n subscribe,\n unsubscribe\n} from './subscriptions';\n\nexport {\n initializeMockApp,\n mockMessages\n} from './testing';\n\nexport {\n camelCaseObject,\n convertKeyNames,\n getQueryParameters,\n isValidVariableName,\n modifyObjectKeys,\n snakeCaseObject\n} from './utils';\n\nexport * from './slots';\n"]}
package/dist/shell/app.js CHANGED
@@ -1,13 +1,21 @@
1
- import { WidgetOperationTypes } from '../runtime';
1
+ import { getActiveRoles, getProvidesAsStrings, WidgetOperationTypes } from '../runtime';
2
2
  import { Footer } from './footer';
3
3
  import { Header } from './header';
4
- const inactive = [
5
- 'org.openedx.frontend.role.login',
6
- 'org.openedx.frontend.role.register',
7
- 'org.openedx.frontend.role.resetPassword',
8
- 'org.openedx.frontend.role.confirmPassword',
9
- 'org.openedx.frontend.role.welcome'
10
- ];
4
+ import { providesChromelessRolesId } from './constants';
5
+ /*
6
+ * Returns false when the current route should be chromeless (no header or
7
+ * footer). Apps request chromeless mode by listing their route roles under
8
+ * providesChromelessRolesId in their `provides` entry, e.g.:
9
+ *
10
+ * provides: { [providesChromelessRolesId]: ['org.openedx.frontend.role.authn'] }
11
+ *
12
+ * The widget is disabled when any of those roles is currently active.
13
+ */
14
+ function isChromeVisible() {
15
+ const activeRoles = getActiveRoles();
16
+ const chromelessRoles = getProvidesAsStrings(providesChromelessRolesId);
17
+ return !chromelessRoles.some(role => activeRoles.includes(role));
18
+ }
11
19
  const app = {
12
20
  appId: 'org.openedx.frontend.app.shell',
13
21
  slots: [
@@ -17,7 +25,7 @@ const app = {
17
25
  op: WidgetOperationTypes.APPEND,
18
26
  component: Header,
19
27
  condition: {
20
- inactive,
28
+ callback: isChromeVisible,
21
29
  }
22
30
  },
23
31
  {
@@ -26,7 +34,7 @@ const app = {
26
34
  op: WidgetOperationTypes.APPEND,
27
35
  component: Footer,
28
36
  condition: {
29
- inactive,
37
+ callback: isChromeVisible,
30
38
  }
31
39
  },
32
40
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../shell/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,QAAQ,GAAG;IACf,iCAAiC;IACjC,oCAAoC;IACpC,yCAAyC;IACzC,2CAA2C;IAC3C,mCAAmC;CACpC,CAAC;AAEF,MAAM,GAAG,GAAQ;IACf,KAAK,EAAE,gCAAgC;IACvC,KAAK,EAAE;QACL;YACE,MAAM,EAAE,0CAA0C;YAClD,EAAE,EAAE,4CAA4C;YAChD,EAAE,EAAE,oBAAoB,CAAC,MAAM;YAC/B,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,QAAQ;aACT;SACF;QACD;YACE,MAAM,EAAE,0CAA0C;YAClD,EAAE,EAAE,4CAA4C;YAChD,EAAE,EAAE,oBAAoB,CAAC,MAAM;YAC/B,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,QAAQ;aACT;SACF;KACF;CACF,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { WidgetOperationTypes } from '../runtime';\nimport { App } from '../types';\nimport { Footer } from './footer';\nimport { Header } from './header';\n\nconst inactive = [\n 'org.openedx.frontend.role.login',\n 'org.openedx.frontend.role.register',\n 'org.openedx.frontend.role.resetPassword',\n 'org.openedx.frontend.role.confirmPassword',\n 'org.openedx.frontend.role.welcome'\n];\n\nconst app: App = {\n appId: 'org.openedx.frontend.app.shell',\n slots: [\n {\n slotId: 'org.openedx.frontend.slot.header.main.v1',\n id: 'org.openedx.frontend.widget.header.main.v1',\n op: WidgetOperationTypes.APPEND,\n component: Header,\n condition: {\n inactive,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.footer.main.v1',\n id: 'org.openedx.frontend.widget.footer.main.v1',\n op: WidgetOperationTypes.APPEND,\n component: Footer,\n condition: {\n inactive,\n }\n },\n ]\n};\n\nexport default app;\n"]}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../shell/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAExF,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;;;GAQG;AACH,SAAS,eAAe;IACtB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,MAAM,eAAe,GAAG,oBAAoB,CAAC,yBAAyB,CAAC,CAAC;IACxE,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,GAAG,GAAQ;IACf,KAAK,EAAE,gCAAgC;IACvC,KAAK,EAAE;QACL;YACE,MAAM,EAAE,0CAA0C;YAClD,EAAE,EAAE,4CAA4C;YAChD,EAAE,EAAE,oBAAoB,CAAC,MAAM;YAC/B,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,QAAQ,EAAE,eAAe;aAC1B;SACF;QACD;YACE,MAAM,EAAE,0CAA0C;YAClD,EAAE,EAAE,4CAA4C;YAChD,EAAE,EAAE,oBAAoB,CAAC,MAAM;YAC/B,SAAS,EAAE,MAAM;YACjB,SAAS,EAAE;gBACT,QAAQ,EAAE,eAAe;aAC1B;SACF;KACF;CACF,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { getActiveRoles, getProvidesAsStrings, WidgetOperationTypes } from '../runtime';\nimport { App } from '../types';\nimport { Footer } from './footer';\nimport { Header } from './header';\nimport { providesChromelessRolesId } from './constants';\n\n/*\n * Returns false when the current route should be chromeless (no header or\n * footer). Apps request chromeless mode by listing their route roles under\n * providesChromelessRolesId in their `provides` entry, e.g.:\n *\n * provides: { [providesChromelessRolesId]: ['org.openedx.frontend.role.authn'] }\n *\n * The widget is disabled when any of those roles is currently active.\n */\nfunction isChromeVisible(): boolean {\n const activeRoles = getActiveRoles();\n const chromelessRoles = getProvidesAsStrings(providesChromelessRolesId);\n return !chromelessRoles.some(role => activeRoles.includes(role));\n}\n\nconst app: App = {\n appId: 'org.openedx.frontend.app.shell',\n slots: [\n {\n slotId: 'org.openedx.frontend.slot.header.main.v1',\n id: 'org.openedx.frontend.widget.header.main.v1',\n op: WidgetOperationTypes.APPEND,\n component: Header,\n condition: {\n callback: isChromeVisible,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.footer.main.v1',\n id: 'org.openedx.frontend.widget.footer.main.v1',\n op: WidgetOperationTypes.APPEND,\n component: Footer,\n condition: {\n callback: isChromeVisible,\n }\n },\n ]\n};\n\nexport default app;\n"]}
@@ -0,0 +1 @@
1
+ export declare const providesChromelessRolesId = "org.openedx.frontend.provides.chromelessRoles.v1";
@@ -0,0 +1,2 @@
1
+ export const providesChromelessRolesId = 'org.openedx.frontend.provides.chromelessRoles.v1';
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../shell/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG,kDAAkD,CAAC","sourcesContent":["export const providesChromelessRolesId = 'org.openedx.frontend.provides.chromelessRoles.v1';\n"]}
@@ -13,7 +13,7 @@ import MobileNavLinks from './mobile/MobileNavLinks';
13
13
  import messages from '../Shell.messages';
14
14
  import CourseTabsNavigation from './course-navigation-bar/CourseTabsNavigation';
15
15
  import { isCourseNavigationRoute } from './course-navigation-bar/utils';
16
- import { appId, courseNavigationBarSlotId, courseTabsNavigationWidgetId } from './constants';
16
+ import { appId } from './constants';
17
17
  const config = {
18
18
  appId,
19
19
  slots: [
@@ -117,8 +117,8 @@ const config = {
117
117
  }
118
118
  },
119
119
  {
120
- slotId: courseNavigationBarSlotId,
121
- id: courseTabsNavigationWidgetId,
120
+ slotId: 'org.openedx.frontend.slot.header.courseNavigationBar.v1',
121
+ id: 'org.openedx.frontend.widget.header.courseNavigationBar.v1',
122
122
  op: WidgetOperationTypes.APPEND,
123
123
  component: CourseTabsNavigation,
124
124
  condition: {
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../../../shell/header/app.tsx"],"names":[],"mappings":"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","sourcesContent":["import { WidgetOperationTypes } from '../../runtime';\nimport { App } from '../../types';\nimport Logo from '../Logo';\nimport LinkMenuItem from '../menus/LinkMenuItem';\nimport ProfileLinkMenuItem from '../menus/ProfileLinkMenuItem';\nimport AnonymousMenu from './anonymous-menu/AnonymousMenu';\nimport AuthenticatedMenu from './AuthenticatedMenu';\nimport DesktopLayout from './desktop/DesktopLayout';\nimport PrimaryNavLinks from './desktop/PrimaryNavLinks';\nimport SecondaryNavLinks from './desktop/SecondaryNavLinks';\nimport MobileLayout from './mobile/MobileLayout';\nimport MobileNavLinks from './mobile/MobileNavLinks';\n\nimport messages from '../Shell.messages';\nimport CourseTabsNavigation from './course-navigation-bar/CourseTabsNavigation';\nimport { isCourseNavigationRoute } from './course-navigation-bar/utils';\nimport { appId, courseNavigationBarSlotId, courseTabsNavigationWidgetId } from './constants';\n\nconst config: App = {\n appId,\n slots: [\n // Layouts\n {\n slotId: 'org.openedx.frontend.slot.header.desktop.v1',\n id: 'org.openedx.frontend.widget.header.desktopLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: DesktopLayout\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobile.v1',\n id: 'org.openedx.frontend.widget.header.mobileLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileLayout\n },\n\n // Desktop\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopPrimaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: PrimaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopSecondaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: SecondaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuProfile.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <ProfileLinkMenuItem\n label={messages['header.user.menu.profile']}\n role=\"org.openedx.frontend.role.profile\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuAccount.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.account']}\n role=\"org.openedx.frontend.role.account\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuLogout.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.logout']}\n role=\"org.openedx.frontend.role.logout\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n\n // Mobile\n {\n slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',\n id: 'org.openedx.frontend.widget.header.mobileLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileMenu.v1',\n id: 'org.openedx.frontend.widget.header.mobileMenuLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: courseNavigationBarSlotId,\n id: courseTabsNavigationWidgetId,\n op: WidgetOperationTypes.APPEND,\n component: CourseTabsNavigation,\n condition: {\n callback: () => isCourseNavigationRoute(),\n }\n }\n ]\n};\n\nexport default config;\n"]}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../../shell/header/app.tsx"],"names":[],"mappings":"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","sourcesContent":["import { WidgetOperationTypes } from '../../runtime';\nimport { App } from '../../types';\nimport Logo from '../Logo';\nimport LinkMenuItem from '../menus/LinkMenuItem';\nimport ProfileLinkMenuItem from '../menus/ProfileLinkMenuItem';\nimport AnonymousMenu from './anonymous-menu/AnonymousMenu';\nimport AuthenticatedMenu from './AuthenticatedMenu';\nimport DesktopLayout from './desktop/DesktopLayout';\nimport PrimaryNavLinks from './desktop/PrimaryNavLinks';\nimport SecondaryNavLinks from './desktop/SecondaryNavLinks';\nimport MobileLayout from './mobile/MobileLayout';\nimport MobileNavLinks from './mobile/MobileNavLinks';\n\nimport messages from '../Shell.messages';\nimport CourseTabsNavigation from './course-navigation-bar/CourseTabsNavigation';\nimport { isCourseNavigationRoute } from './course-navigation-bar/utils';\nimport { appId } from './constants';\n\nconst config: App = {\n appId,\n slots: [\n // Layouts\n {\n slotId: 'org.openedx.frontend.slot.header.desktop.v1',\n id: 'org.openedx.frontend.widget.header.desktopLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: DesktopLayout\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobile.v1',\n id: 'org.openedx.frontend.widget.header.mobileLayout.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileLayout\n },\n\n // Desktop\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopLeft.v1',\n id: 'org.openedx.frontend.widget.header.desktopPrimaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: PrimaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopSecondaryLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: SecondaryNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuProfile.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <ProfileLinkMenuItem\n label={messages['header.user.menu.profile']}\n role=\"org.openedx.frontend.role.profile\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuAccount.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.account']}\n role=\"org.openedx.frontend.role.account\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.authenticatedMenu.v1',\n id: 'org.openedx.frontend.widget.header.desktopAuthenticatedMenuLogout.v1',\n op: WidgetOperationTypes.APPEND,\n element: (\n <LinkMenuItem\n label={messages['header.user.menu.logout']}\n role=\"org.openedx.frontend.role.logout\"\n variant=\"dropdownItem\"\n />\n )\n },\n {\n slotId: 'org.openedx.frontend.slot.header.desktopRight.v1',\n id: 'org.openedx.frontend.widget.header.desktopAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n\n // Mobile\n {\n slotId: 'org.openedx.frontend.slot.header.mobileCenter.v1',\n id: 'org.openedx.frontend.widget.header.mobileLogo.v1',\n op: WidgetOperationTypes.APPEND,\n element: <Logo />,\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileMenu.v1',\n id: 'org.openedx.frontend.widget.header.mobileMenuLinks.v1',\n op: WidgetOperationTypes.APPEND,\n component: MobileNavLinks\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAuthenticatedMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AuthenticatedMenu />,\n condition: {\n authenticated: true,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.mobileRight.v1',\n id: 'org.openedx.frontend.widget.header.mobileAnonymousMenu.v1',\n op: WidgetOperationTypes.APPEND,\n element: <AnonymousMenu />,\n condition: {\n authenticated: false,\n }\n },\n {\n slotId: 'org.openedx.frontend.slot.header.courseNavigationBar.v1',\n id: 'org.openedx.frontend.widget.header.courseNavigationBar.v1',\n op: WidgetOperationTypes.APPEND,\n component: CourseTabsNavigation,\n condition: {\n callback: () => isCourseNavigationRoute(),\n }\n }\n ]\n};\n\nexport default config;\n"]}
@@ -1,4 +1,2 @@
1
1
  export declare const appId = "org.openedx.frontend.app.header";
2
- export declare const courseNavigationBarSlotId = "org.openedx.frontend.slot.header.courseNavigationBar.v1";
3
- export declare const courseTabsNavigationWidgetId = "org.openedx.frontend.widget.header.courseTabsNavigation.v1";
4
- export declare const courseNavigationRolesProvidesKey = "org.openedx.frontend.provides.courseNavigationRoles.v1";
2
+ export declare const providesCourseNavigationRolesId = "org.openedx.frontend.provides.courseNavigationRoles.v1";
@@ -1,5 +1,3 @@
1
1
  export const appId = 'org.openedx.frontend.app.header';
2
- export const courseNavigationBarSlotId = 'org.openedx.frontend.slot.header.courseNavigationBar.v1';
3
- export const courseTabsNavigationWidgetId = 'org.openedx.frontend.widget.header.courseTabsNavigation.v1';
4
- export const courseNavigationRolesProvidesKey = 'org.openedx.frontend.provides.courseNavigationRoles.v1';
2
+ export const providesCourseNavigationRolesId = 'org.openedx.frontend.provides.courseNavigationRoles.v1';
5
3
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../shell/header/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,iCAAiC,CAAC;AACvD,MAAM,CAAC,MAAM,yBAAyB,GAAG,yDAAyD,CAAC;AACnG,MAAM,CAAC,MAAM,4BAA4B,GAAG,4DAA4D,CAAC;AACzG,MAAM,CAAC,MAAM,gCAAgC,GAAG,wDAAwD,CAAC","sourcesContent":["export const appId = 'org.openedx.frontend.app.header';\nexport const courseNavigationBarSlotId = 'org.openedx.frontend.slot.header.courseNavigationBar.v1';\nexport const courseTabsNavigationWidgetId = 'org.openedx.frontend.widget.header.courseTabsNavigation.v1';\nexport const courseNavigationRolesProvidesKey = 'org.openedx.frontend.provides.courseNavigationRoles.v1';\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../shell/header/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,iCAAiC,CAAC;AACvD,MAAM,CAAC,MAAM,+BAA+B,GAAG,wDAAwD,CAAC","sourcesContent":["export const appId = 'org.openedx.frontend.app.header';\nexport const providesCourseNavigationRolesId = 'org.openedx.frontend.provides.courseNavigationRoles.v1';\n"]}
@@ -8,13 +8,18 @@ import { Nav, Navbar, Skeleton } from '@openedx/paragon';
8
8
  import messages from './messages';
9
9
  import { isClientRoute } from './utils';
10
10
  import './course-tabs-navigation.scss';
11
- // Returns the tabId of the tab whose pathname is the longest prefix match
12
- // against the current path. Uses react-router's matchPath for segment-aware
13
- // matching. For example, given tabs with paths /course/ (tabId: "outline")
14
- // and /course/dates/ (tabId: "dates"):
15
- // /course/dates/foo -> "dates" (longest prefix match)
16
- // /course/outline -> "outline"
17
- // /courseware -> null (not a segment boundary)
11
+ /*
12
+ * Returns the tabId of the tab whose pathname is the longest prefix match
13
+ * against the current path. Uses react-router's matchPath for segment-aware
14
+ * matching.
15
+ *
16
+ * For example, given tabs with paths /course/ (tabId: "outline")
17
+ * and /course/dates/ (tabId: "dates"):
18
+ *
19
+ * /course/dates/foo -> "dates" (longest prefix match)
20
+ * /course/outline -> "outline"
21
+ * /courseware -> null (not a segment boundary)
22
+ */
18
23
  const getActiveTabId = (currentPath, tabs) => {
19
24
  var _a;
20
25
  let best = null;
@@ -1 +1 @@
1
- {"version":3,"file":"CourseTabsNavigation.js","sourceRoot":"","sources":["../../../../shell/header/course-navigation-bar/CourseTabsNavigation.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { Link, matchPath, useLocation, useParams } from 'react-router-dom';\nimport { useQuery } from '@tanstack/react-query';\nimport { Slot, useIntl } from '../../../runtime';\nimport { CourseTab, getCourseHomeCourseMetadata } from './data/service';\nimport { Nav, Navbar, Skeleton } from '@openedx/paragon';\nimport messages from './messages';\nimport { isClientRoute } from './utils';\nimport './course-tabs-navigation.scss';\n\ninterface ResolvedTab extends CourseTab {\n pathname: string,\n clientPath: string | null,\n}\n\n// Returns the tabId of the tab whose pathname is the longest prefix match\n// against the current path. Uses react-router's matchPath for segment-aware\n// matching. For example, given tabs with paths /course/ (tabId: \"outline\")\n// and /course/dates/ (tabId: \"dates\"):\n// /course/dates/foo -> \"dates\" (longest prefix match)\n// /course/outline -> \"outline\"\n// /courseware -> null (not a segment boundary)\nconst getActiveTabId = (currentPath: string, tabs: ResolvedTab[]): string | null => {\n let best: ResolvedTab | null = null;\n for (const tab of tabs) {\n const match = matchPath({ path: `${tab.pathname}/*`, end: false }, currentPath);\n if (match && (!best || tab.pathname.length > best.pathname.length)) {\n best = tab;\n }\n }\n return best?.tabId ?? null;\n};\n\nconst CourseTabsNavigation = () => {\n const location = useLocation();\n const { courseId = '' } = useParams();\n const intl = useIntl();\n\n const { data = { tabs: [] }, isLoading } = useQuery({\n queryKey: ['org.openedx.frontend.app.header.course-meta', courseId],\n queryFn: () => getCourseHomeCourseMetadata(courseId),\n retry: 2,\n enabled: !!courseId,\n });\n\n const { tabs } = data;\n\n const resolvedTabs: ResolvedTab[] = useMemo(\n () => tabs.map(tab => {\n // Tab URLs from the course_home API are always absolute.\n const pathname = new URL(tab.url).pathname;\n return { ...tab, pathname, clientPath: isClientRoute(pathname) ? pathname : null };\n }),\n [tabs]\n );\n\n const currentTab = useMemo(\n () => resolvedTabs.length > 0 ? getActiveTabId(location.pathname, resolvedTabs) : null,\n [location.pathname, resolvedTabs]\n );\n\n if (isLoading) {\n return <Skeleton className=\"lead mt-3\" />;\n }\n\n if (!courseId || resolvedTabs.length === 0) {\n return null;\n }\n\n return (\n <Navbar expand=\"sm\" className=\"course-tabs-navigation pb-0\" aria-label={intl.formatMessage(messages.courseMaterial)}>\n <Nav\n variant=\"tabs\"\n activeKey={currentTab}\n >\n <Navbar.Toggle aria-controls=\"course-nav\" />\n <Navbar.Collapse id=\"course-nav\">\n {\n resolvedTabs.map(tab => (\n <Nav.Item key={tab.tabId}>\n <Nav.Link\n {...(tab.clientPath\n ? { to: tab.clientPath, as: Link }\n : { href: tab.url }\n )}\n active={tab.tabId === currentTab}\n >\n {tab.title}\n </Nav.Link>\n </Nav.Item>\n ))\n }\n </Navbar.Collapse>\n <Slot id=\"org.openedx.frontend.slot.header.courseNavigationBar.extraContent.v1\" />\n </Nav>\n </Navbar>\n );\n};\n\nexport default CourseTabsNavigation;\n"]}
1
+ {"version":3,"file":"CourseTabsNavigation.js","sourceRoot":"","sources":["../../../../shell/header/course-navigation-bar/CourseTabsNavigation.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { Link, matchPath, useLocation, useParams } from 'react-router-dom';\nimport { useQuery } from '@tanstack/react-query';\nimport { Slot, useIntl } from '../../../runtime';\nimport { CourseTab, getCourseHomeCourseMetadata } from './data/service';\nimport { Nav, Navbar, Skeleton } from '@openedx/paragon';\nimport messages from './messages';\nimport { isClientRoute } from './utils';\nimport './course-tabs-navigation.scss';\n\ninterface ResolvedTab extends CourseTab {\n pathname: string,\n clientPath: string | null,\n}\n\n/*\n * Returns the tabId of the tab whose pathname is the longest prefix match\n * against the current path. Uses react-router's matchPath for segment-aware\n * matching.\n *\n * For example, given tabs with paths /course/ (tabId: \"outline\")\n * and /course/dates/ (tabId: \"dates\"):\n *\n * /course/dates/foo -> \"dates\" (longest prefix match)\n * /course/outline -> \"outline\"\n * /courseware -> null (not a segment boundary)\n */\nconst getActiveTabId = (currentPath: string, tabs: ResolvedTab[]): string | null => {\n let best: ResolvedTab | null = null;\n for (const tab of tabs) {\n const match = matchPath({ path: `${tab.pathname}/*`, end: false }, currentPath);\n if (match && (!best || tab.pathname.length > best.pathname.length)) {\n best = tab;\n }\n }\n return best?.tabId ?? null;\n};\n\nconst CourseTabsNavigation = () => {\n const location = useLocation();\n const { courseId = '' } = useParams();\n const intl = useIntl();\n\n const { data = { tabs: [] }, isLoading } = useQuery({\n queryKey: ['org.openedx.frontend.app.header.course-meta', courseId],\n queryFn: () => getCourseHomeCourseMetadata(courseId),\n retry: 2,\n enabled: !!courseId,\n });\n\n const { tabs } = data;\n\n const resolvedTabs: ResolvedTab[] = useMemo(\n () => tabs.map(tab => {\n // Tab URLs from the course_home API are always absolute.\n const pathname = new URL(tab.url).pathname;\n return { ...tab, pathname, clientPath: isClientRoute(pathname) ? pathname : null };\n }),\n [tabs]\n );\n\n const currentTab = useMemo(\n () => resolvedTabs.length > 0 ? getActiveTabId(location.pathname, resolvedTabs) : null,\n [location.pathname, resolvedTabs]\n );\n\n if (isLoading) {\n return <Skeleton className=\"lead mt-3\" />;\n }\n\n if (!courseId || resolvedTabs.length === 0) {\n return null;\n }\n\n return (\n <Navbar expand=\"sm\" className=\"course-tabs-navigation pb-0\" aria-label={intl.formatMessage(messages.courseMaterial)}>\n <Nav\n variant=\"tabs\"\n activeKey={currentTab}\n >\n <Navbar.Toggle aria-controls=\"course-nav\" />\n <Navbar.Collapse id=\"course-nav\">\n {\n resolvedTabs.map(tab => (\n <Nav.Item key={tab.tabId}>\n <Nav.Link\n {...(tab.clientPath\n ? { to: tab.clientPath, as: Link }\n : { href: tab.url }\n )}\n active={tab.tabId === currentTab}\n >\n {tab.title}\n </Nav.Link>\n </Nav.Item>\n ))\n }\n </Navbar.Collapse>\n <Slot id=\"org.openedx.frontend.slot.header.courseNavigationBar.extraContent.v1\" />\n </Nav>\n </Navbar>\n );\n};\n\nexport default CourseTabsNavigation;\n"]}
@@ -1,21 +1,23 @@
1
1
  import { matchPath } from 'react-router-dom';
2
- import { getActiveRoles, getProvidedData, getUrlByRouteRole } from '../../../runtime';
3
- import { courseNavigationRolesProvidesKey } from '../constants';
4
- function getProviders() {
5
- return getProvidedData(courseNavigationRolesProvidesKey).filter((data) => data !== null
6
- && typeof data === 'object'
7
- && 'courseNavigationRoles' in data
8
- && Array.isArray(data.courseNavigationRoles));
9
- }
10
- function getProvidedRoles() {
11
- return getProviders().flatMap(data => data.courseNavigationRoles);
2
+ import { getActiveRoles, getProvidesAsStrings, getUrlByRouteRole } from '../../../runtime';
3
+ import { providesCourseNavigationRolesId } from '../constants';
4
+ /*
5
+ * Collects route role strings from all apps that opted into the course
6
+ * navigation bar feature. Each app declares its roles as a string array:
7
+ *
8
+ * provides: {
9
+ * [providesCourseNavigationRolesId]: ['org.openedx.frontend.role.learning'],
10
+ * }
11
+ */
12
+ function getCourseNavigationBarRoles() {
13
+ return getProvidesAsStrings(providesCourseNavigationRolesId);
12
14
  }
13
15
  export function isCourseNavigationRoute() {
14
16
  const activeRoles = getActiveRoles();
15
- return getProvidedRoles().some(role => activeRoles.includes(role));
17
+ return getCourseNavigationBarRoles().some(role => activeRoles.includes(role));
16
18
  }
17
19
  export function isClientRoute(pathname) {
18
- return getProvidedRoles().some(role => {
20
+ return getCourseNavigationBarRoles().some(role => {
19
21
  const routePath = getUrlByRouteRole(role);
20
22
  return routePath !== null
21
23
  && routePath.startsWith('/')
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../shell/header/course-navigation-bar/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,gCAAgC,EAAE,MAAM,cAAc,CAAC;AAMhE,SAAS,YAAY;IACnB,OAAO,eAAe,CAAC,gCAAgC,CAAC,CAAC,MAAM,CAC7D,CAAC,IAAI,EAAwC,EAAE,CAC7C,IAAI,KAAK,IAAI;WACV,OAAO,IAAI,KAAK,QAAQ;WACxB,uBAAuB,IAAI,IAAI;WAC/B,KAAK,CAAC,OAAO,CAAE,IAAqC,CAAC,qBAAqB,CAAC,CACjF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB;IACvB,OAAO,YAAY,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,OAAO,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,gBAAgB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACpC,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC1C,OAAO,SAAS,KAAK,IAAI;eACpB,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;eACzB,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { matchPath } from 'react-router-dom';\nimport { getActiveRoles, getProvidedData, getUrlByRouteRole } from '../../../runtime';\nimport { courseNavigationRolesProvidesKey } from '../constants';\n\ninterface CourseNavigationProviderData {\n courseNavigationRoles: string[],\n}\n\nfunction getProviders(): CourseNavigationProviderData[] {\n return getProvidedData(courseNavigationRolesProvidesKey).filter(\n (data): data is CourseNavigationProviderData =>\n data !== null\n && typeof data === 'object'\n && 'courseNavigationRoles' in data\n && Array.isArray((data as CourseNavigationProviderData).courseNavigationRoles)\n );\n}\n\nfunction getProvidedRoles(): string[] {\n return getProviders().flatMap(data => data.courseNavigationRoles);\n}\n\nexport function isCourseNavigationRoute(): boolean {\n const activeRoles = getActiveRoles();\n return getProvidedRoles().some(role => activeRoles.includes(role));\n}\n\nexport function isClientRoute(pathname: string): boolean {\n return getProvidedRoles().some(role => {\n const routePath = getUrlByRouteRole(role);\n return routePath !== null\n && routePath.startsWith('/')\n && matchPath({ path: routePath, end: false }, pathname) !== null;\n });\n}\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../shell/header/course-navigation-bar/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAE/D;;;;;;;GAOG;AACH,SAAS,2BAA2B;IAClC,OAAO,oBAAoB,CAAC,+BAA+B,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,OAAO,2BAA2B,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,2BAA2B,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC/C,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC1C,OAAO,SAAS,KAAK,IAAI;eACpB,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;eACzB,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { matchPath } from 'react-router-dom';\nimport { getActiveRoles, getProvidesAsStrings, getUrlByRouteRole } from '../../../runtime';\nimport { providesCourseNavigationRolesId } from '../constants';\n\n/*\n * Collects route role strings from all apps that opted into the course\n * navigation bar feature. Each app declares its roles as a string array:\n *\n * provides: {\n * [providesCourseNavigationRolesId]: ['org.openedx.frontend.role.learning'],\n * }\n */\nfunction getCourseNavigationBarRoles(): string[] {\n return getProvidesAsStrings(providesCourseNavigationRolesId);\n}\n\nexport function isCourseNavigationRoute(): boolean {\n const activeRoles = getActiveRoles();\n return getCourseNavigationBarRoles().some(role => activeRoles.includes(role));\n}\n\nexport function isClientRoute(pathname: string): boolean {\n return getCourseNavigationBarRoles().some(role => {\n const routePath = getUrlByRouteRole(role);\n return routePath !== null\n && routePath.startsWith('/')\n && matchPath({ path: routePath, end: false }, pathname) !== null;\n });\n}\n"]}
@@ -1,2 +1,3 @@
1
1
  export { default as headerApp } from './app';
2
+ export { providesCourseNavigationRolesId } from './constants';
2
3
  export { default as Header } from './Header';
@@ -1,3 +1,4 @@
1
1
  export { default as headerApp } from './app';
2
+ export { providesCourseNavigationRolesId } from './constants';
2
3
  export { default as Header } from './Header';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../shell/header/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export { default as headerApp } from './app';\nexport { default as Header } from './Header';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../shell/header/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export { default as headerApp } from './app';\nexport { providesCourseNavigationRolesId } from './constants';\nexport { default as Header } from './Header';\n"]}
@@ -2,6 +2,7 @@ export { default as DefaultLayout } from './DefaultLayout';
2
2
  export { default as DefaultMain } from './DefaultMain';
3
3
  export { default as shellApp } from './app';
4
4
  export { Footer, footerApp } from './footer';
5
- export { Header, headerApp } from './header';
5
+ export { providesCourseNavigationRolesId, Header, headerApp } from './header';
6
+ export { providesChromelessRolesId } from './constants';
6
7
  export { default as LinkMenuItem } from './menus/LinkMenuItem';
7
8
  export { default as NavDropdownMenuSlot } from './menus/NavDropdownMenuSlot';
@@ -2,7 +2,8 @@ export { default as DefaultLayout } from './DefaultLayout';
2
2
  export { default as DefaultMain } from './DefaultMain';
3
3
  export { default as shellApp } from './app';
4
4
  export { Footer, footerApp } from './footer';
5
- export { Header, headerApp } from './header';
5
+ export { providesCourseNavigationRolesId, Header, headerApp } from './header';
6
+ export { providesChromelessRolesId } from './constants';
6
7
  export { default as LinkMenuItem } from './menus/LinkMenuItem';
7
8
  export { default as NavDropdownMenuSlot } from './menus/NavDropdownMenuSlot';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { default as DefaultLayout } from './DefaultLayout';\nexport { default as DefaultMain } from './DefaultMain';\nexport { default as shellApp } from './app';\nexport { Footer, footerApp } from './footer';\nexport { Header, headerApp } from './header';\nexport { default as LinkMenuItem } from './menus/LinkMenuItem';\nexport { default as NavDropdownMenuSlot } from './menus/NavDropdownMenuSlot';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,+BAA+B,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC","sourcesContent":["export { default as DefaultLayout } from './DefaultLayout';\nexport { default as DefaultMain } from './DefaultMain';\nexport { default as shellApp } from './app';\nexport { Footer, footerApp } from './footer';\nexport { providesCourseNavigationRolesId, Header, headerApp } from './header';\nexport { providesChromelessRolesId } from './constants';\nexport { default as LinkMenuItem } from './menus/LinkMenuItem';\nexport { default as NavDropdownMenuSlot } from './menus/NavDropdownMenuSlot';\n"]}
@@ -7,8 +7,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { jsx as _jsx } from "react/jsx-runtime";
11
- import { StrictMode } from 'react';
10
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import { lazy, StrictMode, Suspense } from 'react';
12
12
  import { createRoot } from 'react-dom/client';
13
13
  import { RouterProvider } from 'react-router-dom';
14
14
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -16,12 +16,23 @@ import { SITE_INIT_ERROR, SITE_READY, initialize, subscribe } from '../runtime';
16
16
  import { addAppConfigs } from '../runtime/config';
17
17
  import messages from 'site.i18n';
18
18
  import createRouter from './router/createRouter';
19
+ /*
20
+ * Use a dynamic import guarded by process.env.NODE_ENV so that webpack
21
+ * places the devtools in a separate async chunk that is only requested
22
+ * in development. In production the ternary resolves to null and the
23
+ * chunk is never loaded.
24
+ */
25
+ const ReactQueryDevtools = process.env.NODE_ENV === 'development'
26
+ ? lazy(() => import('@tanstack/react-query-devtools')
27
+ .then((m) => ({ default: m.ReactQueryDevtools }))
28
+ .catch(() => ({ default: () => null })))
29
+ : null;
19
30
  subscribe(SITE_READY, () => __awaiter(void 0, void 0, void 0, function* () {
20
31
  const queryClient = new QueryClient();
21
32
  const router = createRouter();
22
33
  addAppConfigs();
23
34
  const root = createRoot(document.getElementById('root'));
24
- root.render(_jsx(StrictMode, { children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx(RouterProvider, { router: router }) }) }));
35
+ root.render(_jsx(StrictMode, { children: _jsxs(QueryClientProvider, { client: queryClient, children: [_jsx(RouterProvider, { router: router }), ReactQueryDevtools && (_jsx(Suspense, { fallback: null, children: _jsx(ReactQueryDevtools, { initialIsOpen: false }) }))] }) }));
25
36
  }));
26
37
  subscribe(SITE_INIT_ERROR, (error) => {
27
38
  const root = createRoot(document.getElementById('root'));
@@ -1 +1 @@
1
- {"version":3,"file":"site.js","sourceRoot":"","sources":["../../shell/site.tsx"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EACL,eAAe,EACf,UAAU,EACV,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,YAAY,MAAM,uBAAuB,CAAC;AAEjD,SAAS,CAAC,UAAU,EAAE,GAAS,EAAE;IAC/B,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,aAAa,EAAE,CAAC;IAEhB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAgB,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,CACT,KAAC,UAAU,cACT,KAAC,mBAAmB,IAAC,MAAM,EAAE,WAAW,YACtC,KAAC,cAAc,IAAC,MAAM,EAAE,MAAM,GAAI,GACd,GACX,CACd,CAAC;AACJ,CAAC,CAAA,CAAC,CAAC;AAEH,SAAS,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;IACnC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAgB,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,CAAC,wBAAM,KAAK,CAAC,OAAO,GAAO,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,UAAU,CAAC;IACT,QAAQ;CACT,CAAC,CAAC","sourcesContent":["import { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { RouterProvider } from 'react-router-dom';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport {\n SITE_INIT_ERROR,\n SITE_READY,\n initialize,\n subscribe\n} from '../runtime';\nimport { addAppConfigs } from '../runtime/config';\nimport messages from 'site.i18n';\nimport createRouter from './router/createRouter';\n\nsubscribe(SITE_READY, async () => {\n const queryClient = new QueryClient();\n const router = createRouter();\n\n addAppConfigs();\n\n const root = createRoot(document.getElementById('root') as HTMLElement);\n root.render(\n <StrictMode>\n <QueryClientProvider client={queryClient}>\n <RouterProvider router={router} />\n </QueryClientProvider>\n </StrictMode>\n );\n});\n\nsubscribe(SITE_INIT_ERROR, (error) => {\n const root = createRoot(document.getElementById('root') as HTMLElement);\n root.render(<div>{error.message}</div>);\n});\n\ninitialize({\n messages,\n});\n"]}
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../shell/site.tsx"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EACL,eAAe,EACf,UAAU,EACV,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,YAAY,MAAM,uBAAuB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa;IAC/D,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gCAAgC,CAAC;SAClD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC;SAChD,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,IAAI,CAAC;AAET,SAAS,CAAC,UAAU,EAAE,GAAS,EAAE;IAC/B,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAE9B,aAAa,EAAE,CAAC;IAEhB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAgB,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,CACT,KAAC,UAAU,cACT,MAAC,mBAAmB,IAAC,MAAM,EAAE,WAAW,aACtC,KAAC,cAAc,IAAC,MAAM,EAAE,MAAM,GAAI,EACjC,kBAAkB,IAAI,CACrB,KAAC,QAAQ,IAAC,QAAQ,EAAE,IAAI,YACtB,KAAC,kBAAkB,IAAC,aAAa,EAAE,KAAK,GAAI,GACnC,CACZ,IACmB,GACX,CACd,CAAC;AACJ,CAAC,CAAA,CAAC,CAAC;AAEH,SAAS,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;IACnC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAgB,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,CAAC,wBAAM,KAAK,CAAC,OAAO,GAAO,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,UAAU,CAAC;IACT,QAAQ;CACT,CAAC,CAAC","sourcesContent":["import { lazy, StrictMode, Suspense } from 'react';\nimport { createRoot } from 'react-dom/client';\nimport { RouterProvider } from 'react-router-dom';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\n\nimport {\n SITE_INIT_ERROR,\n SITE_READY,\n initialize,\n subscribe\n} from '../runtime';\nimport { addAppConfigs } from '../runtime/config';\nimport messages from 'site.i18n';\nimport createRouter from './router/createRouter';\n\n/*\n * Use a dynamic import guarded by process.env.NODE_ENV so that webpack\n * places the devtools in a separate async chunk that is only requested\n * in development. In production the ternary resolves to null and the\n * chunk is never loaded.\n */\nconst ReactQueryDevtools = process.env.NODE_ENV === 'development'\n ? lazy(() => import('@tanstack/react-query-devtools')\n .then((m) => ({ default: m.ReactQueryDevtools }))\n .catch(() => ({ default: () => null })))\n : null;\n\nsubscribe(SITE_READY, async () => {\n const queryClient = new QueryClient();\n const router = createRouter();\n\n addAppConfigs();\n\n const root = createRoot(document.getElementById('root') as HTMLElement);\n root.render(\n <StrictMode>\n <QueryClientProvider client={queryClient}>\n <RouterProvider router={router} />\n {ReactQueryDevtools && (\n <Suspense fallback={null}>\n <ReactQueryDevtools initialIsOpen={false} />\n </Suspense>\n )}\n </QueryClientProvider>\n </StrictMode>\n );\n});\n\nsubscribe(SITE_INIT_ERROR, (error) => {\n const root = createRoot(document.getElementById('root') as HTMLElement);\n root.render(<div>{error.message}</div>);\n});\n\ninitialize({\n messages,\n});\n"]}
package/dist/types.d.ts CHANGED
@@ -22,9 +22,9 @@ export interface App {
22
22
  appId: string;
23
23
  routes?: RoleRouteObject[];
24
24
  providers?: AppProvider[];
25
- provides?: Record<string, unknown>;
26
25
  slots?: SlotOperation[];
27
26
  config?: AppConfig;
27
+ provides?: Record<string, unknown>;
28
28
  }
29
29
  export interface RequiredSiteConfig {
30
30
  siteId: string;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAiHA,MAAM,CAAN,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,6CAAyB,CAAA;IACzB,+CAA2B,CAAA;IAC3B,iCAAa,CAAA;AACf,CAAC,EAJW,gBAAgB,KAAhB,gBAAgB,QAI3B","sourcesContent":["import { FC, ReactElement, ReactNode } from 'react';\nimport { MessageDescriptor } from 'react-intl';\nimport { RouteObject } from 'react-router';\nimport { SlotOperation } from './runtime/slots/types';\n\n// Apps\n\nexport interface ExternalRoute {\n role: string,\n url: string,\n}\n\nexport type RoleRouteObject = RouteObject & {\n handle?: {\n /**\n * A route role is used to identify a route that fulfills a particular role in the site.\n */\n role?: string,\n },\n};\n\nexport type AppConfig = Record<string, unknown>;\n\nexport type AppProvider = FC<{ children?: ReactNode }>;\n\nexport interface App {\n appId: string,\n routes?: RoleRouteObject[],\n providers?: AppProvider[],\n provides?: Record<string, unknown>,\n slots?: SlotOperation[],\n config?: AppConfig,\n}\n\n// Site Config\n\nexport interface RequiredSiteConfig {\n siteId: string,\n siteName: string,\n baseUrl: string,\n\n // Backends\n lmsBaseUrl: string,\n\n // Frontends\n loginUrl: string,\n logoutUrl: string,\n}\n\nexport type LocalizedMessages = Record<string, Record<string, string>>;\nexport type SiteMessages = LocalizedMessages[];\n\nexport interface OptionalSiteConfig {\n // Site environment\n environment: EnvironmentTypes,\n\n // Apps, routes, and URLs\n apps: App[],\n basename: string,\n externalRoutes: ExternalRoute[],\n externalLinkUrlOverrides: string[],\n runtimeConfigJsonUrl: string | null,\n commonAppConfig: AppConfig,\n headerLogoImageUrl: string,\n\n // Theme\n theme: Theme,\n\n // Cookies\n accessTokenCookieName: string,\n languagePreferenceCookieName: string,\n userInfoCookieName: string,\n\n // Paths\n csrfTokenApiPath: string,\n refreshAccessTokenApiPath: string,\n\n // Logging\n ignoredErrorRegex: RegExp | null,\n\n // Analytics\n segmentKey: string | null,\n}\n\nexport type SiteConfig = RequiredSiteConfig & Partial<OptionalSiteConfig>;\n\nexport interface ThemeVariant {\n url: string,\n}\n\nexport interface ThemeDefaults {\n light?: string,\n dark?: string,\n}\n\nexport type ThemeVariants = Record<string, ThemeVariant>;\n\nexport interface Theme {\n core?: ThemeVariant,\n defaults?: ThemeDefaults,\n variants?: ThemeVariants,\n}\n\nexport interface User {\n administrator: boolean,\n email: string,\n name: string,\n roles: string[],\n userId: number,\n username: string,\n avatar: string,\n}\n\nexport enum EnvironmentTypes {\n PRODUCTION = 'production',\n DEVELOPMENT = 'development',\n TEST = 'test',\n}\n\n// Menu Items\n\nexport type MenuItemName = string | MessageDescriptor | ReactElement;\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAiHA,MAAM,CAAN,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,6CAAyB,CAAA;IACzB,+CAA2B,CAAA;IAC3B,iCAAa,CAAA;AACf,CAAC,EAJW,gBAAgB,KAAhB,gBAAgB,QAI3B","sourcesContent":["import { FC, ReactElement, ReactNode } from 'react';\nimport { MessageDescriptor } from 'react-intl';\nimport { RouteObject } from 'react-router';\nimport { SlotOperation } from './runtime/slots/types';\n\n// Apps\n\nexport interface ExternalRoute {\n role: string,\n url: string,\n}\n\nexport type RoleRouteObject = RouteObject & {\n handle?: {\n /**\n * A route role is used to identify a route that fulfills a particular role in the site.\n */\n role?: string,\n },\n};\n\nexport type AppConfig = Record<string, unknown>;\n\nexport type AppProvider = FC<{ children?: ReactNode }>;\n\nexport interface App {\n appId: string,\n routes?: RoleRouteObject[],\n providers?: AppProvider[],\n slots?: SlotOperation[],\n config?: AppConfig,\n provides?: Record<string, unknown>,\n}\n\n// Site Config\n\nexport interface RequiredSiteConfig {\n siteId: string,\n siteName: string,\n baseUrl: string,\n\n // Backends\n lmsBaseUrl: string,\n\n // Frontends\n loginUrl: string,\n logoutUrl: string,\n}\n\nexport type LocalizedMessages = Record<string, Record<string, string>>;\nexport type SiteMessages = LocalizedMessages[];\n\nexport interface OptionalSiteConfig {\n // Site environment\n environment: EnvironmentTypes,\n\n // Apps, routes, and URLs\n apps: App[],\n basename: string,\n externalRoutes: ExternalRoute[],\n externalLinkUrlOverrides: string[],\n runtimeConfigJsonUrl: string | null,\n commonAppConfig: AppConfig,\n headerLogoImageUrl: string,\n\n // Theme\n theme: Theme,\n\n // Cookies\n accessTokenCookieName: string,\n languagePreferenceCookieName: string,\n userInfoCookieName: string,\n\n // Paths\n csrfTokenApiPath: string,\n refreshAccessTokenApiPath: string,\n\n // Logging\n ignoredErrorRegex: RegExp | null,\n\n // Analytics\n segmentKey: string | null,\n}\n\nexport type SiteConfig = RequiredSiteConfig & Partial<OptionalSiteConfig>;\n\nexport interface ThemeVariant {\n url: string,\n}\n\nexport interface ThemeDefaults {\n light?: string,\n dark?: string,\n}\n\nexport type ThemeVariants = Record<string, ThemeVariant>;\n\nexport interface Theme {\n core?: ThemeVariant,\n defaults?: ThemeDefaults,\n variants?: ThemeVariants,\n}\n\nexport interface User {\n administrator: boolean,\n email: string,\n name: string,\n roles: string[],\n userId: number,\n username: string,\n avatar: string,\n}\n\nexport enum EnvironmentTypes {\n PRODUCTION = 'production',\n DEVELOPMENT = 'development',\n TEST = 'test',\n}\n\n// Menu Items\n\nexport type MenuItemName = string | MessageDescriptor | ReactElement;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/frontend-base",
3
- "version": "1.0.0-alpha.23",
3
+ "version": "1.0.0-alpha.25",
4
4
  "description": "Build tools, setup and config for frontend apps",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -68,6 +68,7 @@
68
68
  "@formatjs/ts-transformer": "^3.13.14",
69
69
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
70
70
  "@stylistic/eslint-plugin": "^2.9.0",
71
+ "@tanstack/react-query-devtools": "^5.99.0",
71
72
  "@types/eslint__js": "^8.42.3",
72
73
  "@types/gradient-string": "^1.1.6",
73
74
  "@types/lodash.keyby": "^4.6.9",