@huloglobal/vendure-plugin-visitor-analytics 0.7.0 → 0.8.1

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 (62) hide show
  1. package/CHANGELOG.md +165 -15
  2. package/dist/abandoned-cart.controller.d.ts +78 -0
  3. package/dist/abandoned-cart.controller.d.ts.map +1 -0
  4. package/dist/abandoned-cart.controller.js +251 -0
  5. package/dist/abandoned-cart.controller.js.map +1 -0
  6. package/dist/abandoned-cart.entity.d.ts +61 -0
  7. package/dist/abandoned-cart.entity.d.ts.map +1 -0
  8. package/dist/abandoned-cart.entity.js +143 -0
  9. package/dist/abandoned-cart.entity.js.map +1 -0
  10. package/dist/abandoned-cart.scanner.d.ts +34 -0
  11. package/dist/abandoned-cart.scanner.d.ts.map +1 -0
  12. package/dist/abandoned-cart.scanner.js +85 -0
  13. package/dist/abandoned-cart.scanner.js.map +1 -0
  14. package/dist/abandoned-cart.service.d.ts +92 -0
  15. package/dist/abandoned-cart.service.d.ts.map +1 -0
  16. package/dist/abandoned-cart.service.js +276 -0
  17. package/dist/abandoned-cart.service.js.map +1 -0
  18. package/dist/hulo-storefront.d.ts +17 -0
  19. package/dist/hulo-storefront.d.ts.map +1 -0
  20. package/dist/hulo-storefront.js +289 -0
  21. package/dist/hulo-storefront.js.map +1 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +13 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/journey-buffs.controller.d.ts +43 -0
  27. package/dist/journey-buffs.controller.d.ts.map +1 -0
  28. package/dist/journey-buffs.controller.js +79 -0
  29. package/dist/journey-buffs.controller.js.map +1 -0
  30. package/dist/journey-buffs.service.d.ts +53 -0
  31. package/dist/journey-buffs.service.d.ts.map +1 -0
  32. package/dist/journey-buffs.service.js +141 -0
  33. package/dist/journey-buffs.service.js.map +1 -0
  34. package/dist/plugin.d.ts +5 -0
  35. package/dist/plugin.d.ts.map +1 -1
  36. package/dist/plugin.js +30 -3
  37. package/dist/plugin.js.map +1 -1
  38. package/dist/product-co-view.entity.d.ts +19 -0
  39. package/dist/product-co-view.entity.d.ts.map +1 -0
  40. package/dist/product-co-view.entity.js +52 -0
  41. package/dist/product-co-view.entity.js.map +1 -0
  42. package/dist/recommendations.controller.d.ts +72 -0
  43. package/dist/recommendations.controller.d.ts.map +1 -0
  44. package/dist/recommendations.controller.js +123 -0
  45. package/dist/recommendations.controller.js.map +1 -0
  46. package/dist/recommendations.service.d.ts +62 -0
  47. package/dist/recommendations.service.d.ts.map +1 -0
  48. package/dist/recommendations.service.js +167 -0
  49. package/dist/recommendations.service.js.map +1 -0
  50. package/dist/search-analytics.controller.d.ts +36 -0
  51. package/dist/search-analytics.controller.d.ts.map +1 -0
  52. package/dist/search-analytics.controller.js +86 -0
  53. package/dist/search-analytics.controller.js.map +1 -0
  54. package/dist/search-analytics.service.d.ts +36 -0
  55. package/dist/search-analytics.service.d.ts.map +1 -0
  56. package/dist/search-analytics.service.js +105 -0
  57. package/dist/search-analytics.service.js.map +1 -0
  58. package/dist/storefront-helper.controller.d.ts +16 -0
  59. package/dist/storefront-helper.controller.d.ts.map +1 -0
  60. package/dist/storefront-helper.controller.js +56 -0
  61. package/dist/storefront-helper.controller.js.map +1 -0
  62. package/package.json +1 -1
