Skip to content

Latest commit

 

History

History
1660 lines (1106 loc) · 46.7 KB

services.md

File metadata and controls

1660 lines (1106 loc) · 46.7 KB

Services 🔧

This document describes the services provided by lin-pulsar-core.


🔌 HTTP Client Service

Provides a singleton Axios instance shared across all modules.

Function Description
setHttpClient Initializes the shared Axios instance (call once)
getHttpClient Returns the shared Axios instance

setHttpClient

Initializes the shared Axios instance to be used by all modules. Should be called once during application bootstrapping.

import { setHttpClient } from '@linagora/lin-pulsar-core';

Parameters

Parameter Type Description
client AxiosInstance The Axios instance to be shared.

Returns

No return value.

Behavior

  1. Singleton: Only the first call sets the instance; subsequent calls log a warning and do not overwrite the instance.
  2. Required: Must be called before any call to getHttpClient().

getHttpClient

Retrieves the shared Axios instance initialized by setHttpClient.

import { getHttpClient } from '@linagora/lin-pulsar-core';

Parameters

None.

Returns

Type Description
AxiosInstance The shared Axios instance.

Behavior

  1. Singleton: Always returns the same instance set by setHttpClient.
  2. Error Handling: Throws an error if called before initialization.

Usage Examples

Basic Usage

import { setHttpClient, getHttpClient } from '@linagora/lin-pulsar-core';
import axios from 'axios';

// Host application bootstrapping
const axiosInstance = axios.create({
  baseURL: '/api',
  timeout: 10000,
});
setHttpClient(axiosInstance);

// In any module
const http = getHttpClient();
http.get('/users').then((response) => {
  // handle response
});

Error Handling

Calling getHttpClient before initialization
import { getHttpClient } from '@linagora/lin-pulsar-core';

try {
  const http = getHttpClient();
  // Throws: [LinPulsar Core] HTTP client is not initialized. Call setHttpClient() first.
} catch (error) {
  // Handle error
}
Attempting to re-initialize
import { setHttpClient } from '@linagora/lin-pulsar-core';
import axios from 'axios';

setHttpClient(axios.create());
// Second call logs a warning and does not overwrite the instance
setHttpClient(axios.create());
// Logs: [LinPulsar Core] HTTP client has already been initialized. Re-initialization is ignored.

TypeScript Support

The functions are fully typed for use with Axios:

import type { AxiosInstance } from 'axios';

const http: AxiosInstance = getHttpClient();

The HTTP Client Service ensures all modules share a single, consistent Axios instance for HTTP requests.

Rules

  • ✅ Host calls setHttpClient() once during boot
  • ✅ Modules use getHttpClient() to access the same instance
  • ⚠️ Warning logged if re-initialization is attempted
  • ❌ Error thrown if getHttpClient() is called before initialization

🔗 Federation Service

Provides helpers wrapping Module Federation (enhanced runtime) functionalities and enforcing a consistent initialization and loading model across the application.

This service centralizes access to the Module Federation instance and exposes high-level helpers for loading federated Vue components.

Function Description
setModuleFederation Registers the Module Federation instance from host
getModuleFederation Returns the active Module Federation instance from host
loadAsyncComponent Loads a remote Vue component from a Module Federation remote

🧠 Runtime Initialization Model

This library relies on a single Module Federation instance generated by Vite in the host during build, shared across all consumers.

The Module Federation instance from host is registered by linPulsarModuleFederation.init() (see Module Lifecycle), which passes getInstance() (from the @module-federation/enhanced/runtime library) to setModuleFederation(). All subsequent federation operations depend on this instance.

Failure to initialize the runtime before loading federated modules will result in a runtime error.


setModuleFederation

Registers the active Module Federation instance from host for the application.

This function is called by linPulsarModuleFederation.init(): a host using init() must not call it. Call it only when the remotes are not loaded through init(), with the instance returned by the getInstance method from the @module-federation/enhanced/runtime library.

Subsequent calls are ignored to prevent accidental re-initialization.

Signature

setModuleFederation(instance:ModuleFederation): void

Parameters

Parameter Type Description
instance ModuleFederation An initialized Module Federation instance

Behavior

  • Registers the active Module Federation instance globally
  • Ignores re-initialization attempts and logs a warning
  • Required before calling getModuleFederation() or loadAsyncComponent()

getModuleFederation

Returns the active Module Federation instance.

This accessor enforces correct initialization order.

Signature

getModuleFederation(): ModuleFederation;

Throws

  • Error if the runtime has not been initialized via setModuleFederation()

loadAsyncComponent

Loads a remote Vue component from a Module Federation remote using the enhanced runtime.

This helper wraps:

  • Vue’s defineAsyncComponent
  • The Module Federation runtime’s loadRemote() method

It provides a safe, declarative way to consume federated Vue components.

import { loadAsyncComponent } from '@linagora/lin-pulsar-core';

Parameters

Parameter Type Description
plugin string Remote module identifier in the format remoteName/modulePath
Example: 'myRemote/MyComponent'

Returns

Returns a Vue async component that can be:

  • used in templates via <component :is="...">
  • rendered programmatically
  • lazily resolved at runtime

Behavior

  1. Runtime dependency Requires the Module Federation instance to be retrieved via getInstance() and registered with setModuleFederation()

  2. Lazy loading The remote module is fetched only when the component is rendered

  3. ESM compliance Expects the remote module to be an ESM namespace object exposing a default export

  4. Error handling Throws an error if:

    • the remote module is null or undefined
    • the module does not expose a default export
  5. Type safety Expects the remote module to follow the FederatedModule<T> contract


Usage Examples

Basic Usage
<template>
  <component :is="remoteComponent" />
</template>

<script setup lang="ts">
import { loadAsyncComponent } from '@linagora/lin-pulsar-core';
import { ref } from 'vue';
import type { Component } from 'vue';

const remoteComponent = ref<Component | null>(null);

remoteComponent.value = loadAsyncComponent('myRemote/HeaderWidget');
</script>

Dynamic Component Loading
<template>
  <component
    v-for="plugin in plugins"
    :key="plugin"
    :is="loadAsyncComponent(plugin)"
  />
</template>

<script setup lang="ts">
import { loadAsyncComponent } from '@linagora/lin-pulsar-core';

const plugins = ['remote1/ComponentA', 'remote2/ComponentB'];
</script>

Error Handling

Module Not Found

If the remote module cannot be loaded:

const component = loadAsyncComponent('invalidRemote/Component');
// Nothing is thrown here: once the component is mounted, the Module Federation runtime error is reported through Vue's error handling (`onErrorCaptured` / `app.config.errorHandler`)
Missing Default Export

If the module doesn't export a default component:

// Remote module exports: export { MyComponent }
const component = loadAsyncComponent('myRemote/MyComponent');
// Nothing is thrown here: once the component is mounted, the loader rejects with "Failed to load component from myRemote/MyComponent", reported through Vue's error handling (`onErrorCaptured` / `app.config.errorHandler`)

Solution: Ensure your remote module exports a default component:

<!-- ✅ Correct - remote module (.vue file) -->
<template>
  <div>My Component</div>
</template>

<script setup lang="ts">
// Default export automatic with <script setup>
// Nothing special to do!
</script>

Alternative with Options API:

<!-- ✅ Correct - remote module (.vue file) -->
<template>
  <div>My Component</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'MyComponent',
  // ...
});
</script>

❌ Wrong - TypeScript/JavaScript file (not .vue):

// In a .ts file (not .vue)
// ❌ Named export only - doesn't work with loadAsyncComponent
export const MyComponent = defineComponent({
  // ...
});

// ✅ Correct - default export
export default defineComponent({
  name: 'MyComponent',
  // ...
});

TypeScript Support

Remote Module Structure

The function expects remotes to follow this structure:

/**
 * ESM namespace object returned when loading a remote component through
 * Module Federation (for example via `loadRemote`).
 *
 * In a Vite + native ESM environment, federated modules are not auto-unwrapped:
 * the effective export is exposed on the `default` property of the namespace.
 */
interface FederatedModule<Component> {
  /**
   * The default exported Vue component.
   */
  default: Component;
}
Type Inference

The returned component is fully typed as a Vue Component:

import type { Component } from 'vue';

const myComponent: Component = loadAsyncComponent('remote/Component');

