@blamejs/blamejs-shop 0.0.72 → 0.0.76

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/announcement-bar.js +753 -0
  3. package/lib/banner-ab-tests.js +806 -0
  4. package/lib/bin-locations.js +791 -0
  5. package/lib/blog-articles.js +1173 -0
  6. package/lib/carrier-accounts.js +805 -0
  7. package/lib/cart-recovery.js +1133 -0
  8. package/lib/category-navigation.js +934 -0
  9. package/lib/consent-ledger.js +539 -0
  10. package/lib/customer-impersonation.js +743 -0
  11. package/lib/customer-merge.js +879 -0
  12. package/lib/demand-forecast.js +1121 -0
  13. package/lib/dispute-resolution.js +886 -0
  14. package/lib/email-ab-tests.js +918 -0
  15. package/lib/email-engagement-score.js +649 -0
  16. package/lib/event-log.js +713 -0
  17. package/lib/fulfillment-sla.js +791 -0
  18. package/lib/index.js +41 -0
  19. package/lib/inventory-audits.js +852 -0
  20. package/lib/line-gift-wrap.js +430 -0
  21. package/lib/marketing-budget.js +792 -0
  22. package/lib/operator-activity-feed.js +977 -0
  23. package/lib/operator-approvals.js +942 -0
  24. package/lib/operator-help-center.js +1020 -0
  25. package/lib/operator-inbox.js +889 -0
  26. package/lib/operator-sessions.js +701 -0
  27. package/lib/order-exchanges.js +602 -0
  28. package/lib/product-compare.js +804 -0
  29. package/lib/pwa-manifest.js +1005 -0
  30. package/lib/referral-leaderboard.js +612 -0
  31. package/lib/sales-tax-filings.js +807 -0
  32. package/lib/search-ranking.js +859 -0
  33. package/lib/shipping-insurance.js +757 -0
  34. package/lib/shrinkage-report.js +1182 -0
  35. package/lib/sidebar-widgets.js +952 -0
  36. package/lib/smart-restocking.js +1048 -0
  37. package/lib/stock-receipts.js +834 -0
  38. package/lib/subscription-analytics.js +1032 -0
  39. package/lib/suggestion-box.js +921 -0
  40. package/lib/tax-remittance.js +625 -0
  41. package/lib/vendor-invoices.js +1021 -0
  42. package/lib/winback-campaigns.js +1350 -0
  43. package/lib/wishlist-digest.js +1133 -0
  44. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -8,6 +8,14 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.0.x
10
10
 
