@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 +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/consent-ledger.js +50 -3
- package/package.json +1 -1
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.
|
package/lib/asset-manifest.json
CHANGED
package/lib/consent-ledger.js
CHANGED
|
@@ -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