@antelopejs/dms-marketing 0.5.0 → 0.6.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.
Files changed (132) hide show
  1. package/README.md +57 -131
  2. package/dist/config.d.ts +10 -1
  3. package/dist/config.js +29 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/db/models/funnels.model.d.ts +2 -0
  6. package/dist/db/models/funnels.model.js +18 -0
  7. package/dist/db/models/funnels.model.js.map +1 -1
  8. package/dist/db/models/index.d.ts +1 -0
  9. package/dist/db/models/index.js +1 -0
  10. package/dist/db/models/index.js.map +1 -1
  11. package/dist/db/models/marketing_events.model.d.ts +1 -0
  12. package/dist/db/models/marketing_imports.model.d.ts +27 -0
  13. package/dist/db/models/marketing_imports.model.js +38 -0
  14. package/dist/db/models/marketing_imports.model.js.map +1 -0
  15. package/dist/db/models/marketing_preferences.model.d.ts +1 -0
  16. package/dist/db/models/marketing_sessions.model.d.ts +1 -0
  17. package/dist/db/models/marketing_settings.model.d.ts +1 -0
  18. package/dist/db/models/page_snapshots.model.d.ts +2 -0
  19. package/dist/db/models/page_snapshots.model.js +14 -0
  20. package/dist/db/models/page_snapshots.model.js.map +1 -1
  21. package/dist/db/models/website_statistics.model.d.ts +1 -0
  22. package/dist/db/models/websites.model.d.ts +2 -0
  23. package/dist/db/models/websites.model.js +29 -0
  24. package/dist/db/models/websites.model.js.map +1 -1
  25. package/dist/db/tables/funnels.table.d.ts +1 -0
  26. package/dist/db/tables/funnels.table.js +4 -0
  27. package/dist/db/tables/funnels.table.js.map +1 -1
  28. package/dist/db/tables/index.d.ts +1 -0
  29. package/dist/db/tables/index.js +1 -0
  30. package/dist/db/tables/index.js.map +1 -1
  31. package/dist/db/tables/marketing_imports.table.d.ts +15 -0
  32. package/dist/db/tables/marketing_imports.table.js +63 -0
  33. package/dist/db/tables/marketing_imports.table.js.map +1 -0
  34. package/dist/db/tables/page_snapshots.table.d.ts +1 -0
  35. package/dist/db/tables/page_snapshots.table.js +4 -0
  36. package/dist/db/tables/page_snapshots.table.js.map +1 -1
  37. package/dist/db/tables/websites.table.d.ts +1 -0
  38. package/dist/db/tables/websites.table.js +4 -0
  39. package/dist/db/tables/websites.table.js.map +1 -1
  40. package/dist/hooks/index.js +1 -0
  41. package/dist/hooks/index.js.map +1 -1
  42. package/dist/implementations/dms-marketing/index.d.ts +5 -1
  43. package/dist/implementations/dms-marketing/index.js +25 -0
  44. package/dist/implementations/dms-marketing/index.js.map +1 -1
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +4 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/routes/dashboard-tracking.d.ts +9 -0
  49. package/dist/routes/dashboard-tracking.js +57 -0
  50. package/dist/routes/dashboard-tracking.js.map +1 -0
  51. package/dist/routes/index.d.ts +1 -0
  52. package/dist/routes/index.js +1 -0
  53. package/dist/routes/index.js.map +1 -1
  54. package/dist/routes/stats.js.map +1 -1
  55. package/dist/routes/websites.js +12 -25
  56. package/dist/routes/websites.js.map +1 -1
  57. package/dist/services/analytics-import.d.ts +2 -0
  58. package/dist/services/analytics-import.js +294 -0
  59. package/dist/services/analytics-import.js.map +1 -0
  60. package/dist/services/collect.d.ts +20 -0
  61. package/dist/services/collect.js +43 -17
  62. package/dist/services/collect.js.map +1 -1
  63. package/dist/services/context.d.ts +8 -3
  64. package/dist/services/context.js +21 -7
  65. package/dist/services/context.js.map +1 -1
  66. package/dist/services/dashboard-tracking.d.ts +25 -0
  67. package/dist/services/dashboard-tracking.js +85 -0
  68. package/dist/services/dashboard-tracking.js.map +1 -0
  69. package/dist/services/dashboard.js +7 -3
  70. package/dist/services/dashboard.js.map +1 -1
  71. package/dist/services/declarations.d.ts +11 -0
  72. package/dist/services/declarations.js +64 -0
  73. package/dist/services/declarations.js.map +1 -0
  74. package/dist/services/declared-funnel.d.ts +2 -0
  75. package/dist/services/declared-funnel.js +230 -0
  76. package/dist/services/declared-funnel.js.map +1 -0
  77. package/dist/services/declared-snapshot.d.ts +2 -0
  78. package/dist/services/declared-snapshot.js +113 -0
  79. package/dist/services/declared-snapshot.js.map +1 -0
  80. package/dist/services/declared-website.d.ts +2 -0
  81. package/dist/services/declared-website.js +118 -0
  82. package/dist/services/declared-website.js.map +1 -0
  83. package/dist/services/geoip.js.map +1 -1
  84. package/dist/services/index.d.ts +1 -0
  85. package/dist/services/index.js +1 -0
  86. package/dist/services/index.js.map +1 -1
  87. package/dist/services/readiness.d.ts +2 -0
  88. package/dist/services/readiness.js +15 -0
  89. package/dist/services/readiness.js.map +1 -0
  90. package/dist/services/sessionize.d.ts +1 -0
  91. package/dist/services/sessionize.js +3 -0
  92. package/dist/services/sessionize.js.map +1 -1
  93. package/dist/services/snapshots.d.ts +3 -0
  94. package/dist/services/snapshots.js +6 -0
  95. package/dist/services/snapshots.js.map +1 -1
  96. package/dist/services/tenant-website.js +1 -0
  97. package/dist/services/tenant-website.js.map +1 -1
  98. package/dist/services/website-report.js +4 -3
  99. package/dist/services/website-report.js.map +1 -1
  100. package/dist/services/website-rules.d.ts +8 -0
  101. package/dist/services/website-rules.js +34 -0
  102. package/dist/services/website-rules.js.map +1 -0
  103. package/dist/types/constants.d.ts +3 -0
  104. package/dist/types/constants.js +5 -2
  105. package/dist/types/constants.js.map +1 -1
  106. package/dist/types/snapshots.d.ts +55 -0
  107. package/dist/types/snapshots.js +8 -1
  108. package/dist/types/snapshots.js.map +1 -1
  109. package/frontend-vue/app/components/Context.vue +5 -1
  110. package/frontend-vue/app/components/FirstRun.vue +1 -1
  111. package/frontend-vue/app/components/PagePreview.vue +1 -0
  112. package/frontend-vue/app/components/WebsitesGrid.vue +6 -2
  113. package/frontend-vue/app/composables/useMarketingApi.ts +12 -0
  114. package/frontend-vue/app/composables/useMarketingContext.ts +2 -1
  115. package/frontend-vue/app/composables/useMarketingReads.ts +6 -0
  116. package/frontend-vue/app/constants.ts +6 -0
  117. package/frontend-vue/app/plugins/dashboard-tracking.client.ts +44 -0
  118. package/frontend-vue/app/utils/dashboard-tracker.ts +127 -0
  119. package/frontend-vue/app/utils/format.ts +6 -3
  120. package/frontend-vue/dms.frontend.ts +10 -0
  121. package/frontend-vue/i18n/locales/marketing-en-GB.json +2 -0
  122. package/frontend-vue/i18n/locales/marketing-fr-FR.json +2 -0
  123. package/frontend-vue/package.json +1 -1
  124. package/frontend-vue/tests/dashboard-tracking.test.ts +219 -0
  125. package/frontend-vue/tests/format.test.ts +9 -0
  126. package/frontend-vue/tests/marketing-reads.test.ts +23 -0
  127. package/frontend-vue/tests/server-parity.test.ts +5 -0
  128. package/frontend-vue/tests/stubs/frontend-module.ts +7 -0
  129. package/package.json +34 -15
  130. package/skills/dms-marketing/SKILL.md +107 -0
  131. package/static/tracker.js +90 -5
  132. package/frontend-vue/pnpm-workspace.yaml +0 -2
