Socket
Book a DemoInstallSign in
Socket

@trap_stevo/ventry

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@trap_stevo/ventry

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.

latest
npmnpm
Version
0.0.12
Version published
Weekly downloads
23
-39.47%
Maintainers
1
Weekly downloads
 
Created
Source

🌐 @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

RequirementVersion
Node.js≥ 18.x
npm≥ 9.x (recommended)
OSWindows, macOS, Linux

⚙️ Configuration

🔧 Constructor

new Ventry(options?: VentryOptions)

VentryOptions (Top-Level)

KeyTypeDefaultDescription
contentVaultStarVault | string | nullExisting Star-Vault instance or a path/string to trigger internal creation (see vaultOptions).
vaultOptionsobject{}Options passed to internal ContentVaultInstanceManager (and used to construct Star-Vault when contentVault is not provided).
contentMetricsMetricTide | string | nullExisting MetricTide instance or options for internal creation (see metricOptions).
metricOptionsobject{}Options passed to internal Contalytics instance.
vaultActionInfoDefaultobject{}Default actionInfo metadata (propagated into vault calls).
vaultClientAuthDefaultobject | nullnullDefault clientAuth context for vault operations.
metricPrefixstring"ugc"Prefix for metric names (e.g., ugc.visit, ugc.like).
eventPrefixstring"ugc"Prefix for emitted events.
autoStartSchedulerbooleantrueAuto-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,         // default: "./Ventry"
  logPath,        // default: "./Ventry_Logs"
  shardCount,     // default: 4
  maxLogSize,     // default: "869MB"
  logRetention,   // default: "1w"
  options         // default: {}
)

vaultOptions (Ventry)

KeyTypeDefaultUsed For
dbPathstring"./Ventry"Star-Vault constructor dbPath.
logPathstring"./Ventry_Logs"Star-Vault constructor logPath.
shardCountnumber4Star-Vault shardCount.
maxLogSizestring"869MB"Star-Vault maxLogSize.
logRetentionstring"1w"Star-Vault logRetention.
optionsobject{}Star-Vault low-level options (permissions, encryption, auth, etc.).
prefixstring"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,
  // ...your metricOptions (merged)
})

metricOptions (Ventry)

KeyTypeDefaultDescription
dbPathstring"./.ventry-metrics"Storage for metrics (MetricTide).
persistenceobject{ alwaysOn: true }MetricTide persistence config.
loadMetricsOnLaunchbooleantrueHydrate metrics on boot.
metricPrefixstringfrom VentryPrefix applied to tracked metrics (e.g., ugc.like).
(any MetricTide option)anyForwarded 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({ /* ...options... */ });

v.registerType({
     typeID : "post",

     // Identity & docs
     title : "User Post",
     description : "Short text posts with optional media attachments",

     // Default fields applied to new items of this type
     defaults : {
          status : "active",
          deployment : { scope : "global" },
          commentsEnabled : true,
          data : {
               maxChars : 280
          },
          labels : [],
          tags : []
     },

     // Validation run on create/update (throw string to reject)
     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";
          }
     },

     // Allowed lifecycle states for this type
     allowedStatuses : [ "active", "hidden", "archived", "banned" ],

     // Visibility policy used by feed/query (overrides default scoring if returns a number)
     // Return a number for rank score, or a boolean to allow/deny.
     visibilityPolicy : ({ item, metrics, boosts, geoContext, visibilityContext, now }) =>  {
          // Example: deny if explicitly flagged
          if (item.data.labels?.includes("flagged")) { return false; }

          // Example: soft boost if "featured"
          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;
     },

     // Indexing hints (forwarded to the underlying vault)
     // Useful keys to accelerate queries
     indexes : [
          { key : "ownerID" },
          { key : "status" },
          { key : "tags" },
          { key : "timestamp" }
     ],

     // Subvault branches commonly used with this type (optional, declarative)
     // Purely a convention aid; you can still use any branch name ad-hoc.
     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

