@blamejs/blamejs-shop 0.3.64 → 0.3.65

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,8 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.3.x
10
10
 
11
+ - v0.3.65 (2026-06-04) — **Operator-tuned search ranking applies to edge-served visitors.** Search-ranking weight sets and manual pins applied only on container-served search — anonymous visitors, who are served from the edge cache and make up most traffic, got unranked results no matter what the operator tuned. The edge search path now applies the identical ranking: pinned products lead in their configured positions, the rest order by the active weight set, with byte-identical scoring pinned by a parity test in the same style as the existing faceting and synonym parity guards. Ranking and pin changes reach edge visitors within the same sixty-second cache window as every other operator-tunable edge surface. If the ranking tables are absent or unconfigured, edge search degrades to its previous unranked order rather than failing. **Fixed:** *Edge search results honor ranking weights and manual pins* — The /search route served from the edge ordered results by filter match alone, ignoring the active search weight set and any merchandiser pins, so ranking tuning was visible only to signed-in (container-served) visitors. The edge now loads the active weight set and pins on each cache fill and applies the same scoring as the container — pins first in position order, then weighted score descending, with identical tie-breaking. The two implementations are locked together by a parity test that runs the same fixtures through both. Changes the operator makes take effect for edge visitors within the standard sixty-second cache freshness window; a missing or unconfigured ranking table degrades to the unranked order, never an error.
12
+
11
13
  - v0.3.64 (2026-06-04) — **Google Pay and Apple Pay express buttons can run on the payment page.** The app-level Permissions-Policy denies the Payment Request API everywhere — including the payment page, the one surface whose job is payment — so Stripe's express wallet buttons (Google Pay, Apple Pay) could not operate and the browser console filled with policy violations. The payment page now sends a route-scoped Permissions-Policy that grants the payment feature to the page itself, Stripe, and Google Pay, while every other feature stays denied and every other route keeps the fully strict header — the same route-scoping pattern the payment pages already use for their Content-Security-Policy. Card payments and 3-D Secure challenges were verified live before and after; a remaining cosmetic Trusted-Types console message from Stripe's loader is documented in code with the test recipe for future verification, and does not affect card, wallet, or 3-D Secure flows. **Fixed:** *Payment page grants the Payment Request API to Stripe and Google Pay* — The default Permissions-Policy sets payment=() sitewide, which blocks the Payment Request API that Stripe's express wallet buttons need. The /pay route now emits the same policy with exactly one change — payment=(self "https://js.stripe.com" "https://pay.google.com") — derived from the same source as the default header so the two can never drift apart. Camera, microphone, geolocation, and every other feature remain denied on the payment page, and all other routes keep payment=(). Tests pin the scoped header on the payment route, the strict header elsewhere, and that exactly one feature differs from the default.
12
14
 
