@huloglobal/vendure-plugin-visitor-analytics 0.17.1 → 0.18.0

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.
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESUMABLE_ORDER_STATES = void 0;
4
+ exports.isResumableOrderState = isResumableOrderState;
5
+ exports.recoveryStepRank = recoveryStepRank;
6
+ exports.advanceRecoveryStep = advanceRecoveryStep;
7
+ exports.normaliseEmail = normaliseEmail;
8
+ exports.hashEmail = hashEmail;
9
+ exports.buildOptOutToken = buildOptOutToken;
10
+ exports.verifyOptOutToken = verifyOptOutToken;
11
+ exports.buildOptOutUrl = buildOptOutUrl;
12
+ exports.buildListUnsubscribeHeaders = buildListUnsubscribeHeaders;
13
+ exports.sanitiseOrderCode = sanitiseOrderCode;
14
+ const crypto_1 = require("crypto");
15
+ /**
16
+ * Pure helpers behind the cart-recovery attribution + opt-out features
17
+ * (0.18.0). No I/O — everything here is unit-tested in isolation.
18
+ */
19
+ /** Vendure order states from which a customer can still pick up the same
20
+ * order (the storefront can add items / arrange payment). Anything later —
21
+ * `PaymentAuthorized`, `PaymentSettled`, `Cancelled`, … — is a finished or
22
+ * dead order and the storefront should rebuild a fresh cart instead. */
23
+ exports.RESUMABLE_ORDER_STATES = ['AddingItems', 'ArrangingPayment'];
24
+ function isResumableOrderState(state) {
25
+ return !!state && exports.RESUMABLE_ORDER_STATES.includes(String(state));
26
+ }
27
+ const RECOVERY_STEP_ORDER = ['link_issued', 'link_opened', 'resumed', 'converted'];
28
+ function recoveryStepRank(step) {
29
+ const i = RECOVERY_STEP_ORDER.indexOf(String(step || ''));
30
+ return i;
31
+ }
32
+ /** Returns the later of `current` and `target`; never regresses. Unknown
33
+ * values in `current` (legacy rows, hand edits) are treated as "no step". */
34
+ function advanceRecoveryStep(current, target) {
35
+ return recoveryStepRank(current) >= recoveryStepRank(target)
36
+ ? current
37
+ : target;
38
+ }
39
+ /** Lower-cased, trimmed. Returns '' for anything that is not an email. */
40
+ function normaliseEmail(raw) {
41
+ const s = String(raw || '').trim().toLowerCase();
42
+ if (s.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s))
43
+ return '';
44
+ return s;
45
+ }
46
+ /** SHA-256 hex of the normalised email — the same hash `abandoned_cart.emailHash`
47
+ * stores, so opt-outs can be matched without keeping the raw address. */
48
+ function hashEmail(email) {
49
+ return (0, crypto_1.createHash)('sha256').update(normaliseEmail(email)).digest('hex');
50
+ }
51
+ function b64url(buf) {
52
+ return buf.toString('base64url');
53
+ }
54
+ function optOutMac(email, secret) {
55
+ return b64url((0, crypto_1.createHmac)('sha256', secret).update(`opt-out:${email}`).digest());
56
+ }
57
+ /**
58
+ * Opt-out token: `<base64url(email)>.<base64url(hmac-sha256(secret, email))>`.
59
+ * Self-describing so the endpoint needs no DB lookup to know *who* is opting
60
+ * out, and unforgeable so a third party cannot unsubscribe someone else.
61
+ * Returns null when there is no secret (feature disabled) or the email is
62
+ * malformed.
63
+ */
64
+ function buildOptOutToken(email, secret) {
65
+ const e = normaliseEmail(email);
66
+ if (!e || !secret)
67
+ return null;
68
+ return `${b64url(Buffer.from(e, 'utf8'))}.${optOutMac(e, secret)}`;
69
+ }
70
+ /** Inverse of `buildOptOutToken`. Returns the normalised email on success,
71
+ * null on any failure (bad shape, bad MAC, wrong secret). Constant-time
72
+ * MAC comparison. */
73
+ function verifyOptOutToken(token, secret) {
74
+ if (!secret)
75
+ return null;
76
+ const t = String(token || '').trim();
77
+ const dot = t.indexOf('.');
78
+ if (dot <= 0 || dot === t.length - 1)
79
+ return null;
80
+ let email = '';
81
+ try {
82
+ email = normaliseEmail(Buffer.from(t.slice(0, dot), 'base64url').toString('utf8'));
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ if (!email)
88
+ return null;
89
+ const expected = Buffer.from(optOutMac(email, secret));
90
+ const given = Buffer.from(t.slice(dot + 1));
91
+ if (expected.length !== given.length)
92
+ return null;
93
+ return (0, crypto_1.timingSafeEqual)(expected, given) ? email : null;
94
+ }
95
+ /** Build the absolute opt-out URL for an email. `backendBaseUrl` is the
96
+ * Vendure server's public origin (the endpoint lives on the API, not the
97
+ * storefront). */
98
+ function buildOptOutUrl(backendBaseUrl, email, secret) {
99
+ const token = buildOptOutToken(email, secret);
100
+ if (!token)
101
+ return null;
102
+ return `${String(backendBaseUrl || '').replace(/\/$/, '')}/ees/abandoned-carts/opt-out?e=${encodeURIComponent(token)}`;
103
+ }
104
+ /**
105
+ * Headers a mailer should add to every abandoned-cart email so Gmail /
106
+ * Outlook / Yahoo render their native "Unsubscribe" affordance
107
+ * (RFC 2369 + RFC 8058 one-click). Returns null when opt-out links are
108
+ * disabled.
109
+ */
110
+ function buildListUnsubscribeHeaders(backendBaseUrl, email, secret) {
111
+ const url = buildOptOutUrl(backendBaseUrl, email, secret);
112
+ if (!url)
113
+ return null;
114
+ return {
115
+ 'List-Unsubscribe': `<${url}>`,
116
+ 'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
117
+ };
118
+ }
119
+ /** Order codes are Vendure-generated (16 upper-case alphanumerics by default)
120
+ * but hosts can override the generator, so accept a generous safe subset. */
121
+ function sanitiseOrderCode(raw) {
122
+ const s = String(raw || '').trim();
123
+ return /^[A-Za-z0-9_-]{4,64}$/.test(s) ? s : '';
124
+ }
125
+ //# sourceMappingURL=recovery-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-tokens.js","sourceRoot":"","sources":["../src/recovery-tokens.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=recovery-tokens.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-tokens.test.d.ts","sourceRoot":"","sources":["../src/recovery-tokens.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const recovery_tokens_1 = require("./recovery-tokens");
5
+ const SECRET = 'unit-test-secret-do-not-use';
6
+ (0, vitest_1.describe)('isResumableOrderState', () => {
7
+ (0, vitest_1.it)('accepts the two open states only', () => {
8
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('AddingItems')).toBe(true);
9
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('ArrangingPayment')).toBe(true);
10
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('PaymentAuthorized')).toBe(false);
11
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('PaymentSettled')).toBe(false);
12
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('Cancelled')).toBe(false);
13
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)('')).toBe(false);
14
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)(null)).toBe(false);
15
+ (0, vitest_1.expect)((0, recovery_tokens_1.isResumableOrderState)(undefined)).toBe(false);
16
+ });
17
+ });
18
+ (0, vitest_1.describe)('recovery step lifecycle', () => {
19
+ (0, vitest_1.it)('ranks the steps in funnel order', () => {
20
+ (0, vitest_1.expect)((0, recovery_tokens_1.recoveryStepRank)('link_issued')).toBeLessThan((0, recovery_tokens_1.recoveryStepRank)('link_opened'));
21
+ (0, vitest_1.expect)((0, recovery_tokens_1.recoveryStepRank)('link_opened')).toBeLessThan((0, recovery_tokens_1.recoveryStepRank)('resumed'));
22
+ (0, vitest_1.expect)((0, recovery_tokens_1.recoveryStepRank)('resumed')).toBeLessThan((0, recovery_tokens_1.recoveryStepRank)('converted'));
23
+ (0, vitest_1.expect)((0, recovery_tokens_1.recoveryStepRank)(null)).toBe(-1);
24
+ (0, vitest_1.expect)((0, recovery_tokens_1.recoveryStepRank)('garbage')).toBe(-1);
25
+ });
26
+ (0, vitest_1.it)('advances forward', () => {
27
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)(null, 'link_issued')).toBe('link_issued');
28
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('link_issued', 'link_opened')).toBe('link_opened');
29
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('link_opened', 'converted')).toBe('converted');
30
+ });
31
+ (0, vitest_1.it)('never regresses', () => {
32
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('converted', 'link_issued')).toBe('converted');
33
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('resumed', 'link_opened')).toBe('resumed');
34
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('link_opened', 'link_opened')).toBe('link_opened');
35
+ });
36
+ (0, vitest_1.it)('treats unknown current values as no step', () => {
37
+ (0, vitest_1.expect)((0, recovery_tokens_1.advanceRecoveryStep)('legacy-value', 'link_opened')).toBe('link_opened');
38
+ });
39
+ });
40
+ (0, vitest_1.describe)('normaliseEmail / hashEmail', () => {
41
+ (0, vitest_1.it)('lower-cases and trims', () => {
42
+ (0, vitest_1.expect)((0, recovery_tokens_1.normaliseEmail)(' Buyer@Example.COM ')).toBe('buyer@example.com');
43
+ });
44
+ (0, vitest_1.it)('rejects non-emails', () => {
45
+ (0, vitest_1.expect)((0, recovery_tokens_1.normaliseEmail)('not an email')).toBe('');
46
+ (0, vitest_1.expect)((0, recovery_tokens_1.normaliseEmail)('')).toBe('');
47
+ (0, vitest_1.expect)((0, recovery_tokens_1.normaliseEmail)(null)).toBe('');
48
+ (0, vitest_1.expect)((0, recovery_tokens_1.normaliseEmail)('a@b')).toBe('');
49
+ });
50
+ (0, vitest_1.it)('hashes the normalised form so case differences collapse', () => {
51
+ (0, vitest_1.expect)((0, recovery_tokens_1.hashEmail)('Buyer@Example.com')).toBe((0, recovery_tokens_1.hashEmail)('buyer@example.com'));
52
+ (0, vitest_1.expect)((0, recovery_tokens_1.hashEmail)('buyer@example.com')).toMatch(/^[0-9a-f]{64}$/);
53
+ });
54
+ });
55
+ (0, vitest_1.describe)('opt-out tokens', () => {
56
+ (0, vitest_1.it)('round-trips', () => {
57
+ const token = (0, recovery_tokens_1.buildOptOutToken)('Buyer@Example.com', SECRET);
58
+ (0, vitest_1.expect)(token).toBeTruthy();
59
+ (0, vitest_1.expect)(token).toMatch(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/);
60
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(token, SECRET)).toBe('buyer@example.com');
61
+ });
62
+ (0, vitest_1.it)('is disabled without a secret', () => {
63
+ (0, vitest_1.expect)((0, recovery_tokens_1.buildOptOutToken)('buyer@example.com', '')).toBeNull();
64
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)('anything.anything', '')).toBeNull();
65
+ });
66
+ (0, vitest_1.it)('refuses malformed emails', () => {
67
+ (0, vitest_1.expect)((0, recovery_tokens_1.buildOptOutToken)('nope', SECRET)).toBeNull();
68
+ });
69
+ (0, vitest_1.it)('rejects a tampered email part', () => {
70
+ const token = (0, recovery_tokens_1.buildOptOutToken)('buyer@example.com', SECRET);
71
+ const [, mac] = token.split('.');
72
+ const forged = `${Buffer.from('victim@example.com').toString('base64url')}.${mac}`;
73
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(forged, SECRET)).toBeNull();
74
+ });
75
+ (0, vitest_1.it)('rejects a tampered MAC and a different secret', () => {
76
+ const token = (0, recovery_tokens_1.buildOptOutToken)('buyer@example.com', SECRET);
77
+ const [email, mac] = token.split('.');
78
+ const flipped = mac[0] === 'A' ? 'B' : 'A';
79
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(`${email}.${flipped}${mac.slice(1)}`, SECRET)).toBeNull();
80
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(token, 'other-secret')).toBeNull();
81
+ });
82
+ (0, vitest_1.it)('rejects junk shapes', () => {
83
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)('', SECRET)).toBeNull();
84
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)('no-dot', SECRET)).toBeNull();
85
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)('.mac', SECRET)).toBeNull();
86
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)('email.', SECRET)).toBeNull();
87
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(null, SECRET)).toBeNull();
88
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(undefined, SECRET)).toBeNull();
89
+ });
90
+ (0, vitest_1.it)('builds an absolute URL on the backend origin', () => {
91
+ const url = (0, recovery_tokens_1.buildOptOutUrl)('https://api.example.com/', 'buyer@example.com', SECRET);
92
+ (0, vitest_1.expect)(url.startsWith('https://api.example.com/ees/abandoned-carts/opt-out?e=')).toBe(true);
93
+ const e = new URL(url).searchParams.get('e');
94
+ (0, vitest_1.expect)((0, recovery_tokens_1.verifyOptOutToken)(e, SECRET)).toBe('buyer@example.com');
95
+ });
96
+ (0, vitest_1.it)('produces RFC 8058 headers', () => {
97
+ const h = (0, recovery_tokens_1.buildListUnsubscribeHeaders)('https://api.example.com', 'buyer@example.com', SECRET);
98
+ (0, vitest_1.expect)(h['List-Unsubscribe']).toMatch(/^<https:\/\/api\.example\.com\/ees\/abandoned-carts\/opt-out\?e=.+>$/);
99
+ (0, vitest_1.expect)(h['List-Unsubscribe-Post']).toBe('List-Unsubscribe=One-Click');
100
+ (0, vitest_1.expect)((0, recovery_tokens_1.buildListUnsubscribeHeaders)('https://api.example.com', 'buyer@example.com', '')).toBeNull();
101
+ });
102
+ });
103
+ (0, vitest_1.describe)('sanitiseOrderCode', () => {
104
+ (0, vitest_1.it)('accepts Vendure-style codes', () => {
105
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)('S2BZ54TEKQWERTYU')).toBe('S2BZ54TEKQWERTYU');
106
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)(' abc-123_x ')).toBe('abc-123_x');
107
+ });
108
+ (0, vitest_1.it)('rejects anything else', () => {
109
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)('')).toBe('');
110
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)('abc')).toBe('');
111
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)("S2B'; DROP TABLE")).toBe('');
112
+ (0, vitest_1.expect)((0, recovery_tokens_1.sanitiseOrderCode)(null)).toBe('');
113
+ });
114
+ });
115
+ //# sourceMappingURL=recovery-tokens.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery-tokens.test.js","sourceRoot":"","sources":["../src/recovery-tokens.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huloglobal/vendure-plugin-visitor-analytics",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "description": "Full-funnel visitor analytics for Vendure storefronts \u2014 pageviews, time-on-page, session journey, exit pages, funnel drop-off, and per-visitor profile drawer with parsed user-agent + MaxMind GeoLite2 enrichment. Auto-issues visitor + session cookies on first request; logs guest and signed-in events against the same visitor id so the journey survives login.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Wayne Garrison <wayne@garrison.me.uk>",
@@ -43,7 +43,7 @@
43
43
  "devDependencies": {
44
44
  "@huloglobal/vendure-licence-sdk": "^0.14.0",
45
45
  "@nestjs/common": "^10.0.0",
46
- "@vendure/core": "^3.7.1",
46
+ "@vendure/core": "^3.7.3",
47
47
  "typeorm": "^0.3.0",
48
48
  "typescript": "^5.4.0",
49
49
  "vitest": "^2.0.0"