Skip to main content

PersistenceHandler

PersistenceHandler is Oidis' cross-environment persistent key-value storage system. It provides automatic serialization of complex objects, auto-selects the best available storage backend (localStorage or cookies), and includes data integrity validation via CRC checksums. The system works on both frontend and backend and is used extensively throughout the framework for UI state persistence — form values, last-viewed records, viewer state, and autofill data.

note

PersistenceHandler is browser/environment-level storage for transient UI state and session data — a key-value layer over localStorage / cookies. It is distinct from ApplicationSettings (app-wide, DB-backed, admin-editable), User Profile (per-user, DB-backed, personal data), and IndexedDB (model-level frontend persistence with @Entity and CRUD operations). See the comparison table below for when to use each.

How It Works​

The entry point is PersistenceFactory, a static factory that creates and caches IPersistenceHandler instances. Each handler is identified by a persistence type (a string or PersistenceType enum value) and an optional owner string. The factory generates a SHA-1 key from these two values and reuses the same handler instance for the same key.

On first use, the factory auto-detects the best storage backend:

  1. localStorage (StorageHandler) — preferred, used when localStorage is available
  2. Cookies (CookiesHandler) — fallback, used when localStorage is not supported (e.g., JRE environments)

Detection happens once and applies to all handlers created during the session.

import { PersistenceFactory } from "@io-oidis-commons/.../PersistenceApi/PersistenceFactory.js";
import { PersistenceType } from "@io-oidis-gui/.../Enums/PersistenceType.js";

// Get a persistence handler for form values
const handler : IPersistenceHandler = PersistenceFactory.getPersistence(PersistenceType.FORM_VALUES);

// Store a value
handler.Variable("lastSearch", "active users");

// Retrieve a value
const search : string = handler.Variable("lastSearch");

The API​

All persistence handlers implement IPersistenceHandler. The interface provides a concise set of methods for storing, retrieving, and managing persistent data:

MethodSignatureDescription
Variable($name, $value?, $asyncHandler?) : anyCore method. Get or set a variable. Pass only $name to read; pass $name + $value to write. Accepts ArrayList for bulk writes.
Exists($name) : booleanCheck whether a variable exists in this handler
Destroy($name) : voidRemove a variable (or ArrayList of variables)
Clear() : voidRemove all data from this handler
ExpireTime($value?) : numberGet or set TTL. Pass a relative time string (e.g., "+1 hour", "+30 minutes") to set expiration.
DisableCRC() : voidDisable CRC integrity checking for this handler
getSessionId() : stringReturns the handler's internal session ID (SHA-1 hash)
getSize() : numberReturns the size of stored data in characters
getLoadTime() : numberReturns the time in milliseconds needed to load the data
getRawData($asyncHandler?) : stringReturns the raw serialized data. Pass a callback for async mode.
LoadPersistenceAsynchronously($asyncHandler, $sourceFilePath?) : voidLoad persistence data asynchronously, optionally from a file

The Variable Method in Detail​

Variable is the primary method for interacting with persistence. Its behavior depends on the arguments:

// READ — pass only the variable name
const value = handler.Variable("selectedTab");

// WRITE — pass name and value
handler.Variable("selectedTab", "inventory");

// BULK WRITE — pass an ArrayList of key-value pairs
const batch = new ArrayList<any>();
batch.Add("inventory", "selectedTab");
batch.Add(25, "pageSize");
handler.Variable(batch);

// ASYNC WRITE — pass name, value, and callback
handler.Variable("largeDataSet", complexObject, () : void => {
LogIt.Info("Data persisted asynchronously");
});

Storage Backends​

StorageHandler (localStorage)​

The preferred backend. Stores data directly in the browser's localStorage API with no size chunking needed. Each persistence handler maps to a single localStorage key identified by its SHA-1 session ID.

localStorage.getItem("a1b2c3d4...")  →  serialized session data

StorageHandler.IsSupported() checks whether localStorage is accessible. If not (e.g., private browsing restrictions, JRE environment), the factory falls back to CookiesHandler.

The fallback backend for environments without localStorage. Because cookies have a ~4 KB per-cookie limit, CookiesHandler automatically chunks the serialized data into 4 KB segments stored across multiple cookies:

a1b2c3d4...0  →  first 4KB chunk
a1b2c3d4...1 → second 4KB chunk
a1b2c3d4...2 → third 4KB chunk (if needed)

Cookie data is additionally Base64-encoded before storage. Cookies are set with a 5-year expiration by default.

Forcing a Backend​

In rare cases, you can force a specific backend before any persistence handler is created:

import { PersistenceFactory } from "@io-oidis-commons/.../PersistenceApi/PersistenceFactory.js";
import { PersistenceHandlerType } from "@io-oidis-commons/.../Enums/PersistenceHandlerType.js";

PersistenceFactory.setPersistenceHandler(PersistenceHandlerType.COOKIES);
warning

Call setPersistenceHandler before any call to getPersistence. Once the handler type is auto-detected and handlers are created, changing it has no effect on existing instances.

