
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@aegis-framework/artemis
Advanced tools
Artemis is a lightweight JavaScript/TypeScript library providing common utilities for web development including DOM manipulation, storage wrappers, HTTP requests, and platform detection.
# Using npm
npm install @aegis-framework/artemis
# Using yarn
yarn add @aegis-framework/artemis
# Using bun
bun add @aegis-framework/artemis
import { $_, Text, Space, SpaceAdapter } from '@aegis-framework/artemis';
<script src="path/to/artemis.browser.js"></script>
<script>
const { $_, Text, Space, SpaceAdapter } = Artemis;
</script>
jQuery-like DOM manipulation with a modern API.
import { $_, $_ready, $_create } from '@aegis-framework/artemis';
$_ready(() => {
// Select and manipulate elements
$_('h1').text('Hello World').addClass('title');
// Chained operations
$_('.card')
.addClass('active')
.style({ 'background-color': '#fff', 'padding': '1rem' })
.fadeIn(400);
// Event handling
$_('button').click((e) => {
console.log('Button clicked!');
});
// Event delegation
$_('ul').on('click', 'li', (e) => {
console.log('List item clicked:', e.target);
});
// Removing event handlers
const handler = (e) => console.log('Clicked!');
$_('button').on('click', handler);
$_('button').off('click', handler); // Remove specific handler
$_('button').off('click'); // Remove all click handlers
$_('button').off(); // Remove all handlers
// Delegated handler removal
$_('ul').off('click', 'li'); // Remove all delegated 'li' handlers
$_('ul').off('click', 'li', handler); // Remove specific delegated handler
// Create new elements
const div = $_create('div', { class: 'container', id: 'main' });
// Traversal
$_('.item').parent().addClass('has-item');
$_('.item').parents(); // All ancestors
$_('.item').siblings().removeClass('active');
$_('.item').children();
$_('.item').next().addClass('following');
$_('.item').prev().addClass('previous');
$_('.item').closest('.wrapper');
$_('.item').find('.child');
// Visibility
$_('#box').hide();
$_('#box').show(); // Default: display 'block'
$_('#box').show('flex'); // Custom display value
$_('#box').isVisible(); // true if any element in collection is visible
// Animations
$_('#box').fadeOut(400, () => console.log('Hidden'));
$_('#box').fadeIn(400, () => console.log('Visible'));
$_('#box').animate(
[{ transform: 'scale(1)' }, { transform: 'scale(1.2)' }],
{ duration: 300, fill: 'forwards' }
);
// Dimensions and position
const width = $_('#box').width();
const height = $_('#box').height();
const pos = $_('#box').offset(); // { top, left } relative to document
// Scroll
$_('#section').scrollIntoView({ behavior: 'smooth' });
});
| Method | Description |
|---|---|
hide() | Set display: none |
show(display?) | Set display (default: 'block') |
addClass(name) | Add a class |
removeClass(name?) | Remove class, or all classes if no arg |
toggleClass(names) | Toggle space-separated classes |
hasClass(name) | Check if all elements have class |
text(value?) | Get/set text content |
html(value?) | Get/set HTML content |
value(value?) | Get/set form element value |
attribute(name, value?) | Get/set attribute |
removeAttribute(name) | Remove an attribute |
hasAttribute(name) | Check if all elements have attribute |
data(name, value?) | Get/set data attributes |
removeData(name) | Remove a data attribute |
style(prop, value?) | Get/set inline styles (also accepts object) |
property(name, value?) | Get/set DOM properties |
on(event, callback) | Add event listener |
on(event, selector, callback) | Add delegated event listener |
off(event?, selectorOrCallback?, callback?) | Remove event listener(s) |
trigger(event, detail?) | Dispatch event (CustomEvent if detail provided) |
click(callback) | Shortcut for on('click', callback) |
keyup(callback) | Shortcut for on('keyup', callback) |
keydown(callback) | Shortcut for on('keydown', callback) |
submit(callback) | Shortcut for on('submit', callback) |
change(callback) | Shortcut for on('change', callback) |
scroll(callback) | Shortcut for on('scroll', callback) |
input(callback) | Shortcut for on('input', callback) |
find(selector) | Find descendants |
closest(selector) | Find closest ancestor |
filter(selector) | Filter collection by selector |
matches(selector) | Check if all elements match selector |
parent() | Get parent elements |
parents() | Get all ancestors |
children() | Get child elements |
siblings() | Get sibling elements |
next() | Get next sibling |
prev() | Get previous sibling |
first() | Get first element as new DOM instance |
last() | Get last element as new DOM instance |
eq(index) | Get element at index (negative counts from end) |
get(index) | Get raw HTMLElement at index |
each(callback) | Iterate with (element, index) callback |
exists() | Check if collection is non-empty |
append(content) | Append HTML string or Element |
prepend(content) | Prepend HTML string or Element |
after(content) | Insert HTML after each element |
before(content) | Insert HTML before each element |
remove() | Remove elements from DOM |
empty() | Remove all children |
clone(deep?) | Clone elements (default: deep) |
replaceWith(content) | Replace elements with HTML string or Element |
reset() | Reset form elements |
focus() | Focus the first element |
blur() | Blur the first element |
isVisible() | Check if any element is visible |
offset() | Get { top, left } relative to document |
width() | Get width of first element |
height() | Get height of first element |
fadeIn(duration?, callback?) | Fade in (default: 400ms) |
fadeOut(duration?, callback?) | Fade out (default: 400ms) |
animate(keyframes, options) | Web Animations API |
scrollIntoView(options?) | Scroll first element into view |
Storage wrapper with namespacing, versioning, and multiple backend adapters.
import { Space, SpaceAdapter } from '@aegis-framework/artemis';
// LocalStorage adapter
const storage = new Space(SpaceAdapter.LocalStorage, {
name: 'MyApp',
version: '1.0.0'
});
await storage.open();
// Basic operations
await storage.set('user', { name: 'John', age: 30 });
const user = await storage.get('user');
await storage.update('user', { age: 31 }); // Merges with existing
await storage.remove('user');
await storage.clear();
// Get all data
const allData = await storage.getAll();
const keys = await storage.keys();
// Get key by index (LocalStorage/SessionStorage only)
const firstKey = await storage.key(0);
// Iterate
await storage.each((key, value) => {
console.log(key, value);
});
// Check existence (resolves if exists, rejects if not)
await storage.contains('user');
// Callbacks
storage.onCreate((key, value) => console.log('Created:', key));
storage.onUpdate((key, value) => console.log('Updated:', key));
storage.onDelete((key, value) => console.log('Deleted:', key));
// Transformations (modify data on get/set)
storage.addTransformation({
id: 'timestamps',
set: (key, value) => ({ ...value, updatedAt: Date.now() }),
get: (key, value) => value
});
storage.removeTransformation('timestamps');
// Read/update configuration
const config = storage.configuration();
storage.configuration({ name: 'NewName' });
// Rename the space (LocalStorage/SessionStorage only)
await storage.rename('NewName');
LocalStorage - Persistent browser storage
new Space(SpaceAdapter.LocalStorage, { name: 'App', version: '1.0.0' });
SessionStorage - Session-only storage (no version upgrades)
new Space(SpaceAdapter.SessionStorage, { name: 'App' });
IndexedDB - Large-scale structured storage (requires name, version, and store)
new Space(SpaceAdapter.IndexedDB, {
name: 'App',
version: '1.0.0',
store: 'users',
props: { keyPath: 'id', autoIncrement: true },
index: {
email: { name: 'Email Index', field: 'email', props: { unique: true } }
}
});
RemoteStorage - REST API backend
new Space(SpaceAdapter.RemoteStorage, {
name: 'App',
version: '1.0.0',
endpoint: 'https://api.example.com/',
store: 'users'
});
const storage = new Space(SpaceAdapter.LocalStorage, {
name: 'App',
version: '2.0.0'
});
// Define upgrades before opening
await storage.upgrade('1.0.0', '2.0.0', async (adapter) => {
const oldData = await adapter.get('config');
await adapter.set('config', { ...oldData, newField: 'value' });
});
await storage.open(); // Upgrades run automatically
import {
LocalStorageKeyNotFoundError,
IndexedDBKeyNotFoundError,
RemoteStorageKeyNotFoundError
} from '@aegis-framework/artemis';
try {
await storage.get('missing-key');
} catch (error) {
if (error instanceof LocalStorageKeyNotFoundError) {
console.log('Key not found in LocalStorage');
}
}
HTTP client built on the Fetch API with timeout support and error handling.
import { Request, RequestError, RequestTimeoutError } from '@aegis-framework/artemis';
// GET request
const response = await Request.get('https://api.example.com/users', {
page: 1,
limit: 10
});
// POST with JSON
const created = await Request.postJson('https://api.example.com/users', {
name: 'John',
email: 'john@example.com'
});
// PUT, PATCH, DELETE
await Request.put(url, data);
await Request.patch(url, data);
await Request.delete(url);
// HEAD request
const headResponse = await Request.head(url);
// Check if resource exists (HEAD + check status)
const exists = await Request.exists('https://api.example.com/users/1');
// With timeout
const data = await Request.json('https://api.example.com/slow', {}, {
timeout: 5000 // 5 seconds
});
// Different response types
const json = await Request.json(url);
const text = await Request.text(url);
const blob = await Request.blob(url);
const buffer = await Request.arrayBuffer(url);
// Custom headers
await Request.post(url, data, {
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/json'
}
});
// Error handling
try {
const data = await Request.json(url);
} catch (error) {
if (error instanceof RequestError) {
console.log('HTTP Error:', error.status, error.statusText);
console.log('Response:', error.response);
} else if (error instanceof RequestTimeoutError) {
console.log('Request timed out');
}
}
// Serialize data to query string
Request.serialize({ name: 'John', tags: ['a', 'b'] });
// 'name=John&tags[]=a&tags[]=b'
Request.serialize({ filter: { status: 'active' } });
// 'filter[status]=active'
| Method | Description |
|---|---|
get(url, data?, options?) | GET request (data appended as query params) |
post(url, data, options?) | POST request |
put(url, data, options?) | PUT request |
patch(url, data, options?) | PATCH request |
delete(url, data?, options?) | DELETE request (data as query params) |
head(url, data?, options?) | HEAD request (data as query params) |
json<T>(url, data?, options?) | GET and parse JSON response |
postJson<T>(url, data, options?) | POST with JSON body, parse JSON response |
blob(url, data?, options?) | GET and return as Blob |
text(url, data?, options?) | GET and return as text |
arrayBuffer(url, data?, options?) | GET and return as ArrayBuffer |
exists(url, options?) | Check if URL returns 2xx (HEAD request) |
serialize(data, prefix?) | Serialize object to query string |
Form filling and value retrieval utilities. Forms are identified by their data-form attribute.
<form data-form="UserForm">
<input type="text" name="username">
<input type="email" name="email">
<input type="number" name="age">
<input type="checkbox" name="newsletter">
<select name="country">
<option value="us">USA</option>
<option value="uk">UK</option>
</select>
</form>
import { Form } from '@aegis-framework/artemis';
// Fill form with data
Form.fill('UserForm', {
username: 'john_doe',
email: 'john@example.com',
age: 30,
newsletter: true,
country: 'us'
});
// Get form values with type parsing
const values = Form.values('UserForm', {
parseNumbers: true, // Parse number inputs as numbers (default: true)
parseBooleans: true // Parse single checkboxes as booleans (default: true)
});
// { username: 'john_doe', email: '...', age: 30, newsletter: true, country: 'us' }
// Reset form
Form.reset('UserForm');
// Validation
if (Form.isValid('UserForm')) {
// Submit form
}
// Show validation messages
Form.reportValidity('UserForm');
Platform and feature detection using matchMedia and NavigatorUAData where available.
import { Platform } from '@aegis-framework/artemis';
// Desktop detection
Platform.desktop(); // true if any desktop
Platform.desktop('macOS'); // true if macOS
Platform.desktop('Windows'); // true if Windows
Platform.desktop('Linux'); // true if Linux (excludes Android)
Platform.desktop('ChromeOS'); // true if ChromeOS
Platform.desktop('FreeBSD'); // true if FreeBSD
// Mobile detection
Platform.mobile(); // true if any mobile
Platform.mobile('iOS'); // true if iPhone/iPod
Platform.mobile('iPadOS'); // true if iPad (detects modern iPads reporting as macOS)
Platform.mobile('Android'); // true if Android
Platform.mobile('WindowsMobile'); // true if Windows Phone
Platform.mobile('BlackBerry'); // true if BlackBerry
// Display
Platform.orientation; // 'portrait' | 'landscape'
Platform.portrait; // true if portrait
Platform.landscape; // true if landscape
Platform.retina; // true if high DPI display (devicePixelRatio >= 2)
// Input capabilities
Platform.touch; // true if touch supported
Platform.canHover; // true if hover supported
Platform.coarsePointer; // true if touch is primary input
Platform.finePointer; // true if mouse/trackpad is primary input
// User preferences
Platform.darkMode; // true if dark mode preferred
Platform.reducedMotion; // true if reduced motion preferred
// Runtime environment
Platform.standalone; // true if installed PWA
Platform.electron; // true if Electron
Platform.electrobun; // true if Electrobun
Platform.desktopApp; // true if Electron or Electrobun
Platform.cordova; // true if Cordova/PhoneGap
// Feature support
Platform.serviceWorkers; // true if service workers available (requires secure context)
Text transformation utilities.
import { Text } from '@aegis-framework/artemis';
// Capitalize words
Text.capitalize('hello world'); // 'Hello World'
Text.capitalize('API docs', { preserveCase: true }); // 'API Docs'
// URL-friendly slug
Text.friendly('Hello World!'); // 'hello-world'
Text.friendly('Café Münich'); // 'cafe-munich'
// Truncate with ellipsis
Text.truncate('Long text here', 10); // 'Long te...'
Text.truncate('Long text', 10, '…'); // 'Long text' (under limit, returned as-is)
Text.truncate('Hello', 2, '...'); // '..' (maxLength < ellipsis, truncates ellipsis)
// Extract parts of a string
Text.prefix('@', 'user@example.com'); // 'user'
Text.suffix('@', 'user@example.com'); // 'example.com'
// Check for blank
Text.isBlank(''); // true
Text.isBlank(' '); // true
Text.isBlank(null); // true
Text.isBlank('text'); // false
// Get selected text in the document
const selected = Text.selection();
File operations and utilities.
import { FileSystem } from '@aegis-framework/artemis';
// Read local file (from file input, drag and drop, etc.)
const text = await FileSystem.read(file, 'text');
const base64 = await FileSystem.read(file, 'base64'); // Returns data URL
const buffer = await FileSystem.read(file, 'buffer'); // Returns ArrayBuffer
const binary = await FileSystem.read(file, 'binary'); // Returns binary string
// Read remote file
const content = await FileSystem.readRemote('https://example.com/file.txt', 'text');
// Create and download files
const newFile = FileSystem.create('data.json', JSON.stringify(data), 'application/json');
FileSystem.download(newFile);
FileSystem.download(blob, 'custom-name.txt');
// File type checks
FileSystem.isImage('photo.jpg'); // true (jpg, jpeg, png, gif, svg, webp, avif, bmp, ico, tiff, heic)
FileSystem.isVideo('movie.mp4'); // true (mp4, webm, ogg, mov, avi, mkv, m4v)
FileSystem.isAudio('song.mp3'); // true (mp3, wav, ogg, flac, aac, m4a, wma)
// File extension
FileSystem.extension('file.txt'); // 'txt'
FileSystem.extension('.gitignore'); // '' (hidden files return empty by default)
FileSystem.extension('.gitignore', true); // 'gitignore' (allowHiddenFiles)
FileSystem.extension('archive.tar.gz'); // 'gz'
// Human-readable file size
FileSystem.humanSize(1536); // '1.5 KB'
FileSystem.humanSize(1048576); // '1 MB'
General utilities.
import { Util } from '@aegis-framework/artemis';
// Generate UUID v4
const id = Util.uuid(); // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
// Ensure async execution
await Util.callAsync(someFunction, context, arg1, arg2);
// Debounce (delay until quiet period)
const debouncedSearch = Util.debounce((query) => {
fetch(`/search?q=${query}`);
}, 300);
input.addEventListener('input', (e) => debouncedSearch(e.target.value));
// Throttle (limit call frequency, leading edge only)
const throttledScroll = Util.throttle(() => {
console.log('Scroll position:', window.scrollY);
}, 100);
window.addEventListener('scroll', throttledScroll);
Conditional console logging with debug levels. All console methods are gated behind a configurable level so debug output can be silenced in production.
import { Debug, DebugLevel } from '@aegis-framework/artemis';
// Set debug level
Debug.setLevel(DebugLevel.DEBUG); // Show all logs
Debug.setLevel(DebugLevel.ERROR); // Only errors
Debug.setLevel(DebugLevel.NONE); // Silent
// Levels: NONE (0) < ERROR (1) < WARNING (2) < INFO (3) < DEBUG (4) < ALL (5)
// Alternative getter/setter
Debug.level(DebugLevel.INFO); // Set level, returns current
const current = Debug.level(); // Get current level
const level = Debug.currentLevel; // Getter for current level
// Logging (respects debug level)
Debug.log('General log'); // DEBUG+
Debug.debug('Debug info'); // DEBUG+
Debug.info('Information'); // INFO+
Debug.warning('Warning!'); // WARNING+
Debug.warn('Also warning'); // WARNING+ (alias)
Debug.error('Error!'); // ERROR+
// Assertions (ERROR+)
Debug.assert(value > 0, 'Value must be positive');
// Stack trace (DEBUG+)
Debug.trace('Trace point');
// Grouping (DEBUG+)
Debug.group('Network requests');
Debug.log('Request 1');
Debug.log('Request 2');
Debug.groupEnd();
Debug.groupCollapsed('Collapsed group');
Debug.log('Hidden by default');
Debug.groupEnd();
// Tables (DEBUG+)
Debug.table([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }]);
// Timing (DEBUG+)
Debug.time('Operation');
// ... do work ...
Debug.timeLog('Operation', 'still running...');
// ... more work ...
Debug.timeEnd('Operation'); // Logs: Operation: 123.45ms
// Counting (DEBUG+)
Debug.count('myFunction called'); // myFunction called: 1
Debug.count('myFunction called'); // myFunction called: 2
Debug.countReset('myFunction called');
// Object inspection (DEBUG+)
Debug.dir(complexObject);
Debug.dirxml(htmlElement);
// Clear console (DEBUG+)
Debug.clear();
// Check level before expensive operations
if (Debug.isEnabled(DebugLevel.DEBUG)) {
Debug.log(JSON.stringify(largeObject));
}
// Format strings (no level check, pure utility)
const msg = Debug.format('User %s has %d items: %j', 'Alice', 3, ['a', 'b']);
// 'User Alice has 3 items: ["a","b"]'
// Supports: %s (string), %d/%i (integer), %o/%O (JSON), %j (JSON), %c (ignored), %% (literal %)
Asset preloading utilities for images, audio, fonts, stylesheets, scripts, and generic files.
import { Preload } from '@aegis-framework/artemis';
// Preload and decode a single image (ready to render without delay)
const img = await Preload.image('/assets/hero.jpg');
document.body.appendChild(img);
// Preload multiple images in parallel
const images = await Preload.images([
'/assets/1.jpg',
'/assets/2.jpg',
'/assets/3.jpg'
]);
// Preload and decode audio into an AudioBuffer
const audioCtx = new AudioContext();
const buffer = await Preload.audio('/sounds/click.mp3', audioCtx);
// Without providing a context (a temporary one is created and closed automatically)
const buffer2 = await Preload.audio('/sounds/alert.mp3');
// Preload multiple audio files (shares a single AudioContext for efficiency)
const buffers = await Preload.audios(
['/sounds/a.mp3', '/sounds/b.mp3'],
audioCtx
);
// Preload generic files with fetch priority hint
await Preload.file('/data/config.json', 'high');
await Preload.files(['/a.js', '/b.js'], 'low');
// Preload specific asset types (preloads only, does not apply/execute)
await Preload.stylesheet('/styles/main.css');
await Preload.script('/js/vendor.js');
await Preload.font('/fonts/custom.woff2'); // crossOrigin: true by default
await Preload.font('/fonts/local.woff2', false); // No crossOrigin
// Cache API integration
const isCached = await Preload.isCached('my-cache', '/assets/image.jpg');
await Preload.addToCache('my-cache', '/assets/image.jpg');
await Preload.addAllToCache('my-cache', ['/a.js', '/b.js', '/c.css']);
Artemis is written in TypeScript and includes full type definitions.
import {
// DOM
$_, $_ready, $_create,
DOM,
// Storage
Space, SpaceAdapter,
LocalStorage, SessionStorage, IndexedDB, RemoteStorage,
// Storage errors
LocalStorageKeyNotFoundError,
IndexedDBKeyNotFoundError,
RemoteStorageKeyNotFoundError,
// HTTP
Request, RequestError, RequestTimeoutError,
// Utilities
Platform, Text, FileSystem, Form, Util,
Debug, DebugLevel,
Preload
} from '@aegis-framework/artemis';
// Type imports
import type {
// DOM types
DOMSelector, DOMOffset, StyleProperties,
EventCallback, ElementCallback,
// Storage types
SpaceConfiguration, StorageValue, KeyValueResult,
UpgradeCallback, SpaceAdapterType, SpaceAdapterConstructor,
SpaceCallback, Transformation, TransformationFunction,
// Request types
RequestData, RequestOptions,
// Platform types
DesktopPlatform, MobilePlatform, Orientation,
// Form types
FormValue, FormValues, FormParseOptions,
// FileSystem types
FileReadType, FileReadResult,
// Text types
CapitalizeOptions,
// Util types
Callable
} from '@aegis-framework/artemis';
MIT License - See LICENSE for details.
FAQs
Aegis Framework Javascript Library
We found that @aegis-framework/artemis demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.