FieldTypeRequiredDescription
typeIDstringYesUnique identifier for the type.
titlestringNoHuman-friendly name.
descriptionstringNoWhat this type represents.
defaultsobjectNoDefault fields merged into new items of this type. Supports any item fields (status, deployment, expiration, boosts, commentsEnabled, data, tags, labels, attachments).
validatefunction({ record, phase })NoThrow a string to reject; receives { phase : "create" | "update" }.
allowedStatusesstring[]NoWhitelist of legal statuses for items of this type.
visibilityPolicyfunction(ctx)NoCustom ranking / gate. Return false to hide, true to allow (use default scoring), or number to override score.
indexes{ key : string }[]NoIndex hints passed to the vault.
branchesRecord<string, { ttlMs? : number }>NoCommon 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.
  • commentsEnabledboolean 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" : {}
     }
});

// Usage
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 }) =>  {
          // Promote fresh, popular, reasonably priced items
          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" : {}
     }
});

// Create with type defaults + validation
const { id } = await v.create("seller-42", {
     typeID : "market_listing",
     data : { title : "Gaming Laptop", price : 1299.00, inventory : 3 },
     tags : [ "electronics", "laptop" ]
});

// Move to active (allowed by type)
await v.update(id, { id : "seller-42", strictOwner : true }, { status : "active" });

🔍 API Specifications

🔧 Core Methods

MethodSignatureReturnsDescriptionAsync/Sync
create(ownerID, payload, actionInfo?)(string, object, object?){ id } | nullCreates a new content item.Async
getItems(actionInfo?)(object?)StarVaultQueryBuilderOpens a chainable query on the items collection. Call `.execute(truefalse)` to get results.
get(id, actionInfo?)(string, object?)object | nullRetrieves an item by ID.Async
update(id, actor, patch, actionInfo?)(string, object, object, object?)object | nullUpdates an item with patch semantics.Async
remove(id, actor, opts?, actionInfo?)(string, object, { hard?: boolean }, object?)object | nullSoft- 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?)objectForces history/log sync (e.g. compaction, ingestion).Async
registerType(config, actionInfo?)(object, object?)object | nullRegisters or upserts a content type.Sync
updateType(typeID, patch, actionInfo?)(string, object, object?)object | nullPatches 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?)voidTracks 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?)voidTracks a report metric, emits item.report.Sync
addComment(itemID, ownerID, payload)(string, string, object)objectAdds a comment via CommentsManager.Sync
listComments(itemID, params?)(string, object?)Array<object>Lists comments.Sync
reactToComment(id, actorID, rt)(string, string, string)objectReacts to a comment.Sync
reportComment(id, reporter, reason, meta?)(string, string, any, any?)objectReports a comment.Sync
proposeOffer(itemID, bidderID, input)(string, string, object)objectOffer lifecycle (OffersManager).Sync
acceptOffer(offerID, ownerID)(string, string)objectAccepts an offer.Sync
rejectOffer(offerID, ownerID, reason)(string, string, any)objectRejects an offer.Sync
withdrawOffer(offerID, bidderID)(string, string)objectWithdraws an offer.Sync
listOffers(itemID, filter?, page?)(string, any?, any?)Array<object>Lists offers.Sync
markItemSold(itemID, ownerID, sale)(string, string, object)objectMarks an item sold.Sync
toggleForSale(itemID, ownerID, forSale)(string, string, boolean)objectToggles listing state.Sync
updatePrice(itemID, ownerID, price)(string, string, number)objectUpdates listing price.Sync
startAuction(itemID, ownerID, cfg)(string, string, object)objectStarts an auction.Sync
cancelAuction(auctionID, ownerID, reason)(string, string, any)objectCancels an auction.Sync
getAuction(auctionID)(string)object | nullReads an auction.Sync
listAuctions(filter?, page?)(object?, any?)Array<object>Lists auctions.Sync
watchAuction(auctionID, actorID)(string, string)objectWatch an auction.Sync
unwatchAuction(auctionID, actorID)(string, string)objectUnwatch an auction.Sync
placeBid(auctionID, bidderID, input)(string, string, object)objectPlaces a bid.Sync
retractBid(bidID, bidderID, reason)(string, string, any)objectRetracts a bid.Sync
listBids(auctionID, page?)(string, any?)Array<object>Lists bids.Sync
getLeadingBid(auctionID)(string)object | nullGets current leader.Sync
endAuction(auctionID, systemActor)(string, object)objectEnds an auction.Sync
settleAuction(auctionID, ownerID, settlement)(string, string, object)objectSettles 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?)objectAggregated counts for an item.Async
getItemMetricTimeSeries(itemID, metric, opts?)(string, string, object?)objectMetric 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)