Serialization and Data Integrity​

Object Serialization​

PersistenceHandler uses ObjectEncoder / ObjectDecoder to serialize complex objects — including ArrayList instances, nested structures, and typed data — into string format suitable for storage. CookiesHandler adds an additional Base64 encoding layer on top.

Both synchronous and asynchronous serialization are supported. The async path is useful for large datasets that might block the main thread.

CRC Integrity Validation​

By default, every handler computes a CRC checksum over the serialized variables on each write and validates it on each read. If the checksum does not match (indicating external tampering or corruption), the handler:

  1. Creates a backup of the corrupted data (prefixed with "back" in the storage key)
  2. Removes the corrupted data
  3. Throws a FatalError with a descriptive message

This protects against external tools or browser extensions modifying stored data.

// Disable CRC if you know external tools will modify the data
handler.DisableCRC();

TTL (Time-to-Live)​

Each handler supports optional expiration. When ExpireTime is set and the stored TTL timestamp has passed, all data is automatically cleared on the next access:

handler.ExpireTime("+2 hours");
handler.Variable("tempToken", "abc123");
// Data will be cleared after 2 hours

Common Usage Patterns​

Class-Scoped Persistence​

The most common pattern uses the class name as the persistence key, keeping each controller's state isolated:

import { PersistenceFactory } from "@io-oidis-commons/.../PersistenceApi/PersistenceFactory.js";

export class InventoryController extends BasePanelController {

private persistence : IPersistenceHandler;

public override OnPanelReady() : void {
this.persistence = PersistenceFactory.getPersistence(this.getClassName());

// Restore last-viewed item
const lastId = this.persistence.Variable("lastViewedId");
if (!ObjectValidator.IsEmptyOrNull(lastId)) {
this.LoadItem(lastId);
}
}

protected onItemSelected($id : string) : void {
this.persistence.Variable("lastViewedId", $id);
}
}

Enum-Based Persistence Types​

Use the built-in PersistenceType enum for shared persistence scopes that multiple components can access:

import { PersistenceType } from "@io-oidis-gui/.../Enums/PersistenceType.js";

// Store form autofill data — accessible from any component using AUTOFILL type
const autofill = PersistenceFactory.getPersistence(PersistenceType.AUTOFILL);
autofill.Variable("lastEmail", "user@example.com");

// Store general GUI state
const gui = PersistenceFactory.getPersistence(PersistenceType.GUI_COMMONS);
gui.Variable("sidebarCollapsed", true);

Global Cleanup​

Clear all registered persistence handlers at once — useful during logout or session reset:

PersistenceFactory.DestroyAll();

Built-in Persistence Types​

The framework defines persistence types across two packages. The commons package provides base types, and the gui package extends them with GUI-specific scopes:

Commons Types (io-oidis-commons)​

TypeValuePurpose
BROWSER"browser"Browser session identification
CLIENT_IP"clientip"Client IP-based session

GUI Types (io-oidis-gui)​

TypeValuePurpose
GUI_COMMONS"guicommons"General GUI state (sidebar, layout preferences)
VIEWER"viewer"Viewer/display state (zoom, scroll position)
AUTOFILL"autofill"Form autofill data
FORM_VALUES"formvalues"Form field persistence (draft values, filters)
ERROR_FLAGS"errorflags"Validation error state tracking
tip

Application projects can extend PersistenceType with project-specific scopes, following the same inheritance pattern used by io-oidis-gui:

import { PersistenceType as Parent } from "@io-oidis-gui/.../Enums/PersistenceType.js";

export class PersistenceType extends Parent {
public static readonly DASHBOARD_LAYOUT : string = "dashboardlayout";
public static readonly REPORT_FILTERS : string = "reportfilters";
}

PersistenceHandler vs ApplicationSettings vs User Profile​

AspectPersistenceHandlerApplicationSettingsUser Profile
ScopePer-browser, per-environmentApp-widePer-user
StoragelocalStorage / cookiesMongoDBMongoDB
SurvivesBrowser session (until cleared)Server restartsServer restarts
Managed byCode (automatic)Admin panelAdmin or self-service
Use caseUI state, form drafts, autofillMail settings, feature flags, defaultsPersonal data, avatar, preferences
Packageio-oidis-commonsio-oidis-hubio-oidis-hub

Quick Reference​

FilePackagePurpose
PersistenceFactory.tsio-oidis-commonsFactory, auto-detection, handler caching
IPersistenceHandler.tsio-oidis-commonsPublic API interface
BasePersistenceHandler.tsio-oidis-commonsShared logic (CRC, TTL, encode/decode)
StorageHandler.tsio-oidis-commonslocalStorage implementation
CookiesHandler.tsio-oidis-commonsCookie implementation (with auto-chunking)
PersistenceType.tsio-oidis-commonsBase enum (BROWSER, CLIENT_IP)
PersistenceType.tsio-oidis-guiExtended enum (FORM_VALUES, AUTOFILL, etc.)

See also: Application Settings | User Profile | ObjectValidator | StringUtils