@happyvertical/smrt-analytics 0.55.6 → 0.55.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +100 -78
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +258 -26
- package/dist/models/AnalyticsProperty.d.ts.map +1 -1
- package/dist/models/AnalyticsReport.d.ts.map +1 -1
- package/dist/recipes.d.ts +98 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +195 -27
- package/package.json +9 -8
package/dist/smrt-knowledge.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"sensitiveFieldsExcluded": true,
|
|
4
4
|
"generatedAt": "1970-01-01T00:00:00.000Z",
|
|
5
5
|
"packageName": "@happyvertical/smrt-analytics",
|
|
6
|
-
"packageVersion": "0.55.
|
|
6
|
+
"packageVersion": "0.55.8",
|
|
7
7
|
"sourceManifestPath": "dist/manifest.json",
|
|
8
8
|
"agentDocPath": "AGENTS.md",
|
|
9
9
|
"sourceHashes": {
|
|
10
|
-
"manifest": "
|
|
11
|
-
"packageJson": "
|
|
12
|
-
"agents": "
|
|
10
|
+
"manifest": "13e06c4864f1626ead1e71361caa78ea17d36759f93466401b89777a837f7840",
|
|
11
|
+
"packageJson": "555165f082315ad6ae43e615f456c7b855b2a1faf9d3275c2646926427eef1fd",
|
|
12
|
+
"agents": "52d16fea29774e6ac50038329c1c644f2f25d2b02df4fc75c96effe7ce477713"
|
|
13
13
|
},
|
|
14
14
|
"exports": [
|
|
15
15
|
".",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@happyvertical/smrt-tenancy": "workspace:*",
|
|
26
26
|
"@happyvertical/smrt-types": "workspace:*",
|
|
27
27
|
"@happyvertical/smrt-ui": "workspace:*",
|
|
28
|
+
"@happyvertical/smrt-scanner": "workspace:*",
|
|
28
29
|
"@happyvertical/smrt-vitest": "workspace:*",
|
|
29
30
|
"@happyvertical/sql": "catalog:",
|
|
30
31
|
"@sveltejs/package": "^2.5.8",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
"smrtDependencies": [
|
|
40
41
|
"@happyvertical/smrt-core",
|
|
41
42
|
"@happyvertical/smrt-prompts",
|
|
43
|
+
"@happyvertical/smrt-scanner",
|
|
42
44
|
"@happyvertical/smrt-tenancy",
|
|
43
45
|
"@happyvertical/smrt-types",
|
|
44
46
|
"@happyvertical/smrt-ui",
|
|
@@ -1524,6 +1526,7 @@
|
|
|
1524
1526
|
"name": "AnalyticsDataStream",
|
|
1525
1527
|
"qualifiedName": "@happyvertical/smrt-analytics:AnalyticsDataStream",
|
|
1526
1528
|
"collection": "analyticsdatastreams",
|
|
1529
|
+
"description": "AnalyticsDataStream represents a data stream (web, iOS, Android) for an analytics property.",
|
|
1527
1530
|
"tableName": "analytics_data_streams",
|
|
1528
1531
|
"packageName": "@happyvertical/smrt-analytics",
|
|
1529
1532
|
"extends": "SmrtObject",
|
|
@@ -1826,6 +1829,7 @@
|
|
|
1826
1829
|
"name": "AnalyticsEvent",
|
|
1827
1830
|
"qualifiedName": "@happyvertical/smrt-analytics:AnalyticsEvent",
|
|
1828
1831
|
"collection": "analyticsevents",
|
|
1832
|
+
"description": "AnalyticsEvent represents a tracked analytics event (server-side tracking log).",
|
|
1829
1833
|
"tableName": "analytics_events",
|
|
1830
1834
|
"packageName": "@happyvertical/smrt-analytics",
|
|
1831
1835
|
"extends": "SmrtObject",
|
|
@@ -2258,6 +2262,7 @@
|
|
|
2258
2262
|
"qualifiedName": "@happyvertical/smrt-analytics:AnalyticsProperty",
|
|
2259
2263
|
"displayLabelField": "name",
|
|
2260
2264
|
"collection": "analyticsproperties",
|
|
2265
|
+
"description": "AnalyticsProperty represents an analytics property (GA4 property, Plausible site, or Matomo site).",
|
|
2261
2266
|
"tableName": "analytics_properties",
|
|
2262
2267
|
"packageName": "@happyvertical/smrt-analytics",
|
|
2263
2268
|
"extends": "SmrtObject",
|
|
@@ -2292,48 +2297,55 @@
|
|
|
2292
2297
|
"type": "text",
|
|
2293
2298
|
"required": false,
|
|
2294
2299
|
"columnType": "TEXT",
|
|
2295
|
-
"default": ""
|
|
2300
|
+
"default": "",
|
|
2301
|
+
"description": "The name people see for this website or app."
|
|
2296
2302
|
},
|
|
2297
2303
|
{
|
|
2298
2304
|
"name": "provider",
|
|
2299
2305
|
"type": "text",
|
|
2300
2306
|
"required": false,
|
|
2301
|
-
"columnType": "TEXT"
|
|
2307
|
+
"columnType": "TEXT",
|
|
2308
|
+
"description": "Which analytics service tracks it: Google Analytics 4, Plausible or Matomo."
|
|
2302
2309
|
},
|
|
2303
2310
|
{
|
|
2304
2311
|
"name": "externalId",
|
|
2305
2312
|
"type": "text",
|
|
2306
2313
|
"required": false,
|
|
2307
2314
|
"columnType": "TEXT",
|
|
2308
|
-
"default": ""
|
|
2315
|
+
"default": "",
|
|
2316
|
+
"description": "The id the analytics service gives the property, such as properties/123456789 or a Matomo site id."
|
|
2309
2317
|
},
|
|
2310
2318
|
{
|
|
2311
2319
|
"name": "measurementId",
|
|
2312
2320
|
"type": "text",
|
|
2313
2321
|
"required": false,
|
|
2314
2322
|
"columnType": "TEXT",
|
|
2315
|
-
"default": ""
|
|
2323
|
+
"default": "",
|
|
2324
|
+
"description": "The tracking id for a web stream, such as G-XXXXXXXXXX."
|
|
2316
2325
|
},
|
|
2317
2326
|
{
|
|
2318
2327
|
"name": "siteDomain",
|
|
2319
2328
|
"type": "text",
|
|
2320
2329
|
"required": false,
|
|
2321
2330
|
"columnType": "TEXT",
|
|
2322
|
-
"default": ""
|
|
2331
|
+
"default": "",
|
|
2332
|
+
"description": "The website address, used by Plausible and Matomo."
|
|
2323
2333
|
},
|
|
2324
2334
|
{
|
|
2325
2335
|
"name": "timeZone",
|
|
2326
2336
|
"type": "text",
|
|
2327
2337
|
"required": false,
|
|
2328
2338
|
"columnType": "TEXT",
|
|
2329
|
-
"default": "America/Los_Angeles"
|
|
2339
|
+
"default": "America/Los_Angeles",
|
|
2340
|
+
"description": "The time zone the figures are reported in."
|
|
2330
2341
|
},
|
|
2331
2342
|
{
|
|
2332
2343
|
"name": "currencyCode",
|
|
2333
2344
|
"type": "text",
|
|
2334
2345
|
"required": false,
|
|
2335
2346
|
"columnType": "TEXT",
|
|
2336
|
-
"default": "USD"
|
|
2347
|
+
"default": "USD",
|
|
2348
|
+
"description": "The currency used for revenue figures, such as USD."
|
|
2337
2349
|
},
|
|
2338
2350
|
{
|
|
2339
2351
|
"name": "industryCategory",
|
|
@@ -2353,13 +2365,15 @@
|
|
|
2353
2365
|
"name": "status",
|
|
2354
2366
|
"type": "text",
|
|
2355
2367
|
"required": false,
|
|
2356
|
-
"columnType": "TEXT"
|
|
2368
|
+
"columnType": "TEXT",
|
|
2369
|
+
"description": "Whether tracking is active, inactive or still being set up."
|
|
2357
2370
|
},
|
|
2358
2371
|
{
|
|
2359
2372
|
"name": "lastSyncAt",
|
|
2360
2373
|
"type": "datetime",
|
|
2361
2374
|
"required": false,
|
|
2362
|
-
"columnType": "TIMESTAMP"
|
|
2375
|
+
"columnType": "TIMESTAMP",
|
|
2376
|
+
"description": "When the property was last synced with the analytics service."
|
|
2363
2377
|
}
|
|
2364
2378
|
],
|
|
2365
2379
|
"relationships": [],
|
|
@@ -2620,6 +2634,7 @@
|
|
|
2620
2634
|
"qualifiedName": "@happyvertical/smrt-analytics:AnalyticsReport",
|
|
2621
2635
|
"displayLabelField": "name",
|
|
2622
2636
|
"collection": "analyticsreports",
|
|
2637
|
+
"description": "AnalyticsReport represents a saved report configuration with optional scheduling.",
|
|
2623
2638
|
"tableName": "analytics_reports",
|
|
2624
2639
|
"packageName": "@happyvertical/smrt-analytics",
|
|
2625
2640
|
"extends": "SmrtObject",
|
|
@@ -2647,49 +2662,56 @@
|
|
|
2647
2662
|
"type": "foreignKey",
|
|
2648
2663
|
"required": false,
|
|
2649
2664
|
"related": "AnalyticsProperty",
|
|
2650
|
-
"columnType": "UUID"
|
|
2665
|
+
"columnType": "UUID",
|
|
2666
|
+
"description": "The website or app this report is about."
|
|
2651
2667
|
},
|
|
2652
2668
|
{
|
|
2653
2669
|
"name": "name",
|
|
2654
2670
|
"type": "text",
|
|
2655
2671
|
"required": false,
|
|
2656
2672
|
"columnType": "TEXT",
|
|
2657
|
-
"default": ""
|
|
2673
|
+
"default": "",
|
|
2674
|
+
"description": "A short name for the report, such as Weekly traffic."
|
|
2658
2675
|
},
|
|
2659
2676
|
{
|
|
2660
2677
|
"name": "description",
|
|
2661
2678
|
"type": "text",
|
|
2662
2679
|
"required": false,
|
|
2663
2680
|
"columnType": "TEXT",
|
|
2664
|
-
"default": ""
|
|
2681
|
+
"default": "",
|
|
2682
|
+
"description": "What the report is for."
|
|
2665
2683
|
},
|
|
2666
2684
|
{
|
|
2667
2685
|
"name": "dimensions",
|
|
2668
2686
|
"type": "text",
|
|
2669
2687
|
"required": false,
|
|
2670
2688
|
"columnType": "TEXT",
|
|
2671
|
-
"default": "[]"
|
|
2689
|
+
"default": "[]",
|
|
2690
|
+
"description": "What to break the figures down by, such as country or device, as a list."
|
|
2672
2691
|
},
|
|
2673
2692
|
{
|
|
2674
2693
|
"name": "metrics",
|
|
2675
2694
|
"type": "text",
|
|
2676
2695
|
"required": false,
|
|
2677
2696
|
"columnType": "TEXT",
|
|
2678
|
-
"default": "[]"
|
|
2697
|
+
"default": "[]",
|
|
2698
|
+
"description": "What to count, such as active users or sessions, as a list."
|
|
2679
2699
|
},
|
|
2680
2700
|
{
|
|
2681
2701
|
"name": "dateRangeStart",
|
|
2682
2702
|
"type": "text",
|
|
2683
2703
|
"required": false,
|
|
2684
2704
|
"columnType": "TEXT",
|
|
2685
|
-
"default": "7daysAgo"
|
|
2705
|
+
"default": "7daysAgo",
|
|
2706
|
+
"description": "Where the period begins: a date, or a relative value such as 7daysAgo."
|
|
2686
2707
|
},
|
|
2687
2708
|
{
|
|
2688
2709
|
"name": "dateRangeEnd",
|
|
2689
2710
|
"type": "text",
|
|
2690
2711
|
"required": false,
|
|
2691
2712
|
"columnType": "TEXT",
|
|
2692
|
-
"default": "today"
|
|
2713
|
+
"default": "today",
|
|
2714
|
+
"description": "Where the period ends: a date, or a relative value such as today."
|
|
2693
2715
|
},
|
|
2694
2716
|
{
|
|
2695
2717
|
"name": "dimensionFilter",
|
|
@@ -2723,25 +2745,29 @@
|
|
|
2723
2745
|
"name": "status",
|
|
2724
2746
|
"type": "text",
|
|
2725
2747
|
"required": false,
|
|
2726
|
-
"columnType": "TEXT"
|
|
2748
|
+
"columnType": "TEXT",
|
|
2749
|
+
"description": "Where the report stands: draft, scheduled, running, completed or failed."
|
|
2727
2750
|
},
|
|
2728
2751
|
{
|
|
2729
2752
|
"name": "frequency",
|
|
2730
2753
|
"type": "text",
|
|
2731
2754
|
"required": false,
|
|
2732
|
-
"columnType": "TEXT"
|
|
2755
|
+
"columnType": "TEXT",
|
|
2756
|
+
"description": "How often the report repeats: once, daily, weekly or monthly."
|
|
2733
2757
|
},
|
|
2734
2758
|
{
|
|
2735
2759
|
"name": "lastRunAt",
|
|
2736
2760
|
"type": "datetime",
|
|
2737
2761
|
"required": false,
|
|
2738
|
-
"columnType": "TIMESTAMP"
|
|
2762
|
+
"columnType": "TIMESTAMP",
|
|
2763
|
+
"description": "When the report last ran."
|
|
2739
2764
|
},
|
|
2740
2765
|
{
|
|
2741
2766
|
"name": "nextRunAt",
|
|
2742
2767
|
"type": "datetime",
|
|
2743
2768
|
"required": false,
|
|
2744
|
-
"columnType": "TIMESTAMP"
|
|
2769
|
+
"columnType": "TIMESTAMP",
|
|
2770
|
+
"description": "When the report is next due to run."
|
|
2745
2771
|
},
|
|
2746
2772
|
{
|
|
2747
2773
|
"name": "resultData",
|
|
@@ -2755,14 +2781,16 @@
|
|
|
2755
2781
|
"type": "integer",
|
|
2756
2782
|
"required": false,
|
|
2757
2783
|
"columnType": "INTEGER",
|
|
2758
|
-
"default": 0
|
|
2784
|
+
"default": 0,
|
|
2785
|
+
"description": "How many rows the last run returned."
|
|
2759
2786
|
},
|
|
2760
2787
|
{
|
|
2761
2788
|
"name": "lastError",
|
|
2762
2789
|
"type": "text",
|
|
2763
2790
|
"required": false,
|
|
2764
2791
|
"columnType": "TEXT",
|
|
2765
|
-
"default": ""
|
|
2792
|
+
"default": "",
|
|
2793
|
+
"description": "Why the last run failed. Empty when it worked."
|
|
2766
2794
|
}
|
|
2767
2795
|
],
|
|
2768
2796
|
"relationships": [
|
|
@@ -4629,5 +4657,145 @@
|
|
|
4629
4657
|
"polymorphicAssociations": 0,
|
|
4630
4658
|
"uuidColumns": 22
|
|
4631
4659
|
},
|
|
4632
|
-
"agentDoc": "# @happyvertical/smrt-analytics\n\nGA4/Plausible/Matomo analytics integration with server-side event tracking and AI-powered reporting.\n\n## Models\n\n- **AnalyticsProperty**: GA4 property, Plausible site, or Matomo site. Provider metadata, status, sync timestamps.\n- **AnalyticsDataStream**: web/iOS/Android data streams. Measurement IDs (G-XXXXXX for GA4, Firebase app ID for mobile).\n- **AnalyticsEvent**: tracked events with params (JSON), retry queue, conversion classification.\n- **AnalyticsReport**: saved report configs with dimensions/metrics (JSON), scheduling (daily/weekly/monthly), result caching.\n\nAll models use STI (`tableStrategy: 'sti'`).\n\nAll four persisted models are `@TenantScoped({ mode: 'optional' })` with a nullable `tenantId` (#1410), matching the framework convention used by sibling domain packages (ads/jobs/commerce). **When a tenant context is active**, the interceptor auto-filters `list`/`get`/raw reads and binds `create`/`save` writes to that tenant, keeping one tenant's properties, data streams, events (end-user PII), and cached report rows from reaching another. Because the mode is `optional`, the interceptor intentionally passes **through unfiltered when no tenant context is established** — so a surface is only safe insofar as it establishes tenant context first. Generated SvelteKit routes do this (#1540); the CLI/MCP code paths do not yet, which is a known framework-level residual (fail-closed tenant context for CLI/MCP) tracked outside this package, not an analytics-specific gap.\n\n## Key Collection Methods\n\n`findByExternalId()`, `findByProvider()`, `findNeedingSync()`, `findGA4Properties()`, `findPlausibleSites()`, `findMatomoSites()`\n\n## AI Operations\n\n`analyzePerformance()`, `analyzeResults()`, `hasPositiveTrends()` route through registered prompts (see Prompt Registry below) so tenant overrides apply via `resolvePrompt()`. `isPerformingWell()` still uses the inline `is()` shortcut.\n\n## Prompt Registry\n\nThree prompts are registered at module-load time via `definePrompt()` from `@happyvertical/smrt-prompts`. Re-exported from the package root for tenant override targeting.\n\n| Key | Method | Variables (PII-conscious) |\n|-----|--------|---------------------------|\n| `smrtAnalytics.property.analyzePerformance` | `AnalyticsProperty.analyzePerformance()` | `period`, `propertyDisplayName`, `propertyProvider` |\n| `smrtAnalytics.report.analyzeResults` | `AnalyticsReport.analyzeResults()` | `reportName`, `reportDimensions`, `reportMetrics`, `dateRangeStart`, `dateRangeEnd`, `rowCount`, `reportData` |\n| `smrtAnalytics.report.hasPositiveTrends` | `AnalyticsReport.hasPositiveTrends()` | `reportMetrics`, `reportData` |\n\n**Excluded from variables (never reach the AI provider):**\n- `id`, `propertyId`, `tenantId`, `externalId` — internal foreign keys / UUIDs that link to identifying records.\n- `apiSecret`, `measurementId`, `siteDomain` — provider credentials and platform-specific identifiers (GA4 API secrets, Matomo `idSite`, custom `G-XXXX` measurement IDs).\n- `providerMetadata` — extensible JSON blob that may contain credentials, account IDs, or other configuration secrets.\n- `lastError`, raw `dimensionFilter` / `metricFilter` JSON — internal error strings (may contain auth tokens) and filter expressions that may reference cookie IDs, user-pseudo-IDs, or IP-derived geos.\n\n**`resultData` is forwarded verbatim.** This package does not — and cannot — strip PII from result rows, because the row schema is determined by the dimensions/metrics the caller asked the analytics provider to return. If the caller persists rows containing a `userPseudoId`, `clientId`, IP-derived geolocation, or any other identifier, those fields WILL reach the AI provider. Callers must:\n\n- exclude PII-bearing dimensions before persisting `resultData` (e.g. don't request `userPseudoId` as a GA4 dimension), or\n- apply a column allowlist at the call site before invoking `analyzeResults()`, or\n- override the prompt template via `PromptOverride` to redact rows.\n\nThe forwarding contract is pinned by a regression test in `src/__tests__/analytics-report-prompt.test.ts`.\n\nSee `src/prompts.ts` for the full rationale block.\n\n## UI Registry\n\nSvelte components live in `src/svelte/` and auto-register with `ModuleUIRegistry` from `@happyvertical/smrt-ui/registry` when `@happyvertical/smrt-analytics/svelte` is imported.\n\nSlot declarations live in `src/ui.ts`, exported via the `./ui` package subpath. The slots are: `analytics-summary`, `events-table`, `property-info`, `property-status-badge`, `stat-card`, `trend-badge` (see `ANALYTICS_UI_SLOTS` for icons / categories / display order).\n\n```typescript\nimport '@happyvertical/smrt-analytics/svelte'; // side-effect: registers slots\nimport { ModuleUIRegistry } from '@happyvertical/smrt-ui/registry';\nconst StatCard = ModuleUIRegistry.get('@happyvertical/smrt-analytics', 'stat-card');\n```\n\n## Gotchas\n\n- **`apiSecret` / `providerMetadata` stripped from API/MCP**: marked `@field({ sensitive: true })` (#1540) so they never appear in generated responses or `where` filters. They are still stored at rest **unencrypted** — envelope encryption via `@happyvertical/smrt-secrets` is a separate, breaking follow-up (tracked, not done here).\n- **Event retry at model level**: no background job integration\n- **JSON fields**: params, dimensions, metrics use string storage with getter/setter helpers\n- **Measurement IDs differ per platform**: G-XXXXXX (GA4 web) vs Firebase app ID (mobile)\n- **`analyzeResults` is now a single AI call**: previously issued a second freeform `do()` to summarize \"top 3 insights\"; folded into the registered template, and `insights` mirrors `analysis` in the response shape.\n"
|
|
4660
|
+
"agentDoc": "# @happyvertical/smrt-analytics\n\nGA4/Plausible/Matomo analytics integration with server-side event tracking and AI-powered reporting.\n\n## Models\n\n- **AnalyticsProperty**: GA4 property, Plausible site, or Matomo site. Provider metadata, status, sync timestamps.\n- **AnalyticsDataStream**: web/iOS/Android data streams. Measurement IDs (G-XXXXXX for GA4, Firebase app ID for mobile).\n- **AnalyticsEvent**: tracked events with params (JSON), retry queue, conversion classification.\n- **AnalyticsReport**: saved report configs with dimensions/metrics (JSON), scheduling (daily/weekly/monthly), result caching.\n\nAll models use STI (`tableStrategy: 'sti'`).\n\nAll four persisted models are `@TenantScoped({ mode: 'optional' })` with a nullable `tenantId` (#1410), matching the framework convention used by sibling domain packages (ads/jobs/commerce). **When a tenant context is active**, the interceptor auto-filters `list`/`get`/raw reads and binds `create`/`save` writes to that tenant, keeping one tenant's properties, data streams, events (end-user PII), and cached report rows from reaching another. Because the mode is `optional`, the interceptor intentionally passes **through unfiltered when no tenant context is established** — so a surface is only safe insofar as it establishes tenant context first. Generated SvelteKit routes do this (#1540); the CLI/MCP code paths do not yet, which is a known framework-level residual (fail-closed tenant context for CLI/MCP) tracked outside this package, not an analytics-specific gap.\n\n## Key Collection Methods\n\n`findByExternalId()`, `findByProvider()`, `findNeedingSync()`, `findGA4Properties()`, `findPlausibleSites()`, `findMatomoSites()`\n\n## AI Operations\n\n`analyzePerformance()`, `analyzeResults()`, `hasPositiveTrends()` route through registered prompts (see Prompt Registry below) so tenant overrides apply via `resolvePrompt()`. `isPerformingWell()` still uses the inline `is()` shortcut.\n\n## Prompt Registry\n\nThree prompts are registered at module-load time via `definePrompt()` from `@happyvertical/smrt-prompts`. Re-exported from the package root for tenant override targeting.\n\n| Key | Method | Variables (PII-conscious) |\n|-----|--------|---------------------------|\n| `smrtAnalytics.property.analyzePerformance` | `AnalyticsProperty.analyzePerformance()` | `period`, `propertyDisplayName`, `propertyProvider` |\n| `smrtAnalytics.report.analyzeResults` | `AnalyticsReport.analyzeResults()` | `reportName`, `reportDimensions`, `reportMetrics`, `dateRangeStart`, `dateRangeEnd`, `rowCount`, `reportData` |\n| `smrtAnalytics.report.hasPositiveTrends` | `AnalyticsReport.hasPositiveTrends()` | `reportMetrics`, `reportData` |\n\n**Excluded from variables (never reach the AI provider):**\n- `id`, `propertyId`, `tenantId`, `externalId` — internal foreign keys / UUIDs that link to identifying records.\n- `apiSecret`, `measurementId`, `siteDomain` — provider credentials and platform-specific identifiers (GA4 API secrets, Matomo `idSite`, custom `G-XXXX` measurement IDs).\n- `providerMetadata` — extensible JSON blob that may contain credentials, account IDs, or other configuration secrets.\n- `lastError`, raw `dimensionFilter` / `metricFilter` JSON — internal error strings (may contain auth tokens) and filter expressions that may reference cookie IDs, user-pseudo-IDs, or IP-derived geos.\n\n**`resultData` is forwarded verbatim.** This package does not — and cannot — strip PII from result rows, because the row schema is determined by the dimensions/metrics the caller asked the analytics provider to return. If the caller persists rows containing a `userPseudoId`, `clientId`, IP-derived geolocation, or any other identifier, those fields WILL reach the AI provider. Callers must:\n\n- exclude PII-bearing dimensions before persisting `resultData` (e.g. don't request `userPseudoId` as a GA4 dimension), or\n- apply a column allowlist at the call site before invoking `analyzeResults()`, or\n- override the prompt template via `PromptOverride` to redact rows.\n\nThe forwarding contract is pinned by a regression test in `src/__tests__/analytics-report-prompt.test.ts`.\n\nSee `src/prompts.ts` for the full rationale block.\n\n## UI Registry\n\nSvelte components live in `src/svelte/` and auto-register with `ModuleUIRegistry` from `@happyvertical/smrt-ui/registry` when `@happyvertical/smrt-analytics/svelte` is imported.\n\nSlot declarations live in `src/ui.ts`, exported via the `./ui` package subpath. The slots are: `analytics-summary`, `events-table`, `property-info`, `property-status-badge`, `stat-card`, `trend-badge` (see `ANALYTICS_UI_SLOTS` for icons / categories / display order).\n\n```typescript\nimport '@happyvertical/smrt-analytics/svelte'; // side-effect: registers slots\nimport { ModuleUIRegistry } from '@happyvertical/smrt-ui/registry';\nconst StatCard = ModuleUIRegistry.get('@happyvertical/smrt-analytics', 'stat-card');\n```\n\n## Recipes (#3719)\n\n`src/recipes.ts` declares `analytics.reports` (`AnalyticsReportsRecipe`:\n`AnalyticsProperty` and `AnalyticsReport`), emitted into the built\n`manifest.json` and `smrt-knowledge.json` `recipes`. `AnalyticsEvent` and\n`AnalyticsDataStream` are left out: event tracking and stream setup are separate\nconcerns. User-facing help is `src/reports.recipe.md` (#3591); keep it and the\n`@field({ description })` text on the two models in plain language, and never\nreference a `sensitive` field (`apiSecret`, `providerMetadata`) in help.\n\n- **Runtime is `server`.** Reports and their rows live in the app database, the\n AI summary needs a server-held key, and provider credentials sit on the\n property row (so no `providers` are declared: there are no env secrets to name).\n- **One route, deliberately.** `/analytics/summary` mounts\n `@happyvertical/smrt-analytics/svelte#AnalyticsSummary`, which renders an empty\n state without data. `PropertyInfo` and `EventsTable` take required row props\n and throw with none, so they are not routes; a host places them in its own\n pages. Add a route only for a component that survives being mounted bare.\n- **No demo seed.** The playground sample data is module-local, not an export.\n- **What the assistant can do.** Generated MCP exposes `list` and `get` on\n `AnalyticsReport` only; `analyzeResults()` is a model method the app calls,\n not a tool. The help says so and points at `reports.query` (declared reports,\n `@happyvertical/smrt-reports`) and `reports.runtime.*` (runtime reports,\n `@happyvertical/smrt-chat`) for aggregates over the app's own data.\n\n`src/__tests__/recipes.test.ts` runs the real scan, manifest and generation\npasses in memory and renders the help. See\n[core recipes](../core/agents/recipes.md).\n\n## Gotchas\n\n- **`apiSecret` / `providerMetadata` stripped from API/MCP**: marked `@field({ sensitive: true })` (#1540) so they never appear in generated responses or `where` filters. They are still stored at rest **unencrypted** — envelope encryption via `@happyvertical/smrt-secrets` is a separate, breaking follow-up (tracked, not done here).\n- **Event retry at model level**: no background job integration\n- **JSON fields**: params, dimensions, metrics use string storage with getter/setter helpers\n- **Measurement IDs differ per platform**: G-XXXXXX (GA4 web) vs Firebase app ID (mobile)\n- **`analyzeResults` is now a single AI call**: previously issued a second freeform `do()` to summarize \"top 3 insights\"; folded into the registered template, and `insights` mirrors `analysis` in the response shape.\n",
|
|
4661
|
+
"recipes": [
|
|
4662
|
+
{
|
|
4663
|
+
"id": "analytics.reports",
|
|
4664
|
+
"className": "AnalyticsReportsRecipe",
|
|
4665
|
+
"label": "Analytics",
|
|
4666
|
+
"summary": "Save traffic reports for your websites and apps and get a plain-language read on the results.",
|
|
4667
|
+
"synonyms": [
|
|
4668
|
+
"analytics",
|
|
4669
|
+
"traffic",
|
|
4670
|
+
"pageviews",
|
|
4671
|
+
"visitors",
|
|
4672
|
+
"web stats",
|
|
4673
|
+
"google analytics",
|
|
4674
|
+
"plausible",
|
|
4675
|
+
"matomo"
|
|
4676
|
+
],
|
|
4677
|
+
"models": [
|
|
4678
|
+
"@happyvertical/smrt-analytics:AnalyticsProperty",
|
|
4679
|
+
"@happyvertical/smrt-analytics:AnalyticsReport"
|
|
4680
|
+
],
|
|
4681
|
+
"nav": [
|
|
4682
|
+
{
|
|
4683
|
+
"label": "Analytics properties",
|
|
4684
|
+
"model": "@happyvertical/smrt-analytics:AnalyticsProperty",
|
|
4685
|
+
"icon": "home",
|
|
4686
|
+
"description": "The websites and apps you track, and who tracks them."
|
|
4687
|
+
},
|
|
4688
|
+
{
|
|
4689
|
+
"label": "Analytics reports",
|
|
4690
|
+
"model": "@happyvertical/smrt-analytics:AnalyticsReport",
|
|
4691
|
+
"icon": "fileText",
|
|
4692
|
+
"description": "Saved reports, how often they run and what they found."
|
|
4693
|
+
}
|
|
4694
|
+
],
|
|
4695
|
+
"requires": [],
|
|
4696
|
+
"section": {
|
|
4697
|
+
"id": "analytics",
|
|
4698
|
+
"label": "Analytics",
|
|
4699
|
+
"icon": "layers",
|
|
4700
|
+
"description": "How your websites and apps are doing."
|
|
4701
|
+
},
|
|
4702
|
+
"options": {
|
|
4703
|
+
"@happyvertical/smrt-analytics:AnalyticsProperty": {
|
|
4704
|
+
"fields": {
|
|
4705
|
+
"name": {
|
|
4706
|
+
"visibility": "hidden"
|
|
4707
|
+
},
|
|
4708
|
+
"industryCategory": {
|
|
4709
|
+
"visibility": "advanced"
|
|
4710
|
+
},
|
|
4711
|
+
"serviceLevel": {
|
|
4712
|
+
"visibility": "advanced"
|
|
4713
|
+
},
|
|
4714
|
+
"lastSyncAt": {
|
|
4715
|
+
"locked": true
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
},
|
|
4719
|
+
"@happyvertical/smrt-analytics:AnalyticsReport": {
|
|
4720
|
+
"fields": {
|
|
4721
|
+
"dimensionFilter": {
|
|
4722
|
+
"visibility": "advanced"
|
|
4723
|
+
},
|
|
4724
|
+
"metricFilter": {
|
|
4725
|
+
"visibility": "advanced"
|
|
4726
|
+
},
|
|
4727
|
+
"orderBy": {
|
|
4728
|
+
"visibility": "advanced"
|
|
4729
|
+
},
|
|
4730
|
+
"maxResults": {
|
|
4731
|
+
"visibility": "advanced"
|
|
4732
|
+
},
|
|
4733
|
+
"resultData": {
|
|
4734
|
+
"visibility": "hidden",
|
|
4735
|
+
"locked": true
|
|
4736
|
+
},
|
|
4737
|
+
"rowCount": {
|
|
4738
|
+
"locked": true
|
|
4739
|
+
},
|
|
4740
|
+
"lastRunAt": {
|
|
4741
|
+
"locked": true
|
|
4742
|
+
},
|
|
4743
|
+
"nextRunAt": {
|
|
4744
|
+
"locked": true
|
|
4745
|
+
},
|
|
4746
|
+
"lastError": {
|
|
4747
|
+
"locked": true
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
},
|
|
4752
|
+
"help": {
|
|
4753
|
+
"markdown": "## Overview\n\nAnalytics keeps the traffic reports you care about for your websites and apps.\nAdd a property for each site or app you track in Google Analytics 4, Plausible\nor Matomo. Save a report on it that says what to count, what to break the\nfigures down by, and over which dates. The rows each run returns are stored\nwith the report, and the assistant can read them back to you in plain words.\n\nProperties and reports are kept on your server. Provider secrets are stored on\nthe property and never sent to the assistant or returned in responses, but they\nare not encrypted in the database.\n\n## Tasks\n\n### Add a property\n\n1. Create a new property.\n2. Enter the **{field:AnalyticsProperty.displayName}** and pick the\n **{field:AnalyticsProperty.provider}**.\n3. Enter the **{field:AnalyticsProperty.externalId}** the analytics service\n gives it.\n4. For Google Analytics, enter the **{field:AnalyticsProperty.measurementId}**.\n For Plausible or Matomo, enter the **{field:AnalyticsProperty.siteDomain}**.\n5. Keep **{field:AnalyticsProperty.status}** on active while you want it\n tracked.\n\n### Save a report\n\n1. Create a new report and choose the property in\n **{field:AnalyticsReport.propertyId}**.\n2. Give it a **{field:AnalyticsReport.name}**.\n3. List what to break the figures down by in\n **{field:AnalyticsReport.dimensions}** and what to count in\n **{field:AnalyticsReport.metrics}**.\n4. Set the period with **{field:AnalyticsReport.dateRangeStart}** and\n **{field:AnalyticsReport.dateRangeEnd}**.\n5. Pick **{field:AnalyticsReport.frequency}** to repeat it daily, weekly or\n monthly.\n\n### Check what a report found\n\n1. **{field:AnalyticsReport.status}** says where the report stands, and\n **{field:AnalyticsReport.lastRunAt}** when it last ran.\n2. **{field:AnalyticsReport.rowCount}** shows how many rows came back.\n3. If a run failed, **{field:AnalyticsReport.lastError}** says why.\n\n### Ask the assistant\n\n1. Ask about a saved report by name, for example \"how did last week's traffic\n report look?\". When your app has the analytics tools switched on, the\n assistant can list your saved reports and open one to read its figures.\n2. A written summary of a report comes from your app, which sends the stored\n rows to the AI provider as they are. Leave out dimensions that identify a\n person.\n3. For totals your own app calculates, such as orders or signups, ask for a\n refreshed report. The assistant reads those with `reports.query`; see\n Reports.\n4. For a one-off question those reports do not answer, the assistant can\n propose a report with `reports.runtime.define`. Nothing is saved until you\n confirm it.\n",
|
|
4754
|
+
"fieldRefs": [
|
|
4755
|
+
"AnalyticsProperty.displayName",
|
|
4756
|
+
"AnalyticsProperty.externalId",
|
|
4757
|
+
"AnalyticsProperty.measurementId",
|
|
4758
|
+
"AnalyticsProperty.provider",
|
|
4759
|
+
"AnalyticsProperty.siteDomain",
|
|
4760
|
+
"AnalyticsProperty.status",
|
|
4761
|
+
"AnalyticsReport.dateRangeEnd",
|
|
4762
|
+
"AnalyticsReport.dateRangeStart",
|
|
4763
|
+
"AnalyticsReport.dimensions",
|
|
4764
|
+
"AnalyticsReport.frequency",
|
|
4765
|
+
"AnalyticsReport.lastError",
|
|
4766
|
+
"AnalyticsReport.lastRunAt",
|
|
4767
|
+
"AnalyticsReport.metrics",
|
|
4768
|
+
"AnalyticsReport.name",
|
|
4769
|
+
"AnalyticsReport.propertyId",
|
|
4770
|
+
"AnalyticsReport.rowCount",
|
|
4771
|
+
"AnalyticsReport.status"
|
|
4772
|
+
]
|
|
4773
|
+
},
|
|
4774
|
+
"surfaces": [
|
|
4775
|
+
{
|
|
4776
|
+
"kind": "route",
|
|
4777
|
+
"path": "/analytics/summary",
|
|
4778
|
+
"export": "@happyvertical/smrt-analytics/svelte#AnalyticsSummary",
|
|
4779
|
+
"label": "Traffic summary"
|
|
4780
|
+
}
|
|
4781
|
+
],
|
|
4782
|
+
"runtime": "server",
|
|
4783
|
+
"demo": {
|
|
4784
|
+
"mode": "server",
|
|
4785
|
+
"reasons": [
|
|
4786
|
+
"The recipe declares runtime \"server\".",
|
|
4787
|
+
"Needs a server because its package depends on @happyvertical/smrt-tenancy, which do not build for a browser (#3624)."
|
|
4788
|
+
]
|
|
4789
|
+
}
|
|
4790
|
+
}
|
|
4791
|
+
],
|
|
4792
|
+
"browser": {
|
|
4793
|
+
"status": "server-only",
|
|
4794
|
+
"issues": [
|
|
4795
|
+
"#3624"
|
|
4796
|
+
],
|
|
4797
|
+
"via": [
|
|
4798
|
+
"@happyvertical/smrt-tenancy"
|
|
4799
|
+
]
|
|
4800
|
+
}
|
|
4633
4801
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-analytics",
|
|
3
|
-
"version": "0.55.
|
|
3
|
+
"version": "0.55.8",
|
|
4
4
|
"smrtJsdoc": "strict",
|
|
5
5
|
"description": "Analytics integration models for SMRT framework",
|
|
6
6
|
"type": "module",
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@happyvertical/smrt-core": "0.55.
|
|
37
|
-
"@happyvertical/smrt-prompts": "0.55.
|
|
38
|
-
"@happyvertical/smrt-tenancy": "0.55.
|
|
39
|
-
"@happyvertical/smrt-types": "0.55.
|
|
40
|
-
"@happyvertical/smrt-ui": "0.55.
|
|
36
|
+
"@happyvertical/smrt-core": "0.55.8",
|
|
37
|
+
"@happyvertical/smrt-prompts": "0.55.8",
|
|
38
|
+
"@happyvertical/smrt-tenancy": "0.55.8",
|
|
39
|
+
"@happyvertical/smrt-types": "0.55.8",
|
|
40
|
+
"@happyvertical/smrt-ui": "0.55.8"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"svelte": "^5.57.0"
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@happyvertical/smrt-
|
|
52
|
-
"@happyvertical/
|
|
51
|
+
"@happyvertical/smrt-scanner": "0.55.8",
|
|
52
|
+
"@happyvertical/smrt-vitest": "0.55.8",
|
|
53
|
+
"@happyvertical/sql": "^0.103.1",
|
|
53
54
|
"@sveltejs/package": "^2.5.8",
|
|
54
55
|
"@types/node": "24.13.6",
|
|
55
56
|
"fast-glob": "3.3.3",
|