KeyTypeDefaultNotes
includeArchivedbooleantrueInclude rotated *.archived segments.
verifyChecksumsbooleantrueVerify SHA-256 checksums of entries; skip failures.
includeSystembooleantrueMerge related system-level events (DELETE, TX markers).
actionsstring | string[]undefinedAllow-list: e.g., "UPDATE" or ["WRITE","UPDATE"]. Case-insensitive.
excludeActionsstring | string[]undefinedBlock-list: e.g., ["DELETE"]. Case-insensitive.
sincenumber | string | DateundefinedOnly include entries at/after this timestamp.
untilnumber | string | DateundefinedOnly include entries at/before this timestamp.
match(row) => booleanundefinedCustom predicate applied after other filters.
includeDecryptedbooleanautoWhen true and encryption is enabled, decrypt entries and populate dataDecrypted.
auditbooleanundefinedEnables 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

// Full history (archived segments, checksum verification)
const history = v.getContentRecordHistory("users/region1", "1760233994625", {
  includeArchived : true,
  verifyChecksums : true
}, { actor : "admin-1" }, { token : "valid" });

// Windowed, updates only
const updates = v.getContentRecordHistory("users/region1", "1760233994625", {
  actions : "UPDATE",
  since : Date.now() - 60_000,
  until : Date.now()
});

// Compact timeline, no deletions, explicit audit
const timeline = v.getContentRecordTimeline("users/region1", "1760233994625", {
  excludeActions : ["DELETE", "SOFT_DELETE"]
}, { audit : true }, { token : "valid" });

// Collection-level history
const collectionHistory = v.getContentHistory("users/region1", {
  includeArchived : true,
  includeSystem : true
});

// Vault-wide history with collection filtering
const vaultHistory = v.getHistory({
  collections : ["users/*", "orders/*"],
  actions : ["CREATE", "UPDATE"]
});

// Synchronous WAL flush before backup/export
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.

📥 Content Imports

Ventry exposes structured, high-integrity content import helpers for migrating data, seeding environments, snapshot restoration, collection cloning, and bulk ingestion pipelines.
All import functions support per-call options, actionInfo, and clientAuth.

Methods

MethodSignatureReturnsDescriptionSync/Async
importContentRecord(collectionID, record, options?, actionInfo?, clientAuth?)(string, object, object?, object?, object?)objectImports a single record into a collection. Validates structure, preserves timestamps, and applies import-mode semantics.Sync
importManyContentRecords(collectionID, records, options?, actionInfo?, clientAuth?)(string, object[], object?, object?, object?){ imported, failed }Bulk import for many records. Performs batched write-ahead logging and integrity checks.Sync
importContent(collectionID, records, options?, actionInfo?, clientAuth?)(string, any, object?, object?, object?){ imported, failed }Flexible import accepting arrays, maps, or normalized payloads. Best for ETL pipelines or heterogeneous migrations.Sync
importSnapshot(snapshot, options?, actionInfo?, clientAuth?)(object, object?, object?, object?){ collections, count }Restores a full snapshot of collections/items/types into the vault. Supports filtered restore, ID remapping, and migration guards.Sync

