import { StateRepository } from '../core/state'; import { ConfigDescriber } from '../core/config-description'; /** * Repository for storing and retrieving application-wide configuration data. * * {@link ConfigRepository} provides persistent key-value storage for configuration * that applies to all users across the application. Typical use cases include: * - View configurations (card layouts, table columns, form structures) * - Plugin-specific configuration settings * * Each plugin or package typically manages its own configuration namespace, * storing settings that define how components should be rendered or behave * for all users. * * **Important:** This repository is NOT for user preferences. User-specific * settings should use {@link UserPreferencesRepository} instead. * * Configuration data is: * - Stored persistently in the database * - Shared across all users and sessions * - Read-only for non-admin users (set method requires admin privileges) * * The repository extends {@link StateRepository}, providing reactive state * management with automatic UI updates when configuration changes. * * The repository also holds describers for parts of the configuration. A * describer is registered at runtime and is not stored in the database. * Subscriptions cover only the configuration data, not the describers. * * Access the repository through the platform service: * ```typescript * const repo = this.platform.get(PlatformServiceName.ConfigRepository); * ``` * * @example Read view configuration * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * interface CompanyViewConfig { * card: { * fields: string[]; * }; * } * * const configs = platform.get(PlatformServiceName.ConfigRepository); * * // A key holds nothing until an admin saves a config for it, so ask `has` before * // reading * const cardFields = configs.has('webclient_view.company') * ? configs.get('webclient_view.company').card.fields * : []; * ``` * * @example Read plugin configuration with type safety * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * interface MyPluginConfig { * enabled: boolean; * apiEndpoint: string; * } * * const defaultConfig: MyPluginConfig = { * enabled: false, * apiEndpoint: 'https://api.example.com', * }; * * function getPluginConfig(): MyPluginConfig { * const configs = platform.get(PlatformServiceName.ConfigRepository); * * if (!configs.has('my_plugin_config')) { * return defaultConfig; * } * * // An admin may have saved only some of the keys, so merge over the defaults * const saved = configs.get>('my_plugin_config'); * * return { ...defaultConfig, ...saved }; * } * * const isEnabled = getPluginConfig().enabled; * ``` * * @public * @group Config * @see {@link StateRepository} for subscription patterns */ export interface ConfigRepository extends StateRepository { /** * Check if a configuration key exists. * * Returns `true` if configuration data has been stored under the specified key, * `false` otherwise. Use this to check for the existence of optional configuration * before attempting to retrieve it. * * @param key - Name of the configuration key to check * @returns `true` if the configuration exists, `false` otherwise * * @example Check before loading view config * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * const configs = platform.get(PlatformServiceName.ConfigRepository); * * // Render the built-in columns when no admin has configured any * const hasCustomColumns = configs.has('person_table_columns'); * ``` * * @see {@link ConfigRepository.get} to retrieve configuration values * @see {@link ConfigRepository.set} to store configuration values */ has(key: string): boolean; /** * Get configuration data for a key. * * Retrieves the configuration value stored under the specified key. * Returns the stored data, or `undefined` if the key doesn't exist. * * Use the type parameter `` to specify the expected type of the configuration * data for type safety. The repository does not validate types at runtime. * * @param key - Name of the configuration key to retrieve * @returns The stored configuration data, or `undefined` if not found * @throws May throw if the stored data cannot be deserialized * * @example Get view configuration * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * interface CompanyCardConfig { * card: { * fields: string[]; * }; * } * * const configs = platform.get(PlatformServiceName.ConfigRepository); * * // The type parameter says what the config looks like, it does not promise the * // key exists, so guard the value before using it * const cardConfig = configs.get( * 'webclient_view.company' * )?.card; * ``` * * @see {@link ConfigRepository.has} to check if a key exists * @see {@link ConfigRepository.set} to store configuration values */ get(key: string): T; /** * Save configuration data under a key. * * > **Important:** This method requires admin privileges. The backend endpoint * > is protected and will reject requests from non-admin users. * * Stores the provided data persistently under the specified key. If the key * already exists, its value is overwritten. The data is serialized and saved * to the database, making it available to all users across the application. * * After saving, the repository state is updated, triggering any subscriptions. * The data can be any serializable JavaScript value (objects, arrays, primitives). * * @param key - Name of the configuration key to set * @param data - Configuration data to store (must be serializable) * @param version - The version of the plugin config the data was read at, * which the backend migrates it from. Defaults to the version of the data * the repository holds under the key. * @returns Promise that resolves when the data is saved * @throws Error if save fails, data cannot be serialized, or user lacks admin privileges * * @example Set plugin configuration as an admin * ```ts * import { PlatformServiceName } from '@limetech/lime-web-components'; * * interface PluginConfiguration { * enabled: boolean; * apiEndpoint: string; * settings: { * timeout: number; * retries: number; * }; * } * * const pluginConfig: PluginConfiguration = { * enabled: true, * apiEndpoint: 'https://api.example.com', * settings: { * timeout: 5000, * retries: 3, * }, * }; * * const configs = platform.get(PlatformServiceName.ConfigRepository); * * // The backend endpoint rejects this call for anyone who is not an admin * await configs.set('my_plugin_config', pluginConfig); * ``` * * @see {@link ConfigRepository.get} to retrieve stored configuration * @see {@link ConfigRepository.has} to check if a key exists */ set(key: string, data: T, version?: number): Promise; /** * Register a describer for one kind of config part. * * The name must contain a `.`, for example `my-addon.account`, so that * describers from different add-ons do not collide. Registering a describer with a name that is * already taken replaces the earlier describer. * * The platform registers the `describeConfig` of a web component, a * command or a rule primitive under its tag or id. Such a describer * takes the whole part, not only its config. A web component describer * takes a {@link ComponentDescriptor}, a command describer takes a * {@link CommandConfig}, and a rule primitive describer takes a * {@link RuleRef}. * * @param name - The name of the describer. * @param describer - The function that describes the part. * @throws If the name has no `.`. * * @example Describe each account in a list * ```ts * import { * ConfigDescription, * PlatformServiceName, * } from '@limetech/lime-web-components'; * * interface Account { * name: string; * email: string; * role: string; * } * * const translator = platform.get(PlatformServiceName.Translate); * * function describeAccount(account: Account): ConfigDescription { * return { * title: account.name, * icon: 'user', * facts: [ * { * label: translator.get('my-addon.account.email'), * text: account.email, * }, * { * label: translator.get('my-addon.account.role'), * text: account.role, * }, * ], * }; * } * * const configs = platform.get(PlatformServiceName.ConfigRepository); * * configs.registerDescriber?.('my-addon.account', describeAccount); * ``` * * @see {@link ConfigRepository.getDescriber} to get a describer * @beta */ registerDescriber?(name: string, describer: ConfigDescriber): void; /** * Get a registered describer by name. * * @param name - The name the describer was registered with. * @returns The describer, or `undefined` if no describer has the name. * * @see {@link ConfigRepository.registerDescriber} to register a describer * @beta */ getDescriber?(name: string): ConfigDescriber | undefined; }