🌐 @trap_stevo/ventry
Turn anything into user-driven content.
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.
🚀 Features
- 🛠 Universal Content Engine – Create, update, and remove any content type dynamically
- 🧩 Type Registry – Define & evolve content types with custom visibility policies
- 💬 Interactions Out of the Box – Comments, reactions, likes, favorites, reports
- 💸 Commerce Ready – Offers, bids, auctions, settlements
- 📊 Integrated Metrics – Visits, favorites, likes, reports, top items, time series
- 🗓 Eventing & Scheduling – Emitted item lifecycle events + expiration scheduler
- 🌍 Geo / Deployment Filters – Region-aware visibility (allow/deny lists)
- ⚡ Feeds & Ranking – Built-in scoring or pluggable ranking callbacks
⚙️ System Requirements
| Node.js | ≥ 18.x |
| npm | ≥ 9.x (recommended) |
| OS | Windows, macOS, Linux |
⚙️ Configuration
🔧 Constructor
new Ventry(options?: VentryOptions)
VentryOptions (Top-Level)
contentVault | StarVault | string | null | — | Existing Star-Vault instance or a path/string to trigger internal creation (see vaultOptions). |
vaultOptions | object | {} | Options passed to internal ContentVaultInstanceManager (and used to construct Star-Vault when contentVault is not provided). |
contentMetrics | MetricTide | string | null | — | Existing MetricTide instance or options for internal creation (see metricOptions). |
metricOptions | object | {} | Options passed to internal Contalytics instance. |
vaultActionInfoDefault | object | {} | Default actionInfo metadata (propagated into vault calls). |
vaultClientAuthDefault | object | null | null | Default clientAuth context for vault operations. |
metricPrefix | string | "ugc" | Prefix for metric names (e.g., ugc.visit, ugc.like). |
eventPrefix | string | "ugc" | Prefix for emitted events. |
autoStartScheduler | boolean | true | Auto-start expiration scheduler on init. |
🗄️ Content Vault (Star-Vault) via vaultOptions
If you don’t pass an existing contentVault instance, Ventry will construct one with these Star-Vault parameters:
new StarVault(
dbPath,
logPath,
shardCount,
maxLogSize,
logRetention,
options
)
vaultOptions (Ventry)
dbPath | string | "./Ventry" | Star-Vault constructor dbPath. |
logPath | string | "./Ventry_Logs" | Star-Vault constructor logPath. |
shardCount | number | 4 | Star-Vault shardCount. |
maxLogSize | string | "869MB" | Star-Vault maxLogSize. |
logRetention | string | "1w" | Star-Vault logRetention. |
options | object | {} | Star-Vault low-level options (permissions, encryption, auth, etc.). |
prefix | string | "ugc" | Collection key prefix used by Ventry: items stored under ${prefix}-items, types under ${prefix}-types, etc. |
Collections used (derived from prefix):
${prefix}-types
${prefix}-items
${prefix}-comments
${prefix}-offers
${prefix}-auctions
${prefix}-bids
📈 Metrics (MetricTide) via metricOptions
If you don’t pass an existing contentMetrics instance, Ventry will construct one with these MetricTide parameters:
new MetricTide({
dbPath: "./.ventry-metrics",
persistence: { alwaysOn: true },
loadMetricsOnLaunch: true,
})
metricOptions (Ventry)
dbPath | string | "./.ventry-metrics" | Storage for metrics (MetricTide). |
persistence | object | { alwaysOn: true } | MetricTide persistence config. |
loadMetricsOnLaunch | boolean | true | Hydrate metrics on boot. |
metricPrefix | string | from Ventry | Prefix applied to tracked metrics (e.g., ugc.like). |
| (any MetricTide option) | any | — | Forwarded to MetricTide constructor. |
🧠 Content Types
Types let you define how a class of content behaves (posts, listings, experiences, etc.). You can attach visibility policies, defaults, validation rules, allowed statuses, indexing hints, and branch-specific subvault behavior—without changing your item schema.
Register types early at boot. You can update any field later with updateType.
🔧 Registering a Type
const { Ventry } = require("@trap_stevo/ventry");
const v = new Ventry({ });
v.registerType({
typeID : "post",
title : "User Post",
description : "Short text posts with optional media attachments",
defaults : {
status : "active",
deployment : { scope : "global" },
commentsEnabled : true,
data : {
maxChars : 280
},
labels : [],
tags : []
},
validate : ({ record, phase }) => {
if (!record.data || typeof record.data.text !== "string") {
throw "post.data.text is required";
}
if (record.data.text.length > (record.data.maxChars || 280)) {
throw "text exceeds maxChars";
}
},
allowedStatuses : [ "active", "hidden", "archived", "banned" ],
visibilityPolicy : ({ item, metrics, boosts, geoContext, visibilityContext, now }) => {
if (item.data.labels?.includes("flagged")) { return false; }
const base = (metrics.visits24h || 0) + (metrics.likes7d || 0) * 4 + (metrics.favorites7d || 0) * 5;
const isFeatured = item.data.labels?.includes("featured");
return isFeatured ? base * 1.25 : base;
},
indexes : [
{ key : "ownerID" },
{ key : "status" },
{ key : "tags" },
{ key : "timestamp" }
],
branches : {
"post_Reactions" : { ttlMs : 0 },
"post_Moderation" : { ttlMs : 0 }
}
});
Updating a Type
v.updateType("post", {
description : "Short text posts (≤ 280 chars) with optional media",
allowedStatuses : [ "active", "hidden", "archived", "banned", "locked" ],
defaults : {
commentsEnabled : false
}
});
🧩 TypeConfig Shape
typeID | string | Yes | Unique identifier for the type. |
title | string | No | Human-friendly name. |
description | string | No | What this type represents. |
defaults | object | No | Default fields merged into new items of this type. Supports any item fields (status, deployment, expiration, boosts, commentsEnabled, data, tags, labels, attachments). |
validate | function({ record, phase }) | No | Throw a string to reject; receives { phase : "create" | "update" }. |
allowedStatuses | string[] | No | Whitelist of legal statuses for items of this type. |
visibilityPolicy | function(ctx) | No | Custom ranking / gate. Return false to hide, true to allow (use default scoring), or number to override score. |
indexes | { key : string }[] | No | Index hints passed to the vault. |
branches | Record<string, { ttlMs? : number }> | No | Common subvault branches (convention only). |
⚠️ If validate throws, create/update will fail. Keep messages user-readable.
🎯 Important Props You Can Control Per Type
These are item-level fields that your type defaults and validation often manage:
status — One of your allowedStatuses. Common: "active", "hidden", "archived", "banned", "locked".
deployment — { scope : "global" \| "regional", geo? : { allow? : string[], deny? : string[] } }.
expiration — { data : { ttlMs? : number, expiresAt? : number }, onExpire : "hide" \| "archive" \| "delete" }.
boosts — [{ weight : number, startsAt? : number, endsAt? : number }] used by ranking.
commentsEnabled — boolean to toggle comment capability.
labels / tags — Freeform classification for moderation, ranking, search.
attachments — Array of attachment descriptors you define (URLs, blobs, content IDs).
data — Arbitrary payload. Put your domain fields here and validate in validate.
🔒 Status & Moderation
Enforce status constraints in your validate:
v.registerType({
typeID : "listing",
allowedStatuses : [ "draft", "active", "sold", "archived", "banned" ],
validate : ({ record, phase }) => {
const s = record.status;
if (s && ![ "draft", "active", "sold", "archived", "banned" ].includes(s)) {
throw "invalid status for listing";
}
if (s === "sold" && typeof record.data?.price !== "number") {
throw "sold listings must include final price in data.price";
}
}
});
🌍 Geo & Deployment Controls
- Global scope shows everywhere.
- Regional scope applies
deny first, then allow, else fallback allow.
v.registerType({
typeID : "event",
defaults : {
deployment : {
scope : "regional",
geo : { allow : [ "US", "CA" ], deny : [ "PR" ] }
}
}
});
⏳ Expiration Policies (Per Type Defaults)
v.registerType({
typeID : "story",
defaults : {
expiration : {
data : { ttlMs : 24 * 60 * 60 * 1000 },
onExpire : "delete"
}
}
});
⚡ Boosts & Ranking
Use boosts + visibilityPolicy to shape ranking windows:
v.registerType({
typeID : "announcement",
visibilityPolicy : ({ item, metrics, now }) => {
const within24h = (now - (item.timestamp || now)) < 86_400_000;
const base = (metrics.visits24h || 0) + (metrics.likes7d || 0) * 3;
return within24h ? base * 2 : base;
}
});
🧵 Subvault Branch Conventions
Declare common branches as documentation (you can still pass any branch at call-time):
v.registerType({
typeID : "experience",
branches : {
"Experience_Stats" : {},
"Experience_Sessions" : {}
}
});
v.upsertSubvaultByKey(expId, userId, "lifetime", { hours : 12 }, {}, null, "Experience_Stats");
🔍 Indexing Hints
Index keys you frequently filter on (e.g., status, ownerID, typeID, timestamp, tags).
v.registerType({
typeID : "photo",
indexes : [
{ key : "ownerID" },
{ key : "status" },
{ key : "labels" },
{ key : "timestamp" }
]
});
✅ Registering a Type
v.registerType({
typeID : "market_listing",
title : "Marketplace Listing",
description : "Buy/sell listings with ask price and inventory",
allowedStatuses : [ "draft", "active", "paused", "sold", "archived", "banned" ],
defaults : {
status : "draft",
commentsEnabled : true,
deployment : { scope : "global" },
data : {
currency : "USD",
inventory : 1
},
labels : [],
tags : []
},
validate : ({ record, phase }) => {
const d = record.data || {};
if (phase === "create" && typeof d.title !== "string") {
throw "listing.data.title is required";
}
if (typeof d.price !== "number" || d.price <= 0) {
throw "listing.data.price must be a positive number";
}
if (!Number.isInteger(d.inventory) || d.inventory < 0) {
throw "listing.data.inventory must be a non-negative integer";
}
if (record.status === "sold" && d.inventory !== 0) {
throw "sold listings must have inventory 0";
}
},
visibilityPolicy : ({ item, metrics }) => {
const price = item.data.data?.price || 0.50;
const w = price > 0 ? Math.min(1.0, 250 / price) : 1.0;
return ((metrics.visits24h || 0) + (metrics.likes7d || 0) * 3 + (metrics.favorites7d || 0) * 4) * w;
},
indexes : [
{ key : "status" },
{ key : "ownerID" },
{ key : "tags" },
{ key : "timestamp" }
],
branches : {
"Listing_Offers" : {},
"Listing_Audit" : {}
}
});
const { id } = await v.create("seller-42", {
typeID : "market_listing",
data : { title : "Gaming Laptop", price : 1299.00, inventory : 3 },
tags : [ "electronics", "laptop" ]
});
await v.update(id, { id : "seller-42", strictOwner : true }, { status : "active" });
🔍 API Specifications
🔧 Core Methods
create(ownerID, payload, actionInfo?) | (string, object, object?) | { id } | null | Creates a new content item. | Async |
getItems(actionInfo?) | (object?) | StarVaultQueryBuilder | Opens a chainable query on the items collection. Call `.execute(true | false)` to get results. |
get(id, actionInfo?) | (string, object?) | object | null | Retrieves an item by ID. | Async |
update(id, actor, patch, actionInfo?) | (string, object, object, object?) | object | null | Updates an item with patch semantics. | Async |
remove(id, actor, opts?, actionInfo?) | (string, object, { hard?: boolean }, object?) | object | null | Soft- or hard-deletes an item. | Async |
getContentRecordHistory(collectionID, id, options?, actionInfo?, clientAuth?) | (string, string, object?, object?, object?) | Array<object> | History for a specific record in a collection. | Sync |
getContentRecordTimeline(collectionID, id, options?, actionInfo?, clientAuth?) | (string, string, object?, object?, object?) | Array<object> | Timeline (ordered events) for a specific record. | Sync |
getContentHistory(collectionID, options?, actionInfo?, clientAuth?) | (string, object?, object?, object?) | Array<object> | History across a collection (items, types, etc.). | Sync |
getContentTimeline(collectionID, options?, actionInfo?, clientAuth?) | (string, object?, object?, object?) | Array<object> | Timeline view across a collection. | Sync |
getHistory(options?, actionInfo?, clientAuth?) | (object?, object?, object?) | Array<object> | Vault-wide history inspection across collections. | Sync |
syncHistory(actionInfo?, clientAuth?) | (object?, object?) | object | Forces history/log sync (e.g. compaction, ingestion). | Async |
registerType(config, actionInfo?) | (object, object?) | object | null | Registers or upserts a content type. | Sync |
updateType(typeID, patch, actionInfo?) | (string, object, object?) | object | null | Patches a registered content type. | Sync |
listTypes(actionInfo?) | (object?) | Array<object> | Lists all registered content types. | Sync |
feed(params) | (object) | { items, nextCursor } | Ranked feed with optional custom scoring. | Sync |
query(params) | (object) | { items, nextCursor } | Flexible item queries. | Sync |
trackItemMetric(itemID, metric, value?, actorID?, extras?, actionInfo?) | (string, string, number?, string?, { tags?: object, metadata?: object }?, object?) | { ok: boolean, reason?: string } | Universal metric tracker. Auto-tags with itemID, typeID, ownerID, optional actorID; merges extras.tags; passes extras.metadata. Emits item.metric and item.metric.<metric>. | Sync |
trackItemMetrics(batch, actionInfo?) | ({ itemID: string, metric: string, value?: number, actorID?: string, extras?: { tags?: object, metadata?: object } }[], object?) | { ok: boolean, count: number } | Batch version of trackItemMetric. Processes all records; returns count of successful writes. | Sync |
visit(id, actorID, ctx?) | (string, string, object?) | void | Tracks a visit metric, emits item.visit. | Sync |
like(id, actorID) | (string, string) | void | +1 like (metric), emits item.like. | Sync |
unlike(id, actorID) | (string, string) | void | −1 like (metric), emits item.unlike. | Sync |
favorite(id, actorID) | (string, string) | void | +1 favorite (metric), emits item.favorite. | Sync |
unfavorite(id, actorID) | (string, string) | void | −1 favorite (metric), emits item.unfavorite. | Sync |
reportItem(id, reporter, reason, meta?) | (string, string, any, any?) | void | Tracks a report metric, emits item.report. | Sync |
addComment(itemID, ownerID, payload) | (string, string, object) | object | Adds a comment via CommentsManager. | Sync |
listComments(itemID, params?) | (string, object?) | Array<object> | Lists comments. | Sync |
reactToComment(id, actorID, rt) | (string, string, string) | object | Reacts to a comment. | Sync |
reportComment(id, reporter, reason, meta?) | (string, string, any, any?) | object | Reports a comment. | Sync |
proposeOffer(itemID, bidderID, input) | (string, string, object) | object | Offer lifecycle (OffersManager). | Sync |
acceptOffer(offerID, ownerID) | (string, string) | object | Accepts an offer. | Sync |
rejectOffer(offerID, ownerID, reason) | (string, string, any) | object | Rejects an offer. | Sync |
withdrawOffer(offerID, bidderID) | (string, string) | object | Withdraws an offer. | Sync |
listOffers(itemID, filter?, page?) | (string, any?, any?) | Array<object> | Lists offers. | Sync |
markItemSold(itemID, ownerID, sale) | (string, string, object) | object | Marks an item sold. | Sync |
toggleForSale(itemID, ownerID, forSale) | (string, string, boolean) | object | Toggles listing state. | Sync |
updatePrice(itemID, ownerID, price) | (string, string, number) | object | Updates listing price. | Sync |
startAuction(itemID, ownerID, cfg) | (string, string, object) | object | Starts an auction. | Sync |
cancelAuction(auctionID, ownerID, reason) | (string, string, any) | object | Cancels an auction. | Sync |
getAuction(auctionID) | (string) | object | null | Reads an auction. | Sync |
listAuctions(filter?, page?) | (object?, any?) | Array<object> | Lists auctions. | Sync |
watchAuction(auctionID, actorID) | (string, string) | object | Watch an auction. | Sync |
unwatchAuction(auctionID, actorID) | (string, string) | object | Unwatch an auction. | Sync |
placeBid(auctionID, bidderID, input) | (string, string, object) | object | Places a bid. | Sync |
retractBid(bidID, bidderID, reason) | (string, string, any) | object | Retracts a bid. | Sync |
listBids(auctionID, page?) | (string, any?) | Array<object> | Lists bids. | Sync |
getLeadingBid(auctionID) | (string) | object | null | Gets current leader. | Sync |
endAuction(auctionID, systemActor) | (string, object) | object | Ends an auction. | Sync |
settleAuction(auctionID, ownerID, settlement) | (string, string, object) | object | Settles an auction. | Sync |
getAllAnalytics(opts?) | (object?) | Array<object> | All metrics (since window / filters). | Sync |
getItemAnalytics(itemID, opts?) | (string, object?) | Array<object> | Raw metrics for an item. | Sync/Async* |
getItemAnalyticsSummary(itemID, opts?) | (string, object?) | object | Aggregated counts for an item. | Async |
getItemMetricTimeSeries(itemID, metric, opts?) | (string, string, object?) | object | Metric time series (delegates to MetricTide). | Sync |
getTopItemsBy(metric, opts?) | (string, object?) | Array<{ itemID, total }> | Top items by a metric. | Async |
* getItemAnalytics performs an in-memory scan, and optionally augments with persisted “since” lookups via MetricTide if since is provided (async path).
🧭 Record History & Timeline
Ventry reconstructs record history and a human-readable timeline from write-ahead logs. These calls support filtering, integrity checks, decryption-aware output, and optional auditing.
options (history/timeline)
includeArchived | boolean | true | Include rotated *.archived segments. |
verifyChecksums | boolean | true | Verify SHA-256 checksums of entries; skip failures. |
includeSystem | boolean | true | Merge related system-level events (DELETE, TX markers). |
actions | string | string[] | undefined | Allow-list: e.g., "UPDATE" or ["WRITE","UPDATE"]. Case-insensitive. |
excludeActions | string | string[] | undefined | Block-list: e.g., ["DELETE"]. Case-insensitive. |
since | number | string | Date | undefined | Only include entries at/after this timestamp. |
until | number | string | Date | undefined | Only include entries at/before this timestamp. |
match | (row) => boolean | undefined | Custom predicate applied after other filters. |
includeDecrypted | boolean | auto | When true and encryption is enabled, decrypt entries and populate dataDecrypted. |
audit | boolean | undefined | Enables per-call auditing; see “Auditing”. |
History entries include source, collection, action, id, timestamp, client, data, checksum, and when present: eventID, transactionID, idempotencyKey. With includeDecrypted, output may contain dataEncrypted and dataDecrypted.
History Examples
const history = v.getContentRecordHistory("users/region1", "1760233994625", {
includeArchived : true,
verifyChecksums : true
}, { actor : "admin-1" }, { token : "valid" });
const updates = v.getContentRecordHistory("users/region1", "1760233994625", {
actions : "UPDATE",
since : Date.now() - 60_000,
until : Date.now()
});
const timeline = v.getContentRecordTimeline("users/region1", "1760233994625", {
excludeActions : ["DELETE", "SOFT_DELETE"]
}, { audit : true }, { token : "valid" });
const collectionHistory = v.getContentHistory("users/region1", {
includeArchived : true,
includeSystem : true
});
const vaultHistory = v.getHistory({
collections : ["users/*", "orders/*"],
actions : ["CREATE", "UPDATE"]
});
await v.syncHistory({}, { token : "valid" });
🧾 Auditing (History & Timeline Reads)
History/timeline reads can write entries to audit.log for compliance, governance, or access tracking.
Auditing activates when any of the following are true:
options.audit === true
actionInfo.audit === true
- Auditing enabled globally in vault initialization
Each audited read emits:
action : "HISTORY_READ" or "TIMELINE_READ"
details : { kind, collection, id, count, filters }
filters is sanitized (booleans, timestamps, allow/block lists, and a flag if match is used)
Per-call example
v.getContentRecordHistory("orders", "o-123", { audit : true });
Default auditing example
const v = new Ventry({
vaultOptions : {
options : {
auditHistory : true
}
}
});
Audit action names
HISTORY_READ
TIMELINE_READ
These cover record-level, collection-level, and global history/timeline reads.
⚠️ Privacy Notes
- If handling sensitive material, prefer
includeDecrypted : false unless absolutely required.
- For observability/logging, prefer logging IDs and checksums, not full decrypted data.
- Timeline output is intentionally compact; use full history mode for migrations or forensic analysis.
🧩 Item Subvaults
🔧 Subvault Methods
Items support nested, branch-aware data for structured, isolated extensions integrating seamlessly into storage, querying, and event workflows without altering the core item model.
These methods operate on the subvault branch by default, but you can provide a custom branch (e.g. "Experience_Stats").
addSubvault(itemID, ownerID, input?, actionInfo?, branch?) | (string, string, object?, object?, string?) | { id } | Adds a new Subvault record under an item. Emits subvault.created. | Sync |
listSubvault(itemID, opts?, actionInfo?, branch?) | (string, object?, object?, string?) | { items, nextCursor } | Lists all Subvault entries for a given parent item. | Sync |
getSubvaultByKey(itemID, key, actionInfo?, branch?) | (string, string, object?, string?) | object | null | Retrieves a Subvault record by key under the given item. | Sync |
upsertSubvaultByKey(itemID, ownerID, key, payload?, extras?, actionInfo?, branch?) | (string, string, string, object?, object?, object?, string?) | object | Creates or updates a Subvault record under the same key. Emits subvault.upserted. | Sync |
updateSubvault(subvaultID, actor, patch?, actionInfo?, branch?) | (string, object, object?, object?, string?) | object | null | Updates a Subvault entry. Performs patch semantics internally. Emits subvault.updated. | Sync |
removeSubvault(subvaultID, actor, opts?, actionInfo?, branch?) | (string, object, { hard?: boolean }?, object?, string?) | object | Deletes a Subvault (soft by default). Emits subvault.deleted_soft or subvault.deleted_hard. | Sync |
existsSubvaultKey(itemID, key, actionInfo?, branch?) | (string, string, object?, string?) | boolean | Returns whether a Subvault entry exists for the given key. | Sync |
countSubvault(itemID, filter?, actionInfo?, branch?) | (string, object?, object?, string?) | number | Counts Subvault entries matching a filter. | Sync |
listSubvaultKeys(itemID, filter?, actionInfo?, branch?) | (string, object?, object?, string?) | Array<string> | Returns all keys under the specified item. | Sync |
moveSubvault(subvaultID, toItemID, actionInfo?, branch?) | (string, string, object?, string?) | object | Moves a Subvault record to another parent. Emits subvault.moved. | Sync |
cloneSubvaultRecords(fromItemID, toItemID, options?, actionInfo?, fromBranch?, toBranch?) | (string, string, object?, object?, string?, string?) | { created } | Clones all or selected Subvault records between items. Emits subvault.cloned. | Sync |
touchSubvault(subvaultID, actionInfo?, branch?) | (string, object?, string?) | object | Updates updatedAt timestamp. | Sync |
setSubvaultStatus(subvaultID, status, actionInfo?, branch?) | (string, string, object?, string?) | object | Updates the Subvault record’s status. | Sync |
purgeExpiredSubvault(itemID?, actionInfo?, branch?) | (string?, object?, string?) | { deleted } | Removes expired Subvault records (expiresAt <= now). | Sync |
🔔 Subvault Events
The following events are emitted automatically through Ventry’s event system:
subvault.created | When a new Subvault record is added. |
subvault.updated | When a Subvault is modified. |
subvault.upserted | When a Subvault is created or updated by key. |
subvault.deleted_soft | When a Subvault is soft-deleted. |
subvault.deleted_hard | When a Subvault is hard-deleted. |
subvault.moved | When a Subvault is moved to another parent. |
subvault.cloned | When Subvault records are cloned between items. |
🔔 Events & Scheduler
Events API (from EventsManager)
on(event, fn) | Subscribe to Ventry events. |
once(event, fn) | One-shot subscription. |
off(event, fn) | Unsubscribe. |
setEventPrefix(prefix) | Change runtime event prefix. |
configureScheduler(cfg) | Configure expiration scheduler. |
startScheduler() / stopScheduler() | Control scheduler loop. |
Item lifecycle events emitted by Ventry (prefix configurable):
item.created
item.updated
item.deleted_soft
item.deleted_hard
item.visit
item.like / item.unlike
item.favorite / item.unfavorite
item.report
🌍 Deployment & Geo Visibility
Items can define:
deployment: {
scope: "global",
geo: {
allow: ["US", "CA"],
deny: ["CN"]
}
}
At query/feed time Ventry applies deny→allow→fallback logic.
Pass geoContext to query/feed to enable filtering.
⏳ Expiration Policies
Items can define:
expiration: {
data: { ttlMs: 86400000 },
onExpire: "archive"
}
Scheduler will hide/archive/delete expired items.
📈 Feed Ranking
Default score:
score = ((visits24h + likes7d*4 + favorites7d*5) * (1 + boostSum)) / ageHours^1.15
Provide onRanking({ item, metrics, boosts, now }) for custom ranking.
📦 Installation
npm install @trap_stevo/ventry
🛠️ Usage
const { Ventry } = require("@trap_stevo/ventry");
const v = new Ventry({
vaultOptions: {
prefix : "myapp",
dbPath : "./MyApp",
logPath : "./MyApp_Logs",
options : {
enableEncryption : false,
authHandler : (auth) => {
return auth.token === "any-token";
}
}
},
metricOptions: {
dbPath: "./.myappalytics"
},
metricPrefix: "myapp",
eventPrefix: "myapp",
vaultActionInfoDefault : { source : `myapp_core_${Date.now()}` },
vaultClientAuthDefault : { token : "any-token" }
});
const { id } = await v.create("user-123", {
typeID: "post",
data: { text: "Hello, world!" },
commentsEnabled: true
});
v.like(id, "user-456");
v.visit(id, "user-789", { ref: "home" });
const feed = v.feed({ typeID: "post", limit: 10 });
console.log(feed.items);
✨ Example: Rich Create
const { id } = await v.create("seller-1", {
typeID: "listing",
data: { title: "Concert tickets", price: 120 },
tags: ["tickets", "event"],
deployment: { scope: "regional", geo: { allow: ["US"] } },
expiration: { data: { ttlMs: 86400000 }, onExpire: "archive" },
boosts: [{ weight: 0.25, startsAt: Date.now(), endsAt: Date.now() + 3600_000 }],
commentsEnabled: true
});
✨ Example: Subvault Usage
const sv = v.addSubvault("item-123", "user-123", {
key: "stats",
payload: { level: 5, exp: 340 },
tags: ["progress", "gameplay"]
});
v.updateSubvault(sv.id, { id: "user-123" }, { payload: { level: 6 } });
v.upsertSubvaultByKey("item-123", "user-123", "stats", { level: 7 });
const list = v.listSubvault("item-123");
v.cloneSubvaultRecords("item-123", "item-999", { includeKeys: ["stats"] }, null, "subvault", "Experience_Stats");
📜 License
See License in LICENSE.md
🌐 Ventry — The Gateway to User-Driven Content.
Transform anything into interactive, measurable, and living content with one API.