⚠️ Common Errors

Error Message Cause
Module Federation is not initialized setModuleFederation() was not called
Failed to load component from <plugin> Missing or invalid default export

🧾 Summary

  • Module Federation runtime is explicitly managed
  • Initialization is mandatory and centralized
  • loadAsyncComponent provides a safe abstraction over loadRemote
  • All federated modules must export a default value

This design guarantees predictable behavior, testability, and runtime safety across all federated front-end modules.


🗄️ Entity Service

The Entity Service provides a standardized CRUD abstraction for backend entity management. Each operation automatically resolves the correct backend endpoint using the module’s instanceId and its registered configuration.

This service builds on top of the HTTP Client Service and the Module Host Configuration Service.

Function Description
saveEntity Creates a new entity on the backend
updateEntity Updates an existing entity
getEntities Retrieves a paginated list of entities
getEntityById Retrieves a single entity by ID
deleteEntityById Deletes a single entity by ID

saveEntity

Creates and persists a new entity using the backend API associated with the module instance.

Parameters

Parameter Type Description
instanceId string Identifier of the module instance
record T Entity data to create

Returns

Type Description
Y The created entity returned by server

Behavior

  1. Automatically resolves the backend endpoint through getModuleHostConfiguration
  2. Issues a POST request with the entity payload
  3. Returns the deserialized response body

Example

const created = await saveEntity<UserInput, User>('users', newUser);

updateEntity

Updates an existing entity in the backend.

Parameters

Parameter Type Description
instanceId string Identifier of the module instance
entityId string The ID of the entity to update
record T New data to update the entity

Returns

Type Description
Y The updated entity returned by server

getEntities

Retrieves a paginated list of entities.

Parameters

Parameter Type Description
instanceId string Identifier of the module instance
filters QueryFilter Filters to apply to the search
pagination Pagination Pagination parameters

Returns

Type Description
Page<T> A page object containing entity items

getEntityById

Fetches a single entity by its unique identifier.

Parameters

Parameter Type Description
instanceId string Identifier of the module instance
entityId string ID of the entity to retrieve

Returns

Type Description
T The retrieved entity content

deleteEntityById

Deletes a single entity by ID.

Parameters

Parameter Type Description
instanceId string Identifier of the module instance
entityId string ID of the entity to delete

Returns

void

Behavior

  1. Resolves endpoint from module configuration
  2. Performs a DELETE HTTP request
  3. Returns an empty Promise

Usage Example

// Create
await saveEntity('users', { name: 'Alice' });

// Update
await updateEntity('users', '42', { name: 'Alice Cooper' });

// Retrieve paginated list
const page = await getEntities<User>(
  'users',
  { search: 'alice' },
  { page: 0, size: 20 }
);

// Retrieve by ID
const user = await getEntityById<User>('users', '42');

// Delete
await deleteEntityById('users', '42');

This service provides a unified, module-aware way to manage backend CRUD operations while relying on shared configuration and HTTP client infrastructure.


🍍 Pinia Store Service

Provides a singleton Pinia store instance shared across all modules, ensuring a unified state management context for the entire application.

Function Description
setPiniaStore Initializes the shared Pinia store (call once)
getPiniaStore Returns the shared Pinia store instance

setPiniaStore

Initializes the shared Pinia store instance to be used by all modules. Should be called once during application bootstrapping by the host application.

import { setPiniaStore } from '@linagora/lin-pulsar-core';
import { createPinia } from 'pinia';

const pinia = createPinia();
setPiniaStore(pinia);

Parameters

Parameter Type Description
store Pinia The Pinia instance to be shared.

Returns

No return value.

Behavior

  1. Singleton: Only the first call sets the instance; subsequent calls log a warning and do not overwrite the instance.
  2. Required: Must be called before any call to getPiniaStore().

Error Handling

  • If called more than once, logs a warning:
    [LinPulsar Core] Pinia store has already been initialized. Re-initialization is ignored.

getPiniaStore

Retrieves the shared Pinia store instance initialized by setPiniaStore.

import { getPiniaStore } from '@linagora/lin-pulsar-core';

const pinia = getPiniaStore();

Parameters

None.

Returns

Type Description
Pinia The shared Pinia instance.

Behavior

  1. Singleton: Always returns the same instance set by setPiniaStore.
  2. Error Handling: Throws an error if called before initialization.

Error Handling

  • If called before initialization, throws:
    [LinPulsar Core] Pinia store is not initialized. Call setPiniaStore() first.

Usage in Store Definitions

All Pinia stores defined in this library (such as useLinPulsarConfigurationStore and useLinPulsarZoneStore) internally use getPiniaStore() to ensure they are attached to the singleton Pinia instance created by the host application.

When defining a new store (either in the host or a module), you should follow the same architecture:

  1. Export a composable function (e.g., useMyStore) that calls your internal store definition with the Pinia instance returned by getPiniaStore().
  2. Do not call defineStore directly in the host/module—always use the exported composable to ensure the correct Pinia context.

Example Pattern

// myStore.ts
import { defineStore } from 'pinia';
import { getPiniaStore } from '@linagora/lin-pulsar-core';

export const useMyStore = () => _useMyStore(getPiniaStore());

const _useMyStore = defineStore('myStore', {
  // state, getters, actions...
});

This ensures your store is always registered on the singleton Pinia instance shared across all modules.


Usage Examples

Basic Usage

import { setPiniaStore, getPiniaStore } from '@linagora/lin-pulsar-core';
import { createPinia } from 'pinia';

// Host application bootstrapping
const pinia = createPinia();
setPiniaStore(pinia);

// In any module
const store = getPiniaStore();

Using a Store

import { useLinPulsarConfigurationStore } from '@linagora/lin-pulsar-core';

const configStore = useLinPulsarConfigurationStore();

Defining and using a Custom Store in a Module

import { defineStore } from 'pinia';
import { getPiniaStore } from '@linagora/lin-pulsar-core';

export const useCustomStore = () => _useCustomStore(getPiniaStore());

const _useCustomStore = defineStore('customStore', {
  // state, getters, actions...
});

Then use it in your module:

import { useCustomStore } from './customStore';

const store = useCustomStore();

TypeScript Support

The functions are fully typed for use with Pinia:

import type { Pinia } from 'pinia';

const pinia: Pinia = getPiniaStore();

The Pinia Store Service ensures all modules share a single, consistent Pinia instance for state management.

Rules

  • ✅ Host calls setPiniaStore() once during boot
  • ✅ Modules use getPiniaStore() to access the same instance
  • ✅ All stores must be defined using a composable that injects the singleton Pinia instance
  • ⚠️ Warning logged if re-initialization is attempted
  • ❌ Error thrown if getPiniaStore() is called before initialization

🧩 Object Service

The Object Service provides utility helpers for manipulating plain JavaScript objects in a predictable and type-safe way. It is designed for JSON-like structures, configuration objects, and data transformation scenarios commonly encountered across modules.

This service focuses on immutability, deep operations, and safe object introspection.

Function Description
merge Performs an immutable deep merge of two plain objects
fromDot Expands dot-notation keys into a nested object structure
getNestedValue Reads the value located at a dot-notation path inside an object
setNestedValue Immutably sets the value located at a dot-notation path
isObject Determines whether a value is a non-null plain object
renameKeys Recursively renames keys of an object using a provided key modifier
deepEqual Performs a deep equality check between two values
deepEqualUnordered Performs a deep equality check ignoring array element order
import {
  merge,
  fromDot,
  getNestedValue,
  setNestedValue,
  isObject,
  renameKeys,
  deepEqual,
  deepEqualUnordered,
} from '@linagora/lin-pulsar-core';

🧠 Design Principles

  • Immutable by default — input objects are never mutated
  • Deep-safe — recursive handling of nested objects
  • JSON-oriented — optimized for plain objects, not class instances
  • Type-safe — relies on unknown and explicit narrowing
  • Framework-agnostic — no external dependencies

merge

Performs a deep merge of two plain objects.

Nested objects are merged recursively, while primitive values from the source overwrite those in the target.

Signature

merge<T extends PlainObject, S extends PlainObject>(
  target: T,
  source: S
): T & S;

Parameters

Parameter Type Description
target PlainObject The base object to merge into
source PlainObject The object whose values override target

Returns

Type Description
T & S A new object containing the merged structure

