@blamejs/blamejs-shop 0.4.51 → 0.4.52

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.4.x
10
10
 
11
+ - v0.4.52 (2026-06-14) — **Consent records now carry the GDPR Article 6 lawful basis they rest on.** Each decision in the durable per-customer consent ledger now records the GDPR Article 6(1) lawful basis it is processed under — consent, contract, legal obligation, vital interests, public task, or legitimate interests — alongside what was decided and when. The basis defaults from the consent kind (cookie categories, marketing email/SMS, and data-sharing opt-ins are all consent-based), and a caller may pass an explicit, validated basis for a record that rests on another. The basis is surfaced in the subject-access and supervisory-authority exports, so a consent audit now shows not just the decision but the legal ground for it. A migration adds a nullable, constrained lawful_basis column; pre-existing rows keep a null basis rather than being assigned one retroactively. Apply the pending migration on upgrade. **Added:** *Lawful basis on consent-ledger records* — The consent ledger now stamps the GDPR Article 6(1) lawful basis on every recorded decision. The basis defaults from the consent kind — every category the ledger tracks (cookie functional/analytics/marketing/preferences, marketing email, marketing SMS, partner and analytics data-sharing, and general data processing) is consent-based — and an explicit basis can be supplied and is validated against the six Article 6(1) values. Withdrawal records carry the same basis as the grant they revoke. The basis is included in the subject-access export (CSV and JSON) and the jurisdiction bulk export, so a consent audit shows the legal ground each decision rests on. **Changed:** *Migration: lawful_basis column on consent_ledger* — A new migration adds a nullable lawful_basis column to consent_ledger, constrained to the six Article 6(1) bases. The column is nullable and the constraint applies only to rows written after the migration, so existing records keep a null basis (not retroactively assigned) while every new record carries one. Apply the pending migration on upgrade.
12
+
11
13
  - v0.4.51 (2026-06-14) — **The order page now shows a dated activity timeline of the order's lifecycle.** A customer viewing their order now sees a chronological activity feed of what has happened to it — placed, payment received, shipped, delivered, cancelled, and refunds — newest first, each with its date and the relevant detail. A shipped event shows the carrier and tracking number when one was recorded; a refund shows its amount (and marks a partial refund as such); a click-and-collect delivery shows the pickup location. The feed is built from the order's own transition history and is scoped to the order being viewed, so it appears only to the order's owner or a guest holding the order's access link. Internal fulfillment bookkeeping and operator-process detail are not surfaced — only customer-meaningful milestones. The order page renders this server-side with no client JavaScript. No migration to apply. **Added:** *Order activity timeline on the order page* — The order detail page renders a dated, newest-first timeline of the order's lifecycle events: placed, payment received, shipped (with carrier and tracking number when present), delivered (with pickup location for click-and-collect), cancelled, and refunds (with the refunded amount, labelled partial when applicable). Events are drawn from the order's transition history and filtered to customer-meaningful milestones — internal fulfillment steps, raw state transitions, operator-process reasons, and payment-provider identifiers are never shown. Every rendered value is HTML-escaped, and the feed is shown only within the order's existing access scope (the signed-in owner or a guest order's access link).
12
14
 
13
15
  - v0.4.50 (2026-06-14) — **Refresh the vendored blamejs framework to 0.15.11.** Refreshes the vendored blamejs framework from 0.15.9 to 0.15.11 (through 0.15.10). 0.15.11 replaces a family of quadratic-time regexes that hostile input could exploit to stall a worker with linear-time scans, refuses a relocatable sealed-cell downgrade on the read side, fails closed when row-level security is enabled behind a non-native driver, and verifies the vendored crypto against a reviewed pin. 0.15.10 makes S3 Object-Lock versioned erasure reachable through the object store — a versioned delete that targets a specific object version (refused under an active retention rather than silently writing a delete-marker), plus version enumeration for right-to-erasure workflows — and pins the build toolchain's native bundler binary to a reviewed hash. This refresh carries no shop-facing API change and applies no migration; it keeps the bundled framework current and the security posture aligned with the latest release. **Changed:** *Vendored blamejs refreshed to 0.15.11* — The bundled framework is updated to blamejs 0.15.11. Across 0.15.10 and 0.15.11 it hardens a family of regular expressions against quadratic-time blow-up on hostile input (linear-time scans), refuses a relocatable sealed-cell downgrade when reading, fails closed when row-level security is enabled behind a driver that can't enforce it, adds versioned object erasure for S3 Object-Lock buckets (a versioned delete that refuses an active retention instead of leaving the data behind a delete-marker, plus version enumeration), and pins the build toolchain's native binary to a reviewed hash. Storefront and admin behaviour is unchanged by the refresh; the framework's PQC-first crypto, security middleware, and request lifecycle are carried forward as-is.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.4.51",
2
+ "version": "0.4.52",
3
3
  "assets": {
4
4
  "css/admin.css": {
5
5
  "integrity": "sha384-imfe0otYErcB8rr2h6KLSGTtStirysptpXETSPY4zLv3bZoIT75Lo1dOvkOav+xL",
@@ -131,6 +131,7 @@ var CSV_COLUMNS = Object.freeze([
131
131
  "consent_kind",
132
132
  "state",
133
133
  "source",
134
+ "lawful_basis",
134
135
  "jurisdiction",
135
136
  "evidence_ref",
136
137
  "occurred_at",
@@ -138,6 +139,32 @@ var CSV_COLUMNS = Object.freeze([
138
139
 
139
140
  var b = require("./vendor/blamejs");
140
141
 
142
+ // The GDPR Art. 6(1) lawful bases, sourced from the framework's
143
+ // consent primitive so this ledger's vocabulary tracks the
144
+ // primitive's ground truth instead of re-listing the six strings.
145
+ var LAWFUL_BASES = b.consent.LAWFUL_BASES;
146
+
147
+ // Every consent kind this ledger records is consent-gated: cookies
148
+ // are non-essential under ePrivacy Art. 5(3) (the strictly-necessary
149
+ // set lives in cookieConsent, never here), marketing email / SMS rest
150
+ // on Art. 6(1)(a), and third-party data sharing is not a contract
151
+ // necessity. data_processing is the opt-in catch-all — contract /
152
+ // legal-obligation processing is recorded by the domain table that
153
+ // owns it, not by this opt-in ledger. So each kind maps to "consent".
154
+ // A caller may still pass an explicit, validated lawful_basis to
155
+ // override the default for a row that genuinely rests on another basis.
156
+ var KIND_TO_LAWFUL_BASIS = Object.freeze({
157
+ cookies_functional: "consent",
158
+ cookies_analytics: "consent",
159
+ cookies_marketing: "consent",
160
+ cookies_preferences: "consent",
161
+ marketing_email: "consent",
162
+ marketing_sms: "consent",
163
+ data_sharing_partners: "consent",
164
+ data_sharing_analytics: "consent",
165
+ data_processing: "consent",
166
+ });
167
+
141
168
  // ---- monotonic clock ----------------------------------------------------
142
169
  //
143
170
  // Operator-driven writes can land in the same millisecond on fast
@@ -195,6 +222,16 @@ function _source(s) {
195
222
  return s;
196
223
  }
197
224
 
225
+ function _lawfulBasis(s) {
226
+ if (typeof s !== "string" || LAWFUL_BASES.indexOf(s) === -1) {
227
+ throw new TypeError(
228
+ "consentLedger: lawful_basis must be one of " + LAWFUL_BASES.join(", ") +
229
+ ", got " + JSON.stringify(s)
230
+ );
231
+ }
232
+ return s;
233
+ }
234
+
198
235
  function _optJurisdiction(s) {
199
236
  if (s == null || s === "") return null;
200
237
  if (typeof s !== "string") {
@@ -282,6 +319,7 @@ function _rowToRecord(row) {
282
319
  consent_kind: row.consent_kind,
283
320
  state: row.state,
284
321
  source: row.source,
322
+ lawful_basis: row.lawful_basis == null ? null : row.lawful_basis,
285
323
  jurisdiction: row.jurisdiction == null ? null : row.jurisdiction,
286
324
  evidence_ref: row.evidence_ref == null ? null : row.evidence_ref,
287
325
  occurred_at: Number(row.occurred_at),
@@ -327,15 +365,23 @@ function create(opts) {
327
365
  var source = _source(input.source);
328
366
  var jurisdiction = _optJurisdiction(input.jurisdiction);
329
367
  var evidenceRef = _optEvidenceRef(input.evidence_ref);
368
+ // Lawful basis defaults from the kind (every kind here is
369
+ // consent-gated; the basis is state-agnostic so a withdrawal row
370
+ // carries the same basis as its grant). An explicit lawful_basis is
371
+ // validated and overrides the default for a row that rests on a
372
+ // different Art. 6 basis.
373
+ var lawfulBasis = input.lawful_basis == null
374
+ ? KIND_TO_LAWFUL_BASIS[consentKind]
375
+ : _lawfulBasis(input.lawful_basis);
330
376
 
331
377
  var id = b.uuid.v7();
332
378
  var ts = _now();
333
379
 
334
380
  await query(
335
381
  "INSERT INTO consent_ledger " +
336
- "(id, customer_id, consent_kind, state, source, jurisdiction, evidence_ref, occurred_at) " +
337
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
338
- [id, customerId, consentKind, state, source, jurisdiction, evidenceRef, ts],
382
+ "(id, customer_id, consent_kind, state, source, lawful_basis, jurisdiction, evidence_ref, occurred_at) " +
383
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
384
+ [id, customerId, consentKind, state, source, lawfulBasis, jurisdiction, evidenceRef, ts],
339
385
  );
340
386
 
341
387
  return {
@@ -344,6 +390,7 @@ function create(opts) {
344
390
  consent_kind: consentKind,
345
391
  state: state,
346
392
  source: source,
393
+ lawful_basis: lawfulBasis,
347
394
  jurisdiction: jurisdiction,
348
395
  evidence_ref: evidenceRef,
349
396
  occurred_at: ts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.4.51",
3
+ "version": "0.4.52",
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": {