@@ -0,0 +1,141 @@
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.JourneyBuffsService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@vendure/core");
15
+ /**
16
+ * Zero-schema-cost enrichment endpoints for the per-visitor Journey
17
+ * drawer and store-wide UX signals.
18
+ *
19
+ * Each of these queries reads back over the existing `visitor_event`
20
+ * table where the storefront has fired named custom events. No new
21
+ * columns, no new tables — the storefront helper is documented in the
22
+ * plugin README and dispatches events into the existing
23
+ * `POST /ees/track` endpoint.
24
+ */
25
+ let JourneyBuffsService = class JourneyBuffsService {
26
+ constructor(connection) {
27
+ this.connection = connection;
28
+ }
29
+ /**
30
+ * Store-wide rage-click hot spots — the URLs where visitors are
31
+ * most frustrated. `hulo.rageClick(url, selector)` on the
32
+ * storefront populates the underlying event.
33
+ */
34
+ async rageClickHotSpots(sinceDays = 7, channelId = 1, limit = 20) {
35
+ const since = new Date(Date.now() - sinceDays * 86400000);
36
+ const rows = await this.connection.rawConnection.query(`SELECT
37
+ url,
38
+ COUNT(*) AS rageClicks,
39
+ COUNT(DISTINCT visitorId) AS uniqueVisitors
40
+ FROM visitor_event
41
+ WHERE type = 'event'
42
+ AND meta LIKE '%"eventType":"rage_click"%'
43
+ AND channelId = ?
44
+ AND createdAt >= ?
45
+ GROUP BY url
46
+ ORDER BY rageClicks DESC
47
+ LIMIT ?`, [channelId, since, Math.min(Math.max(1, limit), 200)]);
48
+ return rows.map(r => ({
49
+ url: String(r.url || ''),
50
+ rageClicks: Number(r.rageClicks),
51
+ uniqueVisitors: Number(r.uniqueVisitors),
52
+ }));
53
+ }
54
+ /**
55
+ * Dead-click hot spots — clicks on non-interactive elements. Same
56
+ * shape as rage-click aggregation but keyed on the `dead_click`
57
+ * event type. Useful for spotting elements that LOOK clickable
58
+ * (styled headings, disabled buttons, decorative cards).
59
+ */
60
+ async deadClickHotSpots(sinceDays = 7, channelId = 1, limit = 20) {
61
+ const since = new Date(Date.now() - sinceDays * 86400000);
62
+ const rows = await this.connection.rawConnection.query(`SELECT
63
+ url,
64
+ COUNT(*) AS deadClicks,
65
+ COUNT(DISTINCT visitorId) AS uniqueVisitors
66
+ FROM visitor_event
67
+ WHERE type = 'event'
68
+ AND meta LIKE '%"eventType":"dead_click"%'
69
+ AND channelId = ?
70
+ AND createdAt >= ?
71
+ GROUP BY url
72
+ ORDER BY deadClicks DESC
73
+ LIMIT ?`, [channelId, since, Math.min(Math.max(1, limit), 200)]);
74
+ return rows.map(r => ({
75
+ url: String(r.url || ''),
76
+ deadClicks: Number(r.deadClicks),
77
+ uniqueVisitors: Number(r.uniqueVisitors),
78
+ }));
79
+ }
80
+ /**
81
+ * Per-visitor session summary — one row per session with a
82
+ * heuristic `intent` label. Used to enrich the Journey drawer
83
+ * with a one-line "what did this session do".
84
+ */
85
+ async sessionSummary(visitorId, limit = 25) {
86
+ const rows = await this.connection.rawConnection.query(`SELECT
87
+ sessionId,
88
+ MIN(createdAt) AS startedAt,
89
+ MAX(createdAt) AS endedAt,
90
+ COUNT(*) AS pageCount,
91
+ SUM(type = 'event' AND meta LIKE '%"eventType":"add_to_cart"%') > 0 AS addedToCart,
92
+ SUM(type = 'event' AND meta LIKE '%"eventType":"checkout_completed"%') > 0 AS checkedOut,
93
+ SUM(type = 'event' AND meta LIKE '%"eventType":"cart_snapshot"%') > 0 AS hadCartSnapshot,
94
+ SUM(type = 'event' AND meta LIKE '%"eventType":"rage_click"%') > 0 AS rageClicked
95
+ FROM visitor_event
96
+ WHERE visitorId = ?
97
+ GROUP BY sessionId
98
+ ORDER BY startedAt DESC
99
+ LIMIT ?`, [visitorId, Math.min(Math.max(1, limit), 200)]);
100
+ return rows.map(r => {
101
+ const start = new Date(r.startedAt);
102
+ const end = new Date(r.endedAt);
103
+ const durationSec = Math.max(0, Math.round((end.getTime() - start.getTime()) / 1000));
104
+ const pageCount = Number(r.pageCount);
105
+ const addedToCart = !!Number(r.addedToCart);
106
+ const checkedOut = !!Number(r.checkedOut);
107
+ const hadCartSnapshot = !!Number(r.hadCartSnapshot);
108
+ const rageClicked = !!Number(r.rageClicked);
109
+ let intent;
110
+ if (checkedOut)
111
+ intent = 'purchase';
112
+ else if (hadCartSnapshot || addedToCart)
113
+ intent = 'abandon';
114
+ else if (rageClicked)
115
+ intent = 'frustrate';
116
+ else if (pageCount <= 1 && durationSec < 15)
117
+ intent = 'bounce';
118
+ else if (pageCount >= 5)
119
+ intent = 'consider';
120
+ else
121
+ intent = 'browse';
122
+ return {
123
+ sessionId: String(r.sessionId),
124
+ startedAt: start.toISOString(),
125
+ endedAt: end.toISOString(),
126
+ durationSec,
127
+ pageCount,
128
+ addedToCart,
129
+ checkedOut,
130
+ rageClicked,
131
+ intent,
132
+ };
133
+ });
134
+ }
135
+ };
136
+ exports.JourneyBuffsService = JourneyBuffsService;
137
+ exports.JourneyBuffsService = JourneyBuffsService = __decorate([
138
+ (0, common_1.Injectable)(),
139
+ __metadata("design:paramtypes", [core_1.TransactionalConnection])
140
+ ], JourneyBuffsService);
141
+ //# sourceMappingURL=journey-buffs.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-buffs.service.js","sourceRoot":"","sources":["../src/journey-buffs.service.ts"],"names":[],"mappings":"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"}
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Type } from '@vendure/core';
2
2
  import { LicenceStatus, RetentionOptions, UpdateChecker } from '@huloglobal/vendure-licence-sdk';