package/README.md CHANGED
@@ -1,147 +1,73 @@
1
+ <div align="center">
2
+
1
3
  # @antelopejs/dms-marketing
2
4
 
3
- <div align="center">
4
- <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue?style=for-the-badge&labelColor=000000"></a>
5
- <a href="https://discord.gg/sjK28QHrA7"><img src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000" alt="Discord"></a>
6
- <a href="https://antelopejs.com"><img src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000" alt="Documentation"></a>
7
- </div>
5
+ <a href="https://www.npmjs.com/package/@antelopejs/dms-marketing"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/dms-marketing?style=for-the-badge&labelColor=000000&color=000000"></a>
6
+ <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/dms-marketing?style=for-the-badge&labelColor=000000&color=000000"></a>
7
+ <a href="https://discord.gg/sjK28QHrA7"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000"></a>
8
+ <a href="https://dms.antelopejs.com/docs/modules/marketing"><img alt="Documentation" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000"></a>
8
9
 
9
- Marketing analytics module for the AntelopeJS DMS: traffic dashboards,
10
- conversion funnels with optional A/B splits, and click heatmaps — fully
11
- first-party.
12
- No external service, no cookie; data stays in the project's MongoDB.
13
-
14
- <p align="center">
15
- <img src="docs/screenshots/overview.png" alt="Overview: the website and period bar, four KPI cards with their change and sparkline, the session-quality strip, the traffic chart against the previous period, the device split and four tabbed top lists" width="900">
16
- </p>
17
- <p align="center">
18
- <em>The Overview page — every panel reads pre-aggregated daily rollups, never raw events.</em>
19
- </p>
20
-
21
- ## Features
22
-
23
- | Feature | Where | Guide |
24
- |---|---|---|
25
- | **Traffic overview** — pageviews, sessions, new visitors and custom events with their change, session quality, traffic chart, device split, top content/acquisition/audience/event lists | `/modules/marketing/overview` | [usage/overview.md](docs/usage/overview.md) |
26
- | **Acquisition** — direct/organic/social/email/referral/paid split, UTM campaign table (source × medium × campaign), top referrers and UTM details | `/modules/marketing/acquisition` | [usage/acquisition.md](docs/usage/acquisition.md) |
27
- | **Pages & heatmaps** — clicks and scroll depth drawn over a snapshot of any tracked page | `/modules/marketing/pages` | [usage/heatmap.md](docs/usage/heatmap.md) |
28
- | **Funnels & A/B tests** — ordered conversion journeys with per-step drop-off, optionally split into server-assigned variations scored with significance; a report page and a builder per funnel | `/modules/marketing/funnels` | [usage/funnels.md](docs/usage/funnels.md) |
29
- | **Websites & install guide** — the tracked sites, their live state and capture options; a guided tracker install that checks the first pageview | `/modules/marketing/websites` | [install.md](docs/install.md#2-register-a-website) |
30
- | **First-party tracker** — cookieless script, SPA-aware, custom events API | visitor sites | [usage/tracking.md](docs/usage/tracking.md) |
31
- | **Settings** — collection switch, retentions, heatmap sampling | `/modules/marketing/settings` | [usage/settings.md](docs/usage/settings.md) |
32
-
33
- <p align="center">
34
- <img src="docs/screenshots/heatmap.png" alt="Pages & heatmaps: the tracked pages with their views and heat bars on the left, and on the right the click heatmap of the selected page drawn over its snapshot, with count pins on the hottest elements and the Most clicked list beside it" width="900">
35
- </p>
36
- <p align="center">
37
- <em>Click heatmap over the page itself. Clicks are anchored to the element
38
- they hit, so they stay on the right button at every viewport width.</em>
39
- </p>
40
-
41
- <p align="center">
42
- <img src="docs/screenshots/funnel.png" alt="Funnel report: entered, completed, end-to-end and biggest-loss KPIs over the steps figure, each step with its share of entered sessions and the sessions lost before the next one, the worst step flagged" width="900">
43
- </p>
44
- <p align="center">
45
- <em>Conversion funnels with per-step drop-off. Computed at read time over
46
- the period's sessions, so a new definition immediately scores past traffic.</em>
47
- </p>
48
-
49
- <p align="center">
50
- <img src="docs/screenshots/experiments.png" alt="A/B test report: the verdict sentence with the relative lift, the confidence and the exposed sessions, the sample ratio check, then each arm step by step with A neutral and B in the primary colour" width="900">
51
- </p>
52
- <p align="center">
53
- <em>The same funnel split into A/B arms, arm against arm at every step.
54
- Verdicts answer "not enough data" rather than overclaim — a sample too
55
- thin shows raw counts, never bars.</em>
56
- </p>
57
-
58
- ## What makes it hold
59
-
60
- - **First-party and cookieless.** Visitors are identified by a server-side
61
- anonymous hash (salted monthly, raw inputs never stored) — the shape
62
- audience-measurement consent exemptions ask for. Events store no IP, no
63
- user agent, no headers. Page content is stored in one place only — the
64
- heatmap's page snapshots — and that is opt-in per site, form values
65
- always masked, under a short retention of its own.
66
- - **Dashboards never scan raw events.** Statistics are rolled up at write
67
- time into one bounded row per website-day, counters as native server-side
68
- increments; raw events are kept short-term only for heatmaps and funnel
69
- computation.
70
- - **Heatmaps work on any site.** The surface is keyed on the pathnames the
71
- tracker reports — no route registry, no sitemap, no framework convention —
72
- clicks are anchored to the element they hit, not to screen coordinates,
73
- and the backdrop is a snapshot the tracker captured in a visitor's
74
- browser, so it shows on sites that refuse framing (most of them) and on
75
- client-rendered ones alike.
76
- - **A/B without an experiment server or a client SDK.** A split is a
77
- facet of a funnel, not a second object: variations are assigned
78
- server-side from the same anonymous visitor hash, the page asks with one
79
- call (`await dmsMarketing.variation("key")`), and the funnel's own steps
80
- score the arms — z-test verdicts that answer "not enough data" rather
81
- than overclaim.
82
- - **Tenant isolation is structural.** Analytics tables live in per-tenant
83
- database instances, the same model as the rest of the DMS — not a filter
84
- someone can forget.
85
-
86
- The reasoning behind each choice is in
87
- [docs/architecture.md](docs/architecture.md).
10
+ First-party marketing analytics for the AntelopeJS DMS: traffic, funnels, A/B splits and heatmaps.
88
11
 
89
- ## Documentation
12
+ </div>
90
13
 
91
- - **[Installation](docs/install.md)** — DMS side, visitor-site tracker embed,
92
- development playground.
93
- - **[Usage guides](docs/usage/)** — one per feature, linked in the table above.
94
- - **[Architecture](docs/architecture.md)** — data flow, tenancy, auth model,
95
- event contract, HTTP surface, heatmap internals, phasing.
96
- - **[Known issues](KNOWN-ISSUES.md)** — the defects found end-to-end and
97
- how each was closed; none is open.
14
+ ![The Marketing overview: KPI cards, session quality and the traffic chart against the previous period](https://raw.githubusercontent.com/AntelopeJS/dms-marketing/main/packages/dms-marketing/docs/images/overview.png)
98
15
 
99
- ## Status
16
+ ## What it does
100
17
 
101
- Pre-release, published on npm as `@antelopejs/dms-marketing`. Audience
102
- measurement, funnels, A/B splits and click heatmaps are functional end-to-end
103
- against the playground; session cohorts and session replay are not built yet.
104
- The full phase plan and current limits are in
105
- [architecture.md](docs/architecture.md#phasing).
18
+ - Shows pageviews, sessions, new visitors and custom events against the previous period, with acquisition channels and UTM campaigns.
19
+ - Draws clicks and scroll depth over a snapshot of any tracked page, even on sites that refuse framing.
20
+ - Measures ordered conversion funnels with per-step drop-off, optionally split into server-assigned A/B variations.
21
+ - Stays first-party and cookieless: no external service, no IP or user agent stored, and the data stays in the project's MongoDB.
106
22
 
107
- ## Development
23
+ ## Install
108
24
 
109
- This package is one half of the `dms-marketing` pnpm workspace; run the
110
- commands from the repository root.
25
+ Run the AntelopeJS CLI from the root of a project that already loads `@antelopejs/dms`:
111
26
 
112
27
  ```bash
113
- pnpm install --filter @antelopejs/dms-marketing...
114
- pnpm --dir packages/dms-marketing/frontend-vue install
115
- pnpm build # interface, then tsc
116
- pnpm lint # oxlint + oxfmt over the backend, eslint over frontend-vue
117
- pnpm typecheck # tsc over both packages, skipLibCheck off
118
- pnpm knip # unused dependencies
119
- pnpm test # backend suite, then the frontend module's vitest suite
28
+ ajs project modules add @antelopejs/dms-marketing
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ Restart the backend, then the frontend, and open **Modules › Marketing**. Click **Add a website** on the Websites page: the install guide that opens gives the tracker snippet, in HTML, Nuxt or Google Tag Manager form. In HTML:
34
+
35
+ ```html
36
+ <script defer src="https://<backend>/api/marketing/tracker.js"
37
+ data-website-id="<website id>"></script>
120
38
  ```
121
39
 
122
- The admin surfaces ship as a DMS frontend module in `frontend-vue/`: a Vue 3
123
- project whose root `dms.frontend.ts` declares the `DmsMarketing` component
124
- prefix and registers every component of `app/components/` under it, plus its
125
- own i18n catalogs. The pages themselves are declared backend-side as trees
126
- of DMS blocks ([architecture.md](docs/architecture.md#frontend-layout)). It is a separate project
127
- with a lockfile of its own, published inside this package's tarball and
128
- materialized by the DMS frontend loader (`ajs dms`) into the console's Inertia
129
- workspace. Its own vitest suite runs through `pnpm test`.
130
-
131
- The interface this module implements is the workspace's other package,
132
- `packages/interface-dms-marketing`, published under the same `@antelopejs`
133
- scope. This package implements it, so it caps it below the next minor,
134
- `>=<interface version> <0.<minor+1>.0`, rather than taking the fleet's
135
- `<1.0.0` ceiling: a breaking interface minor never reaches a module that does
136
- not implement it. pnpm links that range to the sibling inside the workspace,
137
- so `pnpm build` builds the interface first.
138
- Each is released on its own from GitHub Actions — `release-interface.yml`
139
- then `release.yml`; the module's workflow refuses to run until the interface
140
- version it links is resolvable on npm.
141
-
142
- A full consumer project with a tracked demo site lives under `playground/` —
143
- setup in [docs/install.md](docs/install.md#4-development-playground).
40
+ Behind a reverse proxy, set the DMS `auth.trustedProxies` option: see [Installation](https://dms.antelopejs.com/docs/modules/marketing/installation).
41
+
42
+ ## Compatibility
43
+
44
+ | Package | Version |
45
+ | --- | --- |
46
+ | `@antelopejs/dms` | `>=0.9.0 <1.0.0` |
47
+ | `@antelopejs/interface-dms` | `>=0.7.2 <1.0.0` |
48
+ | `@antelopejs/dms-frontend` (renderer) | `>=0.6.0 <0.7.0` |
49
+ | `@antelopejs/interface-dms-marketing` | `>=0.5.0 <0.6.0` |
50
+ | `@antelopejs/core` (development dependency) | `>=1.16.0 <2` |
51
+ | Database (development dependency) | `@antelopejs/mongodb` `1.4.3` |
52
+
53
+ On DMS 0.8, use `@antelopejs/dms-marketing` 0.4.
54
+
55
+ ## Documentation
56
+
57
+ - [Introduction](https://dms.antelopejs.com/docs/modules/marketing/introduction)
58
+ - [Installation](https://dms.antelopejs.com/docs/modules/marketing/installation)
59
+ - [Configuration](https://dms.antelopejs.com/docs/modules/marketing/configuration)
60
+ - [Usage](https://dms.antelopejs.com/docs/modules/marketing/usage/overview)
61
+ - [Extending](https://dms.antelopejs.com/docs/modules/marketing/extending)
62
+ - [FAQ](https://dms.antelopejs.com/docs/modules/marketing/faq)
63
+ - [Architecture](https://dms.antelopejs.com/docs/modules/marketing/architecture)
64
+
65
+ The guide's sources live in [`packages/dms-marketing/docs`](https://github.com/AntelopeJS/dms-marketing/tree/main/packages/dms-marketing/docs); [`KNOWN-ISSUES.md`](https://github.com/AntelopeJS/dms-marketing/tree/main/packages/dms-marketing/KNOWN-ISSUES.md) lists the defects found end to end and how each was closed. The module ships the `dms-marketing` Claude Code skill in [`packages/dms-marketing/skills`](https://github.com/AntelopeJS/dms-marketing/tree/main/packages/dms-marketing/skills), declared under `antelopeJs.skills`.
66
+
67
+ ## Contributing
68
+
69
+ This package lives in the [`AntelopeJS/dms-marketing`](https://github.com/AntelopeJS/dms-marketing) pnpm workspace, next to `@antelopejs/interface-dms-marketing`. Read [CONTRIBUTING.md](https://github.com/AntelopeJS/dms-marketing/blob/main/CONTRIBUTING.md) and the [repository README](https://github.com/AntelopeJS/dms-marketing#contributing) for the development commands.
144
70
 
145
71
  ## License
146
72
 
147
- Apache-2.0
73
+ [Apache-2.0](./LICENSE)
package/dist/config.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export interface DashboardTrackingConfig {
2
+ enabled: boolean;
3
+ websiteName: string;
4
+ excludePaths: string[];
5
+ }
1
6
  export interface DmsMarketingConfig {
2
7
  trackerEnabled: boolean;
3
8
  rawEventsRetention: number;
@@ -7,8 +12,12 @@ export interface DmsMarketingConfig {
7
12
  heatmapSampleRate: number;
8
13
  visitorHashSecret: string;
9
14
  geoipDatabasePath: string;
15
+ dashboardTracking: DashboardTrackingConfig;
10
16
  }
11
- export declare function setConfig(input?: Partial<DmsMarketingConfig>): void;
17
+ export type DmsMarketingConfigInput = Partial<Omit<DmsMarketingConfig, "dashboardTracking">> & {
18
+ dashboardTracking?: Partial<DashboardTrackingConfig>;
19
+ };
20
+ export declare function setConfig(input?: DmsMarketingConfigInput): void;
12
21
  export declare function applyConfigOverrides(overrides: Partial<DmsMarketingConfig>): void;
13
22
  export declare function getBaseConfig(): DmsMarketingConfig;
14
23
  export declare function getConfig(): DmsMarketingConfig;
package/dist/config.js CHANGED
@@ -6,6 +6,8 @@ exports.getBaseConfig = getBaseConfig;
6
6
  exports.getConfig = getConfig;
7
7
  const defu_1 = require("defu");
8
8
  const constants_1 = require("./types/constants");
9
+ const DEFAULT_DASHBOARD_WEBSITE_NAME = "Dashboard";
10
+ const DASHBOARD_PATH_PATTERN = /^\/[^*,\s]*(?:\/\*\*?)?$/;
9
11
  const DEFAULT_CONFIG = {
10
12
  trackerEnabled: true,
11
13
  rawEventsRetention: constants_1.DEFAULT_RAW_EVENTS_RETENTION_MS,
@@ -15,7 +17,30 @@ const DEFAULT_CONFIG = {
15
17
  heatmapSampleRate: constants_1.DEFAULT_HEATMAP_SAMPLE_RATIO,
16
18
  visitorHashSecret: "",
17
19
  geoipDatabasePath: "",
20
+ dashboardTracking: {
21
+ enabled: false,
22
+ websiteName: DEFAULT_DASHBOARD_WEBSITE_NAME,
23
+ excludePaths: [],
24
+ },
18
25
  };
26
+ function isDashboardPathPattern(value) {
27
+ return typeof value === "string" && DASHBOARD_PATH_PATTERN.test(value);
28
+ }
29
+ function parseDashboardTracking(input) {
30
+ const value = input && typeof input === "object"
31
+ ? input
32
+ : {};
33
+ const name = typeof value.websiteName === "string" ? value.websiteName.trim() : "";
34
+ return {
35
+ enabled: value.enabled === true,
36
+ websiteName: name.length > 0 && name.length <= constants_1.MAX_NAME_LENGTH
37
+ ? name
38
+ : DEFAULT_DASHBOARD_WEBSITE_NAME,
39
+ excludePaths: Array.isArray(value.excludePaths)
40
+ ? [...new Set(value.excludePaths.filter(isDashboardPathPattern))]
41
+ : [],
42
+ };
43
+ }
19
44
  let baseConfig = DEFAULT_CONFIG;
20
45
  let globalConfig = DEFAULT_CONFIG;
21
46
  let runtimeOverrides = {};
@@ -23,7 +48,10 @@ function mergeEffective() {
23
48
  globalConfig = (0, defu_1.defu)(runtimeOverrides, baseConfig);
24
49
  }
25
50
  function setConfig(input) {
26
- baseConfig = (0, defu_1.defu)(input ?? {}, DEFAULT_CONFIG);
51
+ baseConfig = {
52
+ ...(0, defu_1.defu)(input ?? {}, DEFAULT_CONFIG),
53
+ dashboardTracking: parseDashboardTracking(input?.dashboardTracking),
54
+ };
27
55
  mergeEffective();
28
56
  }
29
57
  function applyConfigOverrides(overrides) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AAuDA,8BAGC;AAED,oDAKC;AAGD,sCAEC;AAED,8BAEC;AA1ED,+BAA4B;AAC5B,iDAM2B;AA6B3B,MAAM,cAAc,GAAuB;IACzC,cAAc,EAAE,IAAI;IACpB,kBAAkB,EAAE,2CAA+B;IACnD,mBAAmB,EAAE,2CAA+B;IACpD,iBAAiB,EAAE,yCAA6B;IAChD,kBAAkB,EAAE,2CAA+B;IACnD,iBAAiB,EAAE,wCAA4B;IAC/C,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE,EAAE;CACtB,CAAC;AAEF,IAAI,UAAU,GAAuB,cAAc,CAAC;AACpD,IAAI,YAAY,GAAuB,cAAc,CAAC;AACtD,IAAI,gBAAgB,GAAgC,EAAE,CAAC;AAEvD,SAAS,cAAc;IACrB,YAAY,GAAG,IAAA,WAAI,EAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,SAAS,CAAC,KAAmC;IAC3D,UAAU,GAAG,IAAA,WAAI,EAAC,KAAK,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;IAC/C,cAAc,EAAE,CAAC;AACnB,CAAC;AAED,SAAgB,oBAAoB,CAClC,SAAsC;IAEtC,gBAAgB,GAAG,SAAS,CAAC;IAC7B,cAAc,EAAE,CAAC;AACnB,CAAC;AAGD,SAAgB,aAAa;IAC3B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;AAsHA,8BAMC;AAED,oDAKC;AAGD,sCAEC;AAED,8BAEC;AA5ID,+BAA4B;AAC5B,iDAO2B;AAqD3B,MAAM,8BAA8B,GAAG,WAAW,CAAC;AAEnD,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AAE1D,MAAM,cAAc,GAAuB;IACzC,cAAc,EAAE,IAAI;IACpB,kBAAkB,EAAE,2CAA+B;IACnD,mBAAmB,EAAE,2CAA+B;IACpD,iBAAiB,EAAE,yCAA6B;IAChD,kBAAkB,EAAE,2CAA+B;IACnD,iBAAiB,EAAE,wCAA4B;IAC/C,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE;QACjB,OAAO,EAAE,KAAK;QACd,WAAW,EAAE,8BAA8B;QAC3C,YAAY,EAAE,EAAE;KACjB;CACF,CAAC;AAEF,SAAS,sBAAsB,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzE,CAAC;AAQD,SAAS,sBAAsB,CAAC,KAAc;IAC5C,MAAM,KAAK,GACT,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAChC,CAAC,CAAE,KAAiE;QACpE,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxE,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,IAAI;QAC/B,WAAW,EACT,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,2BAAe;YAC/C,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,8BAA8B;QACpC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;YAC7C,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACjE,CAAC,CAAC,EAAE;KACP,CAAC;AACJ,CAAC;AAED,IAAI,UAAU,GAAuB,cAAc,CAAC;AACpD,IAAI,YAAY,GAAuB,cAAc,CAAC;AACtD,IAAI,gBAAgB,GAAgC,EAAE,CAAC;AAEvD,SAAS,cAAc;IACrB,YAAY,GAAG,IAAA,WAAI,EAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,SAAS,CAAC,KAA+B;IACvD,UAAU,GAAG;QACX,GAAG,IAAA,WAAI,EAAC,KAAK,IAAI,EAAE,EAAE,cAAc,CAAC;QACpC,iBAAiB,EAAE,sBAAsB,CAAC,KAAK,EAAE,iBAAiB,CAAC;KACpE,CAAC;IACF,cAAc,EAAE,CAAC;AACnB,CAAC;AAED,SAAgB,oBAAoB,CAClC,SAAsC;IAEtC,gBAAgB,GAAG,SAAS,CAAC;IAC7B,cAAc,EAAE,CAAC;AACnB,CAAC;AAGD,SAAgB,aAAa;IAC3B,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAgB,SAAS;IACvB,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -12,12 +12,14 @@ declare const FunnelsModel_base: {
12
12
  delete(id: string): Promise<number>;
13
13
  };
14
14
  readonly schemaName: string;
15
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<Funnel>;
15
16
  fromPlainData(obj: any): Funnel;
16
17
  fromDatabase(obj: any): Funnel | undefined;
17
18
  toDatabase(obj: any): Record<string, any>;
18
19
  validate(obj: unknown, options?: import("@antelopejs/interface-database-decorators").ValidateInputOptions): import("@antelopejs/interface-database-decorators").ValidationResult;
19
20
  };
20
21
  export declare class FunnelsModel extends FunnelsModel_base {
22
+ compareDeclared(expected: Funnel, patch: Partial<Funnel>): Promise<boolean>;
21
23
  deleteByWebsite(websiteId: string): Promise<number>;
22
24
  listByWebsite(websiteId: string): Promise<Funnel[]>;
23
25
  listRunning(websiteId: string): Promise<Funnel[]>;
@@ -5,6 +5,24 @@ const interface_database_decorators_1 = require("@antelopejs/interface-database-
5
5
  const funnels_table_1 = require("../tables/funnels.table");
6
6
  const RUNNING_STATUS = "running";
7
7
  class FunnelsModel extends (0, interface_database_decorators_1.BasicDataModel)(funnels_table_1.Funnel, funnels_table_1.funnelsTableName) {
8
+ async compareDeclared(expected, patch) {
9
+ const count = await this.table
10
+ .filter((row) => row
11
+ .key("_id")
12
+ .eq(expected._id)
13
+ .and(row.key("websiteId").eq(expected.websiteId))
14
+ .and(row.key("name").eq(expected.name))
15
+ .and(row.key("steps").eq(expected.steps))
16
+ .and(row.key("conversionWindowMs").eq(expected.conversionWindowMs))
17
+ .and(row.key("experiment").eq(expected.experiment))
18
+ .and(row
19
+ .key("declaredHash")
20
+ .default("")
21
+ .eq(expected.declaredHash ?? "")))
22
+ .update(FunnelsModel.toDatabase({ ...patch, updatedAt: new Date() }))
23
+ .run();
24
+ return count === 1;
25
+ }
8
26
  async deleteByWebsite(websiteId) {
9
27
  return this.table.getAll(websiteId, "websiteId").delete().run();
10
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"funnels.model.js","sourceRoot":"","sources":["../../../src/db/models/funnels.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,2DAIiC;AAEjC,MAAM,cAAc,GAAG,SAAS,CAAC;AAEjC,MAAa,YAAa,SAAQ,IAAA,8CAAc,EAAC,sBAAM,EAAE,gCAAgB,CAAC;IACxE,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;aAC9B,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC;aAC5B,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,WAAW,CAAC,SAAiB;QACjC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;aAC9B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACd,GAAG;aACA,GAAG,CAAC,YAAY,CAAC;aACjB,IAAI,EAAoB;aACxB,GAAG,CAAC,QAAQ,CAAC;aACb,EAAE,CAAC,cAAc,CAAC,CACtB;aACA,GAAG,EAAE,CAAC;IACX,CAAC;IAQD,KAAK,CAAC,MAAM,CACV,SAAiB,EACjB,GAAW,EACX,SAAkB;QAElB,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;aACf,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC;aAC9B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACd,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,EAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAClE,CAAC;QACJ,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAMD,KAAK,CAAC,gBAAgB,CAAC,GAAa;QAClC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,GAAG,CAAC;aACX,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;aAClC,QAAQ,EAAE;aACV,GAAG,EAAE,CAAC;IACX,CAAC;CACF;AA9DD,oCA8DC"}
1
+ {"version":3,"file":"funnels.model.js","sourceRoot":"","sources":["../../../src/db/models/funnels.model.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  export * from "./funnels.model";
2
2
  export * from "./marketing_events.model";
3
+ export * from "./marketing_imports.model";
3
4
  export * from "./marketing_preferences.model";
4
5
  export * from "./marketing_sessions.model";
5
6
  export * from "./marketing_settings.model";
@@ -16,6 +16,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./funnels.model"), exports);
18
18
  __exportStar(require("./marketing_events.model"), exports);
19
+ __exportStar(require("./marketing_imports.model"), exports);
19
20
  __exportStar(require("./marketing_preferences.model"), exports);
20
21
  __exportStar(require("./marketing_sessions.model"), exports);
21
22
  __exportStar(require("./marketing_settings.model"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/models/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,2DAAyC;AACzC,gEAA8C;AAC9C,6DAA2C;AAC3C,6DAA2C;AAC3C,yDAAuC;AACvC,6DAA2C;AAC3C,mDAAiC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/db/models/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,2DAAyC;AACzC,4DAA0C;AAC1C,gEAA8C;AAC9C,6DAA2C;AAC3C,6DAA2C;AAC3C,yDAAuC;AACvC,6DAA2C;AAC3C,mDAAiC"}
@@ -50,6 +50,7 @@ declare const MarketingEventsModel_base: {
50
50
  delete(id: string): Promise<number>;
51
51
  };
52
52
  readonly schemaName: string;
53
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MarketingEvent>;
53
54
  fromPlainData(obj: any): MarketingEvent;
54
55
  fromDatabase(obj: any): MarketingEvent | undefined;
55
56
  toDatabase(obj: any): Record<string, any>;
@@ -0,0 +1,27 @@
1
+ import { MarketingImport } from "../tables/marketing_imports.table";
2
+ declare const MarketingImportsModel_base: {
3
+ new (database: import("@antelopejs/interface-database").SchemaInstance<any>): {
4
+ readonly table: import("@antelopejs/interface-database").Table<MarketingImport>;
5
+ readonly database: import("@antelopejs/interface-database").SchemaInstance<any>;
6
+ get(id: string): PromiseLike<MarketingImport | undefined>;
7
+ getBy(index: keyof MarketingImport, ...keys: any[]): PromiseLike<MarketingImport[]>;
8
+ getAll(): PromiseLike<MarketingImport[]>;
9
+ insert(obj: import("@antelopejs/interface-database-decorators").DeepPartial<MarketingImport> | import("@antelopejs/interface-database-decorators").DeepPartial<MarketingImport>[], options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<string[]>;
10
+ update(id: string, obj: import("@antelopejs/interface-database-decorators").DeepPartial<MarketingImport>, options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<number>;
11
+ update(obj: import("@antelopejs/interface-database-decorators").DeepPartial<MarketingImport>, options?: import("@antelopejs/interface-database-decorators").ValidateOptions): Promise<number>;
12
+ delete(id: string): Promise<number>;
13
+ };
14
+ readonly schemaName: string;
15
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MarketingImport>;
16
+ fromPlainData(obj: any): MarketingImport;
17
+ fromDatabase(obj: any): MarketingImport | undefined;
18
+ toDatabase(obj: any): Record<string, any>;
19
+ validate(obj: unknown, options?: import("@antelopejs/interface-database-decorators").ValidateInputOptions): import("@antelopejs/interface-database-decorators").ValidationResult;
20
+ };
21
+ export declare class MarketingImportsModel extends MarketingImportsModel_base {
22
+ listByWebsite(websiteId: string): Promise<MarketingImport[]>;
23
+ getImportedThroughByWebsites(websiteIds: string[]): Promise<Map<string, number>>;
24
+ releaseClaim(id: string, token: string): Promise<void>;
25
+ deleteByWebsite(websiteId: string): Promise<number>;
26
+ }
27
+ export {};
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MarketingImportsModel = void 0;
4
+ const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
5
+ const marketing_imports_table_1 = require("../tables/marketing_imports.table");
6
+ class MarketingImportsModel extends (0, interface_database_decorators_1.BasicDataModel)(marketing_imports_table_1.MarketingImport, marketing_imports_table_1.marketingImportsTableName) {
7
+ async listByWebsite(websiteId) {
8
+ return this.table.getAll(websiteId, "websiteId").run();
9
+ }
10
+ async getImportedThroughByWebsites(websiteIds) {
11
+ const rows = await Promise.all(websiteIds.map((websiteId) => this.listByWebsite(websiteId)));
12
+ const through = new Map();
13
+ for (const row of rows.flat()) {
14
+ if (row.status === "done" && row.importedThrough) {
15
+ const at = new Date(row.importedThrough).getTime();
16
+ through.set(row.websiteId, Math.max(through.get(row.websiteId) ?? at, at));
17
+ }
18
+ }
19
+ return through;
20
+ }
21
+ async releaseClaim(id, token) {
22
+ const outcome = await this.table
23
+ .atomicMutation(id, {
24
+ type: "deleteIfEqual",
25
+ field: "claimToken",
26
+ expectedValue: token,
27
+ })
28
+ .run();
29
+ if (outcome !== "applied") {
30
+ throw new Error("Analytics import claim release was not confirmed");
31
+ }
32
+ }
33
+ async deleteByWebsite(websiteId) {
34
+ return this.table.getAll(websiteId, "websiteId").delete().run();
35
+ }
36
+ }
37
+ exports.MarketingImportsModel = MarketingImportsModel;
38
+ //# sourceMappingURL=marketing_imports.model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketing_imports.model.js","sourceRoot":"","sources":["../../../src/db/models/marketing_imports.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,+EAG2C;AAE3C,MAAa,qBAAsB,SAAQ,IAAA,8CAAc,EACvD,yCAAe,EACf,mDAAyB,CAC1B;IACC,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,GAAG,EAAE,CAAC;IACzD,CAAC;IAOD,KAAK,CAAC,4BAA4B,CAChC,UAAoB;QAEpB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAC7D,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;gBACjD,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE,CAAC;gBACnD,OAAO,CAAC,GAAG,CACT,GAAG,CAAC,SAAS,EACb,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAC/C,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAGD,KAAK,CAAC,YAAY,CAAC,EAAU,EAAE,KAAa;QAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK;aAC7B,cAAc,CAAC,EAAE,EAAE;YAClB,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,YAAY;YACnB,aAAa,EAAE,KAAK;SACrB,CAAC;aACD,GAAG,EAAE,CAAC;QACT,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IAClE,CAAC;CACF;AAjDD,sDAiDC"}
@@ -12,6 +12,7 @@ declare const MarketingPreferencesModel_base: {
12
12
  delete(id: string): Promise<number>;
13
13
  };
14
14
  readonly schemaName: string;
15
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MarketingPreferences>;
15
16
  fromPlainData(obj: any): MarketingPreferences;
16
17
  fromDatabase(obj: any): MarketingPreferences | undefined;
17
18
  toDatabase(obj: any): Record<string, any>;
@@ -18,6 +18,7 @@ declare const MarketingSessionsModel_base: {
18
18
  delete(id: string): Promise<number>;
19
19
  };
20
20
  readonly schemaName: string;
21
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MarketingSession>;
21
22
  fromPlainData(obj: any): MarketingSession;
22
23
  fromDatabase(obj: any): MarketingSession | undefined;
23
24
  toDatabase(obj: any): Record<string, any>;
@@ -13,6 +13,7 @@ declare const MarketingSettingsModel_base: {
13
13
  delete(id: string): Promise<number>;
14
14
  };
15
15
  readonly schemaName: string;
16
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<MarketingSettings>;
16
17
  fromPlainData(obj: any): MarketingSettings;
17
18
  fromDatabase(obj: any): MarketingSettings | undefined;
18
19
  toDatabase(obj: any): Record<string, any>;
@@ -12,6 +12,7 @@ declare const PageSnapshotsModel_base: {
12
12
  delete(id: string): Promise<number>;
13
13
  };
14
14
  readonly schemaName: string;
15
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<PageSnapshot>;
15
16
  fromPlainData(obj: any): PageSnapshot;
16
17
  fromDatabase(obj: any): PageSnapshot | undefined;
17
18
  toDatabase(obj: any): Record<string, any>;
@@ -21,6 +22,7 @@ export declare class PageSnapshotsModel extends PageSnapshotsModel_base {
21
22
  getByPath(websiteId: string, url: string): Promise<PageSnapshot[]>;
22
23
  countByWebsite(websiteId: string, url?: string): Promise<number>;
23
24
  upsert(row: PageSnapshot): Promise<void>;
25
+ replaceIfUnchanged(expected: PageSnapshot, patch: Partial<PageSnapshot>): Promise<boolean>;
24
26
  deleteByWebsite(websiteId: string, url?: string): Promise<number>;
25
27
  deleteOlderThan(cutoff: Date): Promise<number>;
26
28
  private byWebsite;
@@ -17,6 +17,20 @@ class PageSnapshotsModel extends (0, interface_database_decorators_1.BasicDataMo
17
17
  async upsert(row) {
18
18
  await this.table.insert(row, { conflict: "replace" }).run();
19
19
  }
20
+ async replaceIfUnchanged(expected, patch) {
21
+ const count = await this.table
22
+ .filter((row) => row
23
+ .key("_id")
24
+ .eq(expected._id)
25
+ .and(row.key("capturedAt").eq(expected.capturedAt))
26
+ .and(row
27
+ .key("declaredHash")
28
+ .default("")
29
+ .eq(expected.declaredHash ?? "")))
30
+ .update(PageSnapshotsModel.toDatabase(patch))
31
+ .run();
32
+ return count === 1;
33
+ }
20
34
  async deleteByWebsite(websiteId, url) {
21
35
  return this.byWebsite(websiteId, url).delete().run();
22
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"page_snapshots.model.js","sourceRoot":"","sources":["../../../src/db/models/page_snapshots.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,yEAGwC;AACxC,2CAAgD;AAEhD,MAAa,kBAAmB,SAAQ,IAAA,8CAAc,EACpD,mCAAY,EACZ,6CAAsB,CACvB;IAEC,KAAK,CAAC,SAAS,CAAC,SAAiB,EAAE,GAAW;QAC5C,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC;aAChC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;aACvC,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,GAAY;QAClD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAiB;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB,EAAE,GAAY;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAY;QAChC,OAAO,IAAA,6BAAiB,EAAC,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAGO,SAAS,CAAC,SAAiB,EAAE,GAAY;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;QACzD,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,CAAC;CACF;AAlCD,gDAkCC"}
1
+ {"version":3,"file":"page_snapshots.model.js","sourceRoot":"","sources":["../../../src/db/models/page_snapshots.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,yEAGwC;AACxC,2CAAgD;AAEhD,MAAa,kBAAmB,SAAQ,IAAA,8CAAc,EACpD,mCAAY,EACZ,6CAAsB,CACvB;IAEC,KAAK,CAAC,SAAS,CAAC,SAAiB,EAAE,GAAW;QAC5C,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC;aAChC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;aACvC,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,GAAY;QAClD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAiB;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IAC9D,CAAC;IAOD,KAAK,CAAC,kBAAkB,CACtB,QAAsB,EACtB,KAA4B;QAE5B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK;aAC3B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACd,GAAG;aACA,GAAG,CAAC,KAAK,CAAC;aACV,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;aAChB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;aAClD,GAAG,CACF,GAAG;aACA,GAAG,CAAC,cAAc,CAAC;aACnB,OAAO,CAAC,EAAE,CAAC;aACX,EAAE,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CACnC,CACJ;aACA,MAAM,CAAC,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;aAC5C,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAiB,EAAE,GAAY;QACnD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,MAAY;QAChC,OAAO,IAAA,6BAAiB,EAAC,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAGO,SAAS,CAAC,SAAiB,EAAE,GAAY;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;QACzD,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,CAAC;CACF;AA7DD,gDA6DC"}
@@ -16,6 +16,7 @@ declare const WebsiteStatisticsModel_base: {
16
16
  delete(id: string): Promise<number>;
17
17
  };
18
18
  readonly schemaName: string;
19
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<WebsiteStatistics>;
19
20
  fromPlainData(obj: any): WebsiteStatistics;
20
21
  fromDatabase(obj: any): WebsiteStatistics | undefined;
21
22
  toDatabase(obj: any): Record<string, any>;
@@ -12,12 +12,14 @@ declare const WebsitesModel_base: {
12
12
  delete(id: string): Promise<number>;
13
13
  };
14
14
  readonly schemaName: string;
15
+ readonly tableClass: import("@antelopejs/interface-database-decorators").Constructible<Website>;
15
16
  fromPlainData(obj: any): Website;
16
17
  fromDatabase(obj: any): Website | undefined;
17
18
  toDatabase(obj: any): Record<string, any>;
18
19
  validate(obj: unknown, options?: import("@antelopejs/interface-database-decorators").ValidateInputOptions): import("@antelopejs/interface-database-decorators").ValidationResult;
19
20
  };
20
21
  export declare class WebsitesModel extends WebsitesModel_base {
22
+ compareDeclared(expected: Website, patch: Partial<Website>): Promise<boolean>;
21
23
  getByTenant(tenantId: string): Promise<Website[]>;
22
24
  listIdsByTenant(tenantId: string): Promise<string[]>;
23
25
  listTenantIds(): Promise<string[]>;
@@ -4,6 +4,35 @@ exports.WebsitesModel = void 0;
4
4
  const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
5
5
  const websites_table_1 = require("../tables/websites.table");
6
6
  class WebsitesModel extends (0, interface_database_decorators_1.BasicDataModel)(websites_table_1.Website, websites_table_1.websitesTableName) {
7
+ async compareDeclared(expected, patch) {
8
+ const count = await this.table
9
+ .filter((row) => row
10
+ .key("_id")
11
+ .eq(expected._id)
12
+ .and(row.key("tenantId").eq(expected.tenantId))
13
+ .and(row.key("name").eq(expected.name))
14
+ .and(row.key("domain").eq(expected.domain))
15
+ .and(row
16
+ .key("extraDomains")
17
+ .default([])
18
+ .eq(expected.extraDomains ?? []))
19
+ .and(row.key("trackingEnabled").eq(expected.trackingEnabled))
20
+ .and(row
21
+ .key("snapshotsEnabled")
22
+ .default(false)
23
+ .eq(expected.snapshotsEnabled ?? false))
24
+ .and(row
25
+ .key("snapshotMaskText")
26
+ .default(false)
27
+ .eq(expected.snapshotMaskText ?? false))
28
+ .and(row
29
+ .key("declaredHash")
30
+ .default("")
31
+ .eq(expected.declaredHash ?? "")))
32
+ .update(WebsitesModel.toDatabase({ ...patch, updatedAt: new Date() }))
33
+ .run();
34
+ return count === 1;
35
+ }
7
36
  async getByTenant(tenantId) {
8
37
  return this.table
9
38
  .getAll(tenantId, "tenantId")
@@ -1 +1 @@
1
- {"version":3,"file":"websites.model.js","sourceRoot":"","sources":["../../../src/db/models/websites.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,6DAAsE;AAEtE,MAAa,aAAc,SAAQ,IAAA,8CAAc,EAAC,wBAAO,EAAE,kCAAiB,CAAC;IAC3E,KAAK,CAAC,WAAW,CAAC,QAAgB;QAChC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC;aAC5B,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC;aAC5B,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,eAAe,CAAC,QAAgB;QACpC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC;aAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;aAC5B,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IAC/D,CAAC;CACF;AAvBD,sCAuBC"}
1
+ {"version":3,"file":"websites.model.js","sourceRoot":"","sources":["../../../src/db/models/websites.model.ts"],"names":[],"mappings":";;;AAAA,6FAA2E;AAC3E,6DAAsE;AAEtE,MAAa,aAAc,SAAQ,IAAA,8CAAc,EAAC,wBAAO,EAAE,kCAAiB,CAAC;IAE3E,KAAK,CAAC,eAAe,CACnB,QAAiB,EACjB,KAAuB;QAEvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK;aAC3B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACd,GAAG;aACA,GAAG,CAAC,KAAK,CAAC;aACV,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;aAChB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aAC9C,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;aACtC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aAC1C,GAAG,CACF,GAAG;aACA,GAAG,CAAC,cAAc,CAAC;aACnB,OAAO,CAAC,EAAE,CAAC;aACX,EAAE,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CACnC;aACA,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;aAC5D,GAAG,CACF,GAAG;aACA,GAAG,CAAC,kBAAkB,CAAC;aACvB,OAAO,CAAC,KAAK,CAAC;aACd,EAAE,CAAC,QAAQ,CAAC,gBAAgB,IAAI,KAAK,CAAC,CAC1C;aACA,GAAG,CACF,GAAG;aACA,GAAG,CAAC,kBAAkB,CAAC;aACvB,OAAO,CAAC,KAAK,CAAC;aACd,EAAE,CAAC,QAAQ,CAAC,gBAAgB,IAAI,KAAK,CAAC,CAC1C;aACA,GAAG,CACF,GAAG;aACA,GAAG,CAAC,cAAc,CAAC;aACnB,OAAO,CAAC,EAAE,CAAC;aACX,EAAE,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CACnC,CACJ;aACA,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;aACrE,GAAG,EAAE,CAAC;QACT,OAAO,KAAK,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,QAAgB;QAChC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC;aAC5B,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC;aAC5B,GAAG,EAAE,CAAC;IACX,CAAC;IAGD,KAAK,CAAC,eAAe,CAAC,QAAgB;QACpC,OAAO,IAAI,CAAC,KAAK;aACd,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC;aAC5B,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;aAC5B,GAAG,EAAE,CAAC;IACX,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC;IAC/D,CAAC;CACF;AAnED,sCAmEC"}
@@ -26,6 +26,7 @@ export declare class Funnel extends Table {
26
26
  steps: FunnelStep[];
27
27
  conversionWindowMs: number;
28
28
  experiment: FunnelExperiment | null;
29
+ declaredHash?: string;
29
30
  createdAt: Date;
30
31
  updatedAt: Date;
31
32
  }