11
+ - v0.0.76 (2026-05-23) — **Edge-render path for storefront read routes — `/`, `/search`, `/products/:slug` served from the Worker without a container hop + vendor refresh to blamejs v0.12.5.** The storefront read-side render now runs at the edge. `/`, `/search`, and `/products/:slug` are rendered by the Cloudflare Worker reading D1 directly through the bound binding, then returning HTML — no container hop, no Durable Object handoff. Public visitors see TTFB drop from ~3.4s (container wake-up) to ~50ms. Gated by the `EDGE_RENDER` env var so operators can roll back per-deploy without a re-push. Write-side routes (POST /cart/lines, /checkout, /admin, webhooks) continue to land on the container. Bundles the vendored blamejs refresh from v0.12.4 to v0.12.5 and three CI-tight 50ms `waitUntil` timeouts bumped to 5s for runner-contention resilience. **Added:** *Worker edge-render — `/` home, `/search`, and `/products/:slug` rendered without a container hop* — New Worker-side modules under `worker/render/` (`_lib.js`, `home.js`, `product.js`, `search.js`, `cart.js`) and `worker/data/catalog.js` carry the storefront templates + D1 read-side queries. When `env.EDGE_RENDER` is `"on"`, the Worker queries D1 directly via the bound `env.DB` binding and returns HTML inline. The container's storefront mount continues to own the same routes when the flag is off, so the toggle is operator-controlled and reversible. Cart-count display in the header is fixed at zero on edge-rendered pages until the sealed-session decrypt path lands in a follow-up — visitors landing on `/cart` still see the live count from the container path. · *`EDGE_RENDER` and `SHOP_NAME` wrangler vars* — `wrangler.toml` gains two new `[vars]` entries: `EDGE_RENDER` (default `"off"` — flip to `"on"` to enable the edge-render path) and `SHOP_NAME` (default `"blamejs.shop"` — surfaces in the rendered `<title>` and OpenGraph metadata). **Changed:** *Vendored blamejs refreshed from v0.12.4 to v0.12.5* — `bash scripts/vendor-update.sh blamejs v0.12.5` ran cleanly; `lib/vendor/blamejs/MANIFEST.json` updated. See `lib/vendor/blamejs/CHANGELOG.md` for the upstream surface changes between v0.12.4 and v0.12.5. **Fixed:** *Three 50ms `waitUntil` timeouts bumped to 5s — `live-chat`, `fraud-screen`, `support-tickets` tests* — Nine call sites across `test/layer-1-state/live-chat.test.js`, `test/layer-1-state/fraud-screen.test.js`, and `test/layer-1-state/support-tickets.test.js` were polling for `Date.now()` to advance past a captured timestamp on a 50ms budget. Under CI runner contention that budget sometimes exhausts before the monotonic-clock tick lands. Same fix the v0.0.69 ship applied to `return-labels.test.js`; same rule §11 root cause.
12
+
13
+ - v0.0.75 (2026-05-22) — **Sixteen new primitives across operator tooling, vendor / inventory ops, marketing automation, and storefront content.** Sixteen primitives ship together. Operator + admin: customerImpersonation, carrierAccounts, operatorApprovals, customerMerge, operatorHelpCenter, eventLog. Vendor + inventory: vendorInvoices, inventoryAudits, smartRestocking, taxRemittance. Marketing + retention: winbackCampaigns, wishlistDigest. Storefront: blogArticles, categoryNavigation, productCompare, lineGiftWrap. **Added:** *`customerImpersonation` primitive — operator login-as-customer with audit* — 32-byte plaintext token returned once; namespaceHash at rest. 60-min default TTL. Capability gate via operatorRoles.hasPermission. notifyCustomer fan-out via injected notifications. actionsRecord builds the audit trail. Migration `0190`. · *`carrierAccounts` primitive — per-operator carrier API account management* — Carrier enum: ups / fedex / usps / dhl / canada_post / royal_mail / australia_post. All secrets hashed via namespaceHash under per-field namespaces. rotateCredentials with 24h grace. verifyCredentials timing-safe. Migration `0191`. · *`operatorApprovals` primitive — multi-step approval workflows* — Defines workflows with required_approvers + required_capability + escalation_after_hours. castVote dedup via UNIQUE(request_id, approver_id). Status FSM pending / approved / rejected / executed / cancelled / escalated. Migration `0192`. · *`vendorInvoices` primitive — vendor-issued invoices with PO reconciliation* — FSM received → approved → paid with disputed + voided side branches. Partial-pay first-class. UNIQUE(vendor_slug, invoice_number) for dedup. reconcileAgainstPOs returns variance summary. agingReport bucketing. Migration `0193`. · *`customerMerge` primitive — operator merge of duplicate customer accounts* — Reparents orders + subscriptions + loyalty + reviews + addresses + paymentMethods atomically (pre-flight all, then write all). 7-day rollback window. Jaro-Winkler-scored findDuplicateCandidates. customer_merge_redirects table for forwarding. Migration `0194`. · *`productCompare` primitive — storefront 2-4 product comparison* — Cap of 4 per session. compareTable returns per-attribute rows for the table render. Default attributes: price / sku / brand / vendor / weight / dimensions / inventory_status. defineCompareAttribute adds custom rows. popularCompares for analytics. Migration `0195`. · *`winbackCampaigns` primitive — multi-step re-engagement for lapsed customers* — Operator defines lapse_days_min + lapse_days_max + steps (delay + template + optional coupon). scanForLapsedCustomers finds candidates; enrollCustomer schedules; dispatchTick advances. markRecovered halts further steps. metricsForCampaign returns recovery rate + avg time-to-purchase. Migration `0196`. · *`inventoryAudits` primitive — periodic full-inventory audits* — Kinds: full / quarterly / spot. Scopes: all / category / vendor / location. recordScanLine + markRecount + finalizeAudit. Variance write-through composes inventoryLocations.adjustStock with `inventory-audit:<slug>` reason. compareToPriorAudit returns per-(sku, location) deltas. Migration `0197`. · *`wishlistDigest` primitive — scheduled weekly / monthly wishlist email digests* — Per-customer enrollment + cadence. next_dispatch_at computed via Intl.DateTimeFormat with two-pass DST-fold refinement. composeDigest returns HTML + text. Suppressed customers keep cadence ETA on rails but no email fires. Migration `0198`. · *`eventLog` primitive — universal append-only application event stream* — Drop-silent on bad input. Severity: debug / info / warning / critical. query with HMAC cursor. tail + topKinds + metricsForKind + purgeOlderThan (exclude_critical opt-in). Distinct from analytics (customer events) and operatorAuditLog (operator mutations). Migration `0199`. · *`operatorHelpCenter` primitive — in-admin operator help articles* — audience_roles closed allow-list against operatorRoles.PERMISSIONS. searchSuggest title 3 / section 2 / body 1 weighted ranker. recordHelpfulVote dedup at UNIQUE(slug, operator_id). Migration `0200`. · *`categoryNavigation` primitive — hierarchical storefront category tree* — self-FK parent_slug + CHECK(slug != parent_slug). tree returns nested shape. breadcrumbsFor returns ancestor chain. move walks ancestor chain to refuse cycles (bounded MAX_TREE_DEPTH=16). reorderSiblings requires the complete member set. Migration `0201`. · *`lineGiftWrap` primitive — per-line gift wrap selection* — Distinct from giftOptions (one wrap for the whole order). UNIQUE(order_id, line_id) UPSERT. gift_message ≤ 500 chars + control-byte refusal; recipient_name ≤ 120. feeForOrder sums per-line wrap fees via injected giftOptions. renderPackingSlipLines HTML-escapes every operator field. Migration `0202`. · *`smartRestocking` primitive — EOQ + safety-stock recommendations* — Composes demandForecast + reorderThresholds + costLayers + vendors. recommendOrderQty returns EOQ formula `sqrt(2DS/H)` + service-level safety-stock + reorder point + cost estimate. Service levels: 0.90 / 0.95 / 0.99. definePolicy + applyPolicy assigns SKUs to policies. Migration `0203`. · *`taxRemittance` primitive — per-jurisdiction tax payment tracking* — Records remittances with payment_method enum (bank_transfer / credit_card / ach / wire / check). reconcileWithFiling returns variance. lateRemittances threshold + recordPenalty + metricsForJurisdiction on-time rate. Migration `0204`. · *`blogArticles` primitive — operator-published blog posts* — Author + tags + featured_product_ids + SEO meta + publish FSM (draft → published → archived → draft). Markdown render through b.template.escapeHtml + b.safeUrl. relatedArticles tag-overlap ranking. byAuthor + popularArticles + recordView. Migration `0189`.
14
+
15
+ - v0.0.74 (2026-05-22) — **`emailEngagementScore` primitive + republish 0.0.73.** 0.0.73 source landed on `main` but the npm publish workflow stopped on a smoke check — the email-engagement-score test was committed without its lib counterpart. 0.0.74 adds the missing `emailEngagementScore` primitive and republishes the full v0.0.73 surface so `npm install @blamejs/blamejs-shop@0.0.74` pulls every primitive cleanly. **Added:** *`emailEngagementScore` primitive — per-customer 0-100 email engagement grade* — `bShop.emailEngagementScore.create({ query?, emailCampaigns?, emailSuppressions? })` returns `{ recordEngagementEvent, getScore, recompute, recomputeAll, unengagedCustomers, metricsForBand, historyForCustomer }`. Six event types with calibrated weights (opened +5, clicked +15, unsubscribed -50, spam_reported -75, bounced -10, not_opened_in_window -3). Score starts at 50 baseline, clamped to [0, 100]. Four bands: unengaged (0-19) / lapsed (20-49) / engaged (50-74) / highly_engaged (75-100). Click implies open in the open_rate denominator (compensates for image-blocking clients). Monotonic per-customer `_resolveOccurredAt` to break same-millisecond ties. Migration `0187_email_engagement_score.sql`. **Fixed:** *Republish to npm — 0.0.73 source on `main` reached operators via `git clone` but not via `npm install`* — `npm install @blamejs/blamejs-shop@0.0.73` resolves to a missing version because the publish workflow exited early when the CI smoke gate couldn't find `lib/email-engagement-score.js` (its test file was committed alongside v0.0.73 but the lib file itself was untracked at commit time). 0.0.74 ships the full surface so operators upgrading by version number get every primitive.
16
+
17
+ - v0.0.73 (2026-05-22) — **Twenty new primitives across operator ops, commerce, storefront, and compliance.** Twenty primitives ship together. Operator surfaces: marketingBudget, operatorSessions, operatorInbox, operatorActivityFeed, disputeResolution, shrinkageReport, demandForecast, fulfillmentSLA, salesTaxFilings, binLocations. Commerce: orderExchanges, searchRanking, shippingInsurance, cartRecovery, emailABTests, bannerABTests, referralLeaderboard. Storefront: pwaManifest, sidebarWidgets, announcementBar, stockReceipts, suggestionBox. Subscriptions: subscriptionAnalytics. Compliance: consentLedger. **Added:** *`marketingBudget` primitive — per-channel spend + ROAS* — Eleven channel kinds with per-channel currency lock. Integer-bps ROAS. UTC month budget reconciliation. Migration `0172`. · *`orderExchanges` primitive — different-item exchange FSM* — Six-state FSM with two-arm convergence. closeExchange requires both delivered_at and returned_at non-null. approveExchange opens an inventory hold pre-commit. Migration `0164`. · *`disputeResolution` primitive — chargeback lifecycle* — Four dispute kinds, seven evidence kinds, four outcomes. FSM open / submitted / lost / written_off. Migration `0173`. · *`operatorSessions` primitive — staff login with MFA + lockout* — 32-byte base64url bearer, hashed via namespaceHash. 8-hour TTL. Per-IP binding. Lockout after 5 failures in 15 minutes. Migration `0165`. · *`shippingInsurance` primitive — per-shipment insurance + claims* — Basis-point premium math. Insurance FSM active / cancelled / expired. Claim FSM filed / approved / denied. Four claim types. Migration `0166`. · *`pwaManifest` primitive — PWA manifest + service-worker config* — Display + orientation enums. Byte-stable JSON output via sorted-key serialise. URLs gated via b.safeUrl. Migration `0168`. · *`searchRanking` primitive — operator-tunable search reranker* — Boost/demote weights + manual pins per query. CTR + conversion math. Session ids hashed via namespaceHash. Migration `0167`. · *`emailABTests` primitive — subject + body A/B with Wilson CI* — Deterministic SHA3-512 assignment with 100k-bucket weight mapping. Sticky via the existing sent ledger row. Migration `0169`. · *`operatorInbox` primitive — in-admin notification feed* — Severity enum info / warning / urgent / critical. Exactly-one-of operator_id / role addressing. HMAC cursor pagination. Migration `0175`. · *`demandForecast` primitive — per-SKU demand prediction* — Four model kinds (SMA / WMA / exponential_smoothing / linear_regression). Band-coherence CHECKs. Migration `0179`. · *`stockReceipts` primitive — customer QR-scan delivery confirmation* — 32-byte token hashed at rest. Receipt FSM issued / scanned / completed / expired. Line FSM pending / received / damaged / partial. Migration `0177`. · *`bannerABTests` primitive — promo banner A/B with Wilson CI* — Deterministic per-session assignment. FSM running / paused / concluded / archived. Migration `0174`. · *`cartRecovery` primitive — abandon-cart email sequences* — Operator-defined step schedule. Composes cartAbandonment + email + emailSuppressions. markRecovered halts further steps. Migration `0171`. · *`sidebarWidgets` primitive — placement-targeted storefront blocks* — Nine widget kinds. Audience filter with optional customerSegments handle. Schedule-window gating + drop-silent counters. Migration `0176`. · *`consentLedger` primitive — append-only GDPR consent audit trail* — Nine consent kinds across cookies / marketing / data-sharing / data-processing. Two states (granted / withdrawn). Six sources. CSV / JSON audit export. Migration `0185`. · *`announcementBar` primitive — top-strip storefront message* — Four themes ranked urgency / promo / info / success. Schedule window + audience filter. Session-based dismissal dedup via UNIQUE(slug, session_id_hash). Migration `0180`. · *`shrinkageReport` primitive — loss-prevention dashboard* — Aggregates inventoryWriteoffs by reason / period / location / sku. Sample-stddev anomaly detection. Mixed-currency aggregation refusal. Migration `0170`. · *`subscriptionAnalytics` primitive — MRR / churn / cohort retention* — MRR cadence normalization across plan frequencies. Churn rate voluntary / involuntary / total. Cohort retention by month-zero. Migration `0178`. · *`operatorActivityFeed` primitive — staff-side timeline* — Aggregates operatorAuditLog + supportTickets + operatorInbox + operatorSessions. Cross-operator teamFeed. currentlyOnline 5-minute gate. Migration `0188`. · *`referralLeaderboard` primitive — top-referrer rankings + tier bonuses* — Reads from referrals primitive for ranking. Composes loyalty.earn for tier bonus payouts. UNIQUE(customer_id, period_label, tier) is the re-award gate. Migration `0182`. · *`fulfillmentSLA` primitive — order ship + deliver SLA tracking* — Per-priority policies (standard / expedited / overnight / same_day) with cutoff_local_time + timezone via Intl.DateTimeFormat. Severity bands minor / major / critical. Migration `0183`. · *`binLocations` primitive — warehouse bin / aisle / shelf assignments* — Auto-primary policy + pickPathSort by (aisle, shelf, level). Audit log with variance computation. Bin condition states clean / needs_audit / damaged / unusable. Migration `0186`. · *`salesTaxFilings` primitive — quarterly / monthly filings prep* — Aggregates orders + tax + taxRates + taxExempt into per-jurisdiction filings. FSM draft / computed / submitted / paid / amended. Banker's-rounded integer math on re-derive. Migration `0184`. · *`suggestionBox` primitive — customer feature-request box with up/downvote* — Categories: product_idea / feature_request / improvement / complaint / general. Status FSM open / under_consideration / planned / shipped / declined / duplicate. Vote dedup via UNIQUE(suggestion_id, session_id_hash). Migration `0181`.
18
+
11
19
  - v0.0.72 (2026-05-22) — **`loyaltyEarnRules` primitive — per-action point earning rules.** `loyaltyEarnRules` defines HOW points are earned across eight trigger events: `per_dollar_spent`, `per_purchase`, `per_review`, `per_referral_redeemed`, `birthday`, `signup_bonus`, `first_purchase`, `abandoned_cart_recovered`. Distinct from the existing `loyalty` (ledger) and `tierBenefits` (perks) primitives. **Added:** *`loyaltyEarnRules` primitive — per-action point earning rules with optional per-event caps* — `bShop.loyaltyEarnRules.create({ query?, loyalty? })` returns `{ defineRule, getRule, listRules, updateRule, archiveRule, evaluateForEvent, awardForEvent, metricsForRule, applyBatch }`. Triggers: `per_dollar_spent / per_purchase / per_review / per_referral_redeemed / birthday / signup_bonus / first_purchase / abandoned_cart_recovered`. `evaluateForEvent` returns the points calculation (multiply + floor + zero-floor); `awardForEvent` composes the injected `loyalty.earn` handle and rolls back the audit row on ledger failure. Per-event cap (`max_per_event`) clips runaway awards. `customer_status_in` allowlist filters which customer tiers a rule applies to. UNIQUE(`rule_slug`, `customer_id`, `trigger_event_ref`) dedups re-submitted events. `applyBatch` runs multiple awards atomically with partial-failure reporting. Migration `0163_loyalty_earn_rules.sql`.
12
20
 
13
21
  - v0.0.71 (2026-05-22) — **`splitShipments` — monotonic `proposed_at` so back-to-back plans sort newest-first.** Single-fix release. `splitShipments.planSplit` calls landing in the same wall-clock millisecond now get strictly-increasing `proposed_at` stamps via a process-local monotonic clock. Without the fix, `splitsForOrder(order_id)` returned the rows in nondeterministic order under burst-write conditions — CI runners hit the collision more reliably than local clocks. **Fixed:** *`splitShipments` — process-local monotonic `_now` so consecutive `planSplit` calls sort deterministically* — Two `planSplit` calls inside the same millisecond shared a `proposed_at` value, and the secondary tiebreaker (UUID v7 id) was scrambled by the intra-ms random suffix. The primitive now bumps `_now()` by 1ms when `Date.now()` returns a stale value, so `splitsForOrder` reliably returns newest-first under any insert burstiness. Same fix pattern as `orderNotes` / `productBulkOps` / `cookieConsent` / `liveChat` / `sms-dispatcher` / others — applies cleanly to any primitive where `Date.now()` precision drives sort order.