Behavior

  1. Performs a recursive merge when both values are plain objects
  2. Overwrites primitive values from target with source
  3. Does not mutate either input object
  4. Returns source if either argument is not a plain object

Example

const target = {
  a: {
    b: {
      c: 'x',
      d: 'y',
    },
  },
};

const source = {
  a: {
    d: 'z',
    b: {
      e: 'w',
    },
  },
};

const result = merge(target, source);

/*
{
  a: {
    b: {
      c: 'x',
      d: 'y',
      e: 'w'
    },
    d: 'z'
  }
}
*/

fromDot

Converts an object using dot-notation keys into a nested object structure.

Each segment separated by . represents a deeper level in the resulting object.

Signature

fromDot(obj: PlainObject): PlainObject;

Parameters

Parameter Type Description
obj PlainObject Object with dot-notation keys

Returns

Type Description
PlainObject A new object with nested structure

Behavior

  1. Splits keys using . as a path delimiter
  2. Creates nested objects as needed
  3. Overwrites non-object paths when expanding deeper levels
  4. Preserves immutability of the input object

Example

fromDot({
  'a.b.c': 'value',
  'a.b.d': 'other',
  'a.e': 'test',
});

/*
{
  a: {
    b: {
      c: 'value',
      d: 'other'
    },
    e: 'test'
  }
}
*/

getNestedValue

Reads the value located at a dot-notation path inside an object.

Signature

getNestedValue(obj: PlainObject, path: string): unknown;

Parameters

Parameter Type Description
obj PlainObject The object to read from
path string Dot-notation path of the value (e.g. 'a.b.c')

Returns

Type Description
unknown The value at the given path, or undefined if it cannot be found

Behavior

  1. Splits the path using . as a delimiter
  2. Traverses nested objects segment by segment
  3. Returns undefined when an intermediate value is missing or is not an object
  4. Does not mutate the input object

Example

getNestedValue({ a: { b: { c: 'x' } } }, 'a.b.c'); // 'x'
getNestedValue({ a: { b: 'x' } }, 'a.b.c'); // undefined
getNestedValue({ email: 'john@example.com' }, 'email'); // 'john@example.com'

setNestedValue

Immutably sets the value located at a dot-notation path inside an object.

Signature

setNestedValue(obj: PlainObject, path: string, value: unknown): PlainObject;

Parameters

Parameter Type Description
obj PlainObject The object to update
path string Dot-notation path of the value (e.g. 'a.b.c')
value unknown The value to set at the given path

Returns

Type Description
PlainObject A new object with the value set at the path

Behavior

  1. Splits the path using . as a delimiter
  2. Recreates only the objects along the path, preserving the rest of the structure
  3. Creates missing intermediate objects
  4. Replaces intermediate values that are not objects with objects
  5. Does not mutate the input object

Example

setNestedValue({ a: { b: 'old', c: 'keep' } }, 'a.b', 'new');
// { a: { b: 'new', c: 'keep' } }

setNestedValue({}, 'a.b.c', 'x');
// { a: { b: { c: 'x' } } }

isObject

Determines whether a value is a non-null plain object.

Arrays, null, and primitive values are explicitly excluded.

Signature

isObject(value: unknown): value is PlainObject;

Parameters

Parameter Type Description
value unknown Value to test

Returns

Type Description
boolean true if value is a plain object

Example

isObject({}); // true
isObject([]); // false
isObject(null); // false
isObject('text'); // false

renameKeys

Recursively renames the keys of a plain object using a provided key modifier function.

Signature

renameKeys(obj: object, keyModifier: (key: string) => string): object;

Parameters

Parameter Type Description
obj object The object whose keys will be renamed
keyModifier (key: string) => string Function that receives a key and returns the new key

Returns

Type Description
object A new object with keys renamed recursively

Behavior

  1. Recursively traverses nested objects
  2. Applies keyModifier to every key
  3. Preserves arrays and primitive values as-is
  4. Returns non-object values unchanged
  5. Original object is not mutated

Example

renameKeys({ first_name: 'Alice', address: { city_name: 'Paris' } }, (key) =>
  key.toUpperCase()
);
// { FIRST_NAME: 'Alice', ADDRESS: { CITY_NAME: 'Paris' } }

deepEqual

Performs a deep equality check between two values.

Signature

deepEqual(a: unknown, b: unknown): boolean;

Parameters

Parameter Type Description
a unknown First value to check
b unknown Second value to check

Returns

Type Description
boolean true if values are deeply equal

Behavior

  1. Compares primitive values directly
  2. Recursively compares plain objects and arrays
  3. Returns false if types differ or structures mismatch

Example

deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }); // true
deepEqual([1, 2, 3], [1, 2, 4]); // false

deepEqualUnordered

Performs a deep equality check between two values, ignoring the order of array elements.

Useful for comparing sets of values where order is not significant (e.g. tags, roles, permissions).

Signature

deepEqualUnordered(a: unknown, b: unknown): boolean;

Parameters

Parameter Type Description
a unknown First value to check
b unknown Second value to check

Returns

Type Description
boolean true if values are deeply equal ignoring array order

Behavior

  1. For arrays, checks that both contain the same elements regardless of order
  2. For plain objects, recursively compares properties (also ignoring array order at any nesting level)
  3. For primitives, delegates to deepEqual

Example

deepEqualUnordered([1, 2, 3], [3, 1, 2]); // true
deepEqualUnordered({ tags: ['b', 'a'] }, { tags: ['a', 'b'] }); // true
deepEqualUnordered([1, 2], [1, 2, 3]); // false

🧾 Summary

  • merge enables safe, immutable deep merging of configuration objects
  • fromDot simplifies transformation from flat, dot-based keys to nested objects
  • isObject provides a reliable runtime guard for plain object detection
  • renameKeys — recursive key renaming
  • deepEqual — deep equality checks (order-sensitive for arrays)
  • deepEqualUnordered — deep equality checks ignoring array element order

This service is intended as a foundational utility layer for configuration handling, data normalization, and object manipulation across the application.


🖨️ Nunjucks Service

Provides a singleton Nunjucks Environment instance shared across all modules, ensuring a unified template rendering context for the entire application.

Function Description
setNunjucksEnv Initializes the shared Nunjucks environment (call once)
getNunjucksEnv Returns the shared Nunjucks environment instance

setNunjucksEnv

Initializes the shared Nunjucks Environment instance to be used by all modules. Should be called once during application bootstrapping by the host application.

import { setNunjucksEnv } from '@linagora/lin-pulsar-core';
import nunjucks from 'nunjucks';

const env = new nunjucks.Environment();
setNunjucksEnv(env);

Parameters

Parameter Type Description
env Environment The Nunjucks Environment instance to share.

Returns

No return value.

Behavior

  1. Singleton: Only the first call sets the instance; subsequent calls log a warning and do not overwrite the instance.
  2. Required: Must be called before any call to getNunjucksEnv().

Error Handling

  • If called more than once, logs a warning:
    [LinPulsar Core] Nunjucks environment has already been initialized. Re-initialization is ignored.

getNunjucksEnv

Retrieves the shared Nunjucks Environment instance initialized by setNunjucksEnv.

import { getNunjucksEnv } from '@linagora/lin-pulsar-core';

const env = getNunjucksEnv();

Parameters

None.

Returns

Type Description
Environment The shared Nunjucks Environment instance.

Behavior

  1. Singleton: Always returns the same instance set by setNunjucksEnv.
  2. Error Handling: Throws an error if called before initialization.

Error Handling

  • If called before initialization, throws:
    [LinPulsar Core] Nunjucks environment is not initialized. Call setNunjucksEnv() first.

Usage Examples

Application Bootstrap

import { setNunjucksEnv } from '@linagora/lin-pulsar-core';
import nunjucks from 'nunjucks';

// Host application bootstrapping
const env = new nunjucks.Environment();
setNunjucksEnv(env);

Rendering a Template

import { getNunjucksEnv } from '@linagora/lin-pulsar-core';

const env = getNunjucksEnv();
const result = env.renderString('Hello {{ name }}!', { name: 'Alice' });
// result: "Hello Alice!"

Adding a Custom Filter

Filters specific to the host application can be added after initialization:

import { setNunjucksEnv, getNunjucksEnv } from '@linagora/lin-pulsar-core';
import nunjucks from 'nunjucks';

