@huloglobal/vendure-plugin-visitor-analytics 0.2.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,163 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.VisitorEvent = void 0;
13
+ const core_1 = require("@vendure/core");
14
+ const typeorm_1 = require("typeorm");
15
+ /**
16
+ * Stores a single visitor analytics event — used to map the customer
17
+ * journey across the storefront, including for visitors who never log in.
18
+ *
19
+ * type='pageview' — a new page was opened (one row per navigation)
20
+ * type='unload' — a page was closed / hidden; `timeOnPageMs` is the
21
+ * time the visitor spent on it before leaving
22
+ * type='event' — a custom event raised by the frontend (e.g. an
23
+ * add-to-cart click); the payload lives in `meta`
24
+ *
25
+ * `visitorId` is a long-lived cookie UUID (2 years) — identifies the
26
+ * device across sessions. `sessionId` is a short-lived cookie UUID
27
+ * (30-minute idle expiry) — identifies a single browsing burst.
28
+ *
29
+ * `customerId` is set when the visitor is logged in; the same
30
+ * `visitorId` can carry guest events first and then customer events
31
+ * once the visitor signs in / signs up — that's what makes the funnel
32
+ * analysis work.
33
+ */
34
+ let VisitorEvent = class VisitorEvent extends core_1.VendureEntity {
35
+ constructor(input) { super(input); }
36
+ };
37
+ exports.VisitorEvent = VisitorEvent;
38
+ __decorate([
39
+ (0, typeorm_1.Column)({ length: 64 }),
40
+ __metadata("design:type", String)
41
+ ], VisitorEvent.prototype, "visitorId", void 0);
42
+ __decorate([
43
+ (0, typeorm_1.Column)({ length: 64 }),
44
+ __metadata("design:type", String)
45
+ ], VisitorEvent.prototype, "sessionId", void 0);
46
+ __decorate([
47
+ (0, typeorm_1.Column)({ type: 'int', nullable: true }),
48
+ __metadata("design:type", Object)
49
+ ], VisitorEvent.prototype, "customerId", void 0);
50
+ __decorate([
51
+ (0, typeorm_1.Column)({ type: 'int', default: 1 }),
52
+ __metadata("design:type", Number)
53
+ ], VisitorEvent.prototype, "channelId", void 0);
54
+ __decorate([
55
+ (0, typeorm_1.Column)({ length: 32 }),
56
+ __metadata("design:type", String)
57
+ ], VisitorEvent.prototype, "type", void 0);
58
+ __decorate([
59
+ (0, typeorm_1.Column)({ length: 2048 }),
60
+ __metadata("design:type", String)
61
+ ], VisitorEvent.prototype, "url", void 0);
62
+ __decorate([
63
+ (0, typeorm_1.Column)({ type: 'varchar', length: 500, nullable: true }),
64
+ __metadata("design:type", Object)
65
+ ], VisitorEvent.prototype, "title", void 0);
66
+ __decorate([
67
+ (0, typeorm_1.Column)({ type: 'varchar', length: 2048, nullable: true }),
68
+ __metadata("design:type", Object)
69
+ ], VisitorEvent.prototype, "referrer", void 0);
70
+ __decorate([
71
+ (0, typeorm_1.Column)({ type: 'int', nullable: true }),
72
+ __metadata("design:type", Object)
73
+ ], VisitorEvent.prototype, "timeOnPageMs", void 0);
74
+ __decorate([
75
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
76
+ __metadata("design:type", Object)
77
+ ], VisitorEvent.prototype, "ipHash", void 0);
78
+ __decorate([
79
+ (0, typeorm_1.Column)({ type: 'varchar', length: 45, nullable: true }),
80
+ __metadata("design:type", Object)
81
+ ], VisitorEvent.prototype, "ip", void 0);
82
+ __decorate([
83
+ (0, typeorm_1.Column)({ type: 'varchar', length: 1000, nullable: true }),
84
+ __metadata("design:type", Object)
85
+ ], VisitorEvent.prototype, "userAgent", void 0);
86
+ __decorate([
87
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
88
+ __metadata("design:type", Object)
89
+ ], VisitorEvent.prototype, "browser", void 0);
90
+ __decorate([
91
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
92
+ __metadata("design:type", Object)
93
+ ], VisitorEvent.prototype, "browserVersion", void 0);
94
+ __decorate([
95
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
96
+ __metadata("design:type", Object)
97
+ ], VisitorEvent.prototype, "os", void 0);
98
+ __decorate([
99
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
100
+ __metadata("design:type", Object)
101
+ ], VisitorEvent.prototype, "osVersion", void 0);
102
+ __decorate([
103
+ (0, typeorm_1.Column)({ type: 'varchar', length: 32, nullable: true }),
104
+ __metadata("design:type", Object)
105
+ ], VisitorEvent.prototype, "device", void 0);
106
+ __decorate([
107
+ (0, typeorm_1.Column)({ type: 'varchar', length: 200, nullable: true }),
108
+ __metadata("design:type", Object)
109
+ ], VisitorEvent.prototype, "acceptLanguage", void 0);
110
+ __decorate([
111
+ (0, typeorm_1.Column)({ type: 'varchar', length: 16, nullable: true }),
112
+ __metadata("design:type", Object)
113
+ ], VisitorEvent.prototype, "country", void 0);
114
+ __decorate([
115
+ (0, typeorm_1.Column)({ type: 'varchar', length: 16, nullable: true }),
116
+ __metadata("design:type", Object)
117
+ ], VisitorEvent.prototype, "region", void 0);
118
+ __decorate([
119
+ (0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
120
+ __metadata("design:type", Object)
121
+ ], VisitorEvent.prototype, "city", void 0);
122
+ __decorate([
123
+ (0, typeorm_1.Column)({ type: 'varchar', length: 64, nullable: true }),
124
+ __metadata("design:type", Object)
125
+ ], VisitorEvent.prototype, "timezone", void 0);
126
+ __decorate([
127
+ (0, typeorm_1.Column)({ type: 'text', nullable: true }),
128
+ __metadata("design:type", Object)
129
+ ], VisitorEvent.prototype, "meta", void 0);
130
+ __decorate([
131
+ (0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
132
+ __metadata("design:type", Object)
133
+ ], VisitorEvent.prototype, "utmSource", void 0);
134
+ __decorate([
135
+ (0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
136
+ __metadata("design:type", Object)
137
+ ], VisitorEvent.prototype, "utmMedium", void 0);
138
+ __decorate([
139
+ (0, typeorm_1.Column)({ type: 'varchar', length: 200, nullable: true }),
140
+ __metadata("design:type", Object)
141
+ ], VisitorEvent.prototype, "utmCampaign", void 0);
142
+ __decorate([
143
+ (0, typeorm_1.Column)({ type: 'varchar', length: 100, nullable: true }),
144
+ __metadata("design:type", Object)
145
+ ], VisitorEvent.prototype, "utmTerm", void 0);
146
+ __decorate([
147
+ (0, typeorm_1.Column)({ type: 'varchar', length: 200, nullable: true }),
148
+ __metadata("design:type", Object)
149
+ ], VisitorEvent.prototype, "utmContent", void 0);
150
+ __decorate([
151
+ (0, typeorm_1.Column)({ type: 'varchar', length: 200, nullable: true }),
152
+ __metadata("design:type", Object)
153
+ ], VisitorEvent.prototype, "referrerDomain", void 0);
154
+ exports.VisitorEvent = VisitorEvent = __decorate([
155
+ (0, typeorm_1.Entity)(),
156
+ (0, typeorm_1.Index)('visitor_event_visitor_idx', ['visitorId', 'createdAt']),
157
+ (0, typeorm_1.Index)('visitor_event_session_idx', ['sessionId', 'createdAt']),
158
+ (0, typeorm_1.Index)('visitor_event_customer_idx', ['customerId']),
159
+ (0, typeorm_1.Index)('visitor_event_type_idx', ['type', 'createdAt']),
160
+ (0, typeorm_1.Index)('visitor_event_url_idx', ['url']),
161
+ __metadata("design:paramtypes", [Object])
162
+ ], VisitorEvent);
163
+ //# sourceMappingURL=visitor-event.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-event.entity.js","sourceRoot":"","sources":["../src/visitor-event.entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { RequestContext, TransactionalConnection } from '@vendure/core';
2
+ import { Request, Response } from 'express';
3
+ import { VisitorTrackingService } from './visitor-tracking.service';
4
+ export declare class VisitorTrackingController {
5
+ private connection;
6
+ private tracking;
7
+ constructor(connection: TransactionalConnection, tracking: VisitorTrackingService);
8
+ /**
9
+ * Ingest endpoint hit by the storefront tracker. Anonymous, takes
10
+ * a small JSON batch via fetch() or navigator.sendBeacon() (which
11
+ * uses text/plain). CORS is open because the storefront is on a
12
+ * different origin from the API.
13
+ *
14
+ * POST /ees/track
15
+ * body: {
16
+ * channelId?: number,
17
+ * customerId?: number | null,
18
+ * events: [
19
+ * { type, url, title?, referrer?, timeOnPageMs?, meta?, clientTs? }
20
+ * ]
21
+ * }
22
+ *
23
+ * Cookies `ees_vid` (visitor) and `ees_sid` (session) are issued
24
+ * on the first request and refreshed on every subsequent one.
25
+ */
26
+ track(body: any, req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
27
+ /** Top-line counters + daily series for the dashboard header. */
28
+ summary(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
29
+ /** Traffic sources — UTM source breakdown + organic referrer
30
+ * domains. Visitors are attributed by their first-pageview's UTM
31
+ * / referrer combo per session. */
32
+ sources(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
33
+ /** Top pages by view count. Paginated via take + skip. */
34
+ topPages(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
35
+ /** Funnel: visitors → product views → cart → checkout completed. */
36
+ funnel(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
37
+ /** Exit pages — pages where the last event in the session was a
38
+ * pageview. Paginated via take + skip. */
39
+ exitPages(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
40
+ /** Top custom events — `type` other than pageview / unload. Useful
41
+ * for tracking add-to-cart / search / quote-request / signup
42
+ * conversion rates. Paginated. */
43
+ topEvents(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
44
+ /**
45
+ * Live-now widget — Server-Sent Events stream pushing the current
46
+ * active-visitor count + their most recent URLs every 5 seconds.
47
+ * "Active" = at least one event in the last 5 minutes. SSE keeps
48
+ * the connection open; the admin UI consumes it via `EventSource`.
49
+ *
50
+ * Each event payload:
51
+ * data: { ts, activeCount, recent: [{ visitorId, url, country, secondsAgo }] }
52
+ */
53
+ live(ctx: RequestContext, req: Request, res: Response): Promise<void>;
54
+ /** Journey timeline for one visitor — every event in order. */
55
+ journey(ctx: RequestContext, visitorId: string, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
56
+ /** Recent visitors — paginated via take + skip. */
57
+ recent(ctx: RequestContext, req: Request, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
58
+ /** Full visitor profile — every available signal we have on this
59
+ * visitor, plus the most-recent IP / UA / location / locale,
60
+ * customer link, and per-session breakdown. Drives the clickable
61
+ * detail drawer in the admin UI. */
62
+ profile(ctx: RequestContext, visitorIdRaw: string, res: Response): Promise<Response<any, Record<string, any>> | undefined>;
63
+ private applyCors;
64
+ private parseCookies;
65
+ }
66
+ //# sourceMappingURL=visitor-tracking.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor-tracking.controller.d.ts","sourceRoot":"","sources":["../src/visitor-tracking.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AA4BpE,qBACa,yBAAyB;IAE9B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,QAAQ;gBADR,UAAU,EAAE,uBAAuB,EACnC,QAAQ,EAAE,sBAAsB;IAG5C;;;;;;;;;;;;;;;;;OAiBG;IAEG,KAAK,CAAS,IAAI,EAAE,GAAG,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IA2DxE,iEAAiE;IAE3D,OAAO,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IA0CnF;;wCAEoC;IAE9B,OAAO,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAuDnF,0DAA0D;IAEpD,QAAQ,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAoCpF,oEAAoE;IAE9D,MAAM,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAsBlF;+CAC2C;IAErC,SAAS,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAuCrF;;uCAEmC;IAE7B,SAAS,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAmCrF;;;;;;;;OAQG;IAEG,IAAI,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IAgDhF,+DAA+D;IAEzD,OAAO,CAAQ,GAAG,EAAE,cAAc,EAAsB,SAAS,EAAE,MAAM,EAAS,GAAG,EAAE,QAAQ;IAUrG,mDAAmD;IAE7C,MAAM,CAAQ,GAAG,EAAE,cAAc,EAAS,GAAG,EAAE,OAAO,EAAS,GAAG,EAAE,QAAQ;IA+ClF;;;yCAGqC;IAE/B,OAAO,CAAQ,GAAG,EAAE,cAAc,EAAsB,YAAY,EAAE,MAAM,EAAS,GAAG,EAAE,QAAQ;IAyGxG,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,YAAY;CAYvB"}