shogun-core
Version:
SHOGUN CORE - Core library for Shogun Ecosystem
368 lines (367 loc) • 13.5 kB
JavaScript
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 };