Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

medici

Package Overview
Dependencies
Maintainers
2
Versions
57
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

medici - npm Package Compare versions

Comparing version 6.2.0 to 6.3.0

93

build/Book.js

@@ -23,2 +23,15 @@ "use strict";

};
function fromDistinctToAccounts(distinctResult) {
const accountsSet = new Set();
for (const fullAccountName of distinctResult) {
const paths = fullAccountName.split(":");
let path = paths[0];
accountsSet.add(path);
for (let i = 1; i < paths.length; ++i) {
path += ":" + paths[i];
accountsSet.add(path);
}
}
return Array.from(accountsSet);
}
class Book {

@@ -66,3 +79,3 @@ constructor(name, options = {}) {

accountForBalanceSnapshot = query.account ? [].concat(query.account).join() : undefined;
balanceSnapshot = await (0, balance_1.getBestSnapshot)({
balanceSnapshot = await (0, balance_1.getBestBalanceSnapshot)({
book: parsedQuery.book,

@@ -105,5 +118,5 @@ account: accountForBalanceSnapshot,

// There is a snapshot already. But let's check if it's too old.
const tooOld = Date.now() > balanceSnapshot.createdAt.getTime() + this.balanceSnapshotSec * 1000;
const isSnapshotObsolete = Date.now() > balanceSnapshot.createdAt.getTime() + this.balanceSnapshotSec * 1000;
// If it's too old we would need to cache another snapshot.
if (tooOld) {
if (isSnapshotObsolete) {
delete parsedQuery._id;

@@ -238,13 +251,69 @@ const match = { $match: parsedQuery };

async listAccounts(options = {}) {
const results = await transaction_1.transactionModel.collection.distinct("accounts", { book: this.name }, { session: options.session });
const uniqueAccounts = new Set();
for (const result of results) {
const prev = [];
const paths = result.split(":");
for (const acct of paths) {
prev.push(acct);
uniqueAccounts.add(prev.join(":"));
// If there is a session, we must NOT set any readPreference (as per mongo v5 and v6).
// https://www.mongodb.com/docs/v6.0/core/transactions/#read-concern-write-concern-read-preference
// Otherwise, we are free to use any readPreference.
if (options && !options.session && !options.readPreference) {
// Let's try reading from the secondary node, if available.
options.readPreference = "secondaryPreferred";
}
const query = { book: this.name };
let listAccountsSnapshot = null;
if (this.balanceSnapshotSec) {
listAccountsSnapshot = await (0, balance_1.getBestListAccountsSnapshot)({ book: this.name });
if (listAccountsSnapshot) {
// Use cached balance
query.timestamp = { $gte: listAccountsSnapshot.createdAt };
}
}
return Array.from(uniqueAccounts);
const createdAt = new Date(); // take date earlier
const results = (await transaction_1.transactionModel.collection.distinct("accounts", query, {
session: options.session,
}));
let uniqueAccounts = fromDistinctToAccounts(results);
if (listAccountsSnapshot) {
uniqueAccounts = Array.from(new Set([...uniqueAccounts, ...listAccountsSnapshot.meta.accounts]));
}
if (uniqueAccounts.length === 0)
return uniqueAccounts;
if (this.balanceSnapshotSec) {
// It's the first (ever?) snapshot for this listAccounts. We just need to save whatever we've just aggregated
// so that the very next listAccounts query would use cached snapshot.
if (!listAccountsSnapshot) {
await (0, balance_1.snapshotListAccounts)({
book: this.name,
accounts: uniqueAccounts,
createdAt,
expireInSec: this.expireBalanceSnapshotSec,
});
}
else {
// There is a snapshot already. But let's check if it's too old.
const isSnapshotObsolete = Date.now() > listAccountsSnapshot.createdAt.getTime() + this.balanceSnapshotSec * 1000;
// If it's too old we would need to cache another snapshot.
if (isSnapshotObsolete) {
delete query.timestamp;
// Important! We are going to recalculate the entire listAccounts from the day one.
// Since this operation can take seconds (if you have millions of documents)
// we better run this query IN THE BACKGROUND.
// If this exact balance query would be executed multiple times at the same second we might end up with
// multiple snapshots in the database. Which is fine. The chance of this happening is low.
// Our main goal here is not to delay this .listAccounts() method call. The tradeoff is that
// database will use 100% CPU for few (milli)seconds, which is fine. It's all fine (C)
transaction_1.transactionModel.collection
.distinct("accounts", query, {
session: options.session,
})
.then((results) => (0, balance_1.snapshotListAccounts)({
book: this.name,
accounts: fromDistinctToAccounts(results),
createdAt,
expireInSec: this.expireBalanceSnapshotSec,
}))
.catch((error) => {
console.error("medici: Couldn't do background listAccounts snapshot.", error);
});
}
}
}
return uniqueAccounts;
}

@@ -251,0 +320,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBestSnapshot = exports.snapshotBalance = exports.constructKey = exports.hashKey = exports.setBalanceSchema = exports.balanceModel = void 0;
exports.getBestListAccountsSnapshot = exports.snapshotListAccounts = exports.getBestBalanceSnapshot = exports.snapshotBalance = exports.constructKey = exports.hashKey = exports.setBalanceSchema = exports.balanceModel = void 0;
const crypto_1 = require("crypto");

@@ -71,3 +71,3 @@ const mongoose_1 = require("mongoose");

exports.snapshotBalance = snapshotBalance;
function getBestSnapshot(query, options = {}) {
function getBestBalanceSnapshot(query, options = {}) {
const { book, account, meta, ...extras } = query;

@@ -77,2 +77,26 @@ const key = hashKey(constructKey(book, account, { ...meta, ...extras }));

}
exports.getBestSnapshot = getBestSnapshot;
exports.getBestBalanceSnapshot = getBestBalanceSnapshot;
async function snapshotListAccounts(balanceData, options = {}) {
const rawKey = "@listAccounts:" + balanceData.book;
const key = hashKey(rawKey);
const balanceDoc = {
key,
rawKey,
book: balanceData.book,
meta: { accounts: balanceData.accounts },
createdAt: balanceData.createdAt,
expireAt: new Date(balanceData.createdAt.getTime() + balanceData.expireInSec * 1000),
};
const result = await exports.balanceModel.collection.insertOne(balanceDoc, {
session: options.session,
writeConcern: options.session ? undefined : { w: 1, j: true },
forceServerObjectId: true,
});
return result.acknowledged;
}
exports.snapshotListAccounts = snapshotListAccounts;
async function getBestListAccountsSnapshot(query, options = {}) {
const key = hashKey("@listAccounts:" + query.book);
return (await exports.balanceModel.collection.findOne({ key }, { sort: { _id: -1 }, ...options }));
}
exports.getBestListAccountsSnapshot = getBestListAccountsSnapshot;

7

package.json
{
"name": "medici",
"version": "6.2.0",
"version": "6.3.0",
"description": "Double-entry accounting ledger for Node + Mongoose",

@@ -9,2 +9,4 @@ "main": "build/index.js",

"ci": "npm run build && npm run test",
"ci-mongoose8": "npm i mongoose@8 && npm run test:code",
"ci-mongoose7": "npm i mongoose@7 && npm run test:code",
"ci-mongoose6": "npm i mongoose@6 && npm run test:code",

@@ -53,3 +55,3 @@ "ci-mongoose5": "npm i mongoose@5 && npm run test:code",

"dependencies": {
"mongoose": "5 - 7"
"mongoose": "5 - 8"
},

@@ -60,2 +62,3 @@ "homepage": "https://github.com/flash-oss/medici",

"@types/chai": "^4.3.5",
"@types/luxon": "^3.3.5",
"@types/mocha": "^10.0.1",

@@ -62,0 +65,0 @@ "@types/node": "^18.16.17",

@@ -405,2 +405,16 @@ # medici

### 6.3
- The `book.listAccounts()` method is now cached same way the `book.balance()` is cached.
- Add `mongoose` v8 support.
### 6.2
- Add `mongoose` v7 support.
- Add Node 20 support.
### 6.1
- Add MongoDB v6 support.
### 6.0

@@ -413,3 +427,3 @@

- The balances cache primary key is now a SHA1 hash of the previous value. Before: `"MyBook;Account;clientId.$in.0:12345,clientId.$in.1:67890,currency:USD"`. After: `"\u001b\u0004NÞj\u0013rÅ\u001b¼,F_#\u001cÔk Nv"`. Allows each key to be exactly 40 bytes (20 chars) regadless the actual balance query text length.
- The balances cache primary key is now a SHA1 hash of the previous value. Before: `"MyBook;Account;clientId.$in.0:12345,clientId.$in.1:67890,currency:USD"`. After: `"\u001b\u0004NÞj\u0013rÅ\u001b¼,F_#\u001cÔk Nv"`. Allows each key to be exactly 40 bytes (20 chars) regardless the actual balance query text length.
- But the old raw unhashed key is now stored in `rawKey` of `medici_balances` for DX and troubleshooting purposes.

@@ -416,0 +430,0 @@ - Fixed important bugs #58 and #70 related to retrieving balance for a custom schema properties. Thanks @dolcalmi

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc