@blamejs/blamejs-shop 0.4.89 → 0.4.91

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 CHANGED
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.4.x
10
10
 
11
+ - v0.4.91 (2026-06-24) — **A gift-card refund is now linked to the order it reverses, so an order's gift-card settlement history is complete.** When a refund returns money to a gift card (a cancelled or refunded order that had been paid with the card), the ledger credit now records the order it reverses in the structured order field, not only as a free-text reference. Previously a query for a given order's gift-card movements returned the debits that spent the card but not the refund credits that returned money to it, so the order's gift-card settlement history read as one-sided. The refund credit now carries the order link, so the order's gift-card history shows both the spend and the matching refund. No configuration changes, and existing gift-card behavior — balances, overdraft refusal, and the per-card tamper-evident hash chain — is unchanged. **Fixed:** *Refund-to-gift-card credits carry the order they reverse* — giftCardLedger.credit now accepts an optional order_id, and the order-cancellation/refund path that returns a redeemed amount to a gift card passes the order it reverses. The ledger's transactionsForOrder(order_id) lookup keys on that structured field, so it now returns the refund credits flowing back to a card alongside the debits that drained it for the order — previously the credits were keyed only by a free-text reference and were absent from the lookup. Each refund credit still participates in the per-card hash chain (the order link is covered by the row hash), and credits with no associated order (promotional or manual top-ups) are unaffected.
12
+
13
+ - v0.4.90 (2026-06-24) — **Credit-account releases and aging now attribute payments to orders correctly, so cancelling a paid order can't erase another order's debt.** Two related accounting fixes for B2B credit accounts, no configuration or schema changes. On a credit account, payments are revolving — they pay down the oldest outstanding charge first across all of a customer's orders. Releasing the hold on a cancelled or returned order previously reduced the balance by that order's full charge without accounting for payments already applied to it, so on an account with more than one order, cancelling an order that a payment had effectively settled drove the balance below what was genuinely owed and silently erased another order's debt from the outstanding total and the aging report. A release now reduces the balance only by the order's still-unpaid amount (its charges minus the payments attributed to it, oldest-first), so a cancelled-but-paid order is a no-op and every other order's genuine debt is preserved. Separately, the aging report was pooling order-scoped releases together with order-agnostic payments into a single oldest-first settlement, so releasing a recent order could settle the oldest charge and report a genuinely past-due balance as current; the report now nets each order's releases against that order's own charges before applying payments oldest-first, so the past-due buckets reflect reality. The outstanding total is unchanged in every case — only the misattribution is corrected. Upgrade to pick it up. **Changed:** *Vendored blamejs framework refreshed to 0.15.20* — The vendored framework moves from 0.15.19 to 0.15.20, with no API or configuration change. It refreshes the bundled Mozilla public-suffix list (used for cookie-scope, same-site, and DMARC/BIMI domain classification), updates the bundled WebAuthn server library to 13.3.2 (improved passkey attestation certificate-path validation), and corrects the vendored-dependency SBOM version metadata so CVE scanners match advisories against the versions actually shipped. **Fixed:** *Releasing a credit-account order caps at its unpaid amount, not its full charge* — creditLimits.releaseHold now reduces the outstanding balance only by the target order's unpaid exposure — its charges and holds, minus its own prior releases, minus the revolving payments attributed to it under an oldest-order-first allocation — capped at the live balance. Previously it reduced the balance by the order's full net charge and ignored payments entirely (payments carry no order reference), so on a multi-order account, cancelling an order that a payment had already settled released credit that was never owed and erased another order's genuine debt from the outstanding total. A cancelled order whose charge has effectively been paid now releases nothing (the request is refused as having no outstanding amount); a genuinely unpaid order still releases its full exposure. The check is a single atomic statement, so it stays correct under concurrent releases and payments. · *Aging report nets order releases against their own order before applying payments* — creditLimits.agingReport no longer pools order-scoped releases with order-agnostic payments into one oldest-first drain. It first nets each order's releases against that order's own charges (a release reverses its own order's charge, never another's), then applies the revolving payment pool oldest-charge-first across what remains. Previously, releasing a recent order could settle the oldest charge in the pooled drain, masking a genuinely past-due balance as current and inverting the aging buckets that drive dunning and collections. The total outstanding is identical to before — only the bucket distribution is corrected — and it uses the same payment-attribution model as the release cap, so a release and the report can never disagree on an order's unpaid amount.
14
+
11
15
  - v0.4.89 (2026-06-24) — **Store-credit wallet balances are now tamper-evident, and concurrent gift-card ledger writes retry reliably in production.** The store-credit ledger gains the same per-customer cryptographic hash chain the gift-card ledger already carries, so a balance can no longer be silently altered by a direct database edit. Each wallet row now links to the prior one with a SHA3-512 hash over its contents, and a new verifyChain check recomputes the chain and reports the first row that doesn't link — a denormalized balance snapshot was previously a value, not a proof, so a row edit that inflated a balance or a deletion that dropped a debit passed a balance read undetected. The same change makes the balance computation safe under concurrency through a chain-parent fence and a re-read-and-retry on contention. Separately, a latent reliability bug in the gift-card ledger's concurrent-write retry is fixed: it detected a write collision by matching the database's error text, but the production data-layer redacts that text to a generic error, so a genuine collision between two writes against the same gift card could surface as a failed request instead of retrying — both ledgers now detect a collision by re-reading the chain tip, which is robust regardless of how the error is reported. No configuration changes; the store-credit schema additions apply automatically on upgrade. **Added:** *Store-credit ledger is tamper-evident with a per-customer hash chain* — Every store-credit wallet row now carries prev_hash + row_hash columns forming a per-customer SHA3-512 chain: row_hash = SHA3-512(prev_hash || canonical-json(row fields)), anchored at a genesis sentinel for a wallet's first row. A direct database edit that inflates a balance snapshot, or a deletion that drops a debit, breaks the chain at that row. A new storeCredit.verifyChain(customer_id) walks the wallet and returns the first divergence (or confirms the chain is intact); a wallet whose hash columns have all been cleared reads as unanchored rather than trusting the unverifiable rows. Because deleting a chain's most-recent rows leaves the remaining prefix internally consistent — a truncation an append-only chain can't detect on its own — verifyChain optionally accepts a trusted anchor (a row count and head hash captured from an earlier snapshot) and reports a possible truncation when the chain has fallen below it. The balance and occurred-at values are now computed in the application and protected under concurrency by a chain-parent uniqueness fence — a write derived from a stale view of the wallet collides and is retried against the current tip, so the denormalized balance stays exact under concurrent credits, debits, expirations, and the scheduled expiry sweep. **Fixed:** *Concurrent gift-card and store-credit ledger writes retry reliably in production* — The ledger retries a write that loses a race against a concurrent write to the same entity. It previously recognized that collision by matching the database engine's constraint-violation text — but the production data-layer redacts that text to a generic error, so in production a genuine collision was not recognized and the write surfaced as a failed request instead of retrying (the path only worked under the in-memory test engine, where the full error text survives). Both the gift-card ledger and the new store-credit chain now detect a collision by re-reading the chain tip after a failed write: if the tip has advanced past the row the write tried to extend, a competing write claimed the slot and the write is retried; otherwise the original error is surfaced. The retry is now robust regardless of how the underlying error is reported.
12
16
 
13
17
  - v0.4.88 (2026-06-24) — **Refreshes the vendored framework to 0.15.19 and makes the vendored-dependency SBOM a committed, drift-guarded artifact.** Two supply-chain changes, no application configuration changes. The vendored blamejs framework moves from 0.15.16 to 0.15.19, picking up a set of upstream correctness and confidentiality fixes that ride inside the primitives the store already composes: a rejection on an established encrypted-API session is now returned inside the encrypted session envelope instead of disclosing which check failed in cleartext; stapled-OCSP response-freshness validation is restored; the DPoP proof's request-URI reconstruction now peer-gates forwarded scheme/host headers; the bundled public-suffix list is refreshed; and a family of credential/certificate verifiers continues to fail closed on an unparseable timestamp. Separately, the vendored-dependency SBOM (sbom.vendored.cdx.json) is now committed to the repository and mechanically projected from the vendored manifest, so its component versions can never silently fall behind what actually ships under lib/vendor/: a build gate fails on any drift, and the published, signed SBOM is the same verified file rather than one regenerated at publish time. Upgrade to pick both up. **Added:** *Vendored-dependency SBOM is a committed, drift-guarded artifact* — sbom.vendored.cdx.json — the CycloneDX 1.6 SBOM describing what ships under lib/vendor/ — is now committed to the repository and generated as a deterministic projection of lib/vendor/MANIFEST.json (the source of truth for the vendored version, license, and source). The vendor refresh regenerates it automatically, and a build gate fails if the committed SBOM drifts from the vendored manifest, so its component versions can never silently fall behind the vendored tree — the same protection the changelog, the release-notes rollup, and the asset-manifest already carry. The signed SBOM published as a release asset is now this same verified file rather than one regenerated at publish time, so the artifact downstream scanners consume is byte-reproducible from the committed source. **Changed:** *Vendored blamejs framework refreshed to 0.15.19* — The vendored framework moves from 0.15.16 to 0.15.19. The changes are upstream correctness and confidentiality fixes that ride inside the primitives the store already uses, with no API or configuration change: a rejection on an established encrypted-API session is now encrypted inside the session envelope rather than returned as a cleartext reason that disclosed which check failed; stapled-OCSP response-freshness enforcement (RFC 6960) is restored after a parsing regression had it reject every response; the DPoP htu reconstruction and the new request-host resolver only honor forwarded scheme/host headers from a declared trusted proxy; the bundled Mozilla public-suffix list (used for cookie-scope and same-site decisions) is refreshed to current upstream; and the credential/certificate validity-window verifiers continue to refuse a present-but-unparseable timestamp instead of skipping the check.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.4.89",