const env = new nunjucks.Environment();
env.addFilter('uppercase', (str: string) => str.toUpperCase());
setNunjucksEnv(env);

TypeScript Support

import type { Environment } from 'nunjucks';

const env: Environment = getNunjucksEnv();

Rules

  • ✅ Host calls setNunjucksEnv() once during boot
  • ✅ Modules use getNunjucksEnv() to access the same instance
  • ⚠️ Warning logged if re-initialization is attempted
  • ❌ Error thrown if getNunjucksEnv() is called before initialization

📅 Dayjs Service

Provides a singleton Dayjs instance shared across all modules.

Function Description
setDayjsInstance Initializes the shared Dayjs instance (call once)
getDayjsInstance Returns the shared Dayjs instance

setDayjsInstance

Initializes the shared Dayjs instance to be used by all modules. Should be called once during application bootstrapping, after loading all required Dayjs plugins (e.g. customParseFormat).

import { setDayjsInstance } from '@linagora/lin-pulsar-core';

Parameters

Parameter Type Description
instance typeof dayjs The Dayjs function to be shared.

Returns

No return value.

Behavior

  1. Singleton: Only the first call sets the instance; subsequent calls log a warning and do not overwrite the instance.
  2. Required: Must be called before any call to getDayjsInstance() (or any composable that uses it, such as useDayjs).

getDayjsInstance

Retrieves the shared Dayjs instance initialized by setDayjsInstance.

import { getDayjsInstance } from '@linagora/lin-pulsar-core';

Parameters

None.

Returns

Type Description
typeof dayjs The shared Dayjs instance.

Behavior

  1. Singleton: Always returns the same instance set by setDayjsInstance.
  2. Error Handling: Throws an error if called before initialization.

Usage Examples

Basic Usage

import { setDayjsInstance } from '@linagora/lin-pulsar-core';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';

// Load required plugins before registering the instance
dayjs.extend(customParseFormat);

// Host application bootstrapping
setDayjsInstance(dayjs);

Error Handling

Calling getDayjsInstance before initialization
import { getDayjsInstance } from '@linagora/lin-pulsar-core';

try {
  const dayjs = getDayjsInstance();
  // Throws: [LinPulsar Core] Dayjs instance is not initialized. Call setDayjsInstance() first.
} catch (error) {
  // Handle error
}
Attempting to re-initialize
import { setDayjsInstance } from '@linagora/lin-pulsar-core';
import dayjs from 'dayjs';

setDayjsInstance(dayjs);
// Second call logs a warning and does not overwrite the instance
setDayjsInstance(dayjs);
// Logs: [LinPulsar Core] Dayjs instance has already been initialized. Re-initialization is ignored.

Rules

  • ✅ Host calls setDayjsInstance() once during boot, after extending dayjs with required plugins
  • ✅ Modules use getDayjsInstance() (or composables such as useDayjs) to access the same instance
  • ⚠️ Warning logged if re-initialization is attempted
  • ❌ Error thrown if getDayjsInstance() is called before initialization

🧩 Local Component Service

Declares which components can be referenced by name by a zone entry, and resolves those names at render time.

Zone entries of type component carry a name rather than a component reference, which allows them to be declared from a module configuration file. This service is what turns such a name back into a component.

Function Description
registerLocalComponent Makes a component available to zones under a given name
registerLocalComponents Makes several components available to zones at once
getLocalComponent Returns the value registered under a given name, if any
resolveLocalComponent Resolves the component registered under a given name

registerLocalComponent

Makes a component available to zones under the given name.

Components are stored with markRaw, so they are kept out of the reactivity system. Registering a name that is already known overrides the previous component.

import { registerLocalComponent } from '@linagora/lin-pulsar-core';
import UserRolesCard from './components/UserRolesCard.vue';

registerLocalComponent('UserRolesCard', UserRolesCard);

registerLocalComponents

Makes several components available to zones at once, typically to declare the whole zone surface of an application in a single call during bootstrap.

import { registerLocalComponents } from '@linagora/lin-pulsar-core';
import { QBanner, QSeparator } from 'quasar';

registerLocalComponents({
  QBanner,
  QSeparator,
});

getLocalComponent

