npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@linked-planet/loader-tools

v0.9.0

Published

Tools for to loader apps and observe fields

Readme

Loader-Tools Library

This library is to ease the replacement of fields in the Jira UI / Service Desk UI and also contains a loader to easily load custom app modules.

Usage

Installation and Setup

npm i -D @linked-planet/loader-tools

It is important that the final bundle also includes all requirements as Jira bundles everything in one huge batch.js and cannot load outside modules.

Either use the AppLoader to load an app directly, use the Observer tools to observe and handle field changes, or use registerReplaceElementObserver or replaceElement to replace HTMLElements in pages.

At first, a plugin definition is required, which follows the PluginSettings type:

export const pluginSettings: PluginSettings = {
    pluginName: "plugin-name",
    pluginGroupId: "com.linked-planet.plugin.jira"
}

The plugin settings and the app name are used by the App Loader to load the app, and by the Config tools to get the REST API URL, which can be retrieved using Config.getRestApiBaseUrl(pluginSettings), and looks like:

const restApiBaseUrl = `${window.location.protocol}//${window.location.host}/rest/${pluginSettings.pluginName}/1.0`

BatchJS and Jira Resources

The ultimate goal is to produce a Javascript Loader bundle which can be bundled by Jira. This means the Javascript bundle is copied by the plugin package process to target/classes/js and included from there in the batch.js of Jira.

This has certain requirements:

  • must be a commonJS bundle
  • is not allowed to have any exports

Tools

Several categories of tools are available.

Configs

The Config tools provide the option to fetch and cache configs from a set REST endpoint and provide that globally by using getProjectConfig with the help of the plugin settings, which are used to determine the correct endpoint address for the Jira plugin. The REST baseURL can also be obtained by getRestApiBaseUrl. In many cases we do not need a project config in the observer and app loading.

App Loader

The app loader loads an application. The method for it is initFrontendApp, and it requires the plugin settings and the app name. It automatically determines which mode the app runs in (dev, dev-productionbundle, production). Optionally, a starter function can be provided, which is used to start the app. This is only required if the app does not start when the bundle loading is complete.

Observer

The observer tools are a simple library to ease the creation of MutationObservers on DOM elements, and to organize and handle callbacks used on mutation events of observed elements.

A simple use case could be:

Observer.registerObserverCallback("#content", () => console.log("content was modified"))
Observer.registerObserverCallback("#content", () => console.log("content was modified, callback 2"))
Observer.registerObserver("#content", {
    childList: true,
    subtree: false
})

This can be used in combination with the App Loader to replace Jira fields with custom apps.

Handling Of Fields

Jira recreates elements, which renders connected mutation observers useless and potentially leaks memory. A way around this is to use a mutation observer on a high-level element (i.e. body), which does not get recreated, and re-register the mutation observers using the corresponding selectors of the elements. The Observer library will automatically disconnect old mutation observers and create new ones if the underlying element has changed.

Replacing Fields

In most cases we want to replace a field rendered by Jira with a custom element. For this the registerReplaceElementObserver is a one stop solution.

registerReplaceElementObserver("#content", {
    target: () =>
        document.querySelector<HTMLElement>(selector),
	replacement:
        reactAppDiv, // The replacement element MUST have an ID
	cssBundleSelector:
		"link[rel='stylesheet'][data-lp-css-bundle='true']",
	shouldSkipReplacement: () => {
		return (
			ServiceDesk.isCurrentPageInServiceDeskPortal(
				serviceDeskPortalId
			) ?? false
		)
	},
	onReplacementComplete: () => {
		// add a bit more bottom margin to the help text
		const element =
			document.querySelector<HTMLElement>(selector)
		const divs = element.getElementsByTagName("div")
		for (const div of divs) {
			if (div.id.endsWith("-helper")) {
				div.style.marginBottom = "14px"
			}
		}
	}
})

Fields

The Fields part includes helper utilities to handle fields.

Utils

Utils currently only contain the frontend mode indicator handling, which displays a small hint on the bottom right corner of a page if the app is in dev or dev-productionbundle mode.

Documentation Overview

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools v0.9.0

Namespaces

Interfaces

Type Aliases

Variables

Functions

API Documentation

AppLoader Namespace

@linked-planet / namespaces / AppLoader / type-aliases / InitFrontendAppOptions

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / AppLoader / InitFrontendAppOptions

Type Alias: InitFrontendAppOptions

InitFrontendAppOptions = InitFrontendAppOptions

Defined in: loader/index.ts:11


@linked-planet / namespaces / AppLoader / variables / initFrontendApp

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / AppLoader / initFrontendApp

Variable: initFrontendApp()

const initFrontendApp: (options) => void = _initFrontendApp

Defined in: loader/index.ts:10

Initializes and loads a frontend application module with automatic mode detection and DOM readiness handling.

This is the main entry point for loading frontend applications. It:

  • Automatically detects the appropriate loading mode based on hostname (localhost = development, others = production)
  • Handles DOM readiness states (waits for DOMContentLoaded if needed)
  • Sets up Shadow DOM if requested
  • Manages fallback loading strategies (dev → dev-productionbundle → production)

Parameters

options

InitFrontendAppOptions

Configuration object for frontend app initialization

Returns

void

Throws

Error if appName is not provided

Throws

Error if pluginSettings are not provided

Throws

Error if no valid app root element can be found

Example

'''typescript
initFrontendApp({
  appName: "my-react-app",
  pluginSettings: { pluginName: "my-plugin", pluginGroupId: "com.example" },
  developmentPort: 3000,
  appRootElementSelector: "#app-container",
  useShadowRoot: true,
  starterFunc: (rootElement) => {
    console.log("App loaded in:", rootElement);
  }
});
'''

Fields Namespace

@linked-planet / namespaces / Fields / type-aliases / ReplaceElementObserverOptions

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / ReplaceElementObserverOptions

Type Alias: ReplaceElementObserverOptions

ReplaceElementObserverOptions = ReplaceElementObserverOptions

Defined in: fields/index.ts:20


@linked-planet / namespaces / Fields / variables / getCustomFieldIDByLabel

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / getCustomFieldIDByLabel

Variable: getCustomFieldIDByLabel()

const getCustomFieldIDByLabel: (labelText) => string | null = _getCustomFieldIDByLabel

Defined in: fields/index.ts:15

Get a custom field's ID by its visible label text, automatically handling optional field suffixes. Supports both and wrapping label patterns.

If a matching form control element doesn't have an ID, one will be generated and assigned.

Parameters

labelText

string

The text content of the label to search for (automatically checks with and without "(optional)" suffix)

Returns

string | null

The ID of the associated input/select/textarea element, or null if not found

Example

// Finds labels with text "Priority" or "Priority (optional)"
const fieldId = getCustomFieldIDByLabel("Priority");
if (fieldId) {
  const field = document.getElementById(fieldId);
}

@linked-planet / namespaces / Fields / variables / registerReplaceElementObserver

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / registerReplaceElementObserver

Variable: registerReplaceElementObserver()

const registerReplaceElementObserver: (observingElementSelector, options) => void = _registerReplaceElementObserver

Defined in: fields/index.ts:16

Registers a DOM observer that monitors for changes and triggers element replacement when conditions are met.

This function sets up a MutationObserver that watches for DOM changes on the specified element and automatically calls replaceElement when changes occur. It handles both immediate execution (if DOM is ready) and deferred execution (waiting for DOMContentLoaded).

Parameters

observingElementSelector

string

CSS selector for the element to observe for DOM changes

options

ReplaceElementObserverOptions

Configuration object for element replacement (same as replaceElement options)

Returns

void


@linked-planet / namespaces / Fields / variables / replaceElement

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / replaceElement

Variable: replaceElement()

const replaceElement: (options) => void = _replaceElement

Defined in: fields/index.ts:18

Replaces an element in the DOM with a replacement element and manages CSS bundle state.

This function handles the common pattern of:

  1. Finding a target element in the DOM
  2. Checking if a replacement element is already present (by checking if the replacement element has an id)
  3. Managing CSS bundle enabling/disabling based on element presence
  4. Optionally executing additional operations after the replacement

The original element is hidden rather than removed, and the replacement is inserted before it.

Parameters

options

ReplaceElementObserverOptions

Configuration object for element replacement

Returns

void

Example

const reactApp = document.createElement("div");
reactApp.id = "my-react-app";

replaceElement({
  target: () => document.querySelector(".original-field"),
  shouldSkipReplacement: () => {
    return document.querySelector(".original-field") === null;
  },
  replacement: reactApp,
  cssBundleSelector: "link[data-plugin-name='my-plugin-name'][data-app-name='my-app-name']",
  onReplacementComplete: () => {
    console.log("Replacement completed");
  }
});

@linked-planet / namespaces / Fields / variables / replaceInputWithSpan

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / replaceInputWithSpan

Variable: replaceInputWithSpan()

const replaceInputWithSpan: (id, replacementId) => boolean = _replaceInputWithSpan

Defined in: fields/index.ts:13

Replaces form input elements within a field group with read-only span elements containing their current values. Also hides any buttons within the field group.

Parameters

id

The ID of a custom field element within the target field group (null values are handled gracefully)

string | null

replacementId

string

The ID to assign to the replacement span elements

Returns

boolean

True if the field group was found and processed successfully, false otherwise

Example

'''typescript
const success = replaceInputWithSpan("custom-field-123", "readonly-display-123");
if (success) {
  console.log("Field successfully converted to read-only");
}
'''

@linked-planet / namespaces / Fields / variables / setInputToDate

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Fields / setInputToDate

Variable: setInputToDate()

const setInputToDate: (fieldSelector, date, dateFormat) => void = _setInputToDate

Defined in: fields/index.ts:14

Sets the value of an input element to a formatted date string.

Parameters

fieldSelector

string

CSS selector for the input element to update

date

Date

The Date object to format and set as the input value

dateFormat

string

The format string pattern (supports DD, MM, YYYY tokens, e.g., "DD.MM.YYYY")

Returns

void

Example

setInputToDate("#my-date-field", new Date(), "DD.MM.YYYY");

ServiceDesk Namespace

@linked-planet / namespaces / ServiceDesk / variables / isCurrentPageInServiceDeskPortal

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / ServiceDesk / isCurrentPageInServiceDeskPortal

Variable: isCurrentPageInServiceDeskPortal()

const isCurrentPageInServiceDeskPortal: (serviceDeskPortalId) => boolean = _isCurrentPageInServiceDeskPortal

Defined in: servicedesk/index.ts:4

Validates if the Service Desk portal ID in the current URL matches the expected portal ID.

Extracts the portal ID from the URL path and compares it with the provided Service Desk portal ID. The portal ID is expected to be the 5th segment in the URL path (index 4).

Parameters

serviceDeskPortalId

number

The expected Service Desk portal ID to validate against

Returns

boolean

True if portal IDs match or if portal ID not found in URL, false if they don't match

Example

// URL: /servicedesk/customer/portal/123/...
const result = isCurrentPageInServiceDeskPortal(123); // returns true
const result2 = isCurrentPageInServiceDeskPortal(456); // returns false

Interfaces

interfaces / InitFrontendAppOptions

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / InitFrontendAppOptions

Interface: InitFrontendAppOptions

Defined in: loader/app-loader.ts:9

Configuration options for initializing a frontend application module.

Properties

appName

appName: string | (mode) => string

Defined in: loader/app-loader.ts:13

The name identifier of the frontend module to load.


appRootElement?

optional appRootElement: HTMLElement | ShadowRoot | null

Defined in: loader/app-loader.ts:23

The shadow root to use for the app. If not provided, the app will be loaded into the document head.


appRootElementSelector?

optional appRootElementSelector: string

Defined in: loader/app-loader.ts:21

The element ID containing the app, if not provided, the app will be loaded into the document head, else it is loaded as shadow DOM attached to this element.


cssBundleFile?

optional cssBundleFile: string

Defined in: loader/app-loader.ts:33

The file name of the css bundle to load.

Default

${pluginSettings.pluginName}.css

Example

cssBundleFile: "my-plugin.css"

developmentPort

developmentPort: number

Defined in: loader/app-loader.ts:19

The port to use for the development server.


mode?

optional mode: FrontendModeType

Defined in: loader/app-loader.ts:17

The mode to load the module in.


pluginSettings

pluginSettings: PluginSettings

Defined in: loader/app-loader.ts:11

The settings of the plugin.


starterFunc()?

optional starterFunc: (appRootElement?) => void

Defined in: loader/app-loader.ts:15

The function to call once the module's script has loaded successfully.

Parameters

appRootElement?

HTMLElement | ShadowRoot

Returns

void


useShadowRoot?

optional useShadowRoot: boolean

Defined in: loader/app-loader.ts:25

Whether to use a shadow root for the app.


interfaces / PluginSettings

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / PluginSettings

Interface: PluginSettings

Defined in: configs/project-config.ts:6

Configuration interface for plugin settings containing Maven artifact information.

Properties

pluginGroupId

pluginGroupId: string

Defined in: configs/project-config.ts:10

The Maven group ID of the plugin providing the module.


pluginName

pluginName: string

Defined in: configs/project-config.ts:8

The Maven artifact ID (plugin name) of the plugin.


interfaces / ProjectConfig

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / ProjectConfig

