@trap_stevo/ventry
Version:
The universal engine for creating, tracking, and evolving interactive content — from posts, comments, and likes to offers, auctions, events, and beyond. Define, extend, and analyze content objects in real time. Turn anything into user-driven content.
894 lines (893 loc) • 29.2 kB
JavaScript
"use strict";
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
const {
ContentVaultInstanceManager
} = require("./HUDManagers/ContentVaultInstanceManager.cjs");
const {
ContalyticsInstanceManager
} = require("./HUDManagers/ContalyticsInstanceManager.cjs");
const {
CommentsManager
} = require("./HUDManagers/CommentsManager.cjs");
const {
AuctionsManager
} = require("./HUDManagers/AuctionsManager.cjs");
const {
OffersManager
} = require("./HUDManagers/OffersManager.cjs");
const {
EventsManager
} = require("./HUDManagers/EventsManager.cjs");
const {
geoIncludes
} = require("./HUDComponents/Geo.cjs");
const {
now
} = require("./HUDComponents/Time.cjs");
const {
newID
} = require("./HUDComponents/ID.cjs");
var _vaultActionInfoDefault = /*#__PURE__*/new WeakMap();
var _vaultClientAuthDefault = /*#__PURE__*/new WeakMap();
var _metricPrefix = /*#__PURE__*/new WeakMap();
var _Ventry_brand = /*#__PURE__*/new WeakSet();
class Ventry {
constructor(options = {}) {
_classPrivateMethodInitSpec(this, _Ventry_brand);
_classPrivateFieldInitSpec(this, _vaultActionInfoDefault, {});
_classPrivateFieldInitSpec(this, _vaultClientAuthDefault, null);
_classPrivateFieldInitSpec(this, _metricPrefix, "ventry");
_classPrivateFieldSet(_vaultActionInfoDefault, this, options.vaultActionInfoDefault || {});
_classPrivateFieldSet(_vaultClientAuthDefault, this, options.vaultClientAuthDefault || null);
_classPrivateFieldSet(_metricPrefix, this, options.metricPrefix || "ugc");
this.vaultManager = new ContentVaultInstanceManager(options.contentVault, options.vaultOptions || {}, _classPrivateFieldGet(_vaultActionInfoDefault, this), _classPrivateFieldGet(_vaultClientAuthDefault, this));
this.metricsManager = new ContalyticsInstanceManager(options.contentMetrics, Object.assign({}, options.metricOptions, {
metricPrefix: _classPrivateFieldGet(_metricPrefix, this)
}));
this.eventsManager = new EventsManager(this.vaultManager, {
eventPrefix: options.eventPrefix || _classPrivateFieldGet(_metricPrefix, this) || "ugc",
actionInfo: _classPrivateFieldGet(_vaultActionInfoDefault, this)
});
if (options.autoStartScheduler !== false) {
this.eventsManager.startScheduler();
}
this.commentsManager = new CommentsManager(this.vaultManager, this.metricsManager, this.eventsManager);
this.offersManager = new OffersManager(this.vaultManager, this.metricsManager, this.eventsManager);
this.auctionsManager = new AuctionsManager(this.vaultManager, this.metricsManager, this.eventsManager);
this.visibilityPolicies = new Map();
}
setActionInfo(actionInfo = {}) {
_classPrivateFieldSet(_vaultActionInfoDefault, this, actionInfo);
this.vaultManager.setDefaults(actionInfo, _classPrivateFieldGet(_vaultClientAuthDefault, this));
}
setClientAuth(clientAuth = null) {
_classPrivateFieldSet(_vaultClientAuthDefault, this, clientAuth);
this.vaultManager.setDefaults(_classPrivateFieldGet(_vaultActionInfoDefault, this), clientAuth);
}
setMetricPrefix(prefix = "ugc") {
_classPrivateFieldSet(_metricPrefix, this, prefix || "ugc");
this.metricsManager.setPrefix(_classPrivateFieldGet(_metricPrefix, this));
}
setVaultPrefix(prefix = "ugc") {
this.vaultManager.setPrefix(prefix);
}
on(ev, fn) {
return this.eventsManager.on(ev, fn);
}
once(ev, fn) {
return this.eventsManager.once(ev, fn);
}
off(ev, fn) {
return this.eventsManager.off(ev, fn);
}
setEventPrefix(prefix) {
this.eventsManager.setEventPrefix(prefix);
}
configureScheduler(cfg) {
this.eventsManager.configureScheduler(cfg);
}
startExpirationScheduler() {
this.eventsManager.startScheduler();
}
stopExpirationScheduler() {
this.eventsManager.stopScheduler();
}
registerType(config = {}, actionInfo) {
const {
typeID
} = config;
if (!typeID) {
console.warn("Type ID required.");
return null;
}
this.visibilityPolicies.set(typeID, config.visibilityPolicy || null);
return this.vaultManager.upsertType(config, actionInfo);
}
updateType(typeID, patch = {}, actionInfo) {
if (!typeID) {
console.warn("Type ID required.");
return null;
}
if (patch.visibilityPolicy !== undefined) {
this.visibilityPolicies.set(typeID, patch.visibilityPolicy || null);
}
return this.vaultManager.patchType(typeID, patch, actionInfo);
}
listTypes(actionInfo) {
return this.vaultManager.listTypes(actionInfo);
}
async create(ownerID, contentPayload = {}, actionInfo = {}) {
if (!ownerID) {
console.warn("Owner ID required.");
return null;
}
const id = newID();
const record = {
typeID: contentPayload.typeID || "default",
ownerID,
id,
version: 1,
data: contentPayload.data || {},
attachments: contentPayload.attachments || [],
tags: contentPayload.tags || [],
labels: contentPayload.labels || [],
status: contentPayload.status || "active",
deployment: contentPayload.deployment || {
scope: "global"
},
expiration: contentPayload.expiration || null,
boosts: Array.isArray(contentPayload.boosts) ? contentPayload.boosts : [],
commentsEnabled: contentPayload.commentsEnabled === true
};
const saved = this.vaultManager.createItem(record, actionInfo);
this.eventsManager.emit("item.created", {
id: saved.id,
ownerID,
typeID: record.typeID,
item: saved
});
return {
id: saved.id
};
}
async getItems(actionInfo) {
return this.vaultManager.getItems(actionInfo);
}
async get(id, actionInfo) {
return this.vaultManager.getItem(id, actionInfo);
}
async update(id, actor, patch = {}, actionInfo) {
const item = this.vaultManager.getItem(id, actionInfo);
if (!item) {
console.warn("Not found");
return null;
}
if (actor && actor.strictOwner && actor.id !== item.data.ownerID) {
console.warn("Update unauthorized.");
return null;
}
const updates = {};
if (patch.data !== undefined) {
updates.data = patch.data;
}
if (patch.attachments !== undefined) {
updates.attachments = patch.attachments;
}
if (patch.tags !== undefined) {
updates.tags = patch.tags;
}
if (patch.labels !== undefined) {
updates.labels = patch.labels;
}
if (patch.status !== undefined) {
updates.status = patch.status;
}
if (patch.deployment !== undefined) {
updates.deployment = patch.deployment;
}
if (patch.expiration !== undefined) {
updates.expiration = patch.expiration;
}
if (patch.boosts !== undefined) {
updates.boosts = patch.boosts;
}
if (patch.commentsEnabled !== undefined) {
updates.commentsEnabled = !!patch.commentsEnabled;
}
updates.version = (item.data.version || 1) + 1;
const updated = this.vaultManager.updateItem(id, updates, actionInfo);
this.eventsManager.emit("item.updated", {
id,
updates,
actor,
item: updated
});
return updated;
}
async remove(id, actor, opts = {}, actionInfo = {}) {
const item = this.vaultManager.getItem(id, actionInfo);
if (!item) {
return {
deleted: false
};
}
if (actor && actor.strictOwner && actor.id !== item.data.ownerID) {
console.warn("Update unauthorized.");
return null;
}
if (opts.cascadeSubvault === true) {
const branches = Array.isArray(opts.subvaultBranch) ? opts.subvaultBranch : [opts.subvaultBranch || "subvault"];
for (const branch of branches) {
try {
this.vaultManager.deleteAllSubvaultForParent(id, {
hard: !!opts.hard
}, actionInfo, branch);
} catch (e) {
console.warn("Did not cascade delete subvault ~", e?.message || e, "branch ~", branch);
}
}
}
if (opts.hard === true) {
const r = this.vaultManager.deleteRecord(id, actionInfo);
this.eventsManager.emit("item.deleted_hard", {
id,
actor
});
return r;
}
const r = this.vaultManager.softDelete(id, actionInfo);
this.eventsManager.emit("item.deleted_soft", {
id,
actor
});
return r;
}
getContentRecordHistory(collectionID = "items", id, options = {}, actionInfo, clientAuth) {
return this.vaultManager.getContentRecordHistory(collectionID, id, options, actionInfo, clientAuth);
}
getContentRecordTimeline(collectionID = "items", id, options = {}, actionInfo, clientAuth) {
return this.vaultManager.getRecordTimeline(collectionID, id, options, actionInfo, clientAuth);
}
getContentHistory(collectionID = "items", options = {}, actionInfo, clientAuth) {
return this.vaultManager.getContentHistory(collectionID, options, actionInfo, clientAuth);
}
getContentTimeline(collectionID = "items", options = {}, actionInfo, clientAuth) {
return this.vaultManager.getContentTimeline(collectionID, options, actionInfo, clientAuth);
}
getHistory(options = {}, actionInfo, clientAuth) {
return this.vaultManager.getHistory(options, actionInfo, clientAuth);
}
async syncHistory(actionInfo, clientAuth) {
return await this.vaultManager.syncHistory(actionInfo, clientAuth);
}
importContentRecord(collectionID = "items", record, options = {}, actionInfo, clientAuth) {
return this.vaultManager.importContentRecord(collectionID, record, options, actionInfo, clientAuth);
}
importManyContentRecords(collectionID = "items", records, options = {}, actionInfo, clientAuth) {
return this.vaultManager.importManyContentRecords(collectionID, records, options, actionInfo, clientAuth);
}
importContent(collectionID = "items", records, options = {}, actionInfo, clientAuth) {
return this.vaultManager.importContent(collectionID, records, options, actionInfo, clientAuth);
}
importSnapshot(snapshot, options = {}, actionInfo, clientAuth) {
return this.vaultManager.importSnapshot(snapshot, options, actionInfo, clientAuth);
}
addSubvault(itemID, ownerID, input = {}, actionInfo, branch = "subvault") {
if (!itemID || !ownerID) {
console.warn("itemID and ownerID required.");
return {
id: null
};
}
const parent = this.vaultManager.getItem(itemID, actionInfo);
if (!parent) {
console.warn("Parent not found.");
return {
id: null
};
}
const rec = {
parentID: itemID,
ownerID,
key: input.key || null,
payload: input.payload || {},
tags: Array.isArray(input.tags) ? input.tags : [],
labels: Array.isArray(input.labels) ? input.labels : [],
status: input.status || "active",
createdAt: Date.now(),
updatedAt: Date.now(),
expiresAt: input.expiresAt || null
};
const saved = this.vaultManager.createSubvault(rec, actionInfo, branch);
this.eventsManager.emit("subvault.created", {
id: saved.id,
parentID: itemID,
ownerID,
branch
});
return {
id: saved.id
};
}
listSubvault(itemID, opts = {}, actionInfo, branch = "subvault") {
if (!itemID) {
return {
items: [],
nextCursor: undefined
};
}
const filter = {
parentID: itemID
};
if (Array.isArray(opts.keys) && opts.keys.length > 0) {
filter.key = {
$in: opts.keys
};
}
if (opts.status) {
filter.status = opts.status;
}
const sort = opts.sort || {
updatedAt: -1
};
const page = {
limit: opts.limit || 100,
offset: opts.offset || 0
};
const projection = opts.projection;
const qb = this.vaultManager.querySubvault(filter, sort, page, projection, actionInfo, branch);
const items = qb.execute(true);
return {
items,
nextCursor: undefined
};
}
getSubvaultByKey(itemID, key, actionInfo, branch = "subvault") {
if (!itemID || !key) {
return null;
}
return this.vaultManager.getSubvaultByKey(itemID, key, actionInfo, branch);
}
upsertSubvaultByKey(itemID, ownerID, key, payload = {}, extras = {}, actionInfo, branch = "subvault") {
if (!itemID || !ownerID || !key) {
console.warn("itemID, ownerID and key required.");
return null;
}
const parent = this.vaultManager.getItem(itemID, actionInfo);
if (!parent) {
console.warn("Parent not found.");
return null;
}
const saved = this.vaultManager.upsertSubvaultByKey(itemID, ownerID, key, payload, extras, actionInfo, branch);
const id = saved.id || saved.data && saved.data.id || saved;
this.eventsManager.emit("subvault.upserted", {
parentID: itemID,
key,
id,
ownerID,
branch
});
return saved;
}
updateSubvault(subvaultID, actor, patch = {}, actionInfo, branch = "subvault") {
const rec = this.vaultManager.getSubvault(subvaultID, actionInfo, branch);
if (!rec) {
return null;
}
if (actor && actor.strictOwner && actor.id !== (rec.data && rec.data.ownerID)) {
console.warn("Update unauthorized.");
return null;
}
const set = {};
if (patch.payload !== undefined) {
set.payload = patch.payload;
}
if (patch.key !== undefined) {
set.key = patch.key;
}
if (patch.tags !== undefined) {
set.tags = patch.tags;
}
if (patch.labels !== undefined) {
set.labels = patch.labels;
}
if (patch.status !== undefined) {
set.status = patch.status;
}
if (patch.expiresAt !== undefined) {
set.expiresAt = patch.expiresAt;
}
set.updatedAt = Date.now();
const updated = this.vaultManager.updateSubvault(subvaultID, set, actionInfo, branch);
this.eventsManager.emit("subvault.updated", {
id: subvaultID,
parentID: rec.data ? rec.data.parentID : undefined,
actor,
branch
});
return updated;
}
removeSubvault(subvaultID, actor, opts = {}, actionInfo, branch = "subvault") {
const rec = this.vaultManager.getSubvault(subvaultID, actionInfo, branch);
if (!rec) {
return {
deleted: false
};
}
if (actor && actor.strictOwner && actor.id !== (rec.data && rec.data.ownerID)) {
console.warn("Delete unauthorized.");
return {
deleted: false
};
}
if (opts.hard === true) {
const r = this.vaultManager.deleteSubvault(subvaultID, actionInfo, branch);
this.eventsManager.emit("subvault.deleted_hard", {
id: subvaultID,
parentID: rec.data ? rec.data.parentID : undefined,
actor,
branch
});
return r;
}
const r = this.vaultManager.softDeleteSubvault(subvaultID, actionInfo, branch);
this.eventsManager.emit("subvault.deleted_soft", {
id: subvaultID,
parentID: rec.data ? rec.data.parentID : undefined,
actor,
branch
});
return r;
}
existsSubvaultKey(itemID, key, actionInfo, branch = "subvault") {
return this.vaultManager.existsSubvaultKey(itemID, key, actionInfo, branch);
}
countSubvault(itemID, filter = {}, actionInfo, branch = "subvault") {
return this.vaultManager.countSubvault(itemID, filter, actionInfo, branch);
}
listSubvaultKeys(itemID, filter = {}, actionInfo, branch = "subvault") {
return this.vaultManager.listSubvaultKeys(itemID, filter, actionInfo, branch);
}
moveSubvault(subvaultID, toItemID, actionInfo, branch = "subvault") {
const updated = this.vaultManager.moveSubvault(subvaultID, toItemID, actionInfo, branch);
if (updated) {
this.eventsManager.emit("subvault.moved", {
id: subvaultID,
toParentID: toItemID,
branch
});
}
return updated;
}
cloneSubvaultRecords(fromItemID, toItemID, options = {}, actionInfo, fromBranch = "subvault", toBranch = null) {
const res = this.vaultManager.cloneSubvaultRecords(fromItemID, toItemID, options, actionInfo, fromBranch, toBranch);
this.eventsManager.emit("subvault.cloned", {
fromParentID: fromItemID,
toParentID: toItemID,
created: res.created,
fromBranch,
toBranch: toBranch || fromBranch
});
return res;
}
touchSubvault(subvaultID, actionInfo, branch = "subvault") {
return this.vaultManager.touchSubvault(subvaultID, actionInfo, branch);
}
setSubvaultStatus(subvaultID, status, actionInfo, branch = "subvault") {
return this.vaultManager.setSubvaultStatus(subvaultID, status, actionInfo, branch);
}
purgeExpiredSubvault(itemID, actionInfo, branch = "subvault") {
return this.vaultManager.purgeExpiredSubvault(itemID, actionInfo, branch);
}
trackItemMetric(itemID, metric, value = 1, actorID = null, extras = {}, actionInfo) {
if (!itemID || !metric) {
console.warn("Item ID and metric required.");
return {
ok: false,
reason: "invalid_args"
};
}
const item = this.vaultManager.getItem(itemID, actionInfo);
if (!item) {
console.warn("Item not found.");
return {
ok: false,
reason: "not_found"
};
}
const baseTags = {
itemID: itemID,
typeID: item.data?.typeID,
ownerID: item.data?.ownerID
};
if (actorID) {
baseTags.actorID = actorID;
}
const extraTags = extras?.tags && typeof extras.tags === "object" ? extras.tags : {};
const safeTags = {};
for (const key of Object.keys(extraTags)) {
const val = extraTags[key];
if (["string", "number", "boolean"].includes(typeof val)) {
safeTags[key] = val;
} else if (val != null) {
safeTags[key] = String(val);
}
}
const tags = Object.assign({}, safeTags, baseTags);
const metadata = extras?.metadata && typeof extras.metadata === "object" ? extras.metadata : undefined;
this.metricsManager.track(metric, value, tags, metadata);
this.eventsManager.emit("item.metric", {
id: itemID,
metric: metric,
value: value,
tags: tags,
metadata: metadata,
actorID: actorID
});
this.eventsManager.emit(`item.metric.${metric}`, {
id: itemID,
metric: metric,
value: value,
tags: tags,
metadata: metadata,
actorID: actorID
});
return {
ok: true
};
}
trackItemMetrics(batch = [], actionInfo) {
if (!Array.isArray(batch) || batch.length === 0) {
console.warn("Batch array required.");
return {
ok: false,
reason: "invalid_args"
};
}
let success = 0;
for (const rec of batch) {
const r = this.trackItemMetric(rec.itemID, rec.metric, rec.value ?? 1, rec.actorID ?? null, rec.extras ?? {}, actionInfo);
if (r && r.ok) {
success += 1;
}
}
return {
ok: true,
count: success
};
}
visit(id, actorID, context) {
const item = this.vaultManager.getItem(id);
if (!item) {
return;
}
this.metricsManager.track("visit", 1, {
itemID: id,
typeID: item.data.typeID,
ownerID: item.data.ownerID,
actorID
}, context);
this.eventsManager.emit("item.visit", {
id,
actorID,
context
});
}
like(id, actorID) {
const it = this.vaultManager.getItem(id);
if (!it) {
return;
}
this.metricsManager.track("like", +1, {
itemID: id,
typeID: it.data.typeID,
ownerID: it.data.ownerID,
actorID
});
this.eventsManager.emit("item.like", {
id,
actorID
});
}
unlike(id, actorID) {
const it = this.vaultManager.getItem(id);
if (!it) {
return;
}
this.metricsManager.track("like", -1, {
itemID: id,
typeID: it.data.typeID,
ownerID: it.data.ownerID,
actorID
});
this.eventsManager.emit("item.unlike", {
id,
actorID
});
}
favorite(id, actorID) {
const it = this.vaultManager.getItem(id);
if (!it) {
return;
}
this.metricsManager.track("favorite", +1, {
itemID: id,
typeID: it.data.typeID,
ownerID: it.data.ownerID,
actorID
});
this.eventsManager.emit("item.favorite", {
id,
actorID
});
}
unfavorite(id, actorID) {
const it = this.vaultManager.getItem(id);
if (!it) {
return;
}
this.metricsManager.track("favorite", -1, {
itemID: id,
typeID: it.data.typeID,
ownerID: it.data.ownerID,
actorID
});
this.eventsManager.emit("item.unfavorite", {
id,
actorID
});
}
reportItem(id, reporterID, reason, meta) {
const it = this.vaultManager.getItem(id);
if (!it) {
return;
}
this.metricsManager.track("report", 1, {
itemID: id,
typeID: it.data.typeID,
ownerID: it.data.ownerID,
reporterID,
reason
}, meta);
this.eventsManager.emit("item.report", {
id,
reporterID,
reason,
meta
});
}
addComment(itemID, ownerID, payload) {
return this.commentsManager.addComment(itemID, ownerID, payload);
}
listComments(itemID, params) {
return this.commentsManager.listComments(itemID, params);
}
reactToComment(commentID, actorID, rt) {
return this.commentsManager.reactToComment(commentID, actorID, rt);
}
reportComment(commentID, reporterID, reason, meta) {
return this.commentsManager.reportComment(commentID, reporterID, reason, meta);
}
proposeOffer(itemID, bidderID, input) {
return this.offersManager.proposeOffer(itemID, bidderID, input);
}
acceptOffer(offerID, ownerID) {
return this.offersManager.acceptOffer(offerID, ownerID);
}
rejectOffer(offerID, ownerID, reason) {
return this.offersManager.rejectOffer(offerID, ownerID, reason);
}
withdrawOffer(offerID, bidderID) {
return this.offersManager.withdrawOffer(offerID, bidderID);
}
listOffers(itemID, filter, page) {
return this.offersManager.listOffers(itemID, filter, page);
}
markItemSold(itemID, ownerID, sale) {
return this.offersManager.markItemSold(itemID, ownerID, sale);
}
toggleForSale(itemID, ownerID, forSale) {
return this.offersManager.toggleForSale(itemID, ownerID, forSale);
}
updatePrice(itemID, ownerID, price) {
return this.offersManager.updatePrice(itemID, ownerID, price);
}
startAuction(itemID, ownerID, cfg) {
return this.auctionsManager.startAuction(itemID, ownerID, cfg);
}
cancelAuction(auctionID, ownerID, reason) {
return this.auctionsManager.cancelAuction(auctionID, ownerID, reason);
}
getAuction(auctionID) {
return this.auctionsManager.getAuction(auctionID);
}
listAuctions(filter, page) {
return this.auctionsManager.listAuctions(filter, page);
}
watchAuction(auctionID, actorID) {
return this.auctionsManager.watchAuction(auctionID, actorID);
}
unwatchAuction(auctionID, actorID) {
return this.auctionsManager.unwatchAuction(auctionID, actorID);
}
placeBid(auctionID, bidderID, input) {
return this.auctionsManager.placeBid(auctionID, bidderID, input);
}
retractBid(bidID, bidderID, reason) {
return this.auctionsManager.retractBid(bidID, bidderID, reason);
}
listBids(auctionID, page) {
return this.auctionsManager.listBids(auctionID, page);
}
getLeadingBid(auctionID) {
return this.auctionsManager.getLeadingBid(auctionID);
}
endAuction(auctionID, systemActor) {
return this.auctionsManager.endAuction(auctionID, systemActor);
}
settleAuction(auctionID, ownerID, settlement) {
return this.auctionsManager.settleAuction(auctionID, ownerID, settlement);
}
query(params = {}) {
const {
typeID,
filter,
sort,
page,
projection,
geoContext,
actionInfo
} = params;
const qb = this.vaultManager.queryItems(typeID, filter, sort, page, projection, actionInfo);
let items = qb.execute(false);
const current = now();
items = items.filter(it => _assertClassBrand(_Ventry_brand, this, _filterByDeployment).call(this, it, geoContext)).filter(it => _assertClassBrand(_Ventry_brand, this, _applyExpiration).call(this, it, current));
return {
items,
nextCursor: undefined
};
}
feed(params = {}) {
const {
typeID,
limit = 20,
geoContext,
visibilityContext,
onRanking,
actionInfo
} = params;
const qb = this.vaultManager.queryItems(typeID, params.filter, params.sort, params.page, params.projection, actionInfo);
let items = qb.execute(false);
const current = now();
items = items.filter(it => it.data.status !== "hidden" && it.data.status !== "banned");
items = items.filter(it => _assertClassBrand(_Ventry_brand, this, _filterByDeployment).call(this, it, geoContext)).filter(it => _assertClassBrand(_Ventry_brand, this, _applyExpiration).call(this, it, current));
const scored = [];
for (const item of items) {
const it = item.data;
const metrics = this.metricsManager.readWindows(item.id);
const boosts = Array.isArray(it.boosts) ? it.boosts.filter(b => (!b.startsAt || b.startsAt <= current) && (!b.endsAt || current < b.endsAt)) : [];
const policy = this.visibilityPolicies.get(it.typeID);
let score = 0;
if (typeof onRanking === "function") {
score = _assertClassBrand(_Ventry_brand, this, _coerceScore).call(this, onRanking({
item,
metrics,
boosts,
geoContext,
visibilityContext,
now: current
}));
} else if (typeof policy === "function") {
score = _assertClassBrand(_Ventry_brand, this, _coerceScore).call(this, policy({
item,
metrics,
boosts,
geoContext,
visibilityContext,
now: current
}));
} else {
const ageH = Math.max(1, (current - (item.timestamp || current)) / 3.6e6);
const boostSum = boosts.reduce((s, b) => s + (b.weight || 0), 0);
const visits24h = metrics.visits24h || 0;
const fav7d = metrics.favorites7d || 0;
const likes7d = metrics.likes7d || 0;
score = (visits24h + likes7d * 4 + fav7d * 5) * (1 + boostSum) / Math.pow(ageH, 1.15);
}
scored.push({
item,
score
});
}
scored.sort((a, b) => b.score - a.score);
return {
items: scored.slice(0, limit).map(s => s.item),
nextCursor: undefined
};
}
getAllAnalytics(options = {}) {
return this.metricsManager.getAllMetrics(options);
}
getItemAnalytics(itemID, options = {}) {
return this.metricsManager.getItemMetrics(itemID, options);
}
getItemAnalyticsSummary(itemID, options = {}) {
return this.metricsManager.getItemAnalyticsSummary(itemID, options);
}
getItemMetricTimeSeries(itemID, metric, options = {}) {
return this.metricsManager.getItemMetricTimeSeries(itemID, metric, options);
}
getTopItemsBy(metric, options = {}) {
return this.metricsManager.getTopItemsByMetric(metric, options);
}
}
function _coerceScore(v) {
if (v == null) {
return 0;
}
if (typeof v === "number") {
return v;
}
if (typeof v === "boolean") {
return v ? 1 : 0;
}
if (typeof v === "object" && typeof v.score === "number") {
return v.score;
}
return 0;
}
function _filterByDeployment(item, geoContext) {
const deployment = item.data.deployment || {
scope: "global"
};
if (deployment.scope === "global") {
return true;
}
if (!geoContext) {
return true;
}
const {
allow,
deny
} = deployment.geo || {};
if (Array.isArray(deny) && deny.length > 0 && geoIncludes(geoContext, deny)) {
return false;
}
if (Array.isArray(allow) && allow.length > 0) {
return geoIncludes(geoContext, allow);
}
return true;
}
function _applyExpiration(item, current) {
const exp = item.data.expiration;
if (!exp) {
return true;
}
let expiresAt = exp.data.expiresAt;
if (!expiresAt && exp.data.ttlMs) expiresAt = (item.timestamp || current) + exp.data.ttlMs;
if (expiresAt && current >= expiresAt) {
const action = exp.onExpire || "hide";
if (action === "hide") {
this.vaultManager.updateItem(item.id, {
status: "hidden"
});
} else if (action === "archive") {
this.vaultManager.updateItem(item.id, {
status: "archived"
});
} else if (action === "delete") {
this.vaultManager.deleteRecord(item.id);
}
return false;
}
return true;
}
;
module.exports = {
Ventry
};