2
+ "version": "0.4.91",
3
3
  "assets": {
4
4
  "css/admin.css": {
5
5
  "integrity": "sha384-imfe0otYErcB8rr2h6KLSGTtStirysptpXETSPY4zLv3bZoIT75Lo1dOvkOav+xL",
@@ -256,6 +256,44 @@ function create(opts) {
256
256
  var _BAL_SUB = "COALESCE((SELECT balance_after_minor FROM credit_transactions " +
257
257
  "WHERE customer_id = ?2 ORDER BY occurred_at DESC LIMIT 1), 0)";
258
258
 
259
+ // The UNPAID exposure of ONE order (?4) — what releaseHold may release
260
+ // without consuming balance that belongs to OTHER orders' genuine unpaid
261
+ // charges. Computed PER CHARGE/HOLD ROW (not collapsed per order), row-
262
+ // for-row identical to the agingReport recompute, so the two surfaces can
263
+ // never disagree even when an order carries more than one charge/hold row
264
+ // (a hold plus a capture, or an injected duplicate). The attribution
265
+ // model: a RELEASE nets only against its OWN order's rows, oldest-first;
266
+ // a PAYMENT is revolving (order_id=NULL) and drains the post-release row
267
+ // remainders global oldest-first. So each row's unpaid = its amount,
268
+ // minus the release applied to it (its own order's release filling that
269
+ // order's rows oldest-first), minus the FIFO slice of the payment pool
270
+ // that overlaps the row's position [cum_rem_before, cum_rem_before+rem) on
271
+ // the global oldest-first number line. An order's unpaid is the SUM of its
272
+ // rows' unpaid. Expressed as one correlated subquery (nested CTEs + window
273
+ // functions) so it composes inside the SAME single-statement atomic write
274
+ // as _BAL_SUB: a concurrent payment/release commits first and this
275
+ // subquery re-evaluates against it, so race-safety is preserved with no
276
+ // app-side read-then-write. References ?2 (customer_id) + ?4 (order_id),
277
+ // both bound by _appendTxnAtomic.
278
+ var _UNPAID_SUB =
279
+ "COALESCE((WITH " +
280
+ "pay AS (SELECT COALESCE(SUM(amount_minor), 0) AS p " +
281
+ " FROM credit_transactions WHERE customer_id = ?2 AND kind = 'payment'), " +
282
+ "rel AS (SELECT order_id, COALESCE(SUM(amount_minor), 0) AS r " +
283
+ " FROM credit_transactions WHERE customer_id = ?2 AND kind = 'release' GROUP BY order_id), " +
284
+ "rows0 AS (SELECT id, order_id, occurred_at, amount_minor, " +
285
+ " COALESCE(SUM(amount_minor) OVER (PARTITION BY order_id ORDER BY occurred_at, id " +
286
+ " ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS cum_own_before " +
287
+ " FROM credit_transactions WHERE customer_id = ?2 AND kind IN ('charge', 'hold')), " +
288
+ "rel_applied AS (SELECT r0.id, r0.order_id, r0.occurred_at, " +
289
+ " r0.amount_minor - MAX(0, MIN(r0.amount_minor, COALESCE(rel.r, 0) - r0.cum_own_before)) AS rem " +
290
+ " FROM rows0 r0 LEFT JOIN rel ON rel.order_id = r0.order_id), " +
291
+ "rows1 AS (SELECT order_id, rem, " +
292
+ " COALESCE(SUM(rem) OVER (ORDER BY occurred_at, id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS cum_rem_before " +
293
+ " FROM rel_applied) " +
294
+ "SELECT COALESCE(SUM(rem - MAX(0, MIN(cum_rem_before + rem, (SELECT p FROM pay)) - MAX(cum_rem_before, 0))), 0) " +
295
+ "FROM rows1 WHERE order_id = ?4), 0)";
296
+
259
297
  // Append a ledger transaction ATOMICALLY. The new balance, the
260
298
  // strictly-monotonic occurred_at, and the operation's invariant
261
299
  // (credit limit / sufficient balance / outstanding charge) are all
@@ -616,27 +654,28 @@ function create(opts) {
616
654
  throw closed;
617
655
  }
618
656
 
619
- // Net exposure to this order — charge + hold sums minus prior
620
- // release sums — capped at the live balance, computed and written
621
- // in ONE atomic statement. The racy read-sum-then-write this
622
- // replaced let two concurrent releases (or a release racing a
623
- // payment) both read the same remaining/balance and both write,
624
- // double-releasing the order and corrupting the running balance.
625
- // The guard refuses when nothing is outstanding (never charged /
626
- // already fully released) or the balance is drained, so a release
627
- // can't drive the ledger negative. Capping at _BAL_SUB stays
628
- // defensive against operator-induced ledger drift.
629
- var remainingSub = "COALESCE((SELECT SUM(CASE " +
630
- "WHEN kind IN ('charge', 'hold') THEN amount_minor " +
631
- "WHEN kind = 'release' THEN -amount_minor ELSE 0 END) " +
632
- "FROM credit_transactions WHERE customer_id = ?2 AND order_id = ?4), 0)";
633
- var toReleaseExpr = "CASE WHEN " + remainingSub + " < " + _BAL_SUB +
634
- " THEN " + remainingSub + " ELSE " + _BAL_SUB + " END";
657
+ // UNPAID exposure of this order — its net charge minus the FIFO
658
+ // payments attributed to it (_UNPAID_SUB) — capped at the live
659
+ // balance, computed and written in ONE atomic statement. Subtracting
660
+ // the payment attribution is the fix for the multi-order over-release:
661
+ // the cap used to be the order's GROSS net charge and never subtracted
662
+ // payments (payments are order-agnostic, order_id=NULL), so releasing a
663
+ // conceptually-paid order in a multi-order account consumed balance
664
+ // that belonged to OTHER orders' genuine unpaid charges and erased
665
+ // their debt. Keeping it a single correlated-subquery statement
666
+ // preserves the original race-safety (a concurrent release/payment
667
+ // commits first and _UNPAID_SUB / _BAL_SUB re-evaluate against it). The
668
+ // guard refuses when the order is fully paid / released / never
669
+ // charged, or the balance is drained, so a release can't drive the
670
+ // ledger negative. Capping at _BAL_SUB stays defensive against
671
+ // operator-induced ledger drift.
672
+ var toReleaseExpr = "CASE WHEN " + _UNPAID_SUB + " < " + _BAL_SUB +
673
+ " THEN " + _UNPAID_SUB + " ELSE " + _BAL_SUB + " END";
635
674
  var written = await _appendTxnAtomic(
636
675
  customerId, "release", orderId, null, requested,
637
676
  toReleaseExpr, // amount actually released
638
677
  _BAL_SUB + " - (" + toReleaseExpr + ")", // new balance
639
- remainingSub + " > 0 AND " + _BAL_SUB + " > 0", // nothing outstanding / drained -> refuse
678
+ _UNPAID_SUB + " > 0 AND " + _BAL_SUB + " > 0", // fully paid/released/drained -> refuse
640
679
  [],
641
680
  );
642
681
  if (!written) {
@@ -759,23 +798,80 @@ function create(opts) {
759
798
  }
760
799
  var terms = acct.payment_terms_days;
761
800
 
762
- // Walk all charge/hold rows oldest-first. Apply payments +
763
- // releases FIFO against the oldest outstanding amounts. What
764
- // remains in each charge row after settlement is the
765
- // outstanding slice for that age bucket.
801
+ // Walk all charge/hold rows oldest-first, then settle in two passes
802
+ // that honor what each settlement kind actually reverses:
803
+ //
804
+ // 1. A RELEASE is intrinsically order-scoped — it reverses ITS
805
+ // order's charge — so it nets only against that order's OWN
806
+ // charge slices (oldest-first). Pooling releases with payments
807
+ // into one global FIFO drain (the prior behavior) let a release
808
+ // of a RECENT order settle the OLDEST charge, masking a genuinely
809
+ // past-due balance as current and inverting the aging buckets.
810
+ // 2. A PAYMENT is revolving (order_id=NULL) — it pays down the
811
+ // OLDEST outstanding charge first across ALL orders. It drains
812
+ // AFTER the per-order release netting, over whatever net slices
813
+ // remain.
814
+ //
815
+ // This is the SAME attribution model releaseHold's _UNPAID_SUB cap
816
+ // uses (own-order releases, then global-FIFO payments), so the report
817
+ // and a release can never disagree on an order's unpaid exposure. The
818
+ // total is unchanged (charges − releases − payments); only the bucket
819
+ // distribution is corrected. The (occurred_at, id) ordering matches the
820
+ // ledger's monotonic stamping and _UNPAID_SUB's FIFO key.
766
821
  var chargeRows = (await query(
767
- "SELECT amount_minor, occurred_at, order_id FROM credit_transactions " +
822
+ "SELECT id, amount_minor, occurred_at, order_id FROM credit_transactions " +
768
823
  "WHERE customer_id = ?1 AND kind IN ('charge', 'hold') " +
769
- "ORDER BY occurred_at ASC",
824
+ "ORDER BY occurred_at ASC, id ASC",
770
825
  [customerId],
771
826
  )).rows;
772
827
 
773
- var settleRow = (await query(
828
+ // Per-order release totals — netted against that order's own charges.
829
+ var relRows = (await query(
830
+ "SELECT order_id, COALESCE(SUM(amount_minor), 0) AS rel FROM credit_transactions " +
831
+ "WHERE customer_id = ?1 AND kind = 'release' GROUP BY order_id",
832
+ [customerId],
833
+ )).rows;
834
+ var relByOrder = Object.create(null);
835
+ for (var ri = 0; ri < relRows.length; ri += 1) {
836
+ relByOrder[relRows[ri].order_id] = relRows[ri].rel;
837
+ }
838
+
839
+ // Order-agnostic payment pool — drains FIFO AFTER release netting.
840
+ var paySettleRow = (await query(
774
841
  "SELECT COALESCE(SUM(amount_minor), 0) AS total FROM credit_transactions " +
775
- "WHERE customer_id = ?1 AND kind IN ('payment', 'release')",
842
+ "WHERE customer_id = ?1 AND kind = 'payment'",
776
843
  [customerId],
777
844
  )).rows[0];
778
- var toSettle = settleRow ? settleRow.total : 0;
845
+ var toSettle = paySettleRow ? paySettleRow.total : 0;
846
+
847
+ var slices = [];
848
+ for (var si = 0; si < chargeRows.length; si += 1) {
849
+ slices.push({
850
+ order_id: chargeRows[si].order_id,
851
+ occurred_at: chargeRows[si].occurred_at,
852
+ remaining: chargeRows[si].amount_minor,
853
+ });
854
+ }
855
+ // Pass 1: net each order's releases against its OWN charge slices,
856
+ // oldest-first (slices are already in global oldest-first order).
857
+ for (var p1 = 0; p1 < slices.length; p1 += 1) {
858
+ var sRel = slices[p1];
859
+ var rel = relByOrder[sRel.order_id] || 0;
860
+ if (rel > 0) {
861
+ var rd = rel > sRel.remaining ? sRel.remaining : rel;
862
+ sRel.remaining -= rd;
863
+ relByOrder[sRel.order_id] = rel - rd;
864
+ }
865
+ }
866
+ // Pass 2: drain the order-agnostic payment pool FIFO across the
867
+ // remaining net slices, oldest-first.
868
+ for (var p2 = 0; p2 < slices.length && toSettle > 0; p2 += 1) {
869
+ var sPay = slices[p2];
870
+ if (sPay.remaining <= 0) continue;
871
+ var pd = toSettle > sPay.remaining ? sPay.remaining : toSettle;
872
+ sPay.remaining -= pd;
873
+ toSettle -= pd;
874
+ }
779
875
 
780
876
  var buckets = {};
781
877
  buckets[AGE_BUCKETS.CURRENT] = 0;
@@ -784,22 +880,15 @@ function create(opts) {
784
880
  buckets[AGE_BUCKETS.D90] = 0;
785
881
 
786
882
  var total = 0;
787
- for (var i = 0; i < chargeRows.length; i += 1) {
788
- var row = chargeRows[i];
789
- var remaining = row.amount_minor;
790
- if (toSettle > 0) {
791
- var settled = toSettle > remaining ? remaining : toSettle;
792
- remaining -= settled;
793
- toSettle -= settled;
794
- }
883
+ for (var i = 0; i < slices.length; i += 1) {
884
+ var remaining = slices[i].remaining;
795
885
  if (remaining <= 0) continue;
796
- var ageMs = now - row.occurred_at;
886
+ var ageMs = now - slices[i].occurred_at;
797
887
  var ageDays = Math.floor(ageMs / MS_PER_DAY);
798
- // Bucket by days-past-due so the report reads the same
799
- // regardless of the account's payment terms — a net-15 and
800
- // a net-60 account both report `current` against not-yet-
801
- // due charges, then advance through 30 / 60 / 90+ buckets
802
- // measured from the due date.
888
+ // Bucket by days-past-due so the report reads the same regardless of
889
+ // the account's payment terms — a net-15 and a net-60 account both
890
+ // report `current` against not-yet-due charges, then advance through
891
+ // 30 / 60 / 90+ buckets measured from the due date.
803
892
  var pastDue = ageDays - terms;
804
893
  var bucket;
805
894
  if (pastDue <= 0) bucket = AGE_BUCKETS.CURRENT;
@@ -51,7 +51,7 @@
51
51
  * degrade gracefully rather than refusing.
52
52
  *
53
53
  * Surface:
54
- * - credit({ gift_card_id, amount_minor, source, source_ref, occurred_at? })
54
+ * - credit({ gift_card_id, amount_minor, source, source_ref, order_id?, occurred_at? })
55
55
  * - debit({ gift_card_id, amount_minor, order_id, occurred_at? })
56
56
  * - expire({ gift_card_id, amount_minor, reason, occurred_at? })
57
57
  * - balance(gift_card_id)
@@ -341,6 +341,11 @@ function create(opts) {
341
341
  var amount = _amountMinor(input.amount_minor, "amount_minor");
342
342
  var source = _source(input.source);
343
343
  var sourceRef = _sourceRef(input.source_ref, "source_ref");
344
+ // Optional structured order link — a refund_to_giftcard credit names
345
+ // the order it reverses, so transactionsForOrder(orderId) returns the
346
+ // money flowing BACK to the card alongside the debits that drained it.
347
+ // Other credit sources (promotional / manual top-ups) carry no order.
348
+ var orderId = input.order_id == null ? null : _uuid(input.order_id, "order_id");
344
349
  var requested = _epochMs(input.occurred_at, "occurred_at");
345
350
  if (requested == null) requested = _now();
346
351
 
@@ -349,7 +354,7 @@ function create(opts) {
349
354
  amount: amount,
350
355
  source: source,
351
356
  sourceRef: sourceRef,
352
- orderId: null,
357
+ orderId: orderId,
353
358
  after: latest.balance + amount,
354
359
  ts: _resolveOccurredAt(requested, latest.occurred_at),
355
360
  guarded: false,
@@ -363,6 +368,7 @@ function create(opts) {
363
368
  amount_minor: amount,
364
369
  source: source,
365
370
  source_ref: sourceRef,
371
+ order_id: orderId,
366
372
  balance_after_minor: w.balance_after_minor,
367
373
  occurred_at: w.occurred_at,
368
374
  };
package/lib/order.js CHANGED
@@ -394,6 +394,7 @@ function create(opts) {
394
394
  amount_minor: rev.amount_minor,
395
395
  source: "refund_to_giftcard",
396
396
  source_ref: orderId,
397
+ order_id: orderId,
397
398
  });
398
399
  } catch (_ledgerErr) { /* drop-silent — the card-row balance restore above is authoritative; the ledger credit is the audit trail */ }
399
400
  }
@@ -3,8 +3,8 @@
3
3
  "_about": "blamejs.shop vendors a single framework — blamejs — which itself bundles every server-side crypto/identity dependency. The transitive packages blamejs ships are surfaced in its own MANIFEST.json at lib/vendor/blamejs/lib/vendor/MANIFEST.json — Trivy / Grype rely on that nested data for CVE attribution.",
4
4
  "packages": {
5
5
  "blamejs": {
6
- "version": "0.15.19",
7
- "tag": "v0.15.19",
6
+ "version": "0.15.20",
7
+ "tag": "v0.15.20",
8
8
  "license": "Apache-2.0",
9
9
  "author": "blamejs contributors",
10
10
  "source": "https://github.com/blamejs/blamejs",
@@ -841,6 +841,7 @@
841
841
  "release-notes/v0.15.18.json": "lib/vendor/blamejs/release-notes/v0.15.18.json",
842
842
  "release-notes/v0.15.19.json": "lib/vendor/blamejs/release-notes/v0.15.19.json",
843
843
  "release-notes/v0.15.2.json": "lib/vendor/blamejs/release-notes/v0.15.2.json",
844
+ "release-notes/v0.15.20.json": "lib/vendor/blamejs/release-notes/v0.15.20.json",
844
845
  "release-notes/v0.15.3.json": "lib/vendor/blamejs/release-notes/v0.15.3.json",
845
846
  "release-notes/v0.15.4.json": "lib/vendor/blamejs/release-notes/v0.15.4.json",
846
847
  "release-notes/v0.15.5.json": "lib/vendor/blamejs/release-notes/v0.15.5.json",
@@ -1547,7 +1548,7 @@
1547
1548
  ".npmrc": "sha256:66f104e7d07c496d2d0409988225e8c0e4ceb8d247dbcac3be75b2128d20ce66",
1548
1549
  ".pinact.yaml": "sha256:0213ffda55961dc49b64c0a5dfa3c0567419633b1499d57eaf7c8d842d7da6c7",
1549
1550
  "ARCHITECTURE.md": "sha256:9b1c8d2b1b7a41838eb348b0a008e4b4369718fd72bfe2974b37155f7536d35b",
1550
- "CHANGELOG.md": "sha256:ebd9e3a79a0dce5518430a52c9c6e24cbf7bad0406bbed2c4e7ad5eb1ab297c6",
1551
+ "CHANGELOG.md": "sha256:4642c9fe7ab6aacfbf5b95e3780834b6b1fb4f485139571171d8a30eb702cde7",
1551
1552
  "CODE_OF_CONDUCT.md": "sha256:148a281960fff7c2fe6554dab66da572c72245ddeb00b0d14811558397bff386",
1552
1553
  "CONTRIBUTING.md": "sha256:bb4dbdbc8598da31dbce653a8ed322e08ff46560173f2eb67a4d684653948332",
1553
1554
  "GOVERNANCE.md": "sha256:906df6afb1f552b27b9acb50f7f96c47b917a2f1021cd4e987dbf4ee0e0a821b",
@@ -1557,7 +1558,7 @@
1557
1558
  "NOTICE": "sha256:f487fa47a11aca0f89e2615cdd3c713e9842abf7a30d8d328eeeae1c864aa774",
1558
1559
  "README.md": "sha256:3ddcc197b003da0b02db8bdd1aef1e943c94f7eab613c633d6a45bb11d0a80e9",
1559
1560
  "SECURITY.md": "sha256:23f7ee4a44f21e433ed1d3c6f414575eb3e30f66a328422973a1109a276c537b",
1560
- "api-snapshot.json": "sha256:719da0f2483fb5672ee09e398f4d85304c875f6f9df96f21ad32cdc3aa1cc3b5",
1561
+ "api-snapshot.json": "sha256:7ca3b73f197b253072a2210417668acf336bb36a8407556e7e3663b0b1543aa9",
1561
1562
  "assets/BlameJS_Logo.png": "sha256:3c65699753c771b48ef9ac7f45bb40815ec19a23afcdd0cd30ef4601bbbe293e",
1562
1563
  "assets/BlameJS_Logo.svg": "sha256:dda44f3fb1343d5de9db6b1fcdb75fc649c57e7a99a8e8239fcf852e3841e1a8",
1563
1564
  "bench/README.md": "sha256:74202f2507fd840bfc1ac6c681975d9273cf36cca6e0f72655f138337304033c",
@@ -2298,7 +2299,7 @@
2298
2299
  "lib/vc.js": "sha256:554cf29ea712b231256e1eaa75f6b1f1d39c392aac86b33e818d52e956ea83ae",
2299
2300
  "lib/vendor-data.js": "sha256:ddb77b33d89a2c5345deca98eacb45695d4c8a70014bc5df22c7393f52e11416",
2300
2301
  "lib/vendor/.vendor-data-pubkey": "sha256:73a935b8c72f55d821c22ddb8188453bab324f80d229d033a0a1549819a1f096",
2301
- "lib/vendor/MANIFEST.json": "sha256:09194d50f99999485fb525da118afd815c475be0ef4f6e58fbc3f41ffd91d1dd",
2302
+ "lib/vendor/MANIFEST.json": "sha256:c2b9e40fb993db0f92c49154a5ac078e01324ccd02a19985e38186da06dd7338",
2302
2303
  "lib/vendor/bimi-trust-anchors.data.js": "sha256:aa7a4d33b65a68422a2a2c1670177689f66fdcaa08bd2514d78798b827bd1608",
2303
2304
  "lib/vendor/bimi-trust-anchors.pem": "sha256:81ff9f5ab3c9774132c845684e783be95cf73146f8b670d964105f0a3765b4b4",
2304
2305
  "lib/vendor/common-passwords-top-10000.data.js": "sha256:87b223beca89f33d2c2c32a2cfda0bc187e58061de40e7127bb5ffc4258c6e2a",
@@ -2307,9 +2308,9 @@
2307
2308
  "lib/vendor/noble-curves.cjs": "sha256:ebf254d5eb56aef8705a1c4af9603f47987b4870a9bb5e657e06907b701e2731",
2308
2309
  "lib/vendor/noble-post-quantum.cjs": "sha256:f9190309daadca4c2e2cc2b76beaa6b96e463429cc3c390bd9f0ceaf7b588c68",
2309
2310
  "lib/vendor/pki.cjs": "sha256:9bbc191afaaa2b1e5757f00480457c08134cdc2c55d541df18d9155bba9cbf77",
2310
- "lib/vendor/public-suffix-list.dat": "sha256:0adddeb62057d8d40799dffb29fe14f65dd009259afe02eb2f0b4602b791aae6",
2311
- "lib/vendor/public-suffix-list.data.js": "sha256:82af512cacf0fd2c60925e63f877b69477b1b2f7bb5af698fd862af61369902e",
2312
- "lib/vendor/simplewebauthn-server.cjs": "sha256:f359a782ac57e3ff56ac71083d17f5c082f88ab49d645fc2bede398b47adebdb",
2311
+ "lib/vendor/public-suffix-list.dat": "sha256:42c4f3544726fa2feb4f304c889c5e391feb5d28080c9b6356ff82e267a93706",
2312
+ "lib/vendor/public-suffix-list.data.js": "sha256:c840d9ae5c6bf4a07967340a5c649df2c6a66a287db41e408461c46cc32348e6",
2313
+ "lib/vendor/simplewebauthn-server.cjs": "sha256:49411d893f5e9b0e2fcaa564b4ec7921f73a9a06229b5e53d49c1453ea1a365c",
2313
2314
  "lib/vendor/vendor-data-pubkey.js": "sha256:a12afa34cd7472e2eaebad2fcd44714102d3edd0601e45769404124a513926d0",
2314
2315
  "lib/vex.js": "sha256:b45c1c9729dfc69f2140478d46eec91f47da94b5f440be36fa825cb733a718fc",
2315
2316
  "lib/watcher.js": "sha256:8618da919affabbe4c4d33915647b3ee4b27e6ea091638f032d4bfead797baf8",
@@ -2328,7 +2329,7 @@
2328
2329
  "oss-fuzz/projects/blamejs/README.md": "sha256:ae13b7bb79ed8d69b1b3276e5562807a0349fb6e6b7d11cf1f683aad1eafdb4b",
2329
2330
  "oss-fuzz/projects/blamejs/build.sh": "sha256:0ced1cf21782c97be7f8d74faf5e27a308b60b2f858836fb5ca3b8c4e939a8f7",
2330
2331
  "oss-fuzz/projects/blamejs/project.yaml": "sha256:59f2cb83aa622325a175b77416fe155be15b70a9c798bd1a78bba05763b1b03d",
2331
- "package.json": "sha256:c625373887cfae872dde0fca9ea6955278d6fc721128c04bbd01a31986b9d409",
2332
+ "package.json": "sha256:7409a04e0673a297045ce2248e196cb4f856f2a037af73e837b0264b76984071",
2332
2333
  "release-notes/v0.0.x.json": "sha256:7a49819f30068ee119000cad7010194882bb8bfaa12acbdab4dfc066efb7982f",
2333
2334
  "release-notes/v0.1.x.json": "sha256:6742a8c17f947c5cb76f69dead7eea86b942d80621d914b774ba5488e09937e5",
2334
2335
  "release-notes/v0.10.x.json": "sha256:fe498045daf88337bd3d987e5964aa42c99a50e1685b6f09e51f698b8687726f",
@@ -2349,6 +2350,7 @@
2349
2350
  "release-notes/v0.15.18.json": "sha256:c8b3ce7af0f7863cdc3f5179f0525469331720a80b741b73236fdd6f4243bbaa",
2350
2351
  "release-notes/v0.15.19.json": "sha256:8c6a745accd2c551953464991bb23547e8cf87286d57938c57080755785e58b5",
2351
2352
  "release-notes/v0.15.2.json": "sha256:36e1423dda94ed4e55c660e0fae882019005d8de3ee5e3b6ac4f38ccf8e744a2",
2353
+ "release-notes/v0.15.20.json": "sha256:bc469d136c909f94d89d2816a5a4c7b08ae93380d812e837d4f1c554eae1ce2b",
2352
2354
  "release-notes/v0.15.3.json": "sha256:19a0074c445545468ca3cc411b21ec8bdb27be2669ae1950347cc244f6aa348c",
2353
2355
  "release-notes/v0.15.4.json": "sha256:6ac7fa0ef1728c27e71b2050d1b07a810f9b4b1440ccddbf28ad56e2f54d8585",
2354
2356
  "release-notes/v0.15.5.json": "sha256:cca1d0edd5d6fc41b512d19d98be224b990dcab41478622c11962f0fcb1bb09a",
@@ -2387,7 +2389,7 @@
2387
2389
  "scripts/validate-source-comment-blocks.js": "sha256:f3a8d9ef0dcf9612a694d5c005c8c05b3ffbd7e92a14a8efb4aa97f054528f7f",
2388
2390
  "scripts/vendor-data-gen.js": "sha256:76b627bc6e19b4a122edfca6f514bcb8ca11af02902f0957e641f503337a8a0f",
2389
2391
  "scripts/vendor-data-keygen.js": "sha256:94eaa4d8f832b4aac9ccbcb2a07e6b99cd35cf7b044e1412079cebdefc1f4c0e",
2390
- "scripts/vendor-update.sh": "sha256:c1c879ee620f064a06d776c1d330749b5128a35581352ef385fa8baf4a35f79a",
2392
+ "scripts/vendor-update.sh": "sha256:373d7f024b7caa3345a3598fa3a586078dd842f0071e6fdad00a473f48a3a929",
2391
2393
  "test/00-primitives.js": "sha256:64f3a0ec3678f8b3ab401ed59cdc1139f4e8720f5ed61443a718d8f480f64d5b",
2392
2394
  "test/10-state.js": "sha256:7f286e00fda002ccf50ccc59d658b6dce9a4bccee304232984a932a2ceb31c49",
2393
2395
  "test/20-db.js": "sha256:241ef6b7ef305d077aeafb22ee3bcc75b6b549a8fa9b1a6b5d6d5fba43b48d7d",
@@ -3002,7 +3004,7 @@
3002
3004
  "test/layer-0-primitives/vc.test.js": "sha256:504fadbce3c8210c7ecb74e927686bdec03d7581b1dae4150c2ebefc326c182b",
3003
3005
  "test/layer-0-primitives/vendor-currency-classify.test.js": "sha256:a56c08cce24a600fa827756d220159365a2c3cc30428ade49df97b19466125a6",
3004
3006
  "test/layer-0-primitives/vendor-data.test.js": "sha256:e216ae83b187c66f1002c419e4af2701573e5743666007b564a0257332bc0c9b",
3005
- "test/layer-0-primitives/vendor-manifest.test.js": "sha256:55685bd11686bf76495ec29769470de05a6aaf4648d2b5722d96f213d9e32587",
3007
+ "test/layer-0-primitives/vendor-manifest.test.js": "sha256:494f89868468618592390854903f301b4d675c1b46c11fe5117e475195731c4d",
3006
3008
  "test/layer-0-primitives/vex.test.js": "sha256:4a06e3e9a6ea8ffe3ab2c0af69cbe2835c1d50768c184308bb2e40638c8998cc",
3007
3009
  "test/layer-0-primitives/watcher.test.js": "sha256:346ef70ff89e9d4b6ba27f6a1859be0cd782b8224930c7c3d2ead5d495c08dd7",
3008
3010
  "test/layer-0-primitives/web-push-vapid.test.js": "sha256:4634dcf1c3fdae300b291d20473285a9d9fe2f49f9c54e4e3a2e149768d3fe32",
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.15.x
10
10
 
11
+ - v0.15.20 (2026-06-24) — **Vendored SBOM version fields are derived from the bundle so they cannot drift, and the Public Suffix List + @simplewebauthn/server bundles are refreshed.** The vendor manifest recorded each bundled package's version in two scanner-facing places that were hand-maintained and could drift from the code actually shipped — the structured components[].version (the CycloneDX component versions) and the cpe string. Two had drifted: peculiar-pki's @peculiar/x509 component read 1.13.0 while the bundle shipped 2.0.0, and @noble/curves' cpe read 0.0.0 while the bundle shipped 2.2.0. A CVE scanner (Trivy / Grype / a CycloneDX export, or a consumer mirroring the manifest into its own SBOM) keys on those structured fields, so an advisory was matched against the wrong version — a false negative on a real fix or a false positive on a patched one. Both fields are corrected, the vendor-bundle script now derives them from the actually-installed package versions at bundle time so they cannot drift again, and a manifest gate fails the build if they ever disagree. Separately, the vendored Mozilla Public Suffix List is refreshed to the current upstream revision and @simplewebauthn/server is refreshed to 13.3.2. **Changed:** *Vendored Public Suffix List and @simplewebauthn/server refreshed* — The vendored Mozilla Public Suffix List is refreshed to the current upstream revision (used by b.publicSuffix for DMARC / BIMI / cookie-scope / same-site domain classification). @simplewebauthn/server is refreshed from 13.3.1 to 13.3.2, which improves WebAuthn attestation certificate-path validation; the published-tarball diff was reviewed (no install scripts, no network/eval, a self-contained code change) before re-vendoring. **Fixed:** *Vendored SBOM version metadata is bundle-derived, not hand-maintained* — lib/vendor/MANIFEST.json recorded each package's version in two places a CVE/SBOM scanner reads — the structured components[].version sub-object and the cpe string — both hand-maintained alongside the human version string, so they could (and did) drift from the bundled code. @peculiar/x509's component version read 1.13.0 while the bundle shipped 2.0.0; @noble/curves' cpe read 0.0.0 while the bundle shipped 2.2.0. Either drift makes a scanner match advisories against the wrong version. Both are corrected to the shipped versions, and the durable fix is structural: scripts/vendor-update.sh now writes both the structured component versions and the cpe version from the ACTUALLY-INSTALLED package versions captured at bundle time, so a maintainer can no longer update one field and forget the other. A smoke-time manifest gate additionally fails the build if any component or cpe version disagrees with the package version, catching a manual drift before it ships.
12
+
11
13
  - v0.15.19 (2026-06-22) — **Restores the wiki container build by keeping its base image on the continuously-patched rolling tag.** A follow-up to 0.15.18. The framework code is unchanged from 0.15.18 — this patch reverts one part of that release's supply-chain pass: the example wiki container's Chainguard base images had been pinned to specific digests, but Chainguard rebuilds those images continuously to ship CVE fixes, so the frozen digest fell behind an upstream fix within hours and the container's Trivy CRITICAL/HIGH release gate rejected the build (a fixed npm/undici denial-of-service the rolling tag already carried). For a deployed, Trivy-gated image, tracking the rolling, always-patched tag is the correct posture; the wiki Dockerfile is back on it. The ClusterFuzzLite fuzz base (not deployed, not release-gated) stays digest-pinned with Dependabot keeping it current. **Fixed:** *Wiki container builds again on a continuously-patched base* — 0.15.18 digest-pinned the example wiki container's Chainguard base images (runtime + builder). Because Chainguard rebuilds those images continuously to ship CVE fixes, the pinned digest went stale within hours and the container's Trivy CRITICAL/HIGH release gate rejected the build over an already-fixed npm/undici DoS (CVE-2026-12151) the rolling tag carried. The wiki Dockerfile tracks the rolling tag again, so the deployed image is always CVE-current and the build passes the release gate. To keep the Trivy-scanned image identical to the multi-arch image that is published (they are built separately), the release workflow resolves the rolling tag to a digest once at build time and feeds it to both builds via build-args — scan-equals-publish without a committed pin that goes stale. (This trades the OSSF Scorecard PinnedDependencies signal for CVE currency on a deployed, gate-scanned image — the right call here; the non-deployed ClusterFuzzLite fuzz base remains digest-pinned with Dependabot bumping it.) The framework package itself is identical to 0.15.18.
12
14
 
13
15
  - v0.15.18 (2026-06-22) — **OCSP response-freshness enforcement is restored, DPoP request-URI reconstruction peer-gates forwarded headers, and container/tool supply-chain pinning is tightened.** Three hardening fixes. The stapled-OCSP evaluator parsed each response's thisUpdate / nextUpdate with Date.parse, but those fields are already numeric (unix-ms), so Date.parse returned NaN — the freshness guard then rejected every signature-valid response, fresh or stale, with a misleading "missing thisUpdate", and the real future-dated / past-nextUpdate window checks (RFC 6960 §4.2.2.1) were unreachable dead code. b.network.tls.ocsp.evaluate / requireGood now read the numeric fields directly: a stale or future-dated response is refused and a fresh "good" response is accepted, so OCSP stapling validation works again. b.middleware.dpop reconstructed the absolute request URI — the cryptographically-bound htu — trusting X-Forwarded-Proto / X-Forwarded-Host from any caller whenever trustForwardedHeaders was set, so a direct attacker could forge the scheme or authority and make a proof signed for one origin validate against another; both now resolve through the peer-gated b.requestHelpers.trustedProtocol / trustedHost (honored only from a declared trusted-proxy peer), matching csrf-protect / security-headers / cors, and the bare trustForwardedHeaders boolean is refused. The supply-chain pass pins the wiki container and ClusterFuzzLite fuzz base images to digests (Dependabot keeps them current) and the npm-publish bundle tools to exact versions. **Added:** *b.requestHelpers.requestHost and b.requestHelpers.trustedHost* — Peer-gated request-authority resolvers — the host companions to requestProtocol / trustedProtocol. trustedHost(opts) returns { resolve(req) => string|null, peerGated }: with trustedProxies (CIDRs) X-Forwarded-Host is honored only from a trusted-proxy peer; with hostResolver(req) the operator owns it; with neither, only the request's own Host header is used and a forged X-Forwarded-Host is ignored. requestHost(req, opts?) is the low-level resolver (default Host-only; a peer predicate gates the forwarded header). For reconstructing an absolute request URL (a DPoP htu, an origin/issuer string, a redirect base) behind a proxy without trusting a forgeable header. **Changed:** *Container and bundle-tool supply-chain pinning tightened* — The wiki container's base images (cgr.dev/chainguard/node runtime + builder stages) and the ClusterFuzzLite fuzz base (gcr.io/oss-fuzz-base/base-builder-javascript) are pinned to image digests; Dependabot's docker ecosystem keeps both current, so the pin is reproducible without freezing CVE patches. The npm-publish workflow's bundle tools (esbuild, postject) are pinned to exact versions, matching CI. The OSS-Fuzz project-submission Dockerfile is intentionally left tracking the upstream base-builder, per OSS-Fuzz convention. **Security:** *OCSP response-freshness enforcement restored (RFC 6960 §4.2.2.1)* — The stapled-OCSP evaluator computed thisUpdate / nextUpdate via Date.parse(), but the parser hands those fields back as unix-ms NUMBERS (Date.UTC(...)). Date.parse() coerces its argument to a string and a bare-integer string is not a recognized date, so the result was always NaN: the !isFinite guard then rejected every signature-valid response — fresh OR stale — with a misleading "missing thisUpdate", and the genuine staleness checks (future-dated thisUpdate, past nextUpdate) sat behind it as unreachable dead code, with the stale-rejection branch latently fail-open. b.network.tls.ocsp.evaluate / requireGood now read the numeric fields directly: a stale (past-nextUpdate) or future-dated response is refused, and a fresh "good" response is accepted — OCSP stapling validation, and its replay defense, work again. · *DPoP htu reconstruction peer-gates X-Forwarded-Proto and X-Forwarded-Host* — b.middleware.dpop rebuilds the absolute request URI (scheme + authority + path) that the proof's cryptographically-bound htu claim (RFC 9449 §4.3) is verified against. When the legacy trustForwardedHeaders: true was set it derived the scheme and host from X-Forwarded-Proto / X-Forwarded-Host trusted from ANY caller, with no immediate-peer check — a direct attacker could forge X-Forwarded-Proto: https or a victim X-Forwarded-Host and make a proof signed for one origin validate against another (htu confusion). Both now resolve through the peer-gated b.requestHelpers.trustedProtocol / trustedHost, which honor the forwarded headers only when the immediate connection is a declared trusted proxy — the same fail-closed model csrf-protect (Secure cookie), security-headers (HSTS), cors (same-origin), and bot-guard (secure context) already use. A codebase-patterns detector now flags any further middleware that reads X-Forwarded-Proto / -Host directly for a scheme/authority decision. **Migration:** *DPoP: trustForwardedHeaders is replaced by trustedProxies* — b.middleware.dpop no longer honors the bare trustForwardedHeaders: true boolean — it trusted forgeable X-Forwarded-Proto / X-Forwarded-Host from any caller. Behind a reverse proxy, declare your proxy CIDRs via trustedProxies: ["10.0.0.0/8", …] (peer-gates both headers for the htu reconstruction), or own the reconstruction via protocolResolver(req) / hostResolver(req) / getHtu(req). A bare trustForwardedHeaders: true now throws at create() with that guidance. Apps not behind a proxy need no change — the default already derives the scheme from the TLS socket and the host from the request's Host header.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
- "frameworkVersion": "0.15.19",
4
- "createdAt": "2026-06-23T04:32:56.651Z",
3
+ "frameworkVersion": "0.15.20",
4
+ "createdAt": "2026-06-25T03:02:07.070Z",
5
5
  "exports": {
6
6
  "a2a": {
7
7
  "type": "object",
@@ -18,7 +18,7 @@
18
18
  "hashes": {
19
19
  "server": "sha256:5d539dfc9ef47121d4c09bd7256d76448a1f5ac47ee09ac44c78ff6a062af9ab"
20
20
  },
21
- "refreshedAt": "2026-06-22T16:29:46.590Z"
21
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
22
22
  },
23
23
  "@noble/curves": {
24
24
  "version": "2.2.0",
@@ -36,11 +36,11 @@
36
36
  },
37
37
  "bundler": "esbuild --format=cjs --minify --platform=node",
38
38
  "bundledAt": "2026-05-26T00:00:00Z",
39
- "cpe": "cpe:2.3:a:paulmillr:noble-curves:0.0.0:*:*:*:*:node.js:*:*",
39
+ "cpe": "cpe:2.3:a:paulmillr:noble-curves:2.2.0:*:*:*:*:node.js:*:*",
40
40
  "hashes": {
41
41
  "server": "sha256:ebf254d5eb56aef8705a1c4af9603f47987b4870a9bb5e657e06907b701e2731"
42
42
  },
43
- "refreshedAt": "2026-06-22T16:29:46.590Z"
43
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
44
44
  },
45
45
  "@noble/post-quantum": {
46
46
  "version": "0.6.1",
@@ -71,10 +71,10 @@
71
71
  "hashes": {
72
72
  "server": "sha256:f9190309daadca4c2e2cc2b76beaa6b96e463429cc3c390bd9f0ceaf7b588c68"
73
73
  },
74
- "refreshedAt": "2026-06-22T16:29:46.590Z"
74
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
75
75
  },
76
76
  "@simplewebauthn/server": {
77
- "version": "13.3.1",
77
+ "version": "13.3.2",
78
78
  "license": "MIT",
79
79
  "author": "Matthew Miller",
80
80
  "source": "https://github.com/MasterKale/SimpleWebAuthn",
@@ -89,12 +89,12 @@
89
89
  "server": "lib/vendor/simplewebauthn-server.cjs"
90
90
  },
91
91
  "bundler": "esbuild --format=cjs --minify --platform=node --external:crypto --external:node:crypto",
92
- "bundledAt": "2026-05-27T00:00:00Z",
93
- "cpe": "cpe:2.3:a:simplewebauthn:server:13.3.1:*:*:*:*:node.js:*:*",
92
+ "bundledAt": "2026-06-24T00:00:00Z",
93
+ "cpe": "cpe:2.3:a:simplewebauthn:server:13.3.2:*:*:*:*:node.js:*:*",
94
94
  "hashes": {
95
- "server": "sha256:f359a782ac57e3ff56ac71083d17f5c082f88ab49d645fc2bede398b47adebdb"
95
+ "server": "sha256:49411d893f5e9b0e2fcaa564b4ec7921f73a9a06229b5e53d49c1453ea1a365c"
96
96
  },
97
- "refreshedAt": "2026-06-22T16:29:46.590Z"
97
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
98
98
  },
99
99
  "SecLists-common-passwords-top-10000": {
100
100
  "version": "10k-most-common (master)",
@@ -114,7 +114,7 @@
114
114
  },
115
115
  "runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
116
116
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
117
- "refreshedAt": "2026-06-22T16:29:46.590Z"
117
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
118
118
  },
119
119
  "bimi-trust-anchors": {
120
120
  "version": "operator-managed",
@@ -139,7 +139,7 @@
139
139
  },
140
140
  "runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
141
141
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
142
- "refreshedAt": "2026-06-22T16:29:46.590Z"
142
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
143
143
  },
144
144
  "publicsuffix-list": {
145
145
  "version": "master",
@@ -152,14 +152,14 @@
152
152
  "data_js": "lib/vendor/public-suffix-list.data.js"
153
153
  },
154
154
  "bundler": "curl https://publicsuffix.org/list/public_suffix_list.dat",
155
- "bundledAt": "2026-06-22T00:00:00Z",
155
+ "bundledAt": "2026-06-24T00:00:00Z",
156
156
  "hashes": {
157
- "server": "sha256:0adddeb62057d8d40799dffb29fe14f65dd009259afe02eb2f0b4602b791aae6",
158
- "data_js": "sha256:82af512cacf0fd2c60925e63f877b69477b1b2f7bb5af698fd862af61369902e"
157
+ "server": "sha256:42c4f3544726fa2feb4f304c889c5e391feb5d28080c9b6356ff82e267a93706",
158
+ "data_js": "sha256:c840d9ae5c6bf4a07967340a5c649df2c6a66a287db41e408461c46cc32348e6"
159
159
  },
160
160
  "runtime_artifact": "lib/vendor/public-suffix-list.data.js",
161
161
  "integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
162
- "refreshedAt": "2026-06-22T16:29:46.590Z"
162
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
163
163
  },
164
164
  "peculiar-pki": {
165
165
  "version": "2.0.0+pkijs-3.4.0",
@@ -170,7 +170,7 @@
170
170
  "components": {
171
171
  "@peculiar/x509": {
172
172
  "url": "https://github.com/PeculiarVentures/x509",
173
- "version": "1.13.0"
173
+ "version": "2.0.0"
174
174
  },
175
175
  "pkijs": {
176
176
  "url": "https://github.com/PeculiarVentures/PKI.js",
@@ -190,7 +190,7 @@
190
190
  "hashes": {
191
191
  "server": "sha256:9bbc191afaaa2b1e5757f00480457c08134cdc2c55d541df18d9155bba9cbf77"
192
192
  },
193
- "refreshedAt": "2026-06-22T16:29:46.590Z"
193
+ "refreshedAt": "2026-06-25T02:57:00.483Z"
194
194
  }
195
195
  }
196
196
  }
@@ -5,8 +5,8 @@
5
5
  // Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat,
6
6
  // rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported.
7
7
 
8
- // VERSION: 2026-06-22_11-46-12_UTC
9
- // COMMIT: 27a7b5d881b91def306422e6cc243f05c49f3a58
8
+ // VERSION: 2026-06-24_06-18-09_UTC
9
+ // COMMIT: 18ecca5d54471f21918798da451dd8d03a18f3c7
10
10
 
11
11
  // Instructions on pulling and using this list can be found at https://publicsuffix.org/list/.
12
12
 
@@ -12976,9 +12976,22 @@ cc.cd
12976
12976
  us.ci
12977
12977
  de5.net
12978
12978
 
12979
- // DNShome : https://www.dnshome.de/
12979
+ // dnsHome : https://www.dnshome.de/
12980
12980
  // Submitted by Norbert Auler <mail@dnshome.de>
12981
+ dnshome.at
12982
+ resolve.bar
12983
+ ddns.berlin
12984
+ dnshome.cloud
12985
+ ddnssec.de
12981
12986
  dnshome.de
12987
+ dyndnssec.de
12988
+ heimdns.de
12989
+ srvdns.de
12990
+ dnshome.eu
12991
+ dnshome.it
12992
+ dyn.now
12993
+ heimdns.online
12994
+ ddns.wtf
12982
12995
 
12983
12996
  // DotArai : https://www.dotarai.com/
12984
12997
  // Submitted by Atsadawat Netcharadsang <atsadawat@dotarai.co.th>
@@ -15258,6 +15271,12 @@ nyc.mn
15258
15271
  // Submitted by Kor Nielsen <kor@pubtls.org>
15259
15272
  pubtls.org
15260
15273
 
15274
+ // Puter : https://puter.com
15275
+ // Submitted by Puter Security Team <security@puter.com>
15276
+ puter.app
15277
+ puter.site
15278
+ puter.work
15279
+
15261
15280
  // PythonAnywhere LLP : https://www.pythonanywhere.com
15262
15281
  // Submitted by Giles Thomas <giles@pythonanywhere.com>
15263
15282
  pythonanywhere.com