UNPKG

shogun-core

Version:

SHOGUN CORE - Core library for Shogun Ecosystem

368 lines (367 loc) 13.5 kB
var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import { Observable } from 'rxjs'; import { distinctUntilChanged } from 'rxjs/operators'; /** * RxJS Integration for Holster * Provides reactive programming capabilities for Holster data */ var RxJSHolster = /** @class */ (function () { /** * Initialize RxJSHolster with a Holster instance * @param holster - Holster instance */ function RxJSHolster(holsterInstance) { this.holster = holsterInstance; this.user = holsterInstance.user(); } /** * Get the current user * @returns The current user */ RxJSHolster.prototype.getUser = function () { return this.user; }; /** * Get the current user's public key * @returns The current user's public key */ RxJSHolster.prototype.getUserPub = function () { var _a; return (_a = this.user.is) === null || _a === void 0 ? void 0 : _a.pub; }; /** * Observe a Holster node for changes * Uses Holster's .get().next() API instead of chained .get() * @param path - Path to observe (can be a string, array, or a Holster chain) * @returns Observable that emits whenever the node changes */ RxJSHolster.prototype.observe = function (path) { var _this = this; return new Observable(function (subscriber) { var node; if (Array.isArray(path)) { // Support array paths by chaining next calls node = _this.holster.get(path[0]); for (var i = 1; i < path.length; i++) { node = node.next(path[i]); } } else if (typeof path === 'string') { node = _this.holster.get(path); } else { node = path; } // Subscribe to changes using Holster's .on() var unsub = node.on(function (data) { if (data === null || data === undefined) { subscriber.next(null); return; } // Remove Holster metadata before emitting if (typeof data === 'object' && data !== null) { var cleanData = _this.removeHolsterMeta(data); subscriber.next(cleanData); } else { subscriber.next(data); } }); // Return teardown logic return function () { if (unsub && typeof unsub === 'function') { unsub(); } else { node.off(); } }; }).pipe(distinctUntilChanged(function (prev, curr) { return JSON.stringify(prev) === JSON.stringify(curr); })); }; /** * Match data based on Holster collections and convert to Observable * Note: Holster doesn't have .map(), so we implement it using .on() * @param path - Path to the collection * @param matchFn - Optional function to filter results * @returns Observable array of matched items */ RxJSHolster.prototype.match = function (path, matchFn) { var _this = this; return new Observable(function (subscriber) { if (!path) { subscriber.next([]); subscriber.complete(); return; } var node = typeof path === 'string' ? _this.holster.get(path) : path; var results = {}; // Holster doesn't have .map(), so we use .on() and track keys manually var unsub = node.on(function (data, key) { // Skip internal keys if (key === '_' || !data) return; var itemKey = key || String(Object.keys(results).length); if (matchFn && !matchFn(data)) { // If matchFn is provided and returns false, remove item if (results[itemKey]) { delete results[itemKey]; subscriber.next(Object.values(results)); } return; } var cleanData = typeof data === 'object' ? _this.removeHolsterMeta(data) : data; results[itemKey] = cleanData; subscriber.next(Object.values(results)); }); // Return teardown logic return function () { if (unsub && typeof unsub === 'function') { unsub(); } else { node.off(); } }; }); }; /** * Put data and return an Observable * @param path - Path where to put the data * @param data - Data to put * @returns Observable that completes when the put is acknowledged */ RxJSHolster.prototype.put = function (path, data) { var _this = this; return new Observable(function (subscriber) { var performPut = function (target, value) { target.put(value, function (ack) { if (ack && ack.err) { subscriber.error(new Error(ack.err)); } else { subscriber.next(value); subscriber.complete(); } }); }; if (typeof path === 'string' || Array.isArray(path)) { // Path-based put var node = void 0; if (Array.isArray(path)) { node = _this.holster.get(path[0]); for (var i = 1; i < path.length; i++) { node = node.next(path[i]); } } else { node = _this.holster.get(path); } performPut(node, data); } else { // Root-level put performPut(_this.holster, path); } }); }; /** * Get data once and return as Observable * @param path - Path to get data from * @returns Observable that emits the data once */ RxJSHolster.prototype.once = function (path) { var _this = this; var node; if (typeof path === 'string') { node = this.holster.get(path); } else if (path) { node = path; } else { node = this.holster; } return new Observable(function (subscriber) { var called = false; var wrappedCallback = function (data) { if (!called) { called = true; if (data === undefined || data === null) { subscriber.next(null); subscriber.complete(); return; } var cleanData = typeof data === 'object' ? _this.removeHolsterMeta(data) : data; subscriber.next(cleanData); subscriber.complete(); } }; node.on(wrappedCallback); // Auto-unsubscribe after first call setTimeout(function () { node.off(wrappedCallback); }, 0); }); }; /** * Compute derived values from holster data * @param sources - Array of paths or observables to compute from * @param computeFn - Function that computes a new value from the sources * @returns Observable of computed values */ RxJSHolster.prototype.compute = function (sources, computeFn) { var _this = this; // Convert all sources to observables var observables = sources.map(function (source) { if (typeof source === 'string') { return _this.observe(source); } return source; }); // Combine the latest values from all sources return new Observable(function (subscriber) { var values = new Array(sources.length).fill(undefined); var completed = new Array(sources.length).fill(false); var subscriptions = observables.map(function (obs, index) { return obs.subscribe({ next: function (value) { values[index] = value; // Only compute if we have all values if (values.every(function (v) { return v !== undefined; })) { try { var result = computeFn.apply(void 0, __spreadArray([], __read(values), false)); subscriber.next(result); } catch (error) { subscriber.error(error); } } }, error: function (err) { return subscriber.error(err); }, complete: function () { completed[index] = true; if (completed.every(function (c) { return c; })) { subscriber.complete(); } }, }); }); // Return teardown logic return function () { subscriptions.forEach(function (sub) { return sub.unsubscribe(); }); }; }); }; /** * User put data and return an Observable (for authenticated users) * @param path - Path where to put the data * @param data - Data to put * @returns Observable that completes when the put is acknowledged */ RxJSHolster.prototype.userPut = function (dataOrPath, maybeData, callback) { var _this = this; return new Observable(function (subscriber) { var user = _this.holster.user(); if (typeof dataOrPath === 'string') { user.get(dataOrPath).put(maybeData, function (ack) { if (callback) callback(ack); if (ack && ack.err) { subscriber.error(new Error(ack.err)); } else { subscriber.next(maybeData); subscriber.complete(); } }); } else { user.put(dataOrPath, function (ack) { if (callback) callback(ack); if (ack && ack.err) { subscriber.error(new Error(ack.err)); } else { subscriber.next(dataOrPath); subscriber.complete(); } }); } }); }; /** * Get user data * @param path - Path to get data from * @returns Observable that emits the data once */ RxJSHolster.prototype.userGet = function (path) { return this.observe(this.holster.user().get(path)); }; /** * Observe user data * @param path - Path to observe in user space * @returns Observable that emits whenever the user data changes */ RxJSHolster.prototype.observeUser = function (path) { var user = this.holster.user(); if (path) { return this.observe(user.get(path)); } return this.observe(user.get('~')); }; /** * Remove Holster-specific metadata from data objects * @param obj - Object to clean * @returns Cleaned object without Holster metadata */ RxJSHolster.prototype.removeHolsterMeta = function (obj) { var _this = this; if (!obj || typeof obj !== 'object') return obj; // Create a clean copy var cleanObj = Array.isArray(obj) ? [] : {}; // Copy properties, skipping Holster metadata Object.keys(obj).forEach(function (key) { // Skip Holster metadata if (key === '_' || key.startsWith('~')) return; var val = obj[key]; if (val && typeof val === 'object') { cleanObj[key] = _this.removeHolsterMeta(val); } else { cleanObj[key] = val; } }); return cleanObj; }; return RxJSHolster; }()); export { RxJSHolster };