A TypeScript SDK for the Common Use Self-Service version 2 (CUSS2) platform that facilitates developing applications for airline self-service kiosks.
CUSS2-ts provides a robust TypeScript interface to interact with a CUSS2 platform, enabling developers to create applications for self-service check-in, self-tagging, and self bag-drop terminals in the airline industry. This SDK handles WebSocket communication, OAuth authentication, platform state management, and provides a clean API for interacting with various peripheral devices.
Get started with CUSS2-ts in your browser in under 5 minutes:
<!DOCTYPE html>
<html>
<head>
<title>CUSS2 Quick Start</title>
</head>
<body>
<h1>CUSS2 Platform Connection</h1>
<button id="connect">Connect</button>
<div id="status"></div>
<!-- Load CUSS2-ts from JSR (via esm.sh) -->
<script type="module">
import { Cuss2 } from "https://esm.sh/jsr/@cuss/cuss2-ts@latest";
document.getElementById("connect").addEventListener("click", async () => {
const status = document.getElementById("status");
status.textContent = "Connecting...";
// Connect to your CUSS2 platform (uses default URL http://localhost:22222)
const cuss2 = Cuss2.connect(
"your-client-id", // Replace with your client ID
"your-client-secret", // Replace with your client secret
// Optional: provide custom WebSocket URL as 3rd parameter
// "wss://your-platform.example.com"
);
// Wait for connection
await cuss2.connected;
status.textContent = `Connected! State: ${cuss2.state}`;
// List available components
console.log("Available components:", Object.keys(cuss2.components));
// Request state transitions
await cuss2.requestInitializeState();
await cuss2.requestUnavailableState();
status.textContent = `State: ${cuss2.state}`;
});
</script>
</body>
</html>- Download the latest bundle:
curl -O https://jsr.io/@cuss/cuss2-ts/latest/dist/cuss2.min.js- Include it in your HTML:
<!DOCTYPE html>
<html>
<head>
<title>CUSS2 Quick Start</title>
</head>
<body>
<script src="cuss2.min.js"></script>
<script>
// Connect to the platform (uses default URL http://localhost:22222)
const cuss2 = Cuss2.connect(
"your-client-id", // Replace with your client ID
"your-client-secret", // Replace with your client secret
// Optional: provide custom WebSocket URL as 3rd parameter
// "wss://your-platform.example.com"
);
// Wait for connection and interact
cuss2.connected.then(async () => {
console.log("Connected to CUSS2 platform!");
console.log("Environment:", cuss2.environment);
console.log("Components:", cuss2.components);
// Work with components
if (cuss2.barcodeReader) {
await cuss2.barcodeReader.enable();
cuss2.barcodeReader.on("data", (data) => {
console.log("Barcode scanned:", data);
});
}
});
</script>
</body>
</html>Check out the basic browser example for a complete working demo with connection status, logging, and error handling.
import { Cuss2 } from "jsr:@cuss/cuss2-ts@latest";
const cuss2 = Cuss2.connect(
"client-id",
"client-secret",
// Optional parameters:
// "wss://cuss-platform.example.com", // Custom WebSocket URL (default: https://localhost:22222)
// "device-id", // Device ID (default: "00000000-0000-0000-0000-000000000000")
// "https://oauth.example.com/token" // OAuth token URL (default: wss location)
);
// Wait for connection to be established
await cuss2.connected;
// Now cuss2.environment and cuss2.components are populated
console.log("Environment:", cuss2.environment);
// Request state transitions
await cuss2.requestInitializeState();
await cuss2.requestUnavailableState();
await cuss2.requestAvailableState();
// Work with components
if (cuss2.barcodeReader) {
await cuss2.barcodeReader.enable();
cuss2.barcodeReader.on("data", (data) => {
console.log("Barcode scanned:", data);
});
}- Complete TypeScript Support: Fully typed interfaces for all CUSS2 components and responses
- WebSocket Communication: Manages WebSocket lifecycle with automatic reconnection
- OAuth Authentication: Handles OAuth token acquisition and refresh
- State Management: Easily transition through application states (STOPPED, INITIALIZE, UNAVAILABLE, AVAILABLE, ACTIVE)
- Component Management: Interface with 30+ peripheral device types
- Event-Driven Architecture: Subscribe to events for state changes, component updates, and device data
- Automatic State Synchronization: Automatically manages state based on required component availability
- Connection Resilience: Built-in reconnection with exponential backoff
- Deno-First Development: Built specifically for the Deno runtime
CUSS2 applications transition through defined states:
STOPPED: Application is stoppedINITIALIZE: Initial startup stateUNAVAILABLE: Application is loaded but not available for passenger useAVAILABLE: Application is ready for passenger useACTIVE: Application is actively being used by a passenger
State transitions follow specific rules:
- From
STOPPED→INITIALIZEonly - From
INITIALIZE→UNAVAILABLEonly - From
UNAVAILABLE→AVAILABLEor back toINITIALIZE - From
AVAILABLE→ACTIVEorUNAVAILABLE - From
ACTIVE→AVAILABLEorUNAVAILABLE - From any state →
STOPPED
const cuss2 = Cuss2.connect(clientId, clientSecret, wss, deviceId, tokenUrl);
// Basic usage with defaults:
const cuss2 = Cuss2.connect("EAI", "secret");
// Connection events
cuss2.connection.on("connecting", (attempt) => console.log("Connecting...", attempt));
cuss2.connection.on("authenticating", (attempt) => console.log("Authenticating...", attempt));
cuss2.connection.on("authenticated", (auth) => console.log("Authenticated", auth));
cuss2.connection.on("open", () => console.log("WebSocket open"));
cuss2.connection.on("close", () => console.log("WebSocket closed"));
cuss2.connection.on("error", (error) => console.error("Connection error", error));
// Wait for connection to be ready
await cuss2.connected;// Request state transitions
await cuss2.requestInitializeState(); // From STOPPED
await cuss2.requestUnavailableState(); // From INITIALIZE, AVAILABLE, or ACTIVE
await cuss2.requestAvailableState(); // From UNAVAILABLE or ACTIVE
await cuss2.requestActiveState(); // From AVAILABLE or ACTIVE
await cuss2.requestStoppedState(); // From any state
// Listen for state changes
cuss2.on("stateChange", (stateChange) => {
console.log(`State changed from ${stateChange.previous} to ${stateChange.current}`);
});
// Get current state
console.log("Current state:", cuss2.state);
// Special state transitions
cuss2.on("activated", async (activationData) => {
console.log("Application activated", activationData);
console.log("Multi-tenant mode:", cuss2.multiTenant);
console.log("Accessible mode:", cuss2.accessibleMode);
console.log("Language:", cuss2.language);
// Acknowledge accessible mode if active
if (cuss2.accessibleMode) {
// Configure UI for accessibility (larger fonts, screen reader support, etc.)
await configureAccessibleUI();
// Acknowledge to platform that application is ready for accessible operation
await cuss2.acknowledgeAccessibleMode();
console.log("Accessible mode acknowledged");
}
});
cuss2.on("deactivated", (newState) => {
console.log("Application deactivated, new state:", newState);
});When the platform requests the application to operate in accessible mode, the activated event will fire with accessibleMode set to true. The application should:
- Configure the UI for accessibility (larger fonts, high contrast, screen reader support, etc.)
- Acknowledge receipt of the accessible mode request using
acknowledgeAccessibleMode()
cuss2.on("activated", async (activationData) => {
if (cuss2.accessibleMode) {
console.log("Application activated in accessible mode");
// Configure UI for accessibility
enableHighContrastMode();
enableScreenReader();
increaseFontSizes();
// Acknowledge to platform that we're ready
await cuss2.acknowledgeAccessibleMode();
}
});The library provides two ways to acknowledge accessible mode:
cuss2.acknowledgeAccessibleMode()- Convenience method with validation (recommended)- Only sends acknowledgment when in ACTIVE state and accessibleMode is true
- Throws an error if conditions aren't met
- Automatically sends the current application state with the acknowledgment
cuss2.api.acknowledgeAccessible()- Raw API method- Sends the directive regardless of state (for advanced use cases)
- Includes current
applicationStateCode,accessibleMode, and reason code in the payload
The SDK supports all CUSS2 peripheral types:
Printers
BagTagPrinter- Prints bag tagsBoardingPassPrinter- Prints boarding passes
Data Readers
BarcodeReader- Reads 1D/2D barcodesCardReader- Reads magnetic stripe and chip cardsDocumentReader- Reads travel documentsRFID- Reads RFID tags
Input Devices
Keypad- Physical or virtual keypadsBiometric- Fingerprint/facial recognitionCamera- Image capture
Baggage Handling
Scale- Weighs baggageInsertionBelt- Bag drop insertionVerificationBelt- Bag verificationParkingBelt- Bag parking positionBHS- Baggage handling system interfaceAEASBD- Automated equipment for self bag drop
Output/Feedback
Announcement- Audio announcementsIllumination- LED indicatorsHeadset- Audio output device
Printer Support Components
Feeder- Paper/card feeder for printersDispenser- Output dispenser for printers
Components are automatically discovered during initialization:
// Access specific component types directly
const reader = cuss2.barcodeReader;
const printer = cuss2.boardingPassPrinter;
// Access all components via the components collection
const components = cuss2.components; // Record<string, Component>
// Iterate through all components
Object.entries(components).forEach(([id, component]) => {
console.log(`Component ${id}:`, {
type: component.constructor.name,
ready: component.ready,
enabled: component.enabled,
required: component.required,
});
});All components follow a similar pattern:
// 1. Query component status
const status = await component.query();
// 2. Enable the component
await component.enable();
// 3. Use component-specific methods
// For data readers:
component.on("data", (data) => {
console.log("Data received:", data);
});
// For printers:
await printer.send(dataRecords);
// 4. Disable when done
await component.disable();Mark components as required to enable automatic state management:
// Mark components as required
cuss2.barcodeReader.required = true;
cuss2.boardingPassPrinter.required = true;
// The SDK will automatically:
// - Transition to UNAVAILABLE if any required component becomes unavailable
// - Transition to AVAILABLE when all required components are ready
// Check unavailable components
const unavailable = cuss2.unavailableComponents;
const unavailableRequired = cuss2.unavailableRequiredComponents;
// Manually trigger state sync
cuss2.checkRequiredComponentsAndSyncState();
// Control online/offline status
cuss2.applicationOnline = true; // Triggers state syncconst reader = cuss2.barcodeReader;
if (reader) {
// Enable and listen for data
await reader.enable();
reader.on("data", (data) => {
console.log("Barcode:", data.rawData);
console.log("Format:", data.symbology);
});
// Disable when done
await reader.disable();
}const printer = cuss2.boardingPassPrinter;
if (printer) {
await printer.enable();
// Send print data
const printData = [{
data: "M1DOE/JOHN...", // IATA BCBP format
dsTypes: [CussDataTypes.IATA_BCBP],
}];
await printer.send(printData);
await printer.disable();
}const scale = cuss2.scale;
if (scale) {
await scale.enable();
scale.on("data", (weight) => {
console.log("Weight:", weight.weight, weight.unit);
console.log("Stable:", weight.stable);
});
await scale.disable();
}const announcement = cuss2.announcement;
if (announcement) {
await announcement.enable();
// Play SSML announcement
await announcement.play("<speak>Welcome to the self-service kiosk.</speak>");
// Control playback
await announcement.pause();
await announcement.resume();
await announcement.stop();
await announcement.disable();
}The SDK uses an event-driven architecture:
// Connection events
cuss2.connection.on("open", () => {});
cuss2.connection.on("close", () => {});
cuss2.connection.on("error", (error) => {});
cuss2.connection.on("message", (data) => {});
// Platform events
cuss2.on("connected", (cuss2) => {});
cuss2.on("stateChange", (stateChange) => {});
cuss2.on("activated", (activationData) => {});
cuss2.on("deactivated", (newState) => {});
cuss2.on("message", (platformData) => {});
cuss2.on("sessionTimeout", () => {});
cuss2.on("componentStateChange", (component) => {});
cuss2.on("queryError", (error) => {});
// Component events
component.on("data", (data) => {});
component.on("stateChange", (state) => {});
component.on("statusChange", (status) => {});const env = cuss2.environment;
console.log("Device ID:", env.deviceID);
console.log("CUSS Version:", env.cuss2Version);
console.log("Location:", env.locationCode);
console.log("Capabilities:", env.platformCapabilities);// Handle session timeout
cuss2.on("sessionTimeout", () => {
console.log("Session timed out");
// Implement re-authentication or cleanup
});
// Request reload (closes connection)
await cuss2.requestReload();try {
await cuss2.boardingPassPrinter.enable();
}
catch (error) {
if (error.code === MessageCodes.MEDIA_NOTREADY) {
console.log("Printer out of paper");
}
else if (error.code === MessageCodes.DEVICE_NOTOPERATIONAL) {
console.log("Printer not operational");
}
}Build browser-compatible JavaScript bundles:
# Build the JavaScript bundles
deno task buildThis creates:
dist/cuss2.js- Full browser bundledist/cuss2.min.js- Minified bundle with source map
<script src="dist/cuss2.js"></script>
<script>
// The global Cuss2 object contains all exports
const cuss2 = Cuss2.connect(
"client-id",
"client-secret",
// Optional: custom WebSocket URL as 3rd parameter
// "wss://platform.example.com"
);
// All models are available under Cuss2.Models
const { ApplicationStateCodes, MessageCodes } = Cuss2.Models;
// Wait for connection
cuss2.connected.then(() => {
console.log("Connected!");
});
</script># Run all tests
deno task testYou MUST run these checks before committing code:
deno check
deno lint
deno fmt --check
deno task test
deno task buildThis project is licensed under the MIT License - see the LICENSE file for details.