Interface: ProjectConfig

Defined in: configs/project-config.ts:18

Base interface for project configuration. Extend this interface to add project-specific settings.

Properties

serviceDeskPortalId?

optional serviceDeskPortalId: number

Defined in: configs/project-config.ts:19


Type Aliases

type-aliases / FrontendModeType

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / FrontendModeType

Type Alias: FrontendModeType

FrontendModeType = "production" | "development" | "dev-productionbundle"

Defined in: utils/frontend-mode.ts:17


type-aliases / ObserverCallback

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / ObserverCallback

Type Alias: ObserverCallback()

ObserverCallback = (mutations?) => void

Defined in: observer/observer.ts:8

Callback type for MutationObserver events.

Parameters

mutations?

MutationRecord[]

Optional array of mutation records that occurred

Returns

void


type-aliases / ReplaceElementObserverOptions

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / ReplaceElementObserverOptions

Type Alias: ReplaceElementObserverOptions

ReplaceElementObserverOptions = object

Defined in: fields/replace-element.ts:104

Configuration options for element replacement operations.

Example


const replacement = document.createElement("div");
replacement.id = "my-react-app";

const options: ReplaceElementObserverOptions = {
  target: () => document.querySelector("#my-field"),
  replacement,
  cssBundleSelector: "link[data-my-css='true']",
  shouldSkipReplacement: () => false,
  onReplacementComplete: () => console.log("Done")
};

Properties

cssBundleSelector?

optional cssBundleSelector: string

Defined in: fields/replace-element.ts:109

Optional CSS selector for the CSS bundle link element to enable/disable


onReplacementComplete()?

optional onReplacementComplete: () => void

Defined in: fields/replace-element.ts:110

Optional callback function to execute additional operations during replacement

Returns

void


replacement

replacement: HTMLElement & object | () => HTMLElement & object

Defined in: fields/replace-element.ts:106

The HTML element that will replace the target element, requires an Id


shouldSkipReplacement()?

optional shouldSkipReplacement: () => boolean

Defined in: fields/replace-element.ts:111

Optional function to check if replacement should proceed; if returns true, replacement is skipped

Returns

boolean


target

target: string | () => HTMLElement | null

Defined in: fields/replace-element.ts:105

Function that returns the target HTML element to be replaced, or a CSS selector string


Variables

variables / Config

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Config

Variable: Config

const Config: object

Defined in: configs/project-config.ts:92

Type Declaration

getProjectConfig()

getProjectConfig: <T>(projectConfigUrl, pluginSettings) => Promise<T>

Fetches and caches the project configuration from the REST API.

The configuration is cached globally after the first successful fetch. Subsequent calls return the cached configuration without making additional requests.

Type Parameters

T

T extends ProjectConfig

Parameters

projectConfigUrl

string

The URL path to fetch config from (absolute, relative to REST base, or sub-path)

pluginSettings

PluginSettings

Plugin configuration for constructing the full REST API URL

Returns

Promise<T>

Promise resolving to the project configuration

Throws

Error if projectConfigUrl is empty or if the fetch request fails

Example

'''typescript
const config = await getProjectConfig<MyProjectConfig>(
  "/project/PROJ",
  { pluginName: "my-plugin", pluginGroupId: "com.example" }
);
'''

getRestApiBaseUrl()

getRestApiBaseUrl: (pluginSettings) => string

Constructs the REST API base URL using plugin settings and current window location.

Parameters

pluginSettings

PluginSettings

Plugin configuration containing Maven artifact information

Returns

string

The base URL for REST API calls (e.g., "https://example.com/rest/my-plugin/1.0")

Throws

Error if pluginSettings is not provided


variables / FrontendMode

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / FrontendMode

Variable: FrontendMode

const FrontendMode: object

Defined in: utils/frontend-mode.ts:80

Utility namespace for managing frontend development modes.

Type Declaration

getFrontendMode()

getFrontendMode: () => FrontendModeType

Retrieves the current frontend development mode.

Returns

FrontendModeType

The currently active frontend mode

setFrontendMode()

setFrontendMode: (devMode) => void

Sets the frontend development mode and displays a visual indicator.

Updates the global frontend mode and shows a non-intrusive indicator in the bottom-right corner for non-production modes. The indicator appears after a 500ms delay to prevent flickering.

Parameters

devMode

FrontendModeType

The frontend mode to set (production, development, or dev-productionbundle)

Returns

void


variables / Observer

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Observer

Variable: Observer

const Observer: object

Defined in: observer/observer.ts:164

Type Declaration

hasObserver()

hasObserver: (selector) => boolean

Checks whether an observer is currently registered for the given selector.

Parameters

selector

string

CSS selector to check

Returns

boolean

True if an observer exists for this selector, false otherwise

registerObserver()

registerObserver: (selector, observerOptions) => null | undefined

Creates and registers a MutationObserver for the specified element.

Note: If you need to change observer options, unregister the existing observer first.

Parameters

selector

string

CSS selector for the target element (only the first matching element is observed)

observerOptions

MutationObserverInit

MutationObserver configuration options

Returns

null | undefined

The created MutationObserver instance, or null if element not found or observer already exists

See

https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit for observerOptions details

registerObserverCallback()

registerObserverCallback: (selector, cb) => void

Registers a callback function to be executed when DOM mutations occur on the specified element.

Parameters

selector

string

CSS selector for the target element to observe

cb

ObserverCallback

Callback function to execute when mutations are detected

Returns

void

unregisterObserver()

unregisterObserver: (selector, removeCallbacks) => void

Disconnects and removes an observer for the specified element.

Parameters

selector

string

CSS selector for the target element

removeCallbacks

boolean = true

Whether to also remove all registered callbacks for this selector (default: true)

Returns

void

unregisterObserverCallback()

unregisterObserverCallback: (selector, cb) => void

Removes a previously registered callback from the observer system.

Parameters

selector

string

CSS selector for the target element

cb

ObserverCallback

The specific callback function to unregister

Returns

void


variables / Utils

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / Utils

Variable: Utils

const Utils: object

Defined in: utils/index.ts:6

Type Declaration

fetchFromPlugin()

fetchFromPlugin: <T>(url, pluginSettings, options?) => Promise<T>

Fetches data from a plugin's REST API endpoint with automatic URL construction and error handling.

Automatically constructs the full REST API URL using the plugin settings and handles JSON response parsing and HTTP error checking.

Type Parameters

T

T

Parameters

url

string

The REST API endpoint path (can be absolute, relative to REST base, or sub-path)

pluginSettings

PluginSettings

Plugin configuration containing Maven artifact information for URL construction

options?

RequestInit

Optional fetch request configuration (headers, method, body, etc.)

Returns

Promise<T>

Promise resolving to the parsed JSON response of type T

Throws

Error if the HTTP response is not ok (status >= 400)

Example

interface UserData { id: number; name: string; }

const userData = await fetchFromPlugin<UserData>(
  "/users/123",
  { pluginName: "my-plugin", pluginGroupId: "com.example" },
  { method: "GET" }
);

getLoggerPrefix()

getLoggerPrefix: () => string

Get the logger suffix [LOADER-TOOLS-].

Returns

string

The logger suffix.

setLoggerSuffix()

setLoggerSuffix: (suffix) => void

Set the logger suffix (adds this as postfix to [LOADER-TOOLS]).

Parameters

suffix

string

The suffix to set (adds this as postfix to [LOADER-TOOLS]).

Returns

void


Functions

functions / getLoggerPrefix

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / getLoggerPrefix

Function: getLoggerPrefix()

getLoggerPrefix(): string

Defined in: utils/logger-prefix.ts:15

Get the logger suffix [LOADER-TOOLS-].

Returns

string

The logger suffix.


functions / initFrontendApp

@linked-planet/loader-tools v0.9.0


@linked-planet/loader-tools / initFrontendApp

Function: initFrontendApp()

initFrontendApp(options): void

Defined in: loader/app-loader.ts:436

Initializes and loads a frontend application module with automatic mode detection and DOM readiness handling.

This is the main entry point for loading frontend applications. It:

  • Automatically detects the appropriate loading mode based on hostname (localhost = development, others = production)
  • Handles DOM readiness states (waits for DOMContentLoaded if needed)
  • Sets up Shadow DOM if requested
  • Manages fallback loading strategies (dev → dev-productionbundle → production)

Parameters

options

InitFrontendAppOptions

Configuration object for frontend app initialization

Returns

void

Throws

Error if appName is not provided

Throws

Error if pluginSettings are not provided

Throws

Error if no valid app root element can be found

Example

'''typescript
initFrontendApp({
  appName: "my-react-app",
  pluginSettings: { pluginName: "my-plugin", pluginGroupId: "com.example" },
  developmentPort: 3000,
  appRootElementSelector: "#app-container",
  useShadowRoot: true,
  starterFunc: (rootElement) => {
    console.log("App loaded in:", rootElement);
  }
});
'''