Returns the value registered under the given name, or undefined when the name is unknown.

Unlike resolveLocalComponent, unknown names are not resolved against globally registered components and no warning is logged: the caller is expected to treat undefined as an error. This is what the module configuration loading uses to resolve component references (lifecycleRemote, routesRemote, i18nRemote, route components), where an unregistered name must fail loudly instead of falling back.

import { getLocalComponent } from '@linagora/lin-pulsar-core';

// Returns the registered value
getLocalComponent('UserRolesCard');

// Returns undefined
getLocalComponent('UnknownCard');

resolveLocalComponent

Resolves the component registered under the given name.

Unknown names are not an error: a warning listing the registered names is logged, and the name is returned as-is so that Vue still attempts to resolve it against globally registered components.

import { resolveLocalComponent } from '@linagora/lin-pulsar-core';

// Returns the registered component
resolveLocalComponent('UserRolesCard');

// Logs a warning and returns 'UnknownCard'
resolveLocalComponent('UnknownCard');

Rules

  • ✅ Host declares its zone components once during boot, before any zone is rendered
  • ✅ Zone entries and module configuration files reference those components by name
  • ⚠️ Warning logged when resolving an unknown name, which falls back to global resolution
  • ❌ Never called manually by consumers: LinPulsarZoneRenderer resolves entries on its own

🏷️ Configuration Service

Fetches the entity and API endpoint configurations exposed by the backend, through the shared HTTP client.

Function Description Endpoint
getEntitiesConfiguration Returns the configuration of all entities GET /metadata/entities
getEntityConfiguration Returns the configuration of one entity GET /metadata/entities/:entityId
getApiEndpointsConfiguration Returns the configuration of all API endpoints GET /metadata/routes

The endpoints are relative to the base URL of the HTTP client shared with setHttpClient. The results are typed with LinPulsarEntityConfiguration and LinPulsarApiEndpointConfiguration. Components usually read these configurations from useLinPulsarConfigurationStore, which calls this service.


getEntitiesConfiguration

import { getEntitiesConfiguration } from '@linagora/lin-pulsar-core';

const entities = await getEntitiesConfiguration();

getEntityConfiguration

import { getEntityConfiguration } from '@linagora/lin-pulsar-core';

const accounts = await getEntityConfiguration('accounts');

getApiEndpointsConfiguration

import { getApiEndpointsConfiguration } from '@linagora/lin-pulsar-core';

const endpoints = await getApiEndpointsConfiguration();

🧭 Module Configuration Service

Stores the host configuration (ModuleHostConfig) of each module instance, indexed by its instanceId.

Function Description
registerModuleHostConfiguration Registers the configuration of a module instance
getModuleHostConfiguration Returns the configuration of a module instance

registerModuleHostConfiguration

Registers a module host configuration. A configuration with the same instanceId is overwritten.

This is done by linPulsarModuleFederation.init() during the SETUP phase for every module configuration file, so it only needs to be called manually for a module that is not loaded through init().

import { registerModuleHostConfiguration } from '@linagora/lin-pulsar-core';

registerModuleHostConfiguration(config);

getModuleHostConfiguration

Returns the configuration registered for an instanceId, typed with the module-specific options. Throws an error when no configuration is registered for it.

import { getModuleHostConfiguration } from '@linagora/lin-pulsar-core';

const config =
  getModuleHostConfiguration<MyModuleOptions>('moduleAccountsPage');

📣 UI Event Service

Exposes uiEventSubject, a shared RxJS Subject acting as the event bus for all UI events across the host and the remotes. Each event carries a key identifying its kind and a free data payload:

export interface UiEvent {
  key: string;
  data: unknown;
}
Key Payload Emitted by Documentation
'notify' QNotifyCreateOptions useNotify Notifications
Key passed to useDialog DialogEvent The code opening or closing the dialog Dialogs
import { uiEventSubject } from '@linagora/lin-pulsar-core';

uiEventSubject.subscribe((event) => {
  if (event.key === 'notify') {
    // ...
  }
});

🔗 Other Shared Services

The following shared services are documented with the composables that use them:

Service Functions Documentation
i18n setI18nInstance, getI18nInstance i18n
UI design setUiDesign, getUiDesign UI design