13
15
  - v0.3.63 (2026-06-04) — **Digital-only carts can check out, and the outbound TLS posture is documented end to end.** A cart containing only digital goods could never complete checkout: the shipping resolver correctly filters physical services out for a cart that ships nothing, but checkout then demanded the configured default physical service resolve anyway and refused the order. Digital-only carts now select the operator's digital-shipping service when one is configured, and otherwise complete with a synthesized zero-cost no-shipping selection; a physical cart with a misconfigured service id still fails loudly, since that is a real configuration error. Separately, every outbound TLS connection has been audited against the framework's post-quantum-first key-exchange posture: of all fixed external hosts, only the Stripe API edge refuses the post-quantum groups (already handled by the payment module's explicit agent) — the CAPTCHA providers, PayPal, and Cloudflare all negotiate hybrids cleanly. Operator-supplied destinations — webhook receiver URLs and admin media source URLs — deliberately keep the post-quantum-first default, with the failure mode documented where operators will look for it: a receiver that cannot negotiate a hybrid group sees deliveries fail with a TLS handshake failure, retry on the backoff schedule, and land in the dead-letter queue; a media source URL that cannot fails as the existing clean fetch error. A regression test pins that these dials never silently downgrade. **Changed:** *Outbound TLS posture audited and documented* — Every outbound connection was audited against the framework's post-quantum-first TLS key-exchange list. Fixed external hosts: the Stripe API edge is the only one that refuses the post-quantum groups, and the payment module's explicitly scoped agent already covers it (PayPal currently negotiates hybrids; the agent remains as defense). Operator-supplied destinations keep the post-quantum-first default deliberately: an outbound-webhook receiver that cannot negotiate a hybrid group sees deliveries fail with a TLS handshake failure (alert 40), retry on the backoff schedule, and reach the dead-letter queue after the fifth attempt — front such a receiver with a post-quantum-capable proxy or upgrade its TLS stack; an admin media source URL that cannot negotiate fails as the existing clean fetch error, and the file can be uploaded directly instead. Module documentation now states this where the integrations are configured, and a regression test pins that these dials never silently downgrade. **Fixed:** *Digital-only carts complete checkout* — Shipping rate resolution offers only digital-shipping services to a cart in which no line requires shipping — so the configured default physical service id could never resolve, and checkout refused every all-digital order. Checkout now falls back for all-digital carts: the operator's digital-shipping service is selected when configured, and a zero-cost "No shipping required" selection is synthesized otherwise, so the order completes and records zero shipping. Physical carts are unchanged: an unresolvable service id still fails at the boundary, because that is a real configuration typo.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.3.64",
2
+ "version": "0.3.65",
3
3
  "assets": {
4
4
  "css/admin.css": {
5
5
  "integrity": "sha384-6k53cvkRrxMgmeStLIoLjVXZQHqIJgTmv1Izd8TYhh1HOC4POgE6GCvx1bsalyEP",
package/lib/storefront.js CHANGED
@@ -11281,11 +11281,13 @@ function mount(router, deps) {
11281
11281
  // with no active set + no slug is a safe no-op that preserves input order,
11282
11282
  // so the common "operator hasn't configured ranking" path is inert.
11283
11283
  //
11284
- // NOT dual-render — the edge /search (worker/render/search.js) does NOT
11285
- // rerank. Edge-cached search serves the default order; the container path
11286
- // serves the ranked order. This deliberate non-parity matches the
11287
- // synonyms/facets precedent (both container-only at the edge) — search order
11288
- // is not a price/legal contract, so order differences are acceptable.
11284
+ // DUAL-RENDER — the edge /search reranks too. The Worker can't require this
11285
+ // CommonJS leaf, so it mirrors the same scoring + pin + sort algorithm in
11286
+ // worker/data/search-ranking.js (ESM) against the same projected signal bag
11287
+ // (in_stock + price_minor). The `search-ranking-parity` test pins the mirror
11288
+ // to applyToResults's output so anonymous edge traffic and logged-in
11289
+ // container traffic see identical result order for the same query + active
11290
+ // weights + pins. The same precedent the synonyms/facets mirrors follow.
11289
11291
  async function _rerankUniverse(universe, query) {
11290
11292
  if (!deps.searchRanking || !Array.isArray(universe) || universe.length === 0) {
11291
11293
  return universe;
@@ -11361,8 +11363,9 @@ function mount(router, deps) {
11361
11363
  // Operator-tunable rerank — applied to the FULL matched universe
11362
11364
  // BEFORE the facet adapter windows it, so pins + weights reorder
11363
11365
  // across ALL pages (searchFacets.previewQuery filters in input order
11364
- // and slices, preserving the reranked order). Container-only; never
11365
- // 500s the search page (see _rerankUniverse).
11366
+ // and slices, preserving the reranked order). Mirrored at the edge by
11367
+ // worker/data/search-ranking.js (parity-tested); never 500s the
11368
+ // search page (see _rerankUniverse).
11366
11369
  universe = await _rerankUniverse(universe, q);
11367
11370
  var facetCatalog = {
11368
11371
  // The primitive narrows in-memory and drops the focal facet
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.3.64",
3
+ "version": "0.3.65",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {