claviz-liminal-qlik
v0.0.7
Published
Qlik Engine gateway and Node SDK with pooled sessions, caching, and enigma-style proxies.
Readme
claviz-liminal-qlik
A Qlik Engine gateway that pools sessions and provides an enigma-compatible Node SDK.
Overview
- One Engine session per
userId + appId, shared across callers (in-flight open de-dupe). - Caches common app objects (objects, fields, variables, measures) to reduce duplicate calls.
- Broadcasts
changed(optional layout) and syntheticloadingevents to all subscribers. - Node client SDK that exposes enigma-style proxies.
Server
Quick start
npm install
npm run build
npm startDefaults:
- HTTP:
http://localhost:1337 - Health:
GET /health
Configuration (env + programmatic)
dotenv-flow loads .env*. startServer options override env.
| Setting | Default | Env | Programmatic |
| --- | --- | --- | --- |
| HTTP port | 1337 | PORT | startServer({ port }) |
| Session idle (ms) | 120000 | SESSION_IDLE_MS | startServer({ sessionIdleMs }) |
| Changed debounce (ms) | 125 | CHANGED_DEBOUNCE_MS | startServer({ changedDebounceMs }) |
| Qlik config provider | - | - | startServer({ getEnigmaConfig }) |
| Qlik connection mode | cloud | QLIK_CONNECTION_MODE | getEnigmaConfig().connectionMode |
| Qlik tenant (cloud) | - | QLIK_TENANT | getEnigmaConfig().qlikTenant |
| Qlik engine URL (certs) | - | QLIK_ENGINE_URL | getEnigmaConfig().qlikEngineUrl |
| Certificates path (certs) | - | QLIK_CERTIFICATES_PATH | getEnigmaConfig().certificatesPath |
| Custom session factory | - | - | startServer({ getEnigmaSession }) |
| Response interceptors | - | - | startServer({ getResponseInterceptors }) |
| Error handler | - | - | startServer({ onError }) |
| Logger | console.log | - | startServer({ logger }) |
Notes:
- In
cloudmode, clients must provide a Qlik API token (see Client). - In
certificatesmode, clients must provide a Qlik user directory. - Providing
getEnigmaConfigbypasses env-based config for Qlik connection settings. logger: nulldisables logs.
Behavior notes
- Sessions are keyed by
userId + appId; idle cleanup usessessionIdleMs. - If a session is closed or returns
NOT_CONNECTED, the gateway marks it dead and rehydrates on the next call or subscription. GET /healthincludessessionDetailswith per-session status (dead/alive, idle time, handles, subscriptions).changedcan optionally include a layout (seeincludeLayoutin Client options).- Synthetic
loadinghelps UI state around longer-running calls. createSessionObjectis cached only whenqInfo.qIdis provided; cache is per session and cleared bydestroySessionObject.
Session lifecycle (short)
- Alive session: enigma session is open; handles point to live APIs; calls go through normally.
- Dead session: enigma session closed or a call returned
NOT_CONNECTED; handles are kept but their APIs are cleared. - Rehydrate: on the next call/subscribe, the gateway opens a new enigma session, recreates global/app, and rebinds known handles (objects/fields/variables/measures). Unknown handles throw
SESSION_CLOSED. - Idle cleanup: a session is disposed only when it has no subscriptions and has been idle longer than
sessionIdleMs(same rule for alive and dead).
Client
Usage
import { QlikGatewayClient } from 'claviz-liminal-qlik/client';
const session = await QlikGatewayClient.connectSession('http://localhost:1337', {
userId: '[email protected]',
appId: 'YOUR_APP_ID',
token: process.env.QLIK_API_TOKEN,
userDirectory: 'YOUR_USER_DIRECTORY', // certificates mode
});
const app = await (await session.open()).openDoc();
const viz = await app.getObject('YOUR_OBJECT_ID');
viz.on('changed', ({ layout }) => {
// layout is pushed by the server when includeLayout is true
}, { includeLayout: true });
viz.on('loading', ({ active }) => {
// show/hide overlay
});
await session.close();Tunable options
QlikGatewayClient.connect(url, { timeoutMs?, token?, userDirectory? })(defaulttimeoutMsis30000).QlikGatewayClient.connectSession(url, { userId, appId, timeoutMs?, token?, userDirectory? }).openGlobal/openAppparams:{ userId, appId, token?, userDirectory? }(token used for Qlik Cloud).- Subscription options:
{ includeLayout?, debounceMs? }forchanged. EnigmaGatewaySession.close()only disconnects the socket if it owns it.
