@blamejs/blamejs-shop 0.3.44 → 0.3.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/catalog.js +508 -9
- package/lib/product-bulk-ops.js +10 -6
- package/lib/product-qa.js +40 -0
- package/lib/sales-tax-filings.js +8 -12
- package/lib/storefront.js +250 -244
- package/lib/subscription-analytics.js +30 -17
- package/lib/tax.js +20 -25
- package/lib/wishlist-alerts.js +3 -2
- package/lib/wishlist-digest.js +4 -1
- package/package.json +1 -1
package/lib/product-qa.js
CHANGED
|
@@ -652,6 +652,45 @@ function create(opts) {
|
|
|
652
652
|
return rows;
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
+
// Batched answersForQuestion — every approved answer under EACH of
|
|
656
|
+
// `questionIds`, grouped into a map keyed by question_id. One `IN`
|
|
657
|
+
// query replaces the per-question `answersForQuestion` loop the PDP
|
|
658
|
+
// ran (one D1-bridge hop per question). The approved-only filter and
|
|
659
|
+
// the (pinned DESC, vote_count DESC, occurred_at ASC, id ASC) order
|
|
660
|
+
// are byte-for-byte the single-question method's, so the stitched
|
|
661
|
+
// thread renders identically. `opts.limit` caps the answers PER
|
|
662
|
+
// question (validated like the single method) — applied
|
|
663
|
+
// application-side after the grouped read so a high-fan-out question
|
|
664
|
+
// can't starve another. Each id is validated at entry (a malformed
|
|
665
|
+
// id throws TypeError, which the route catches). An empty input or a
|
|
666
|
+
// question with no approved answers maps to an empty array.
|
|
667
|
+
async function answersForQuestions(questionIds, listOpts) {
|
|
668
|
+
if (!Array.isArray(questionIds) || questionIds.length === 0) return {};
|
|
669
|
+
listOpts = listOpts || {};
|
|
670
|
+
var limit = _limit(listOpts.limit);
|
|
671
|
+
var ids = [];
|
|
672
|
+
var byId = {};
|
|
673
|
+
for (var qi = 0; qi < questionIds.length; qi += 1) {
|
|
674
|
+
var id = _uuid(questionIds[qi], "question_id");
|
|
675
|
+
if (byId[id]) continue;
|
|
676
|
+
byId[id] = [];
|
|
677
|
+
ids.push(id);
|
|
678
|
+
}
|
|
679
|
+
var placeholders = ids.map(function (_v, n) { return "?" + (n + 1); }).join(", ");
|
|
680
|
+
var r = await query(
|
|
681
|
+
"SELECT * FROM product_qa_answers " +
|
|
682
|
+
"WHERE question_id IN (" + placeholders + ") AND status = 'approved' " +
|
|
683
|
+
"ORDER BY pinned DESC, vote_count DESC, occurred_at ASC, id ASC",
|
|
684
|
+
ids,
|
|
685
|
+
);
|
|
686
|
+
for (var i = 0; i < r.rows.length; i += 1) {
|
|
687
|
+
var ans = _decodeAnswer(r.rows[i]);
|
|
688
|
+
var bucket = byId[ans.question_id];
|
|
689
|
+
if (bucket && bucket.length < limit) bucket.push(ans);
|
|
690
|
+
}
|
|
691
|
+
return byId;
|
|
692
|
+
}
|
|
693
|
+
|
|
655
694
|
// Every answer under a question regardless of status — the operator
|
|
656
695
|
// moderation detail needs the pending / rejected answers too, which
|
|
657
696
|
// answersForQuestion (approved-only, storefront-facing) hides. Pinned
|
|
@@ -808,6 +847,7 @@ function create(opts) {
|
|
|
808
847
|
getQuestion: getQuestion,
|
|
809
848
|
topAnswerForQuestion: topAnswerForQuestion,
|
|
810
849
|
answersForQuestion: answersForQuestion,
|
|
850
|
+
answersForQuestions: answersForQuestions,
|
|
811
851
|
listAnswersForQuestion: listAnswersForQuestion,
|
|
812
852
|
listQuestionsByStatus: listQuestionsByStatus,
|
|
813
853
|
listAnswersByStatus: listAnswersByStatus,
|
package/lib/sales-tax-filings.js
CHANGED
|
@@ -559,10 +559,9 @@ function create(opts) {
|
|
|
559
559
|
}
|
|
560
560
|
|
|
561
561
|
// Owed: re-derive from per-rate taxable totals using rate_bps.
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
//
|
|
565
|
-
// primitive may aggregate millions of orders.)
|
|
562
|
+
// owed = taxable × bps / 10000 per rate, half-even via b.money
|
|
563
|
+
// (BigInt — taxable_minor sums every order in the window and can
|
|
564
|
+
// exceed 2^53, so the multiply must not pass through a float).
|
|
566
565
|
var owed = 0;
|
|
567
566
|
var rateKeys = Object.keys(breakdown);
|
|
568
567
|
for (var rk = 0; rk < rateKeys.length; rk += 1) {
|
|
@@ -571,14 +570,11 @@ function create(opts) {
|
|
|
571
570
|
var bps = parseInt(key, 10);
|
|
572
571
|
if (!Number.isFinite(bps)) continue;
|
|
573
572
|
var bucket = breakdown[key];
|
|
574
|
-
var
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (frac > 0.5) rounded = floored + 1;
|
|
580
|
-
else if (frac < 0.5) rounded = floored;
|
|
581
|
-
else rounded = (floored % 2 === 0) ? floored : floored + 1;
|
|
573
|
+
var rounded = Number(
|
|
574
|
+
b.money.fromMinorUnits(BigInt(bucket.taxable_minor), "USD")
|
|
575
|
+
.multiply([BigInt(bps), 10000n], { rounding: "half-even" })
|
|
576
|
+
.toMinorUnits()
|
|
577
|
+
);
|
|
582
578
|
owed += rounded;
|
|
583
579
|
}
|
|
584
580
|
// When no rate rows resolved (taxRatesApi absent / no rates
|