3
+ import { AbandonmentOptions } from './abandoned-cart.service';
3
4
  export interface VisitorAnalyticsPluginOptions {
4
5
  /** Public host of the Vendure server. Used in licence host-match. */
5
6
  publicBaseUrl: string;
@@ -38,6 +39,10 @@ export interface VisitorAnalyticsPluginOptions {
38
39
  corsAllowedOrigins?: string[];
39
40
  /** Auto-prune `visitor_event` rows older than `days` days. */
40
41
  retention?: RetentionOptions;
42
+ /** Options for the abandoned-cart feature. Storefront must fire
43
+ * `cart_snapshot` custom events for anything to be detected. See
44
+ * the README for the recommended storefront helper. */
45
+ abandonment?: AbandonmentOptions;
41
46
  }
42
47
  declare const DEFAULT_OPTIONS: VisitorAnalyticsPluginOptions;
43
48
  export declare function getOptions(): typeof DEFAULT_OPTIONS & VisitorAnalyticsPluginOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,IAAI,EAAiB,MAAM,eAAe,CAAC;AACxE,OAAO,EAAmC,aAAa,EAAE,gBAAgB,EAAqB,aAAa,EAA4C,MAAM,iCAAiC,CAAC;AAW/L,MAAM,WAAW,6BAA6B;IAC1C,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB;;;gDAG4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;6EACyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;yEAEqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;qEAEiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;6CAEyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,mDAAmD;IACnD,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD;;yEAEqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;2EAEuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAG9B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAChC;AAgBD,QAAA,MAAM,eAAe,EAAE,6BAMtB,CAAC;AAGF,wBAAgB,UAAU,IAAI,OAAO,eAAe,GAAG,6BAA6B,CAEnF;AAED;;;;;;;;;;;GAWG;AACH,qBAWa,sBAAsB;IAC/B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkC;IAC3D,OAAO,CAAC,MAAM,CAAC,aAAa,CAA8B;IAC1D,OAAO,CAAC,MAAM,CAAC,SAAS,CAA0B;IAClD,OAAO,CAAC,MAAM,CAAC,aAAa,CAA8B;IAE1D,MAAM,CAAC,gBAAgB,IAAI,aAAa,GAAG,IAAI;IAC/C,MAAM,CAAC,iBAAiB,IAAI,MAAM;IAClC,MAAM,CAAC,cAAc,IAAI,MAAM;IAC/B,MAAM,CAAC,gBAAgB,IAAI,aAAa,GAAG,IAAI;IAE/C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAAC,sBAAsB,CAAC;IAoDjF,MAAM,CAAC,YAAY;;;;;;;;MAUjB;CACL"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,IAAI,EAAiB,MAAM,eAAe,CAAC;AACxE,OAAO,EAAmC,aAAa,EAAE,gBAAgB,EAAqB,aAAa,EAA4C,MAAM,iCAAiC,CAAC;AAM/L,OAAO,EAAwB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAiBpF,MAAM,WAAW,6BAA6B;IAC1C,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB;;;gDAG4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;6EACyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;yEAEqE;IACrE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;qEAEiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;6CAEyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,mDAAmD;IACnD,SAAS,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD;;yEAEqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;2EAEuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAG9B,8DAA8D;IAC9D,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAG7B;;4DAEwD;IACxD,WAAW,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAgBD,QAAA,MAAM,eAAe,EAAE,6BAMtB,CAAC;AAGF,wBAAgB,UAAU,IAAI,OAAO,eAAe,GAAG,6BAA6B,CAEnF;AAED;;;;;;;;;;;GAWG;AACH,qBA0Ba,sBAAsB;IAC/B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAkC;IAC3D,OAAO,CAAC,MAAM,CAAC,aAAa,CAA8B;IAC1D,OAAO,CAAC,MAAM,CAAC,SAAS,CAA0B;IAClD,OAAO,CAAC,MAAM,CAAC,aAAa,CAA8B;IAE1D,MAAM,CAAC,gBAAgB,IAAI,aAAa,GAAG,IAAI;IAC/C,MAAM,CAAC,iBAAiB,IAAI,MAAM;IAClC,MAAM,CAAC,cAAc,IAAI,MAAM;IAC/B,MAAM,CAAC,gBAAgB,IAAI,aAAa,GAAG,IAAI;IAE/C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAAC,sBAAsB,CAAC;IAoDjF,MAAM,CAAC,YAAY;;;;;;;;MAUjB;CACL"}
package/dist/plugin.js CHANGED
@@ -13,9 +13,21 @@ const core_1 = require("@vendure/core");
13
13
  const vendure_licence_sdk_1 = require("@huloglobal/vendure-licence-sdk");
14
14
  const conversion_goal_entity_1 = require("./conversion-goal.entity");
15
15
  const visitor_event_entity_1 = require("./visitor-event.entity");
16
+ const abandoned_cart_entity_1 = require("./abandoned-cart.entity");
17
+ const product_co_view_entity_1 = require("./product-co-view.entity");
16
18
  const visitor_tracking_service_1 = require("./visitor-tracking.service");
19
+ const abandoned_cart_service_1 = require("./abandoned-cart.service");
20
+ const recommendations_service_1 = require("./recommendations.service");
21
+ const search_analytics_service_1 = require("./search-analytics.service");
22
+ const journey_buffs_service_1 = require("./journey-buffs.service");
23
+ const abandoned_cart_scanner_1 = require("./abandoned-cart.scanner");
17
24
  const admin_api_1 = require("./admin-api");
18
25
  const visitor_tracking_controller_1 = require("./visitor-tracking.controller");
26
+ const abandoned_cart_controller_1 = require("./abandoned-cart.controller");
27
+ const recommendations_controller_1 = require("./recommendations.controller");
28
+ const search_analytics_controller_1 = require("./search-analytics.controller");
29
+ const journey_buffs_controller_1 = require("./journey-buffs.controller");
30
+ const storefront_helper_controller_1 = require("./storefront-helper.controller");
19
31
  // eslint-disable-next-line @typescript-eslint/no-var-requires
20
32
  const PKG_VERSION = require('../package.json').version;
21
33
  const PKG_NAME = '@huloglobal/vendure-plugin-visitor-analytics';
@@ -122,9 +134,24 @@ VisitorAnalyticsPlugin.uiExtensions = {
122
134
  exports.VisitorAnalyticsPlugin = VisitorAnalyticsPlugin = VisitorAnalyticsPlugin_1 = __decorate([
123
135
  (0, core_1.VendurePlugin)({
124
136
  imports: [core_1.PluginCommonModule],
125
- providers: [visitor_tracking_service_1.VisitorTrackingService, admin_api_1.VisitorAnalyticsAdminResolver],
126
- controllers: [visitor_tracking_controller_1.VisitorTrackingController],
127
- entities: [visitor_event_entity_1.VisitorEvent, conversion_goal_entity_1.ConversionGoal],
137
+ providers: [
138
+ visitor_tracking_service_1.VisitorTrackingService,
139
+ admin_api_1.VisitorAnalyticsAdminResolver,
140
+ abandoned_cart_service_1.AbandonedCartService,
141
+ recommendations_service_1.RecommendationsService,
142
+ search_analytics_service_1.SearchAnalyticsService,
143
+ journey_buffs_service_1.JourneyBuffsService,
144
+ abandoned_cart_scanner_1.AbandonedCartScanner,
145
+ ],
146
+ controllers: [
147
+ visitor_tracking_controller_1.VisitorTrackingController,
148
+ abandoned_cart_controller_1.AbandonedCartController,
149
+ recommendations_controller_1.RecommendationsController,
150
+ search_analytics_controller_1.SearchAnalyticsController,
151
+ journey_buffs_controller_1.JourneyBuffsController,
152
+ storefront_helper_controller_1.StorefrontHelperController,
153
+ ],
154
+ entities: [visitor_event_entity_1.VisitorEvent, conversion_goal_entity_1.ConversionGoal, abandoned_cart_entity_1.AbandonedCart, product_co_view_entity_1.ProductCoView],
128
155
  compatibility: '^3.0.0',
129
156
  adminApiExtensions: {
130
157
  schema: admin_api_1.visitorAnalyticsAdminApiSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":";;;;;;;;;;AA8EA,gCAEC;AAhFD,wCAAwE;AACxE,yEAA+L;AAC/L,qEAA0D;AAC1D,iEAAsD;AACtD,yEAAoE;AACpE,2CAA4F;AAC5F,+EAA0E;AAE1E,8DAA8D;AAC9D,MAAM,WAAW,GAAW,OAAO,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;AAC/D,MAAM,QAAQ,GAAG,8CAA8C,CAAC;AA6ChE,MAAM,eAAe,GAAG;;;;;;;;yBAQC,CAAC;AAE1B,MAAM,SAAS,GAAG,kCAAkC,CAAC;AACrD,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B;OACvD,4CAA4C,CAAC;AAEpD,MAAM,eAAe,GAAkC;IACnD,aAAa,EAAE,uBAAuB;IACtC,eAAe,EAAE,IAAI;IACrB,WAAW,EAAE,IAAI;IACjB,cAAc,EAAE,KAAK;IACrB,aAAa,EAAE,KAAK;CACvB,CAAC;AAEF,IAAI,aAAa,GAAkC,eAAsB,CAAC;AAC1E,SAAgB,UAAU;IACtB,OAAO,EAAE,GAAG,eAAe,EAAE,GAAG,aAAa,EAAS,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;GAWG;AAYI,IAAM,sBAAsB,8BAA5B,MAAM,sBAAsB;IAM/B,MAAM,CAAC,gBAAgB,KAA2B,OAAO,wBAAsB,CAAC,aAAa,CAAC,CAAC,CAAC;IAChG,MAAM,CAAC,iBAAiB,KAAa,OAAO,WAAW,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,cAAc,KAAa,OAAO,QAAQ,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,gBAAgB,KAA2B,OAAO,wBAAsB,CAAC,aAAa,CAAC,CAAC,CAAC;IAEhG,MAAM,CAAC,IAAI,CAAC,OAAsC;QAC9C,aAAa,GAAG,OAAO,CAAC;QAExB,yDAAyD;QACzD,iDAAiD;QACjD,IAAA,+CAAyB,EAAC;YACtB,iBAAiB,EAAE,QAAQ;YAC3B,oBAAoB,EAAE,WAAW;YACjC,cAAc,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;SACjD,CAAC,CAAC;QAEH,IAAI,CAAC,wBAAsB,CAAC,UAAU,EAAE,CAAC;YACrC,wBAAsB,CAAC,UAAU,GAAG,IAAI,uCAAiB,CAAC,cAAc,CAAC,CAAC;YAC1E,wBAAsB,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,wBAAsB,CAAC,aAAa,EAAE,CAAC;YACxC,wBAAsB,CAAC,aAAa,GAAG,IAAI,mCAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAChF,wBAAsB,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;aACrC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAA,mCAAa,EAAC;YACzB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,QAAQ,EAAE,SAAS;YACnB,IAAI;YACJ,SAAS,EAAE,eAAe;YAC1B,UAAU,EAAE,wBAAsB,CAAC,UAAU,CAAC,aAAa,EAAE;SAChE,CAAC,CAAC;QACH,wBAAsB,CAAC,aAAa,GAAG,MAAM,CAAC;QAE9C,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAChB,sCAAsC;YACtC,OAAO,CAAC,IAAI,CACR,kDAAkD,MAAM,CAAC,OAAO,EAAE;gBAClE,2KAA2K,SAAS,EAAE,CACzL,CAAC;QACN,CAAC;QAED,IAAI,CAAC,wBAAsB,CAAC,SAAS,EAAE,CAAC;YACpC,wBAAsB,CAAC,SAAS,GAAG,IAAI,+BAAS,CAAC;gBAC7C,WAAW,EAAE,QAAQ;gBACrB,cAAc,EAAE,WAAW;gBAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,oBAAoB,EAAE,IAAA,0CAAoB,EAAC,eAAe,CAAC;aAC9D,CAAC,CAAC;YACH,wBAAsB,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAC7C,CAAC;QAED,OAAO,wBAAsB,CAAC;IAClC,CAAC;;AA7DQ,wDAAsB;AAChB,iCAAU,GAA6B,IAAI,AAAjC,CAAkC;AAC5C,oCAAa,GAAyB,IAAI,AAA7B,CAA8B;AAC3C,gCAAS,GAAqB,IAAI,AAAzB,CAA0B;AACnC,oCAAa,GAAyB,IAAI,AAA7B,CAA8B;AA2DnD,mCAAY,GAAG;IAClB,aAAa,EAAE,SAAS,GAAG,QAAQ;IACnC,SAAS,EAAE;QACP;YACI,IAAI,EAAE,MAAe;YACrB,KAAK,EAAE,UAAU;YACjB,gBAAgB,EAAE,oBAAoB;YACtC,YAAY,EAAE,gBAAgB;SACjC;KACJ;CACJ,AAVkB,CAUjB;iCAzEO,sBAAsB;IAXlC,IAAA,oBAAa,EAAC;QACX,OAAO,EAAE,CAAC,yBAAkB,CAAC;QAC7B,SAAS,EAAE,CAAC,iDAAsB,EAAE,yCAA6B,CAAC;QAClE,WAAW,EAAE,CAAC,uDAAyB,CAAC;QACxC,QAAQ,EAAE,CAAC,mCAAY,EAAE,uCAAc,CAAC;QACxC,aAAa,EAAE,QAAQ;QACvB,kBAAkB,EAAE;YAChB,MAAM,EAAE,0CAA8B;YACtC,SAAS,EAAE,CAAC,yCAA6B,CAAC;SAC7C;KACJ,CAAC;GACW,sBAAsB,CA0ElC"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Aggregate table for "customers who viewed X also viewed Y".
3
+ *
4
+ * Denormalised: we store both `(A, B)` and `(B, A)` so lookups by either
5
+ * side are a single indexed scan. Cheap on disk (int, int, int, int),
6
+ * expensive nowhere.
7
+ *
8
+ * Rebuilt by a nightly cron that walks recent `product_view` events per
9
+ * session, extracts all ordered pairs, and increments the counter. The
10
+ * scanner also decays old scores so trending products bubble up.
11
+ */
12
+ export declare class ProductCoView {
13
+ productIdA: number;
14
+ productIdB: number;
15
+ channelId: number;
16
+ viewsTogether: number;
17
+ lastUpdated: Date;
18
+ }
19
+ //# sourceMappingURL=product-co-view.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-co-view.entity.d.ts","sourceRoot":"","sources":["../src/product-co-view.entity.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,qBAEa,aAAa;IAEtB,UAAU,EAAG,MAAM,CAAC;IAGpB,UAAU,EAAG,MAAM,CAAC;IAGpB,SAAS,EAAG,MAAM,CAAC;IAGnB,aAAa,EAAG,MAAM,CAAC;IAGvB,WAAW,EAAG,IAAI,CAAC;CACtB"}
@@ -0,0 +1,52 @@
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.ProductCoView = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ /**
15
+ * Aggregate table for "customers who viewed X also viewed Y".
16
+ *
17
+ * Denormalised: we store both `(A, B)` and `(B, A)` so lookups by either
18
+ * side are a single indexed scan. Cheap on disk (int, int, int, int),
19
+ * expensive nowhere.
20
+ *
21
+ * Rebuilt by a nightly cron that walks recent `product_view` events per
22
+ * session, extracts all ordered pairs, and increments the counter. The
23
+ * scanner also decays old scores so trending products bubble up.
24
+ */
25
+ let ProductCoView = class ProductCoView {
26
+ };
27
+ exports.ProductCoView = ProductCoView;
28
+ __decorate([
29
+ (0, typeorm_1.PrimaryColumn)({ type: 'int' }),
30
+ __metadata("design:type", Number)
31
+ ], ProductCoView.prototype, "productIdA", void 0);
32
+ __decorate([
33
+ (0, typeorm_1.PrimaryColumn)({ type: 'int' }),
34
+ __metadata("design:type", Number)
35
+ ], ProductCoView.prototype, "productIdB", void 0);
36
+ __decorate([
37
+ (0, typeorm_1.PrimaryColumn)({ type: 'int', default: 1 }),
38
+ __metadata("design:type", Number)
39
+ ], ProductCoView.prototype, "channelId", void 0);
40
+ __decorate([
41
+ (0, typeorm_1.Column)({ type: 'int', default: 0 }),
42
+ __metadata("design:type", Number)
43
+ ], ProductCoView.prototype, "viewsTogether", void 0);
44
+ __decorate([
45
+ (0, typeorm_1.Column)({ type: 'datetime', precision: 3 }),
46
+ __metadata("design:type", Date)
47
+ ], ProductCoView.prototype, "lastUpdated", void 0);
48
+ exports.ProductCoView = ProductCoView = __decorate([
49
+ (0, typeorm_1.Entity)('product_co_view'),
50
+ (0, typeorm_1.Index)('product_co_view_score_idx', ['productIdA', 'channelId', 'viewsTogether'])
51
+ ], ProductCoView);
52
+ //# sourceMappingURL=product-co-view.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-co-view.entity.js","sourceRoot":"","sources":["../src/product-co-view.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA+D;AAE/D;;;;;;;;;;GAUG;AAGI,IAAM,aAAa,GAAnB,MAAM,aAAa;CAezB,CAAA;AAfY,sCAAa;AAEtB;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;iDACX;AAGpB;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;iDACX;AAGpB;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;gDACxB;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;oDACb;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;8BAC7B,IAAI;kDAAC;wBAdV,aAAa;IAFzB,IAAA,gBAAM,EAAC,iBAAiB,CAAC;IACzB,IAAA,eAAK,EAAC,2BAA2B,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;GACpE,aAAa,CAezB"}
@@ -0,0 +1,72 @@
1
+ import { RequestContext } from '@vendure/core';
2
+ import { RecommendationsService } from './recommendations.service';
3
+ /**
4
+ * Public + admin API for the co-view recommendations feature.
5
+ *
6
+ * The `also-viewed`, `personal` and `trending` endpoints are safe to
7
+ * hit from the storefront directly — they return only product ids +
8
+ * scores, no PII. Restricting them to Vendure admin would break the
9
+ * primary use case (rendering a recs rail on the product page).
10
+ */
11
+ export declare class RecommendationsController {
12
+ private readonly service;
13
+ constructor(service: RecommendationsService);
14
+ /**
15
+ * "Customers who viewed X also viewed…"
16
+ * /ees/recommendations/also-viewed?productId=42&channelId=1&limit=10
17
+ */
18
+ alsoViewed(pRaw?: string, chRaw?: string, lRaw?: string): Promise<{
19
+ error: string;
20
+ productId?: undefined;
21
+ items?: undefined;
22
+ } | {
23
+ productId: number;
24
+ items: {
25
+ productId: number;
26
+ score: number;
27
+ }[];
28
+ error?: undefined;
29
+ }>;
30
+ /**
31
+ * Personalised recs for a returning visitor:
32
+ * /ees/recommendations/personal?visitorId=abc&channelId=1&limit=10
33
+ *
34
+ * Storefronts should send the current `ees_vid` cookie value —
35
+ * the same one the ingest endpoint issues.
36
+ */
37
+ personal(vRaw?: string, chRaw?: string, lRaw?: string): Promise<{
38
+ error: string;
39
+ visitorId?: undefined;
40
+ items?: undefined;
41
+ } | {
42
+ visitorId: string;
43
+ items: {
44
+ productId: number;
45
+ score: number;
46
+ }[];
47
+ error?: undefined;
48
+ }>;
49
+ /**
50
+ * Most-viewed products in the window:
51
+ * /ees/recommendations/trending?channelId=1&hours=24&limit=10
52
+ */
53
+ trending(chRaw?: string, hRaw?: string, lRaw?: string): Promise<{
54
+ channelId: number;
55
+ hours: number;
56
+ items: {
57
+ productId: number;
58
+ views: number;
59
+ }[];
60
+ }>;
61
+ /**
62
+ * Admin-only: manually kick the aggregation cron. Normally runs
63
+ * nightly, but this is useful for smoke-testing after a data
64
+ * backfill, or for pushing a big spike through immediately.
65
+ */
66
+ aggregateNow(ctx: RequestContext, hRaw?: string): Promise<{
67
+ pairs: number;
68
+ ok: boolean;
69
+ hours: number;
70
+ }>;
71
+ }
72
+ //# sourceMappingURL=recommendations.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommendations.controller.d.ts","sourceRoot":"","sources":["../src/recommendations.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAO,cAAc,EAAqB,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE;;;;;;;GAOG;AACH,qBACa,yBAAyB;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,sBAAsB;IAE5D;;;OAGG;IAEG,UAAU,CACQ,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM;;;;;;;;;;;;IAUrC;;;;;;OAMG;IAEG,QAAQ,CACU,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM;;;;;;;;;;;;IAUrC;;;OAGG;IAEG,QAAQ,CACU,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM;;;;;;;;IASrC;;;;OAIG;IAGG,YAAY,CAAQ,GAAG,EAAE,cAAc,EAAkB,IAAI,CAAC,EAAE,MAAM;;;;;CAK/E"}
@@ -0,0 +1,123 @@
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
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RecommendationsController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const core_1 = require("@vendure/core");
18
+ const recommendations_service_1 = require("./recommendations.service");
19
+ /**
20
+ * Public + admin API for the co-view recommendations feature.
21
+ *
22
+ * The `also-viewed`, `personal` and `trending` endpoints are safe to
23
+ * hit from the storefront directly — they return only product ids +
24
+ * scores, no PII. Restricting them to Vendure admin would break the
25
+ * primary use case (rendering a recs rail on the product page).
26
+ */
27
+ let RecommendationsController = class RecommendationsController {
28
+ constructor(service) {
29
+ this.service = service;
30
+ }
31
+ /**
32
+ * "Customers who viewed X also viewed…"
33
+ * /ees/recommendations/also-viewed?productId=42&channelId=1&limit=10
34
+ */
35
+ async alsoViewed(pRaw, chRaw, lRaw) {
36
+ const productId = parseInt(pRaw || '0', 10);
37
+ if (!productId)
38
+ return { error: 'productId-required' };
39
+ const channelId = parseInt(chRaw || '1', 10) || 1;
40
+ const limit = parseInt(lRaw || '10', 10) || 10;
41
+ const items = await this.service.alsoViewed(productId, channelId, limit);
42
+ return { productId, items };
43
+ }
44
+ /**
45
+ * Personalised recs for a returning visitor:
46
+ * /ees/recommendations/personal?visitorId=abc&channelId=1&limit=10
47
+ *
48
+ * Storefronts should send the current `ees_vid` cookie value —
49
+ * the same one the ingest endpoint issues.
50
+ */
51
+ async personal(vRaw, chRaw, lRaw) {
52
+ const visitorId = String(vRaw || '').trim();
53
+ if (!visitorId)
54
+ return { error: 'visitorId-required' };
55
+ const channelId = parseInt(chRaw || '1', 10) || 1;
56
+ const limit = parseInt(lRaw || '10', 10) || 10;
57
+ const items = await this.service.personalRecommendations(visitorId, channelId, limit);
58
+ return { visitorId, items };
59
+ }
60
+ /**
61
+ * Most-viewed products in the window:
62
+ * /ees/recommendations/trending?channelId=1&hours=24&limit=10
63
+ */
64
+ async trending(chRaw, hRaw, lRaw) {
65
+ const channelId = parseInt(chRaw || '1', 10) || 1;
66
+ const hours = Math.min(Math.max(1, parseInt(hRaw || '24', 10) || 24), 24 * 30);
67
+ const limit = parseInt(lRaw || '10', 10) || 10;
68
+ const items = await this.service.trending(channelId, hours, limit);
69
+ return { channelId, hours, items };
70
+ }
71
+ /**
72
+ * Admin-only: manually kick the aggregation cron. Normally runs
73
+ * nightly, but this is useful for smoke-testing after a data
74
+ * backfill, or for pushing a big spike through immediately.
75
+ */
76
+ async aggregateNow(ctx, hRaw) {
77
+ const hours = Math.min(Math.max(1, parseInt(hRaw || '24', 10) || 24), 24 * 30);
78
+ const result = await this.service.aggregateCoViews(hours);
79
+ return { ok: true, hours, ...result };
80
+ }
81
+ };
82
+ exports.RecommendationsController = RecommendationsController;
83
+ __decorate([
84
+ (0, common_1.Get)('recommendations/also-viewed'),
85
+ __param(0, (0, common_1.Query)('productId')),
86
+ __param(1, (0, common_1.Query)('channelId')),
87
+ __param(2, (0, common_1.Query)('limit')),
88
+ __metadata("design:type", Function),
89
+ __metadata("design:paramtypes", [String, String, String]),
90
+ __metadata("design:returntype", Promise)
91
+ ], RecommendationsController.prototype, "alsoViewed", null);
92
+ __decorate([
93
+ (0, common_1.Get)('recommendations/personal'),
94
+ __param(0, (0, common_1.Query)('visitorId')),
95
+ __param(1, (0, common_1.Query)('channelId')),
96
+ __param(2, (0, common_1.Query)('limit')),
97
+ __metadata("design:type", Function),
98
+ __metadata("design:paramtypes", [String, String, String]),
99
+ __metadata("design:returntype", Promise)
100
+ ], RecommendationsController.prototype, "personal", null);
101
+ __decorate([
102
+ (0, common_1.Get)('recommendations/trending'),
103
+ __param(0, (0, common_1.Query)('channelId')),
104
+ __param(1, (0, common_1.Query)('hours')),
105
+ __param(2, (0, common_1.Query)('limit')),
106
+ __metadata("design:type", Function),
107
+ __metadata("design:paramtypes", [String, String, String]),
108
+ __metadata("design:returntype", Promise)
109
+ ], RecommendationsController.prototype, "trending", null);
110
+ __decorate([
111
+ (0, common_1.Get)('recommendations/aggregate-now'),
112
+ (0, core_1.Allow)(core_1.Permission.SuperAdmin),
113
+ __param(0, (0, core_1.Ctx)()),
114
+ __param(1, (0, common_1.Query)('hours')),
115
+ __metadata("design:type", Function),
116
+ __metadata("design:paramtypes", [core_1.RequestContext, String]),
117
+ __metadata("design:returntype", Promise)
118
+ ], RecommendationsController.prototype, "aggregateNow", null);
119
+ exports.RecommendationsController = RecommendationsController = __decorate([
120
+ (0, common_1.Controller)('ees'),
121
+ __metadata("design:paramtypes", [recommendations_service_1.RecommendationsService])
122
+ ], RecommendationsController);
123
+ //# sourceMappingURL=recommendations.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommendations.controller.js","sourceRoot":"","sources":["../src/recommendations.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwD;AACxD,wCAAuE;AACvE,uEAAmE;AAEnE;;;;;;;GAOG;AAEI,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IAClC,YAA6B,OAA+B;QAA/B,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;IAEhE;;;OAGG;IAEG,AAAN,KAAK,CAAC,UAAU,CACQ,IAAa,EACb,KAAc,EACd,IAAa;QAEjC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QACzE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;OAMG;IAEG,AAAN,KAAK,CAAC,QAAQ,CACU,IAAa,EACb,KAAc,EACd,IAAa;QAEjC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QACtF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChC,CAAC;IAED;;;OAGG;IAEG,AAAN,KAAK,CAAC,QAAQ,CACU,KAAc,EACd,IAAa,EACb,IAAa;QAEjC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,IAAI,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QACnE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACvC,CAAC;IAED;;;;OAIG;IAGG,AAAN,KAAK,CAAC,YAAY,CAAQ,GAAmB,EAAkB,IAAa;QACxE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC1D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1C,CAAC;CACJ,CAAA;AAvEY,8DAAyB;AAQ5B;IADL,IAAA,YAAG,EAAC,6BAA6B,CAAC;IAE9B,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;2DAQlB;AAUK;IADL,IAAA,YAAG,EAAC,0BAA0B,CAAC;IAE3B,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;yDAQlB;AAOK;IADL,IAAA,YAAG,EAAC,0BAA0B,CAAC;IAE3B,WAAA,IAAA,cAAK,EAAC,WAAW,CAAC,CAAA;IAClB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;yDAOlB;AASK;IAFL,IAAA,YAAG,EAAC,+BAA+B,CAAC;IACpC,IAAA,YAAK,EAAC,iBAAU,CAAC,UAAU,CAAC;IACT,WAAA,IAAA,UAAG,GAAE,CAAA;IAAuB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;qCAA/B,qBAAc;;6DAI5C;oCAtEQ,yBAAyB;IADrC,IAAA,mBAAU,EAAC,KAAK,CAAC;qCAEwB,gDAAsB;GADnD,yBAAyB,CAuErC"}
@@ -0,0 +1,62 @@
1
+ import { TransactionalConnection } from '@vendure/core';
2
+ /**
3
+ * Product recommendations driven by observed co-viewing behaviour.
4
+ *
5
+ * The scanner walks recent `product_view` custom events grouped by
6
+ * session, extracts every ordered pair, and increments a counter per
7
+ * `(productIdA, productIdB, channelId)` triple. We denormalise both
8
+ * directions (`A → B` AND `B → A`) so read-side lookups are one
9
+ * indexed scan.
10
+ *
11
+ * "Also viewed" is far simpler than "also bought" — you need orders
12
+ * data for the latter, and small stores don't have enough of it for
13
+ * useful signals. Co-views come from every browsing session, so a
14
+ * store with 1k daily visitors gets useful recs from day one.
15
+ */
16
+ export declare class RecommendationsService {
17
+ private connection;
18
+ constructor(connection: TransactionalConnection);
19
+ /**
20
+ * Rebuild the co-view aggregate from `visitor_event`. Idempotent
21
+ * per (A, B, channelId) — repeated calls over the same window
22
+ * accumulate, so schedule this as a nightly cron with a rolling
23
+ * lookback (e.g. last 24h) rather than a full-history rescan.
24
+ *
25
+ * Returns the number of pair-triples updated. Uses a bounded
26
+ * ordered-pair extractor (max 20 events per session) so a single
27
+ * bot session can't skew the table.
28
+ */
29
+ aggregateCoViews(sinceHours?: number): Promise<{
30
+ pairs: number;
31
+ }>;
32
+ /**
33
+ * Read side: "customers who viewed X also viewed Y" for one
34
+ * product, ordered by co-view score, capped at `limit`. Returns
35
+ * bare product ids so the storefront can hydrate names / images
36
+ * from its usual product-fetch path.
37
+ */
38
+ alsoViewed(productId: number, channelId?: number, limit?: number): Promise<Array<{
39
+ productId: number;
40
+ score: number;
41
+ }>>;
42
+ /**
43
+ * Personalised recommendations for a returning visitor: look at
44
+ * the last N products they viewed, and rank co-viewed products by
45
+ * combined score across those seed products (excluding the seeds
46
+ * themselves).
47
+ */
48
+ personalRecommendations(visitorId: string, channelId?: number, limit?: number): Promise<Array<{
49
+ productId: number;
50
+ score: number;
51
+ }>>;
52
+ /**
53
+ * "Trending now" — most-viewed products in the last N hours.
54
+ * Useful for a homepage rail; uses `product_view` events, so it
55
+ * reflects real interest, not just search-console clicks.
56
+ */
57
+ trending(channelId?: number, sinceHours?: number, limit?: number): Promise<Array<{
58
+ productId: number;
59
+ views: number;
60
+ }>>;
61
+ }
62
+ //# sourceMappingURL=recommendations.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recommendations.service.d.ts","sourceRoot":"","sources":["../src/recommendations.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAIxD;;;;;;;;;;;;;GAaG;AACH,qBACa,sBAAsB;IACnB,OAAO,CAAC,UAAU;gBAAV,UAAU,EAAE,uBAAuB;IAEvD;;;;;;;;;OASG;IACG,gBAAgB,CAAC,UAAU,SAAK,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAyDnE;;;;;OAKG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,SAAI,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC1E,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAYH;;;;;OAKG;IACG,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,SAAI,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACvF,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IA+BH;;;;OAIG;IACG,QAAQ,CAAC,SAAS,SAAI,EAAE,UAAU,SAAK,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QACtE,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CAmBN"}