UNPKG

@just-in/core

Version:

A TypeScript-first framework for building adaptive digital health interventions.

190 lines 9.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JustInLite = exports.JustInLiteWrapper = void 0; const event_handler_manager_1 = require("./event/event-handler-manager"); const task_manager_1 = require("./handlers/task.manager"); const decision_rule_manager_1 = require("./handlers/decision-rule.manager"); const event_executor_1 = require("./event/event-executor"); const logger_manager_1 = require("./logger/logger-manager"); const result_recorder_1 = require("./handlers/result-recorder"); /** * JustInLiteWrapper provides a minimal, serverless-oriented interface for 3rd-party apps: * - configure logger & result writers * - register tasks, decision rules, and event handlers * - keep users in-memory for the current warm instance * - run registered events immediately (no DB/queue) */ class JustInLiteWrapper { constructor() { /** In-memory idempotency (per warm instance only). */ this.processedKeys = new Set(); this.eventHandlerManager = event_handler_manager_1.EventHandlerManager.getInstance(); /** In-memory users for this warm instance (keyed by uniqueIdentifier). */ this.users = new Map(); // Lite/serverless: never touch DataManager from the recorder module. (0, result_recorder_1.setResultRecorderPersistenceEnabled)(false); } /** Returns the singleton Lite instance. */ static getInstance() { if (!JustInLiteWrapper.instance) { JustInLiteWrapper.instance = new JustInLiteWrapper(); } return JustInLiteWrapper.instance; } /** * Reset the singleton (useful for tests). * Includes a 1-tick drain to let any late recorder promises settle. */ async killInstance() { try { // allow any microtasks to flush await new Promise((r) => setImmediate(r)); } finally { this.processedKeys.clear(); this.users.clear(); this.eventHandlerManager.clearEventHandlers(); JustInLiteWrapper.instance = null; } } /** * Static teardown helper for tests/tools. * If an instance exists, delegate to it; otherwise do a best-effort drain+clear. * Safe to call multiple times. */ static async killInstance() { if (JustInLiteWrapper.instance) { await JustInLiteWrapper.instance.killInstance(); return; } // No instance — still clear any registered handlers to avoid leaks across tests. await new Promise((resolve) => setImmediate(resolve)); event_handler_manager_1.EventHandlerManager.getInstance().clearEventHandlers(); } // ──────────────────────────────────────────────────────────────────────────── // Users (in-memory) // ──────────────────────────────────────────────────────────────────────────── /** * Loads users for the current invocation (serverless-safe). * Accepts either `JUser[]` or `NewUserRecord[]`. * Replaces the in-memory set each call (atomic), requires `uniqueIdentifier`, * and throws on duplicates. Returns the normalized `JUser[]`. */ async loadUsers(users) { if (!Array.isArray(users)) { throw new Error('loadUsers expects an array.'); } const next = new Map(); const normalized = []; users.forEach((item, i) => { var _a, _b; const anyItem = item; const uniqueIdentifier = typeof (anyItem === null || anyItem === void 0 ? void 0 : anyItem.uniqueIdentifier) === 'string' ? anyItem.uniqueIdentifier.trim() : ''; const idHint = typeof (anyItem === null || anyItem === void 0 ? void 0 : anyItem.id) === 'string' ? anyItem.id : undefined; if (!uniqueIdentifier) { const msg = `UniqueIdentifier is missing`; logger_manager_1.Log.error(msg); throw new Error(msg); } if (next.has(uniqueIdentifier)) { const msg = `loadUsers: duplicate uniqueIdentifier "${uniqueIdentifier}".`; logger_manager_1.Log.error(msg); throw new Error(msg); } const attrs = 'attributes' in anyItem ? ((_a = anyItem.attributes) !== null && _a !== void 0 ? _a : {}) : 'initialAttributes' in anyItem ? ((_b = anyItem.initialAttributes) !== null && _b !== void 0 ? _b : {}) : {}; const ju = { id: idHint !== null && idHint !== void 0 ? idHint : uniqueIdentifier, uniqueIdentifier, attributes: { ...attrs }, }; next.set(uniqueIdentifier, ju); normalized.push(ju); }); this.users = next; logger_manager_1.Log.info(`JustInLite: loaded ${next.size} users (in-memory, replacing previous set).`); return normalized; } // ──────────────────────────────────────────────────────────────────────────── // Registration (match JustInWrapper vocabulary) // ──────────────────────────────────────────────────────────────────────────── /** Register a Task. */ registerTask(task) { (0, task_manager_1.registerTask)(task); } /** Register a Decision Rule. */ registerDecisionRule(decisionRule) { (0, decision_rule_manager_1.registerDecisionRule)(decisionRule); } // TODO: Look at using EventHandlerManager instead and not from the Full Justin /** * Registers a new event type with ordered handler names. * Also caches the definition locally for introspection. * @param eventType - The type of the event. * @param handlers - Ordered task/decision-rule names for the event. */ async registerEventHandlers(eventType, handlers) { await this.eventHandlerManager.registerEventHandlers(eventType, handlers); } /** Unregister handlers for an event type. */ unregisterEventHandlers(eventType) { this.eventHandlerManager.unregisterEventHandlers(eventType); } // ──────────────────────────────────────────────────────────────────────────── // Execution // ──────────────────────────────────────────────────────────────────────────── /** * Publish (execute) a registered event for the **currently loaded** users. * Signature matches full JustIn; `idempotencyKey` is optional (in-memory only). * * @param eventType Registered event type. * @param generatedTimestamp Event timestamp. * @param eventDetails Optional event details payload. * @param idempotencyKey Optional in-memory dedupe key (skips if seen). */ async publishEvent(eventType, generatedTimestamp, eventDetails, idempotencyKey) { // Optional in-memory idempotency for cloud runs if (idempotencyKey) { if (this.processedKeys.has(idempotencyKey)) { logger_manager_1.Log.warn(`[JustInLite] duplicate execution skipped for key: ${idempotencyKey}`); return; } this.processedKeys.add(idempotencyKey); } if (!this.eventHandlerManager.hasHandlersForEventType(eventType)) { throw new Error(`No handlers registered for event type "${eventType}".`); } // Ensure users are loaded const users = Array.from(this.users.values()); if (users.length === 0) { throw new Error('JustInLite.publishEvent called with no users loaded.'); } const event = { eventType, generatedTimestamp, eventDetails: eventDetails !== null && eventDetails !== void 0 ? eventDetails : {}, }; await (0, event_executor_1.executeEventForUsers)(event, users, this.eventHandlerManager); } // ──────────────────────────────────────────────────────────────────────────── // Logger & Writers (same names as full JustIn) // ──────────────────────────────────────────────────────────────────────────── configureLogger(logger) { (0, logger_manager_1.setLogger)(logger); } configureTaskResultWriter(taskWriter) { (0, result_recorder_1.setTaskResultRecorder)(taskWriter); } configureDecisionRuleResultWriter(decisionRuleWriter) { (0, result_recorder_1.setDecisionRuleResultRecorder)(decisionRuleWriter); } setLoggingLevels(levels) { (0, logger_manager_1.setLogLevels)(levels); } } exports.JustInLiteWrapper = JustInLiteWrapper; JustInLiteWrapper.instance = null; const JustInLite = () => JustInLiteWrapper.getInstance(); exports.JustInLite = JustInLite; //# sourceMappingURL=JustInLite.js.map