AGL.js
is a modern framework for integrating web applications with Epic’s Active Guidelines (AGL) platform. Built with TypeScript but fully usable with modern JavaScript, it abstracts the low-level details of AGL communication while remaining lightweight and flexible.
The framework streamlines interaction between web applications and AGL, including:
- Confirming whether the web app is running inside Epic
- Facilitating communication with the Epic backend
- Triggering and queuing actions in Hyperspace
- Subscribing to and handling AGL events
- Managing application state and navigation history
- Getting Started
- Configuration
- Usage
- Parameters
- Methods
- Queuing
- Error Handling
- Subscriptions
- State Management
- Navigation History
AGL.js
is not affiliated with or endorsed by Epic Systems Corporation. It is intended solely for use by organizations with active Epic licenses. Please ensure compliance with your Epic agreements before using or distributing this library.
For modern JavaScript/TypeScript projects, install via npm or yarn:
# npm
npm install agljs
# yarn
yarn add agljs
import AGL from 'agljs'
const agl = new AGL()
(async () => {
if (await agl.active) {
console.log(agl.active) //Logs true (boolean)
}
})()
import AGL from 'agljs/js/agl.js';
(async () => {
const agl = new AGL();
if (await agl.active) {
console.log(agl.details.availableActions); //Logs available AGL actions
}
})();
- Download the latest release from the GitHub repository.
- Include
agl.min.js
in your project:
<script src="path/to/agl.min.js"></script>
<script>
const agl = new AGL();
//For browsers that don't support async/await
Promise.resolve(agl.active).then(function(active) {
console.log(agl.active); //Logs true or false (boolean)
});
</script>
Add AGL.js
from a CDN, such as jsDelivr:
<script src="https://cdn.jsdelivr.net/npm/agljs/js/agl.min.js"></script>
<script>
const agl = new AGL();
//For browsers that don't support async/await
Promise.resolve(agl.active).then(function(active) {
if (active) {
console.log(agl.details.availableActions); //Logs available AGL actions
}
});
</script>
The AGL
constructor accepts an optional configuration object with the following optional parameters:
Parameter | Type | Description | Default |
---|---|---|---|
debug |
boolean |
Enables debug logging for troubleshooting | false |
timeout |
number |
Timeout (in milliseconds) before failure for action responses | 2000 |
subscribe |
object |
An object containing events to subscribe to during the handshake | {} |
onError |
function |
Callback for handling errors | null |
onNavigate |
function |
Callback for navigation events (e.g., back/forward) | null |
onReload |
function |
Callback for reload events | null |
onAGLEvent |
function |
Callback for custom AGL events | null |
onSubscribed |
function |
Callback for handling subscription results | null |
const agl = new AGL({
debug: true,
timeout: 5000,
subscribe: {
"Epic.Common.RequestToCloseApp": { PauseDuration: 300 },
},
onError: (error) => console.error('Error:', error),
onNavigate: (event) => console.log('Navigation event:', event.direction),
onReload: (event) => console.log('Reload event:', event.state),
});
All actions must wait for the AGL handshake to complete and for the framework to confirm it is running inside Epic. This is done by checking if (await agl.active)
.
Example:
const agl = new AGL();
if (await agl.active) {
agl.do('Epic.Clinical.Informatics.Web.SaveState', { state: 'example' })
.then((success) => console.log('State saved:', success))
.catch((err) => console.error('Error saving state:', err));
}
Example:
const agl = new AGL();
if (!await agl.active) throw new Error('AGL is inactive! We aren't in Epic!');
agl.on('navigate', (event) => {
console.log('User navigated:', event.direction);
});
Indicates whether the AGL.js
framework is active and initialized.
- If
AGL.js
is already initialized, returnstrue
orfalse
. - If
AGL.js
is still initializing, returns a promise that resolves totrue
orfalse
.
Usage:
Before performing any actions, ensure AGL.js
has completed initialization:
Example:
if (await agl.active) {
console.log('AGL is initialized and ready.');
}
For subsequent checks (once initialization is complete), you can use agl.active
without await
:
if (await agl.active) {
console.log(agl.active ? '✔ AGL is active.' : '❌ AGL is inactive.');
}
Provides information from Epic about the current AGL context. Properties include:
Property | Type | Description |
---|---|---|
availableActions |
string[] |
List of actions supported in the current context |
currentState |
string |
State identifier restored during hibernation |
interfaceVersion |
string |
Version of the AGL JavaScript interface |
readOnly |
boolean |
Indicates if the AGL context is read-only |
token |
string |
Token required for posting messages to Epic |
Any additional properties returned by Epic will also be available.
Example:
console.log('Token:', agl.details.token);
console.log('Available actions:', agl.details.availableActions.join(', '));
Enables or disables debug logging dynamically
Example:
agl.debug = true; // Enable debug logging
Executes an action within AGL.
Parameter | Type | Description |
---|---|---|
action |
string |
The name of the action to perform |
args |
object |
Optional Arguments to pass with the action |
haltOnError |
boolean |
Optional Stops the queue if this action fails, preventing subsequent actions from running |
Example: Basic Action
if (await agl.active) {
agl.do('SaveState', { state: 'example' });
}
For convenience, actions without a full namespace (i.e., actions without a dot .) are automatically prefixed with Epic.Clinical.Informatics.Web
.
If you need to override this behavior for specific actions, you can include the full namespace directly:
Example: Full Namespace Action
if (await agl.active) {
agl.do('Custom.Namespace.Action', { someArg: 'value' });
}
Registers a callback for specific AGL events.
Parameter | Type | Description |
---|---|---|
eventName |
string |
The name of the event to listen for |
callback |
function |
The callback function to execute when the event occurs |
Event Name | Example Response |
---|---|
error |
{ message: 'Error description', details: ['Detail 1', ...] } |
navigate |
{ direction: 'Back' } |
reload |
{ state: 'State string', fromHibernation: boolean } |
aglEvent |
{ name: 'Event name', args: { arg1: ... } |
subscribed |
{ EventName: 'Event name', SubscriptionSuccess: boolean } |
Example:
agl.on('navigate', (event) => {
console.log('User navigated:', event.direction);
});
Example: Chaining listeners
agl
.on('navigate', (event) => console.log('User navigated:', event.direction))
.on('reload', (event) => console.log('User reloaded:', event.state));
Actions are added to a queue and executed sequentially to avoid race conditions.
By default, the queue continues processing even if an action fails.
However, setting haltOnFail in do
to true
stops the queue when that action fails or times out, ensuring dependent actions are not executed.
Example:
agl.do('SaveState', { state: 'example' }, true) // Stop queue if SaveState fails
.catch((error) => console.error('SaveState failed:', error));
agl.do('CloseActivity', null) // Will only execute if SaveState succeeds
.catch((error) => console.error('CloseActivity failed:', error));
The library provides built-in error handling. If an error event listener is not provided, errors will default to logging in the browser's console. If an error event listener is set, the callback will handle all errors and override default behavior.
You can override internal error handling by providing a custom onError
callback in the configuration.
Example: During instantiation
const agl = new AGL({
onError: (error) => {
console.error('Custom error handler:', error.message);
},
});
Example: After instantiation
agl.on('error', (error) => {
console.error('Custom error handler:', error.message);
});
You can specify subscriptions during initialization using the subscribe
property. Subscriptions allow your application to register for specific events, such as changes in patient demographics or requests to close the app.
To confirm that each subscription was successfully processed during the handshake, you can define an onSubscribed
callback. This callback receives the subscription results, allowing you to verify whether your subscriptions were accepted by Epic.
Example:
const agl = new AGL({
subscribe: {
"Epic.Patient.Demographics.Updated": { IncludeHistory: true },
"Epic.Common.RequestToCloseApp": { PauseDuration: 200 }
},
onSubscribed: (results) => {
console.log('Subscription handshake completed:', results);
},
});
After subscriptions are successfully established, your application can handle the events triggered by those subscriptions using the aglEvent handler.
Example:
agl.on('aglEvent', (event) => {
if (event.name === 'Epic.Patient.Demographics.Updated') {
console.log('Patient demographics updated:', event.args);
}
if (event.name === 'Epic.Common.RequestToCloseApp') {
agl.do('Epic.Common.CloseApp', {
CanClose: true
});
}
});
AGL.js
supports saving and restoring app state during transitions or hibernation.
To save the current state of your application, use the SaveState
action.
Example: Simple string
agl.do('SaveState', { state: 'dashboard' });
Example: Complex state
agl.do('SaveState', {
state: JSON.stringify({ tab: 'overview', filters: { active: true } })
});
To restore the state during a reload event, listen for the reload
event and activate the page accordingly.
Example:
agl.on('reload', (event) => {
if (event.state) {
const restoredState = JSON.parse(event.state);
console.log('Restored state:', restoredState);
activatePage(restoredState.tab, restoredState.filters);
}
});
The library supports managing navigation history, tracking user interactions with Back/Forward buttons, and maintaining app-specific navigation states.
Use onNavigate
to track Back/Forward button clicks.
Example:
agl.on('navigate', ({ direction }) => {
console.log(`Navigated: ${direction}`);
// Handle navigation here
});
Save custom navigation states using SaveHistoryState
. AGL.js
will persist these states for the current session.
Example:
agl.do('Epic.Clinical.Informatics.Web.SaveHistoryState', {
state: JSON.stringify({ tab: 'dashboard', filters: { active: true } })
});
Disable Back/Forward buttons in unsupported contexts.
Example:
agl.do('Epic.Clinical.Informatics.Web.SetEnabledHistBtns', {
Back: false,
Forward: false
});
Contributions are welcome! Please fork the repository, create a new branch, and submit a pull request.
This project operates under the Kopimi ethos. It is free to use, modify, and share. However, if the code itself is used, borrowed, modified, or incorporated into a commercial product or service, proper attribution is required.