Notes on Import Behavior

  • Import operations bypass typical lifecycle flows (e.g., ranking, comments, reactions) unless explicitly included in the payload.
  • Timestamps (createdAt, updatedAt) are preserved when provided.
  • ID collisions can be handled via options (overwrite, skip, or error).
  • Import obeys all encryption/decryption behavior automatically.
  • Ideal for:
    • Initial seeding
    • Local & CI environment setup
    • Migrating between environments
    • Restoring from backups
    • Cloning tenants/regions/apps
    • Reconstructing data for replay/testing

Example: Import One Record

v.importContentRecord("items", {
  id : "post-001",
  ownerID : "user-123",
  typeID : "post",
  data : { text : "Migrated post" },
  timestamp : 1700000000000
}, {
  overwrite : true
}, { source : "migration" }, { token : "system" });

Example: Bulk Import

const result = v.importManyContentRecords("items", [
  { id : "a", ownerID : "u1", typeID : "post", data : { text : "Hello" } },
  { id : "b", ownerID : "u2", typeID : "post", data : { text : "World" } }
], {
  skipExisting : true
});

Example: Snapshot Import

// A snapshot may contain multiple collections.
const snapshot = {
  "ugc-items" : [{ id : "1", ownerID : "x", typeID : "post", data : { text : "Imported" } }],
  "ugc-types" : [{ typeID : "post", defaults : { commentsEnabled : true } }]
};

v.importSnapshot(snapshot, {
  collections : ["ugc-items", "ugc-types"]
}, { source : "snapshot_restore" }, { token : "system" });

🧩 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").

MethodSignatureReturnsDescriptionAsync/Sync
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 | nullRetrieves 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?)objectCreates 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 | nullUpdates a Subvault entry. Performs patch semantics internally. Emits subvault.updated.Sync
removeSubvault(subvaultID, actor, opts?, actionInfo?, branch?)(string, object, { hard?: boolean }?, object?, string?)objectDeletes a Subvault (soft by default). Emits subvault.deleted_soft or subvault.deleted_hard.Sync
existsSubvaultKey(itemID, key, actionInfo?, branch?)(string, string, object?, string?)booleanReturns whether a Subvault entry exists for the given key.Sync
countSubvault(itemID, filter?, actionInfo?, branch?)(string, object?, object?, string?)numberCounts 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?)objectMoves 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?)objectUpdates updatedAt timestamp.Sync
setSubvaultStatus(subvaultID, status, actionInfo?, branch?)(string, string, object?, string?)objectUpdates 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:

EventTrigger
subvault.createdWhen a new Subvault record is added.
subvault.updatedWhen a Subvault is modified.
subvault.upsertedWhen a Subvault is created or updated by key.
subvault.deleted_softWhen a Subvault is soft-deleted.
subvault.deleted_hardWhen a Subvault is hard-deleted.
subvault.movedWhen a Subvault is moved to another parent.
subvault.clonedWhen Subvault records are cloned between items.

🔔 Events & Scheduler

Events API (from EventsManager)

MethodDescription
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" }
});

// Create an item
const { id } = await v.create("user-123", {
  typeID: "post",
  data: { text: "Hello, world!" },
  commentsEnabled: true
});

// Act on it
v.like(id, "user-456");
v.visit(id, "user-789", { ref: "home" });

// Query a feed
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

// Add experience data under an item
const sv = v.addSubvault("item-123", "user-123", {
  key: "stats",
  payload: { level: 5, exp: 340 },
  tags: ["progress", "gameplay"]
});

// Update later
v.updateSubvault(sv.id, { id: "user-123" }, { payload: { level: 6 } });

// Retrieve or upsert by key
v.upsertSubvaultByKey("item-123", "user-123", "stats", { level: 7 });

// List all Subvaults under an item
const list = v.listSubvault("item-123");

// Clone all Subvaults from one item to another branch
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.

Keywords

ugc

FAQs

Package last updated on 25 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts