@coursebuilder/analytics 1.1.1 → 1.1.2-canary.0.0c9beb2ae
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/dist/components/index.d.ts +17 -1
- package/dist/components/index.js +110 -7
- package/dist/components/index.js.map +1 -1
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/database.d.ts +30 -0
- package/dist/providers/database.js +59 -0
- package/dist/providers/database.js.map +1 -1
- package/dist/providers/index.js +59 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/types.d.ts +39 -1
- package/package.json +4 -4
- package/src/components/omnibus-dashboard.tsx +177 -6
- package/src/engine.ts +3 -2
- package/src/providers/database.ts +68 -0
- package/src/types.ts +49 -0
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog.ts","../src/engine.ts"],"sourcesContent":["import type { SurfaceName } from './types'\n\nexport interface SurfaceEntry {\n\tname: SurfaceName\n\tdescription: string\n\tcategory:\n\t\t| 'revenue'\n\t\t| 'attribution'\n\t\t| 'traffic'\n\t\t| 'youtube'\n\t\t| 'correlation'\n\t\t| 'survey'\n\t\t| 'value-path'\n\tprovider: 'database' | 'ga4' | 'youtube' | 'derived' | 'newsletter' | 'survey'\n\tfn: string\n\tunavailableFix?: string\n}\n\nexport const catalog: Record<SurfaceName, SurfaceEntry> = {\n\tsummary: {\n\t\tname: 'summary',\n\t\tdescription: 'Revenue overview: total, purchase count, AOV',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueSummary',\n\t},\n\t'revenue/daily': {\n\t\tname: 'revenue/daily',\n\t\tdescription: 'Revenue and purchase count per day',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByDay',\n\t},\n\t'revenue/products': {\n\t\tname: 'revenue/products',\n\t\tdescription: 'Revenue grouped by product',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByProduct',\n\t},\n\t'revenue/countries': {\n\t\tname: 'revenue/countries',\n\t\tdescription: 'Revenue grouped by country',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByCountry',\n\t},\n\t'purchases/recent': {\n\t\tname: 'purchases/recent',\n\t\tdescription: 'Last N purchases',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRecentPurchases',\n\t},\n\tattribution: {\n\t\tname: 'attribution',\n\t\tdescription: 'Attribution event counts by type',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributionSummary',\n\t},\n\t'attribution/shortlinks': {\n\t\tname: 'attribution/shortlinks',\n\t\tdescription: 'Per-shortlink click performance',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getShortlinkPerformance',\n\t},\n\t'attribution/sources': {\n\t\tname: 'attribution/sources',\n\t\tdescription: 'Revenue by first-touch source/medium/campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueBySource',\n\t},\n\t'attribution/funnel': {\n\t\tname: 'attribution/funnel',\n\t\tdescription: 'Signup → purchase conversion funnel',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getConversionFunnel',\n\t},\n\t'attribution/content': {\n\t\tname: 'attribution/content',\n\t\tdescription: 'Content consumed by purchasers',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getContentPurchaseCorrelation',\n\t},\n\t'attribution/coverage': {\n\t\tname: 'attribution/coverage',\n\t\tdescription: 'Attributed vs dark paid revenue, with commerce lane context',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributedRevenueSummary',\n\t},\n\t'attribution/commerce-lanes': {\n\t\tname: 'attribution/commerce-lanes',\n\t\tdescription:\n\t\t\t'Commerce record lanes: paid purchases, access grants, free upgrades, synthetic tests',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCommerceLaneSummary',\n\t},\n\ttraffic: {\n\t\tname: 'traffic',\n\t\tdescription: 'GA4 traffic overview',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficOverview',\n\t},\n\t'traffic/daily': {\n\t\tname: 'traffic/daily',\n\t\tdescription: 'GA4 daily sessions',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getSessionsByDay',\n\t},\n\t'traffic/pages': {\n\t\tname: 'traffic/pages',\n\t\tdescription: 'Top pages by pageviews',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTopPages',\n\t},\n\t'traffic/sources': {\n\t\tname: 'traffic/sources',\n\t\tdescription: 'Traffic sources',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficSources',\n\t},\n\tyoutube: {\n\t\tname: 'youtube',\n\t\tdescription: 'Channel overview (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelOverview',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/videos': {\n\t\tname: 'youtube/videos',\n\t\tdescription: 'Per-video performance (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getVideoPerformance',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/daily': {\n\t\tname: 'youtube/daily',\n\t\tdescription: 'Daily views + watch minutes (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelTimeseries',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/sources': {\n\t\tname: 'youtube/sources',\n\t\tdescription: 'YouTube traffic sources (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getYouTubeTrafficSources',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'correlation/traffic-revenue': {\n\t\tname: 'correlation/traffic-revenue',\n\t\tdescription: 'GA4 sessions + revenue by day',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getTrafficRevenueCorrelation',\n\t},\n\t'correlation/youtube-revenue': {\n\t\tname: 'correlation/youtube-revenue',\n\t\tdescription: 'YouTube (≈48h lag) + GA4 + revenue overlay',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getYouTubeRevenueCorrelation',\n\t},\n\tsurveys: {\n\t\tname: 'surveys',\n\t\tdescription: 'Survey overview: total surveys, responses, respondents',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveySummary',\n\t},\n\t'surveys/list': {\n\t\tname: 'surveys/list',\n\t\tdescription: 'All surveys with response counts',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyList',\n\t},\n\t'surveys/daily': {\n\t\tname: 'surveys/daily',\n\t\tdescription: 'Daily survey response volume',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponsesByDay',\n\t},\n\t'surveys/questions': {\n\t\tname: 'surveys/questions',\n\t\tdescription: 'Top questions by response count with answer distribution',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyQuestionBreakdown',\n\t},\n\t'surveys/responses': {\n\t\tname: 'surveys/responses',\n\t\tdescription:\n\t\t\t'Individual survey responses as flat rows (multi-choice and open-ended)',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponses',\n\t},\n\t'attribution/email-campaigns': {\n\t\tname: 'attribution/email-campaigns',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → signups → purchases → revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttribution',\n\t},\n\t'attribution/email-campaigns/strict': {\n\t\tname: 'attribution/email-campaigns/strict',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → strict purchase-field purchases and revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttributionStrict',\n\t},\n\t'attribution/checkout-receipt': {\n\t\tname: 'attribution/checkout-receipt',\n\t\tdescription: 'Read-only checkout attribution receipt for one purchase ID',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutAttributionReceipt',\n\t},\n\t'attribution/checkout-survey-fallback': {\n\t\tname: 'attribution/checkout-survey-fallback',\n\t\tdescription:\n\t\t\t'Report-only dark purchase fallback from checkout survey responses',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutSurveyFallbackReport',\n\t},\n\t'correlation/survey-revenue': {\n\t\tname: 'correlation/survey-revenue',\n\t\tdescription: 'Survey respondents → purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getSurveyRevenueCorrelation',\n\t},\n\t'correlation/survey-revenue/product': {\n\t\tname: 'correlation/survey-revenue/product',\n\t\tdescription:\n\t\t\t'Product-filtered survey respondents → paid purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getProductSurveyRevenueCorrelation',\n\t},\n\t'value-paths/summary': {\n\t\tname: 'value-paths/summary',\n\t\tdescription:\n\t\t\t'Value Path progression, answer selection, drip fallback, and terminal completion',\n\t\tcategory: 'value-path',\n\t\tprovider: 'database',\n\t\tfn: 'getValuePathSummary',\n\t},\n} as const satisfies Record<SurfaceName, SurfaceEntry>\n\nexport const ANALYTICS_CATALOG = catalog\n","import { catalog, type SurfaceEntry } from './catalog'\nimport type {\n\tAnalyticsRange,\n\tQueryOptions,\n\tQueryResult,\n\tSurfaceMap,\n\tSurfaceName,\n} from './types'\n\nexport type AnalyticsProvider = Record<\n\tstring,\n\t((...args: any[]) => Promise<any>) | unknown\n>\n\nexport type ProviderMap = Record<string, AnalyticsProvider>\n\nfunction toGA4Range(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nfunction toYouTubeRange(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nasync function invokeSurface<S extends SurfaceName>(\n\tproviders: ProviderMap,\n\tsurface: S,\n\toptions: Required<Pick<QueryOptions, 'range' | 'limit' | 'offset'>> &\n\t\tOmit<QueryOptions, 'range' | 'limit' | 'offset'>,\n): Promise<SurfaceMap[S] | null> {\n\tconst { range, limit, offset } = options\n\tconst entry = catalog[surface]\n\tconst provider = providers[entry.provider]\n\n\tif (!provider) {\n\t\treturn null\n\t}\n\n\t// Check that the provider actually exposes the expected function.\n\t// A provider may exist but not support every surface in the catalog\n\t// (e.g. CWA's derived provider omits YouTube correlation).\n\tif (typeof provider[entry.fn] !== 'function') {\n\t\treturn null\n\t}\n\n\tswitch (surface) {\n\t\tcase 'purchases/recent': {\n\t\t\tconst fn = provider[entry.fn] as (limit: number) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(limit)\n\t\t}\n\t\tcase 'youtube': {\n\t\t\tconst fn = provider[entry.fn] as () => Promise<SurfaceMap[S] | null>\n\t\t\treturn fn()\n\t\t}\n\t\tcase 'youtube/videos':\n\t\tcase 'youtube/daily':\n\t\tcase 'youtube/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (surface === 'youtube/videos') {\n\t\t\t\treturn fn(toYouTubeRange(range), limit)\n\t\t\t}\n\t\t\treturn fn(toYouTubeRange(range))\n\t\t}\n\t\tcase 'traffic':\n\t\tcase 'traffic/daily':\n\t\tcase 'traffic/pages':\n\t\tcase 'traffic/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\tif (surface === 'traffic/pages' || surface === 'traffic/sources') {\n\t\t\t\treturn fn(toGA4Range(range), limit)\n\t\t\t}\n\t\t\treturn fn(toGA4Range(range))\n\t\t}\n\t\tcase 'surveys':\n\t\tcase 'surveys/list':\n\t\tcase 'surveys/daily': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range)\n\t\t}\n\t\tcase 'attribution/sources':\n\t\tcase 'attribution/coverage':\n\t\tcase 'attribution/commerce-lanes': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/email-campaigns':\n\t\tcase 'attribution/email-campaigns/strict': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/checkout-receipt': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\tfilters: Pick<QueryOptions, 'purchaseId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn({ purchaseId: options.purchaseId })\n\t\t}\n\t\tcase 'attribution/checkout-survey-fallback': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'surveys/questions': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit)\n\t\t}\n\t\tcase 'surveys/responses': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t\toffset: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, offset)\n\t\t}\n\t\tdefault: {\n\t\t\tif (surface === 'correlation/survey-revenue/product') {\n\t\t\t\tconst productSurveyFn = provider[entry.fn] as (\n\t\t\t\t\trange: AnalyticsRange,\n\t\t\t\t\tlimit?: number,\n\t\t\t\t\tfilters?: Pick<\n\t\t\t\t\t\tQueryOptions,\n\t\t\t\t\t\t'productId' | 'surveyId' | 'surveySlug' | 'questionId'\n\t\t\t\t\t>,\n\t\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\t\treturn productSurveyFn(range, limit, {\n\t\t\t\t\tproductId: options.productId,\n\t\t\t\t\tsurveyId: options.surveyId,\n\t\t\t\t\tsurveySlug: options.surveySlug,\n\t\t\t\t\tquestionId: options.questionId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (\n\t\t\t\tsurface === 'attribution/content' ||\n\t\t\t\tsurface === 'correlation/survey-revenue'\n\t\t\t) {\n\t\t\t\treturn fn(range, limit)\n\t\t\t}\n\t\t\treturn fn(range)\n\t\t}\n\t}\n}\n\n/**\n * Creates an analytics engine bound to the given provider map.\n *\n * @param providers - A map of provider name → provider implementation. Keys\n * must match the `provider` field values used in the catalog\n * ('database', 'ga4', 'youtube', 'derived', 'newsletter', 'survey').\n * @returns An object with `query`, `queryMany`, and `getCatalog` methods.\n */\nexport function createAnalyticsEngine(providers: ProviderMap) {\n\tasync function query<S extends SurfaceName>(\n\t\tsurface: S,\n\t\toptions?: QueryOptions,\n\t): Promise<QueryResult<S>> {\n\t\tconst range = options?.range ?? '30d'\n\t\tconst limit = options?.limit ?? 20\n\t\tconst offset = options?.offset ?? 0\n\t\tconst entry = catalog[surface]\n\t\tconst startMs = Date.now()\n\n\t\ttry {\n\t\t\tconst data = await invokeSurface(providers, surface, {\n\t\t\t\trange,\n\t\t\t\tlimit,\n\t\t\t\toffset,\n\t\t\t\tproductId: options?.productId,\n\t\t\t\tpurchaseId: options?.purchaseId,\n\t\t\t\tsurveyId: options?.surveyId,\n\t\t\t\tsurveySlug: options?.surveySlug,\n\t\t\t\tquestionId: options?.questionId,\n\t\t\t})\n\n\t\t\tif (data === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tsurface,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tmessage: `${entry.provider} is not available`,\n\t\t\t\t\t\tcode: `${entry.provider.toUpperCase()}_UNAVAILABLE`,\n\t\t\t\t\t},\n\t\t\t\t\tfix: entry.unavailableFix ?? 'Check provider configuration',\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsurface,\n\t\t\t\trange,\n\t\t\t\tdata,\n\t\t\t\tmeta: {\n\t\t\t\t\tqueryTimeMs: Date.now() - startMs,\n\t\t\t\t\ttruncated: Array.isArray(data) && data.length >= limit,\n\t\t\t\t},\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tsurface,\n\t\t\t\terror: {\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tcode: 'QUERY_FAILED',\n\t\t\t\t},\n\t\t\t\tfix: `The ${surface} query failed. Try a different range or check server logs.`,\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function queryMany<S extends SurfaceName>(\n\t\tsurfaces: S[],\n\t\toptions?: QueryOptions,\n\t): Promise<{ [K in S]: QueryResult<K> }> {\n\t\tconst results = await Promise.all(\n\t\t\tsurfaces.map(\n\t\t\t\tasync (surface) => [surface, await query(surface, options)] as const,\n\t\t\t),\n\t\t)\n\n\t\treturn Object.fromEntries(results) as { [K in S]: QueryResult<K> }\n\t}\n\n\t/**\n\t * Returns catalog entries filtered to only surfaces that this engine\n\t * can actually serve (provider present and function exported).\n\t */\n\tfunction getCatalog(): SurfaceEntry[] {\n\t\treturn Object.values(catalog).filter((entry) => {\n\t\t\tconst provider = providers[entry.provider]\n\t\t\treturn provider && typeof provider[entry.fn] === 'function'\n\t\t})\n\t}\n\n\treturn { query, queryMany, getCatalog }\n}\n"],"mappings":"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","names":["catalog","summary","name","description","category","provider","fn","attribution","traffic","youtube","unavailableFix","surveys","toGA4Range","range","toYouTubeRange","invokeSurface","providers","surface","options","limit","offset","entry","catalog","provider","fn","productId","purchaseId","productSurveyFn","surveyId","surveySlug","questionId","createAnalyticsEngine","query","startMs","Date","now","data","ok","error","message","code","toUpperCase","fix","unavailableFix","meta","queryTimeMs","truncated","Array","isArray","length","Error","String","queryMany","surfaces","results","Promise","all","map","Object","fromEntries","getCatalog","values","filter"]}
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts","../src/engine.ts"],"sourcesContent":["import type { SurfaceName } from './types'\n\nexport interface SurfaceEntry {\n\tname: SurfaceName\n\tdescription: string\n\tcategory:\n\t\t| 'revenue'\n\t\t| 'attribution'\n\t\t| 'traffic'\n\t\t| 'youtube'\n\t\t| 'correlation'\n\t\t| 'survey'\n\t\t| 'value-path'\n\tprovider: 'database' | 'ga4' | 'youtube' | 'derived' | 'newsletter' | 'survey'\n\tfn: string\n\tunavailableFix?: string\n}\n\nexport const catalog: Record<SurfaceName, SurfaceEntry> = {\n\tsummary: {\n\t\tname: 'summary',\n\t\tdescription: 'Revenue overview: total, purchase count, AOV',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueSummary',\n\t},\n\t'revenue/daily': {\n\t\tname: 'revenue/daily',\n\t\tdescription: 'Revenue and purchase count per day',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByDay',\n\t},\n\t'revenue/products': {\n\t\tname: 'revenue/products',\n\t\tdescription: 'Revenue grouped by product',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByProduct',\n\t},\n\t'revenue/countries': {\n\t\tname: 'revenue/countries',\n\t\tdescription: 'Revenue grouped by country',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByCountry',\n\t},\n\t'purchases/recent': {\n\t\tname: 'purchases/recent',\n\t\tdescription: 'Last N purchases',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRecentPurchases',\n\t},\n\tattribution: {\n\t\tname: 'attribution',\n\t\tdescription: 'Attribution event counts by type',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributionSummary',\n\t},\n\t'attribution/shortlinks': {\n\t\tname: 'attribution/shortlinks',\n\t\tdescription: 'Per-shortlink click performance',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getShortlinkPerformance',\n\t},\n\t'attribution/sources': {\n\t\tname: 'attribution/sources',\n\t\tdescription: 'Revenue by first-touch source/medium/campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueBySource',\n\t},\n\t'attribution/funnel': {\n\t\tname: 'attribution/funnel',\n\t\tdescription: 'Signup → purchase conversion funnel',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getConversionFunnel',\n\t},\n\t'attribution/content': {\n\t\tname: 'attribution/content',\n\t\tdescription: 'Content consumed by purchasers',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getContentPurchaseCorrelation',\n\t},\n\t'attribution/coverage': {\n\t\tname: 'attribution/coverage',\n\t\tdescription: 'Attributed vs dark paid revenue, with commerce lane context',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributedRevenueSummary',\n\t},\n\t'attribution/commerce-lanes': {\n\t\tname: 'attribution/commerce-lanes',\n\t\tdescription:\n\t\t\t'Commerce record lanes: paid purchases, access grants, free upgrades, synthetic tests',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCommerceLaneSummary',\n\t},\n\ttraffic: {\n\t\tname: 'traffic',\n\t\tdescription: 'GA4 traffic overview',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficOverview',\n\t},\n\t'traffic/daily': {\n\t\tname: 'traffic/daily',\n\t\tdescription: 'GA4 daily sessions',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getSessionsByDay',\n\t},\n\t'traffic/pages': {\n\t\tname: 'traffic/pages',\n\t\tdescription: 'Top pages by pageviews',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTopPages',\n\t},\n\t'traffic/sources': {\n\t\tname: 'traffic/sources',\n\t\tdescription: 'Traffic sources',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficSources',\n\t},\n\tyoutube: {\n\t\tname: 'youtube',\n\t\tdescription: 'Channel overview (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelOverview',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/videos': {\n\t\tname: 'youtube/videos',\n\t\tdescription: 'Per-video performance (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getVideoPerformance',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/daily': {\n\t\tname: 'youtube/daily',\n\t\tdescription: 'Daily views + watch minutes (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelTimeseries',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/sources': {\n\t\tname: 'youtube/sources',\n\t\tdescription: 'YouTube traffic sources (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getYouTubeTrafficSources',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'correlation/traffic-revenue': {\n\t\tname: 'correlation/traffic-revenue',\n\t\tdescription: 'GA4 sessions + revenue by day',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getTrafficRevenueCorrelation',\n\t},\n\t'correlation/youtube-revenue': {\n\t\tname: 'correlation/youtube-revenue',\n\t\tdescription: 'YouTube (≈48h lag) + GA4 + revenue overlay',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getYouTubeRevenueCorrelation',\n\t},\n\tsurveys: {\n\t\tname: 'surveys',\n\t\tdescription: 'Survey overview: total surveys, responses, respondents',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveySummary',\n\t},\n\t'surveys/list': {\n\t\tname: 'surveys/list',\n\t\tdescription: 'All surveys with response counts',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyList',\n\t},\n\t'surveys/daily': {\n\t\tname: 'surveys/daily',\n\t\tdescription: 'Daily survey response volume',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponsesByDay',\n\t},\n\t'surveys/questions': {\n\t\tname: 'surveys/questions',\n\t\tdescription: 'Top questions by response count with answer distribution',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyQuestionBreakdown',\n\t},\n\t'surveys/responses': {\n\t\tname: 'surveys/responses',\n\t\tdescription:\n\t\t\t'Individual survey responses as flat rows (multi-choice and open-ended)',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponses',\n\t},\n\t'attribution/email-campaigns': {\n\t\tname: 'attribution/email-campaigns',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → signups → purchases → revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttribution',\n\t},\n\t'attribution/email-campaigns/strict': {\n\t\tname: 'attribution/email-campaigns/strict',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → strict purchase-field purchases and revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttributionStrict',\n\t},\n\t'attribution/checkout-receipt': {\n\t\tname: 'attribution/checkout-receipt',\n\t\tdescription: 'Read-only checkout attribution receipt for one purchase ID',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutAttributionReceipt',\n\t},\n\t'attribution/checkout-survey-fallback': {\n\t\tname: 'attribution/checkout-survey-fallback',\n\t\tdescription:\n\t\t\t'Report-only dark purchase fallback from checkout survey responses',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutSurveyFallbackReport',\n\t},\n\t'correlation/survey-revenue': {\n\t\tname: 'correlation/survey-revenue',\n\t\tdescription: 'Survey respondents → purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getSurveyRevenueCorrelation',\n\t},\n\t'correlation/survey-revenue/product': {\n\t\tname: 'correlation/survey-revenue/product',\n\t\tdescription:\n\t\t\t'Product-filtered survey respondents → paid purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getProductSurveyRevenueCorrelation',\n\t},\n\t'value-paths/summary': {\n\t\tname: 'value-paths/summary',\n\t\tdescription:\n\t\t\t'Value Path progression, answer selection, drip fallback, and terminal completion',\n\t\tcategory: 'value-path',\n\t\tprovider: 'database',\n\t\tfn: 'getValuePathSummary',\n\t},\n} as const satisfies Record<SurfaceName, SurfaceEntry>\n\nexport const ANALYTICS_CATALOG = catalog\n","import { catalog, type SurfaceEntry } from './catalog'\nimport type {\n\tAnalyticsRange,\n\tQueryOptions,\n\tQueryResult,\n\tSurfaceMap,\n\tSurfaceName,\n\tTrafficAnalyticsRange,\n} from './types'\n\nexport type AnalyticsProvider = Record<\n\tstring,\n\t((...args: any[]) => Promise<any>) | unknown\n>\n\nexport type ProviderMap = Record<string, AnalyticsProvider>\n\nfunction toGA4Range(range: AnalyticsRange): TrafficAnalyticsRange {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nfunction toYouTubeRange(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nasync function invokeSurface<S extends SurfaceName>(\n\tproviders: ProviderMap,\n\tsurface: S,\n\toptions: Required<Pick<QueryOptions, 'range' | 'limit' | 'offset'>> &\n\t\tOmit<QueryOptions, 'range' | 'limit' | 'offset'>,\n): Promise<SurfaceMap[S] | null> {\n\tconst { range, limit, offset } = options\n\tconst entry = catalog[surface]\n\tconst provider = providers[entry.provider]\n\n\tif (!provider) {\n\t\treturn null\n\t}\n\n\t// Check that the provider actually exposes the expected function.\n\t// A provider may exist but not support every surface in the catalog\n\t// (e.g. CWA's derived provider omits YouTube correlation).\n\tif (typeof provider[entry.fn] !== 'function') {\n\t\treturn null\n\t}\n\n\tswitch (surface) {\n\t\tcase 'purchases/recent': {\n\t\t\tconst fn = provider[entry.fn] as (limit: number) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(limit)\n\t\t}\n\t\tcase 'youtube': {\n\t\t\tconst fn = provider[entry.fn] as () => Promise<SurfaceMap[S] | null>\n\t\t\treturn fn()\n\t\t}\n\t\tcase 'youtube/videos':\n\t\tcase 'youtube/daily':\n\t\tcase 'youtube/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (surface === 'youtube/videos') {\n\t\t\t\treturn fn(toYouTubeRange(range), limit)\n\t\t\t}\n\t\t\treturn fn(toYouTubeRange(range))\n\t\t}\n\t\tcase 'traffic':\n\t\tcase 'traffic/daily':\n\t\tcase 'traffic/pages':\n\t\tcase 'traffic/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: TrafficAnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\tif (surface === 'traffic/pages' || surface === 'traffic/sources') {\n\t\t\t\treturn fn(toGA4Range(range), limit)\n\t\t\t}\n\t\t\treturn fn(toGA4Range(range))\n\t\t}\n\t\tcase 'surveys':\n\t\tcase 'surveys/list':\n\t\tcase 'surveys/daily': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range)\n\t\t}\n\t\tcase 'attribution/sources':\n\t\tcase 'attribution/coverage':\n\t\tcase 'attribution/commerce-lanes': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/email-campaigns':\n\t\tcase 'attribution/email-campaigns/strict': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/checkout-receipt': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\tfilters: Pick<QueryOptions, 'purchaseId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn({ purchaseId: options.purchaseId })\n\t\t}\n\t\tcase 'attribution/checkout-survey-fallback': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'surveys/questions': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit)\n\t\t}\n\t\tcase 'surveys/responses': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t\toffset: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, offset)\n\t\t}\n\t\tdefault: {\n\t\t\tif (surface === 'correlation/survey-revenue/product') {\n\t\t\t\tconst productSurveyFn = provider[entry.fn] as (\n\t\t\t\t\trange: AnalyticsRange,\n\t\t\t\t\tlimit?: number,\n\t\t\t\t\tfilters?: Pick<\n\t\t\t\t\t\tQueryOptions,\n\t\t\t\t\t\t'productId' | 'surveyId' | 'surveySlug' | 'questionId'\n\t\t\t\t\t>,\n\t\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\t\treturn productSurveyFn(range, limit, {\n\t\t\t\t\tproductId: options.productId,\n\t\t\t\t\tsurveyId: options.surveyId,\n\t\t\t\t\tsurveySlug: options.surveySlug,\n\t\t\t\t\tquestionId: options.questionId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (\n\t\t\t\tsurface === 'attribution/content' ||\n\t\t\t\tsurface === 'correlation/survey-revenue'\n\t\t\t) {\n\t\t\t\treturn fn(range, limit)\n\t\t\t}\n\t\t\treturn fn(range)\n\t\t}\n\t}\n}\n\n/**\n * Creates an analytics engine bound to the given provider map.\n *\n * @param providers - A map of provider name → provider implementation. Keys\n * must match the `provider` field values used in the catalog\n * ('database', 'ga4', 'youtube', 'derived', 'newsletter', 'survey').\n * @returns An object with `query`, `queryMany`, and `getCatalog` methods.\n */\nexport function createAnalyticsEngine(providers: ProviderMap) {\n\tasync function query<S extends SurfaceName>(\n\t\tsurface: S,\n\t\toptions?: QueryOptions,\n\t): Promise<QueryResult<S>> {\n\t\tconst range = options?.range ?? '30d'\n\t\tconst limit = options?.limit ?? 20\n\t\tconst offset = options?.offset ?? 0\n\t\tconst entry = catalog[surface]\n\t\tconst startMs = Date.now()\n\n\t\ttry {\n\t\t\tconst data = await invokeSurface(providers, surface, {\n\t\t\t\trange,\n\t\t\t\tlimit,\n\t\t\t\toffset,\n\t\t\t\tproductId: options?.productId,\n\t\t\t\tpurchaseId: options?.purchaseId,\n\t\t\t\tsurveyId: options?.surveyId,\n\t\t\t\tsurveySlug: options?.surveySlug,\n\t\t\t\tquestionId: options?.questionId,\n\t\t\t})\n\n\t\t\tif (data === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tsurface,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tmessage: `${entry.provider} is not available`,\n\t\t\t\t\t\tcode: `${entry.provider.toUpperCase()}_UNAVAILABLE`,\n\t\t\t\t\t},\n\t\t\t\t\tfix: entry.unavailableFix ?? 'Check provider configuration',\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsurface,\n\t\t\t\trange,\n\t\t\t\tdata,\n\t\t\t\tmeta: {\n\t\t\t\t\tqueryTimeMs: Date.now() - startMs,\n\t\t\t\t\ttruncated: Array.isArray(data) && data.length >= limit,\n\t\t\t\t},\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tsurface,\n\t\t\t\terror: {\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tcode: 'QUERY_FAILED',\n\t\t\t\t},\n\t\t\t\tfix: `The ${surface} query failed. Try a different range or check server logs.`,\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function queryMany<S extends SurfaceName>(\n\t\tsurfaces: S[],\n\t\toptions?: QueryOptions,\n\t): Promise<{ [K in S]: QueryResult<K> }> {\n\t\tconst results = await Promise.all(\n\t\t\tsurfaces.map(\n\t\t\t\tasync (surface) => [surface, await query(surface, options)] as const,\n\t\t\t),\n\t\t)\n\n\t\treturn Object.fromEntries(results) as { [K in S]: QueryResult<K> }\n\t}\n\n\t/**\n\t * Returns catalog entries filtered to only surfaces that this engine\n\t * can actually serve (provider present and function exported).\n\t */\n\tfunction getCatalog(): SurfaceEntry[] {\n\t\treturn Object.values(catalog).filter((entry) => {\n\t\t\tconst provider = providers[entry.provider]\n\t\t\treturn provider && typeof provider[entry.fn] === 'function'\n\t\t})\n\t}\n\n\treturn { query, queryMany, getCatalog }\n}\n"],"mappings":"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","names":["catalog","summary","name","description","category","provider","fn","attribution","traffic","youtube","unavailableFix","surveys","toGA4Range","range","toYouTubeRange","invokeSurface","providers","surface","options","limit","offset","entry","catalog","provider","fn","productId","purchaseId","productSurveyFn","surveyId","surveySlug","questionId","createAnalyticsEngine","query","startMs","Date","now","data","ok","error","message","code","toUpperCase","fix","unavailableFix","meta","queryTimeMs","truncated","Array","isArray","length","Error","String","queryMany","surfaces","results","Promise","all","map","Object","fromEntries","getCatalog","values","filter"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AnalyticsRange, AttributionCount, AttributionCoverage, AttributionSignalSummary, CheckoutAttributionReceipt, CheckoutSurveyFallbackAnswer, CheckoutSurveyFallbackReport, CommerceLaneSummary, CommerceRecordKind, ContentCorrelation, ConversionFunnel, Count, EmailCampaignFunnel, EmailCampaignKitLink, EmailCampaignShortlink, EmailRevenueOverview, Minutes, Percentage, ProductSurveyRevenueAnswer, ProductSurveyRevenueCorrelation, QueryOptions, QueryResult, RecentPurchase, RevenueByCountry, RevenueByProduct, RevenueBySource, RevenueDaily, RevenueSummary, Seconds, ShortlinkPerformance, SurfaceMap, SurfaceName, SurveyConversionByQuestion, SurveyListItem, SurveyQuestionBreakdown, SurveyResponseRow, SurveyResponsesDaily, SurveyRevenueCorrelation, SurveySummary, TopPage, TrafficDaily, TrafficOverview, TrafficRevenueCorrelation, TrafficSource, USD, ValuePathAnswerBucket, ValuePathStepBucket, ValuePathSummary, ValuePathTerminalBucket, YouTubeChannelOverview, YouTubeDaily, YouTubeRevenueCorrelation, YouTubeTrafficSource, YouTubeVideoPerformance } from './types.js';
|
|
1
|
+
export { AnalyticsRange, AttributionCount, AttributionCoverage, AttributionQualityLane, AttributionQualityLaneName, AttributionQualitySummary, AttributionSignalSummary, CheckoutAttributionReceipt, CheckoutSurveyFallbackAnswer, CheckoutSurveyFallbackReport, CommerceLaneSummary, CommerceRecordKind, ContentCorrelation, ConversionFunnel, Count, DeviceCategoryBreakdown, EmailCampaignFunnel, EmailCampaignKitLink, EmailCampaignShortlink, EmailRevenueOverview, Minutes, OperatingSystemBreakdown, Percentage, ProductSurveyRevenueAnswer, ProductSurveyRevenueCorrelation, QueryOptions, QueryResult, RecentPurchase, RevenueByCountry, RevenueByProduct, RevenueBySource, RevenueDaily, RevenueSummary, ScreenResolutionBreakdown, Seconds, ShortlinkPerformance, SurfaceMap, SurfaceName, SurveyConversionByQuestion, SurveyListItem, SurveyQuestionBreakdown, SurveyResponseRow, SurveyResponsesDaily, SurveyRevenueCorrelation, SurveySummary, TopPage, TrafficAnalyticsRange, TrafficBreakdownRow, TrafficDaily, TrafficOverview, TrafficRevenueCorrelation, TrafficSource, USD, ValuePathAnswerBucket, ValuePathStepBucket, ValuePathSummary, ValuePathTerminalBucket, YouTubeChannelOverview, YouTubeDaily, YouTubeRevenueCorrelation, YouTubeTrafficSource, YouTubeVideoPerformance } from './types.js';
|
|
2
2
|
export { ANALYTICS_CATALOG, SurfaceEntry, catalog } from './catalog.js';
|
|
3
3
|
export { AnalyticsProvider, ProviderMap, createAnalyticsEngine } from './engine.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog.ts","../src/engine.ts"],"sourcesContent":["import type { SurfaceName } from './types'\n\nexport interface SurfaceEntry {\n\tname: SurfaceName\n\tdescription: string\n\tcategory:\n\t\t| 'revenue'\n\t\t| 'attribution'\n\t\t| 'traffic'\n\t\t| 'youtube'\n\t\t| 'correlation'\n\t\t| 'survey'\n\t\t| 'value-path'\n\tprovider: 'database' | 'ga4' | 'youtube' | 'derived' | 'newsletter' | 'survey'\n\tfn: string\n\tunavailableFix?: string\n}\n\nexport const catalog: Record<SurfaceName, SurfaceEntry> = {\n\tsummary: {\n\t\tname: 'summary',\n\t\tdescription: 'Revenue overview: total, purchase count, AOV',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueSummary',\n\t},\n\t'revenue/daily': {\n\t\tname: 'revenue/daily',\n\t\tdescription: 'Revenue and purchase count per day',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByDay',\n\t},\n\t'revenue/products': {\n\t\tname: 'revenue/products',\n\t\tdescription: 'Revenue grouped by product',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByProduct',\n\t},\n\t'revenue/countries': {\n\t\tname: 'revenue/countries',\n\t\tdescription: 'Revenue grouped by country',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByCountry',\n\t},\n\t'purchases/recent': {\n\t\tname: 'purchases/recent',\n\t\tdescription: 'Last N purchases',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRecentPurchases',\n\t},\n\tattribution: {\n\t\tname: 'attribution',\n\t\tdescription: 'Attribution event counts by type',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributionSummary',\n\t},\n\t'attribution/shortlinks': {\n\t\tname: 'attribution/shortlinks',\n\t\tdescription: 'Per-shortlink click performance',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getShortlinkPerformance',\n\t},\n\t'attribution/sources': {\n\t\tname: 'attribution/sources',\n\t\tdescription: 'Revenue by first-touch source/medium/campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueBySource',\n\t},\n\t'attribution/funnel': {\n\t\tname: 'attribution/funnel',\n\t\tdescription: 'Signup → purchase conversion funnel',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getConversionFunnel',\n\t},\n\t'attribution/content': {\n\t\tname: 'attribution/content',\n\t\tdescription: 'Content consumed by purchasers',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getContentPurchaseCorrelation',\n\t},\n\t'attribution/coverage': {\n\t\tname: 'attribution/coverage',\n\t\tdescription: 'Attributed vs dark paid revenue, with commerce lane context',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributedRevenueSummary',\n\t},\n\t'attribution/commerce-lanes': {\n\t\tname: 'attribution/commerce-lanes',\n\t\tdescription:\n\t\t\t'Commerce record lanes: paid purchases, access grants, free upgrades, synthetic tests',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCommerceLaneSummary',\n\t},\n\ttraffic: {\n\t\tname: 'traffic',\n\t\tdescription: 'GA4 traffic overview',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficOverview',\n\t},\n\t'traffic/daily': {\n\t\tname: 'traffic/daily',\n\t\tdescription: 'GA4 daily sessions',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getSessionsByDay',\n\t},\n\t'traffic/pages': {\n\t\tname: 'traffic/pages',\n\t\tdescription: 'Top pages by pageviews',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTopPages',\n\t},\n\t'traffic/sources': {\n\t\tname: 'traffic/sources',\n\t\tdescription: 'Traffic sources',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficSources',\n\t},\n\tyoutube: {\n\t\tname: 'youtube',\n\t\tdescription: 'Channel overview (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelOverview',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/videos': {\n\t\tname: 'youtube/videos',\n\t\tdescription: 'Per-video performance (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getVideoPerformance',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/daily': {\n\t\tname: 'youtube/daily',\n\t\tdescription: 'Daily views + watch minutes (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelTimeseries',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/sources': {\n\t\tname: 'youtube/sources',\n\t\tdescription: 'YouTube traffic sources (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getYouTubeTrafficSources',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'correlation/traffic-revenue': {\n\t\tname: 'correlation/traffic-revenue',\n\t\tdescription: 'GA4 sessions + revenue by day',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getTrafficRevenueCorrelation',\n\t},\n\t'correlation/youtube-revenue': {\n\t\tname: 'correlation/youtube-revenue',\n\t\tdescription: 'YouTube (≈48h lag) + GA4 + revenue overlay',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getYouTubeRevenueCorrelation',\n\t},\n\tsurveys: {\n\t\tname: 'surveys',\n\t\tdescription: 'Survey overview: total surveys, responses, respondents',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveySummary',\n\t},\n\t'surveys/list': {\n\t\tname: 'surveys/list',\n\t\tdescription: 'All surveys with response counts',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyList',\n\t},\n\t'surveys/daily': {\n\t\tname: 'surveys/daily',\n\t\tdescription: 'Daily survey response volume',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponsesByDay',\n\t},\n\t'surveys/questions': {\n\t\tname: 'surveys/questions',\n\t\tdescription: 'Top questions by response count with answer distribution',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyQuestionBreakdown',\n\t},\n\t'surveys/responses': {\n\t\tname: 'surveys/responses',\n\t\tdescription:\n\t\t\t'Individual survey responses as flat rows (multi-choice and open-ended)',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponses',\n\t},\n\t'attribution/email-campaigns': {\n\t\tname: 'attribution/email-campaigns',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → signups → purchases → revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttribution',\n\t},\n\t'attribution/email-campaigns/strict': {\n\t\tname: 'attribution/email-campaigns/strict',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → strict purchase-field purchases and revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttributionStrict',\n\t},\n\t'attribution/checkout-receipt': {\n\t\tname: 'attribution/checkout-receipt',\n\t\tdescription: 'Read-only checkout attribution receipt for one purchase ID',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutAttributionReceipt',\n\t},\n\t'attribution/checkout-survey-fallback': {\n\t\tname: 'attribution/checkout-survey-fallback',\n\t\tdescription:\n\t\t\t'Report-only dark purchase fallback from checkout survey responses',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutSurveyFallbackReport',\n\t},\n\t'correlation/survey-revenue': {\n\t\tname: 'correlation/survey-revenue',\n\t\tdescription: 'Survey respondents → purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getSurveyRevenueCorrelation',\n\t},\n\t'correlation/survey-revenue/product': {\n\t\tname: 'correlation/survey-revenue/product',\n\t\tdescription:\n\t\t\t'Product-filtered survey respondents → paid purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getProductSurveyRevenueCorrelation',\n\t},\n\t'value-paths/summary': {\n\t\tname: 'value-paths/summary',\n\t\tdescription:\n\t\t\t'Value Path progression, answer selection, drip fallback, and terminal completion',\n\t\tcategory: 'value-path',\n\t\tprovider: 'database',\n\t\tfn: 'getValuePathSummary',\n\t},\n} as const satisfies Record<SurfaceName, SurfaceEntry>\n\nexport const ANALYTICS_CATALOG = catalog\n","import { catalog, type SurfaceEntry } from './catalog'\nimport type {\n\tAnalyticsRange,\n\tQueryOptions,\n\tQueryResult,\n\tSurfaceMap,\n\tSurfaceName,\n} from './types'\n\nexport type AnalyticsProvider = Record<\n\tstring,\n\t((...args: any[]) => Promise<any>) | unknown\n>\n\nexport type ProviderMap = Record<string, AnalyticsProvider>\n\nfunction toGA4Range(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nfunction toYouTubeRange(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nasync function invokeSurface<S extends SurfaceName>(\n\tproviders: ProviderMap,\n\tsurface: S,\n\toptions: Required<Pick<QueryOptions, 'range' | 'limit' | 'offset'>> &\n\t\tOmit<QueryOptions, 'range' | 'limit' | 'offset'>,\n): Promise<SurfaceMap[S] | null> {\n\tconst { range, limit, offset } = options\n\tconst entry = catalog[surface]\n\tconst provider = providers[entry.provider]\n\n\tif (!provider) {\n\t\treturn null\n\t}\n\n\t// Check that the provider actually exposes the expected function.\n\t// A provider may exist but not support every surface in the catalog\n\t// (e.g. CWA's derived provider omits YouTube correlation).\n\tif (typeof provider[entry.fn] !== 'function') {\n\t\treturn null\n\t}\n\n\tswitch (surface) {\n\t\tcase 'purchases/recent': {\n\t\t\tconst fn = provider[entry.fn] as (limit: number) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(limit)\n\t\t}\n\t\tcase 'youtube': {\n\t\t\tconst fn = provider[entry.fn] as () => Promise<SurfaceMap[S] | null>\n\t\t\treturn fn()\n\t\t}\n\t\tcase 'youtube/videos':\n\t\tcase 'youtube/daily':\n\t\tcase 'youtube/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (surface === 'youtube/videos') {\n\t\t\t\treturn fn(toYouTubeRange(range), limit)\n\t\t\t}\n\t\t\treturn fn(toYouTubeRange(range))\n\t\t}\n\t\tcase 'traffic':\n\t\tcase 'traffic/daily':\n\t\tcase 'traffic/pages':\n\t\tcase 'traffic/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\tif (surface === 'traffic/pages' || surface === 'traffic/sources') {\n\t\t\t\treturn fn(toGA4Range(range), limit)\n\t\t\t}\n\t\t\treturn fn(toGA4Range(range))\n\t\t}\n\t\tcase 'surveys':\n\t\tcase 'surveys/list':\n\t\tcase 'surveys/daily': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range)\n\t\t}\n\t\tcase 'attribution/sources':\n\t\tcase 'attribution/coverage':\n\t\tcase 'attribution/commerce-lanes': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/email-campaigns':\n\t\tcase 'attribution/email-campaigns/strict': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/checkout-receipt': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\tfilters: Pick<QueryOptions, 'purchaseId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn({ purchaseId: options.purchaseId })\n\t\t}\n\t\tcase 'attribution/checkout-survey-fallback': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'surveys/questions': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit)\n\t\t}\n\t\tcase 'surveys/responses': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t\toffset: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, offset)\n\t\t}\n\t\tdefault: {\n\t\t\tif (surface === 'correlation/survey-revenue/product') {\n\t\t\t\tconst productSurveyFn = provider[entry.fn] as (\n\t\t\t\t\trange: AnalyticsRange,\n\t\t\t\t\tlimit?: number,\n\t\t\t\t\tfilters?: Pick<\n\t\t\t\t\t\tQueryOptions,\n\t\t\t\t\t\t'productId' | 'surveyId' | 'surveySlug' | 'questionId'\n\t\t\t\t\t>,\n\t\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\t\treturn productSurveyFn(range, limit, {\n\t\t\t\t\tproductId: options.productId,\n\t\t\t\t\tsurveyId: options.surveyId,\n\t\t\t\t\tsurveySlug: options.surveySlug,\n\t\t\t\t\tquestionId: options.questionId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (\n\t\t\t\tsurface === 'attribution/content' ||\n\t\t\t\tsurface === 'correlation/survey-revenue'\n\t\t\t) {\n\t\t\t\treturn fn(range, limit)\n\t\t\t}\n\t\t\treturn fn(range)\n\t\t}\n\t}\n}\n\n/**\n * Creates an analytics engine bound to the given provider map.\n *\n * @param providers - A map of provider name → provider implementation. Keys\n * must match the `provider` field values used in the catalog\n * ('database', 'ga4', 'youtube', 'derived', 'newsletter', 'survey').\n * @returns An object with `query`, `queryMany`, and `getCatalog` methods.\n */\nexport function createAnalyticsEngine(providers: ProviderMap) {\n\tasync function query<S extends SurfaceName>(\n\t\tsurface: S,\n\t\toptions?: QueryOptions,\n\t): Promise<QueryResult<S>> {\n\t\tconst range = options?.range ?? '30d'\n\t\tconst limit = options?.limit ?? 20\n\t\tconst offset = options?.offset ?? 0\n\t\tconst entry = catalog[surface]\n\t\tconst startMs = Date.now()\n\n\t\ttry {\n\t\t\tconst data = await invokeSurface(providers, surface, {\n\t\t\t\trange,\n\t\t\t\tlimit,\n\t\t\t\toffset,\n\t\t\t\tproductId: options?.productId,\n\t\t\t\tpurchaseId: options?.purchaseId,\n\t\t\t\tsurveyId: options?.surveyId,\n\t\t\t\tsurveySlug: options?.surveySlug,\n\t\t\t\tquestionId: options?.questionId,\n\t\t\t})\n\n\t\t\tif (data === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tsurface,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tmessage: `${entry.provider} is not available`,\n\t\t\t\t\t\tcode: `${entry.provider.toUpperCase()}_UNAVAILABLE`,\n\t\t\t\t\t},\n\t\t\t\t\tfix: entry.unavailableFix ?? 'Check provider configuration',\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsurface,\n\t\t\t\trange,\n\t\t\t\tdata,\n\t\t\t\tmeta: {\n\t\t\t\t\tqueryTimeMs: Date.now() - startMs,\n\t\t\t\t\ttruncated: Array.isArray(data) && data.length >= limit,\n\t\t\t\t},\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tsurface,\n\t\t\t\terror: {\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tcode: 'QUERY_FAILED',\n\t\t\t\t},\n\t\t\t\tfix: `The ${surface} query failed. Try a different range or check server logs.`,\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function queryMany<S extends SurfaceName>(\n\t\tsurfaces: S[],\n\t\toptions?: QueryOptions,\n\t): Promise<{ [K in S]: QueryResult<K> }> {\n\t\tconst results = await Promise.all(\n\t\t\tsurfaces.map(\n\t\t\t\tasync (surface) => [surface, await query(surface, options)] as const,\n\t\t\t),\n\t\t)\n\n\t\treturn Object.fromEntries(results) as { [K in S]: QueryResult<K> }\n\t}\n\n\t/**\n\t * Returns catalog entries filtered to only surfaces that this engine\n\t * can actually serve (provider present and function exported).\n\t */\n\tfunction getCatalog(): SurfaceEntry[] {\n\t\treturn Object.values(catalog).filter((entry) => {\n\t\t\tconst provider = providers[entry.provider]\n\t\t\treturn provider && typeof provider[entry.fn] === 'function'\n\t\t})\n\t}\n\n\treturn { query, queryMany, getCatalog }\n}\n"],"mappings":"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","names":["catalog","summary","name","description","category","provider","fn","attribution","traffic","youtube","unavailableFix","surveys","ANALYTICS_CATALOG","toGA4Range","range","toYouTubeRange","invokeSurface","providers","surface","options","limit","offset","entry","catalog","provider","fn","productId","purchaseId","productSurveyFn","surveyId","surveySlug","questionId","createAnalyticsEngine","query","startMs","Date","now","data","ok","error","message","code","toUpperCase","fix","unavailableFix","meta","queryTimeMs","truncated","Array","isArray","length","Error","String","queryMany","surfaces","results","Promise","all","map","Object","fromEntries","getCatalog","values","filter"]}
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts","../src/engine.ts"],"sourcesContent":["import type { SurfaceName } from './types'\n\nexport interface SurfaceEntry {\n\tname: SurfaceName\n\tdescription: string\n\tcategory:\n\t\t| 'revenue'\n\t\t| 'attribution'\n\t\t| 'traffic'\n\t\t| 'youtube'\n\t\t| 'correlation'\n\t\t| 'survey'\n\t\t| 'value-path'\n\tprovider: 'database' | 'ga4' | 'youtube' | 'derived' | 'newsletter' | 'survey'\n\tfn: string\n\tunavailableFix?: string\n}\n\nexport const catalog: Record<SurfaceName, SurfaceEntry> = {\n\tsummary: {\n\t\tname: 'summary',\n\t\tdescription: 'Revenue overview: total, purchase count, AOV',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueSummary',\n\t},\n\t'revenue/daily': {\n\t\tname: 'revenue/daily',\n\t\tdescription: 'Revenue and purchase count per day',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByDay',\n\t},\n\t'revenue/products': {\n\t\tname: 'revenue/products',\n\t\tdescription: 'Revenue grouped by product',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByProduct',\n\t},\n\t'revenue/countries': {\n\t\tname: 'revenue/countries',\n\t\tdescription: 'Revenue grouped by country',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueByCountry',\n\t},\n\t'purchases/recent': {\n\t\tname: 'purchases/recent',\n\t\tdescription: 'Last N purchases',\n\t\tcategory: 'revenue',\n\t\tprovider: 'database',\n\t\tfn: 'getRecentPurchases',\n\t},\n\tattribution: {\n\t\tname: 'attribution',\n\t\tdescription: 'Attribution event counts by type',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributionSummary',\n\t},\n\t'attribution/shortlinks': {\n\t\tname: 'attribution/shortlinks',\n\t\tdescription: 'Per-shortlink click performance',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getShortlinkPerformance',\n\t},\n\t'attribution/sources': {\n\t\tname: 'attribution/sources',\n\t\tdescription: 'Revenue by first-touch source/medium/campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getRevenueBySource',\n\t},\n\t'attribution/funnel': {\n\t\tname: 'attribution/funnel',\n\t\tdescription: 'Signup → purchase conversion funnel',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getConversionFunnel',\n\t},\n\t'attribution/content': {\n\t\tname: 'attribution/content',\n\t\tdescription: 'Content consumed by purchasers',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getContentPurchaseCorrelation',\n\t},\n\t'attribution/coverage': {\n\t\tname: 'attribution/coverage',\n\t\tdescription: 'Attributed vs dark paid revenue, with commerce lane context',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getAttributedRevenueSummary',\n\t},\n\t'attribution/commerce-lanes': {\n\t\tname: 'attribution/commerce-lanes',\n\t\tdescription:\n\t\t\t'Commerce record lanes: paid purchases, access grants, free upgrades, synthetic tests',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCommerceLaneSummary',\n\t},\n\ttraffic: {\n\t\tname: 'traffic',\n\t\tdescription: 'GA4 traffic overview',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficOverview',\n\t},\n\t'traffic/daily': {\n\t\tname: 'traffic/daily',\n\t\tdescription: 'GA4 daily sessions',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getSessionsByDay',\n\t},\n\t'traffic/pages': {\n\t\tname: 'traffic/pages',\n\t\tdescription: 'Top pages by pageviews',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTopPages',\n\t},\n\t'traffic/sources': {\n\t\tname: 'traffic/sources',\n\t\tdescription: 'Traffic sources',\n\t\tcategory: 'traffic',\n\t\tprovider: 'ga4',\n\t\tfn: 'getTrafficSources',\n\t},\n\tyoutube: {\n\t\tname: 'youtube',\n\t\tdescription: 'Channel overview (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelOverview',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/videos': {\n\t\tname: 'youtube/videos',\n\t\tdescription: 'Per-video performance (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getVideoPerformance',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/daily': {\n\t\tname: 'youtube/daily',\n\t\tdescription: 'Daily views + watch minutes (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getChannelTimeseries',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'youtube/sources': {\n\t\tname: 'youtube/sources',\n\t\tdescription: 'YouTube traffic sources (≈48h lag)',\n\t\tcategory: 'youtube',\n\t\tprovider: 'youtube',\n\t\tfn: 'getYouTubeTrafficSources',\n\t\tunavailableFix: 'Complete OAuth at /api/analytics/youtube-auth',\n\t},\n\t'correlation/traffic-revenue': {\n\t\tname: 'correlation/traffic-revenue',\n\t\tdescription: 'GA4 sessions + revenue by day',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getTrafficRevenueCorrelation',\n\t},\n\t'correlation/youtube-revenue': {\n\t\tname: 'correlation/youtube-revenue',\n\t\tdescription: 'YouTube (≈48h lag) + GA4 + revenue overlay',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getYouTubeRevenueCorrelation',\n\t},\n\tsurveys: {\n\t\tname: 'surveys',\n\t\tdescription: 'Survey overview: total surveys, responses, respondents',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveySummary',\n\t},\n\t'surveys/list': {\n\t\tname: 'surveys/list',\n\t\tdescription: 'All surveys with response counts',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyList',\n\t},\n\t'surveys/daily': {\n\t\tname: 'surveys/daily',\n\t\tdescription: 'Daily survey response volume',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponsesByDay',\n\t},\n\t'surveys/questions': {\n\t\tname: 'surveys/questions',\n\t\tdescription: 'Top questions by response count with answer distribution',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyQuestionBreakdown',\n\t},\n\t'surveys/responses': {\n\t\tname: 'surveys/responses',\n\t\tdescription:\n\t\t\t'Individual survey responses as flat rows (multi-choice and open-ended)',\n\t\tcategory: 'survey',\n\t\tprovider: 'survey',\n\t\tfn: 'getSurveyResponses',\n\t},\n\t'attribution/email-campaigns': {\n\t\tname: 'attribution/email-campaigns',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → signups → purchases → revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttribution',\n\t},\n\t'attribution/email-campaigns/strict': {\n\t\tname: 'attribution/email-campaigns/strict',\n\t\tdescription:\n\t\t\t'Kit email broadcasts → shortlink clicks → strict purchase-field purchases and revenue per campaign',\n\t\tcategory: 'attribution',\n\t\tprovider: 'newsletter',\n\t\tfn: 'getEmailCampaignAttributionStrict',\n\t},\n\t'attribution/checkout-receipt': {\n\t\tname: 'attribution/checkout-receipt',\n\t\tdescription: 'Read-only checkout attribution receipt for one purchase ID',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutAttributionReceipt',\n\t},\n\t'attribution/checkout-survey-fallback': {\n\t\tname: 'attribution/checkout-survey-fallback',\n\t\tdescription:\n\t\t\t'Report-only dark purchase fallback from checkout survey responses',\n\t\tcategory: 'attribution',\n\t\tprovider: 'database',\n\t\tfn: 'getCheckoutSurveyFallbackReport',\n\t},\n\t'correlation/survey-revenue': {\n\t\tname: 'correlation/survey-revenue',\n\t\tdescription: 'Survey respondents → purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getSurveyRevenueCorrelation',\n\t},\n\t'correlation/survey-revenue/product': {\n\t\tname: 'correlation/survey-revenue/product',\n\t\tdescription:\n\t\t\t'Product-filtered survey respondents → paid purchase conversion by question/answer',\n\t\tcategory: 'correlation',\n\t\tprovider: 'derived',\n\t\tfn: 'getProductSurveyRevenueCorrelation',\n\t},\n\t'value-paths/summary': {\n\t\tname: 'value-paths/summary',\n\t\tdescription:\n\t\t\t'Value Path progression, answer selection, drip fallback, and terminal completion',\n\t\tcategory: 'value-path',\n\t\tprovider: 'database',\n\t\tfn: 'getValuePathSummary',\n\t},\n} as const satisfies Record<SurfaceName, SurfaceEntry>\n\nexport const ANALYTICS_CATALOG = catalog\n","import { catalog, type SurfaceEntry } from './catalog'\nimport type {\n\tAnalyticsRange,\n\tQueryOptions,\n\tQueryResult,\n\tSurfaceMap,\n\tSurfaceName,\n\tTrafficAnalyticsRange,\n} from './types'\n\nexport type AnalyticsProvider = Record<\n\tstring,\n\t((...args: any[]) => Promise<any>) | unknown\n>\n\nexport type ProviderMap = Record<string, AnalyticsProvider>\n\nfunction toGA4Range(range: AnalyticsRange): TrafficAnalyticsRange {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nfunction toYouTubeRange(range: AnalyticsRange): '24h' | '7d' | '30d' | '90d' {\n\tif (range === 'all') return '90d'\n\treturn range\n}\n\nasync function invokeSurface<S extends SurfaceName>(\n\tproviders: ProviderMap,\n\tsurface: S,\n\toptions: Required<Pick<QueryOptions, 'range' | 'limit' | 'offset'>> &\n\t\tOmit<QueryOptions, 'range' | 'limit' | 'offset'>,\n): Promise<SurfaceMap[S] | null> {\n\tconst { range, limit, offset } = options\n\tconst entry = catalog[surface]\n\tconst provider = providers[entry.provider]\n\n\tif (!provider) {\n\t\treturn null\n\t}\n\n\t// Check that the provider actually exposes the expected function.\n\t// A provider may exist but not support every surface in the catalog\n\t// (e.g. CWA's derived provider omits YouTube correlation).\n\tif (typeof provider[entry.fn] !== 'function') {\n\t\treturn null\n\t}\n\n\tswitch (surface) {\n\t\tcase 'purchases/recent': {\n\t\t\tconst fn = provider[entry.fn] as (limit: number) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(limit)\n\t\t}\n\t\tcase 'youtube': {\n\t\t\tconst fn = provider[entry.fn] as () => Promise<SurfaceMap[S] | null>\n\t\t\treturn fn()\n\t\t}\n\t\tcase 'youtube/videos':\n\t\tcase 'youtube/daily':\n\t\tcase 'youtube/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: '24h' | '7d' | '30d' | '90d',\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (surface === 'youtube/videos') {\n\t\t\t\treturn fn(toYouTubeRange(range), limit)\n\t\t\t}\n\t\t\treturn fn(toYouTubeRange(range))\n\t\t}\n\t\tcase 'traffic':\n\t\tcase 'traffic/daily':\n\t\tcase 'traffic/pages':\n\t\tcase 'traffic/sources': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: TrafficAnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\tif (surface === 'traffic/pages' || surface === 'traffic/sources') {\n\t\t\t\treturn fn(toGA4Range(range), limit)\n\t\t\t}\n\t\t\treturn fn(toGA4Range(range))\n\t\t}\n\t\tcase 'surveys':\n\t\tcase 'surveys/list':\n\t\tcase 'surveys/daily': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range)\n\t\t}\n\t\tcase 'attribution/sources':\n\t\tcase 'attribution/coverage':\n\t\tcase 'attribution/commerce-lanes': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/email-campaigns':\n\t\tcase 'attribution/email-campaigns/strict': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'attribution/checkout-receipt': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\tfilters: Pick<QueryOptions, 'purchaseId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn({ purchaseId: options.purchaseId })\n\t\t}\n\t\tcase 'attribution/checkout-survey-fallback': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t\tfilters?: Pick<QueryOptions, 'productId'>,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, { productId: options.productId })\n\t\t}\n\t\tcase 'surveys/questions': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit)\n\t\t}\n\t\tcase 'surveys/responses': {\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit: number,\n\t\t\t\toffset: number,\n\t\t\t) => Promise<SurfaceMap[S]>\n\t\t\treturn fn(range, limit, offset)\n\t\t}\n\t\tdefault: {\n\t\t\tif (surface === 'correlation/survey-revenue/product') {\n\t\t\t\tconst productSurveyFn = provider[entry.fn] as (\n\t\t\t\t\trange: AnalyticsRange,\n\t\t\t\t\tlimit?: number,\n\t\t\t\t\tfilters?: Pick<\n\t\t\t\t\t\tQueryOptions,\n\t\t\t\t\t\t'productId' | 'surveyId' | 'surveySlug' | 'questionId'\n\t\t\t\t\t>,\n\t\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\t\treturn productSurveyFn(range, limit, {\n\t\t\t\t\tproductId: options.productId,\n\t\t\t\t\tsurveyId: options.surveyId,\n\t\t\t\t\tsurveySlug: options.surveySlug,\n\t\t\t\t\tquestionId: options.questionId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst fn = provider[entry.fn] as (\n\t\t\t\trange: AnalyticsRange,\n\t\t\t\tlimit?: number,\n\t\t\t) => Promise<SurfaceMap[S] | null>\n\t\t\tif (\n\t\t\t\tsurface === 'attribution/content' ||\n\t\t\t\tsurface === 'correlation/survey-revenue'\n\t\t\t) {\n\t\t\t\treturn fn(range, limit)\n\t\t\t}\n\t\t\treturn fn(range)\n\t\t}\n\t}\n}\n\n/**\n * Creates an analytics engine bound to the given provider map.\n *\n * @param providers - A map of provider name → provider implementation. Keys\n * must match the `provider` field values used in the catalog\n * ('database', 'ga4', 'youtube', 'derived', 'newsletter', 'survey').\n * @returns An object with `query`, `queryMany`, and `getCatalog` methods.\n */\nexport function createAnalyticsEngine(providers: ProviderMap) {\n\tasync function query<S extends SurfaceName>(\n\t\tsurface: S,\n\t\toptions?: QueryOptions,\n\t): Promise<QueryResult<S>> {\n\t\tconst range = options?.range ?? '30d'\n\t\tconst limit = options?.limit ?? 20\n\t\tconst offset = options?.offset ?? 0\n\t\tconst entry = catalog[surface]\n\t\tconst startMs = Date.now()\n\n\t\ttry {\n\t\t\tconst data = await invokeSurface(providers, surface, {\n\t\t\t\trange,\n\t\t\t\tlimit,\n\t\t\t\toffset,\n\t\t\t\tproductId: options?.productId,\n\t\t\t\tpurchaseId: options?.purchaseId,\n\t\t\t\tsurveyId: options?.surveyId,\n\t\t\t\tsurveySlug: options?.surveySlug,\n\t\t\t\tquestionId: options?.questionId,\n\t\t\t})\n\n\t\t\tif (data === null) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\tsurface,\n\t\t\t\t\terror: {\n\t\t\t\t\t\tmessage: `${entry.provider} is not available`,\n\t\t\t\t\t\tcode: `${entry.provider.toUpperCase()}_UNAVAILABLE`,\n\t\t\t\t\t},\n\t\t\t\t\tfix: entry.unavailableFix ?? 'Check provider configuration',\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\tsurface,\n\t\t\t\trange,\n\t\t\t\tdata,\n\t\t\t\tmeta: {\n\t\t\t\t\tqueryTimeMs: Date.now() - startMs,\n\t\t\t\t\ttruncated: Array.isArray(data) && data.length >= limit,\n\t\t\t\t},\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tsurface,\n\t\t\t\terror: {\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\tcode: 'QUERY_FAILED',\n\t\t\t\t},\n\t\t\t\tfix: `The ${surface} query failed. Try a different range or check server logs.`,\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function queryMany<S extends SurfaceName>(\n\t\tsurfaces: S[],\n\t\toptions?: QueryOptions,\n\t): Promise<{ [K in S]: QueryResult<K> }> {\n\t\tconst results = await Promise.all(\n\t\t\tsurfaces.map(\n\t\t\t\tasync (surface) => [surface, await query(surface, options)] as const,\n\t\t\t),\n\t\t)\n\n\t\treturn Object.fromEntries(results) as { [K in S]: QueryResult<K> }\n\t}\n\n\t/**\n\t * Returns catalog entries filtered to only surfaces that this engine\n\t * can actually serve (provider present and function exported).\n\t */\n\tfunction getCatalog(): SurfaceEntry[] {\n\t\treturn Object.values(catalog).filter((entry) => {\n\t\t\tconst provider = providers[entry.provider]\n\t\t\treturn provider && typeof provider[entry.fn] === 'function'\n\t\t})\n\t}\n\n\treturn { query, queryMany, getCatalog }\n}\n"],"mappings":"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","names":["catalog","summary","name","description","category","provider","fn","attribution","traffic","youtube","unavailableFix","surveys","ANALYTICS_CATALOG","toGA4Range","range","toYouTubeRange","invokeSurface","providers","surface","options","limit","offset","entry","catalog","provider","fn","productId","purchaseId","productSurveyFn","surveyId","surveySlug","questionId","createAnalyticsEngine","query","startMs","Date","now","data","ok","error","message","code","toUpperCase","fix","unavailableFix","meta","queryTimeMs","truncated","Array","isArray","length","Error","String","queryMany","surfaces","results","Promise","all","map","Object","fromEntries","getCatalog","values","filter"]}
|
|
@@ -118,6 +118,36 @@ declare function createDatabaseProvider(db: any, schema: DatabaseAnalyticsSchema
|
|
|
118
118
|
recoveredFromShortlinkAttributionTable: any;
|
|
119
119
|
internalFreeUpgrade: any;
|
|
120
120
|
};
|
|
121
|
+
quality: {
|
|
122
|
+
strong: {
|
|
123
|
+
lane: "unknown" | "strong" | "medium" | "weak";
|
|
124
|
+
purchases: any;
|
|
125
|
+
revenue: number;
|
|
126
|
+
revenuePercent: number;
|
|
127
|
+
notes: string[];
|
|
128
|
+
};
|
|
129
|
+
medium: {
|
|
130
|
+
lane: "unknown" | "strong" | "medium" | "weak";
|
|
131
|
+
purchases: any;
|
|
132
|
+
revenue: number;
|
|
133
|
+
revenuePercent: number;
|
|
134
|
+
notes: string[];
|
|
135
|
+
};
|
|
136
|
+
weak: {
|
|
137
|
+
lane: "unknown" | "strong" | "medium" | "weak";
|
|
138
|
+
purchases: any;
|
|
139
|
+
revenue: number;
|
|
140
|
+
revenuePercent: number;
|
|
141
|
+
notes: string[];
|
|
142
|
+
};
|
|
143
|
+
unknown: {
|
|
144
|
+
lane: "unknown" | "strong" | "medium" | "weak";
|
|
145
|
+
purchases: any;
|
|
146
|
+
revenue: number;
|
|
147
|
+
revenuePercent: number;
|
|
148
|
+
notes: string[];
|
|
149
|
+
};
|
|
150
|
+
};
|
|
121
151
|
}>;
|
|
122
152
|
getContentPurchaseCorrelation: (range?: AnalyticsTimeRange, limit?: number) => Promise<any>;
|
|
123
153
|
getCheckoutAttributionReceipt: (opts: {
|
|
@@ -61,6 +61,31 @@ function createDatabaseProvider(db, schema) {
|
|
|
61
61
|
${purchaseFieldAttributionSignalSql}
|
|
62
62
|
OR ${exactShortlinkPurchaseAttributionSignalSql}
|
|
63
63
|
)`;
|
|
64
|
+
const strongAttributionQualitySql = sql`(
|
|
65
|
+
JSON_EXTRACT(${purchases.fields}, '$.attribution.clickIds') IS NOT NULL
|
|
66
|
+
OR JSON_EXTRACT(${purchases.fields}, '$.attribution.utm') IS NOT NULL
|
|
67
|
+
OR JSON_EXTRACT(${purchases.fields}, '$.attribution.shortlink') IS NOT NULL
|
|
68
|
+
OR JSON_UNQUOTE(JSON_EXTRACT(${purchases.fields}, '$.utmSource')) IS NOT NULL
|
|
69
|
+
OR JSON_UNQUOTE(JSON_EXTRACT(${purchases.fields}, '$.shortlinkRef')) IS NOT NULL
|
|
70
|
+
)`;
|
|
71
|
+
const mediumAttributionQualitySql = sql`(
|
|
72
|
+
NOT ${strongAttributionQualitySql}
|
|
73
|
+
AND ${exactShortlinkPurchaseAttributionSignalSql}
|
|
74
|
+
)`;
|
|
75
|
+
const weakAttributionQualitySql = sql`(
|
|
76
|
+
NOT ${strongAttributionQualitySql}
|
|
77
|
+
AND NOT ${exactShortlinkPurchaseAttributionSignalSql}
|
|
78
|
+
AND (
|
|
79
|
+
JSON_EXTRACT(${purchases.fields}, '$.attribution.ga.clientId') IS NOT NULL
|
|
80
|
+
OR JSON_UNQUOTE(JSON_EXTRACT(${purchases.fields}, '$.gaClientId')) IS NOT NULL
|
|
81
|
+
OR JSON_EXTRACT(${purchases.fields}, '$.attribution.selfReportedSource') IS NOT NULL
|
|
82
|
+
)
|
|
83
|
+
)`;
|
|
84
|
+
const unknownAttributionQualitySql = sql`(
|
|
85
|
+
NOT ${strongAttributionQualitySql}
|
|
86
|
+
AND NOT ${mediumAttributionQualitySql}
|
|
87
|
+
AND NOT ${weakAttributionQualitySql}
|
|
88
|
+
)`;
|
|
64
89
|
function rangeToDate(range) {
|
|
65
90
|
if (range === "all")
|
|
66
91
|
return null;
|
|
@@ -505,6 +530,20 @@ function createDatabaseProvider(db, schema) {
|
|
|
505
530
|
}).from(purchases).where(and(...signalConditions, condition));
|
|
506
531
|
return row?.count ?? 0;
|
|
507
532
|
}, "signalCount");
|
|
533
|
+
const qualityLane = /* @__PURE__ */ __name(async (lane, condition, notes) => {
|
|
534
|
+
const [row] = await db.select({
|
|
535
|
+
total: sum(purchases.totalAmount),
|
|
536
|
+
count: count()
|
|
537
|
+
}).from(purchases).where(and(...conditions, condition));
|
|
538
|
+
const revenue = Number(row?.total ?? 0);
|
|
539
|
+
return {
|
|
540
|
+
lane,
|
|
541
|
+
purchases: row?.count ?? 0,
|
|
542
|
+
revenue,
|
|
543
|
+
revenuePercent: totalRevenue > 0 ? revenue / totalRevenue : 0,
|
|
544
|
+
notes
|
|
545
|
+
};
|
|
546
|
+
}, "qualityLane");
|
|
508
547
|
const [shortlinkCount, utmCount, clickIdCount, gaClientIdCount, selfReportedSourceCount, recoveredFromShortlinkAttributionTableCount] = await Promise.all([
|
|
509
548
|
signalCount(sql`(
|
|
510
549
|
JSON_EXTRACT(${purchases.fields}, '$.attribution.shortlink.slug') IS NOT NULL
|
|
@@ -529,6 +568,20 @@ function createDatabaseProvider(db, schema) {
|
|
|
529
568
|
const recoveredFromShortlinkAttributionTableRevenue = Number(recoveredFromShortlinkAttributionTable?.total ?? 0);
|
|
530
569
|
const unattributedRevenue = totalRevenue - attributedRevenue;
|
|
531
570
|
const paidPurchases = totals?.count ?? 0;
|
|
571
|
+
const [strongQuality, mediumQuality, weakQuality, unknownQuality] = await Promise.all([
|
|
572
|
+
qualityLane("strong", strongAttributionQualitySql, [
|
|
573
|
+
"Purchase-field UTM, shortlink, or paid click evidence."
|
|
574
|
+
]),
|
|
575
|
+
qualityLane("medium", mediumAttributionQualitySql, [
|
|
576
|
+
"Recovered from exact shortlink attribution table purchase match."
|
|
577
|
+
]),
|
|
578
|
+
qualityLane("weak", weakAttributionQualitySql, [
|
|
579
|
+
"GA client ID or self-reported source without stronger campaign evidence."
|
|
580
|
+
]),
|
|
581
|
+
qualityLane("unknown", unknownAttributionQualitySql, [
|
|
582
|
+
"No usable attribution evidence for paid revenue."
|
|
583
|
+
])
|
|
584
|
+
]);
|
|
532
585
|
return {
|
|
533
586
|
totalRevenue,
|
|
534
587
|
attributedRevenue,
|
|
@@ -553,6 +606,12 @@ function createDatabaseProvider(db, schema) {
|
|
|
553
606
|
selfReportedSource: selfReportedSourceCount,
|
|
554
607
|
recoveredFromShortlinkAttributionTable: recoveredFromShortlinkAttributionTableCount,
|
|
555
608
|
internalFreeUpgrade: commerce.freeUpgrades
|
|
609
|
+
},
|
|
610
|
+
quality: {
|
|
611
|
+
strong: strongQuality,
|
|
612
|
+
medium: mediumQuality,
|
|
613
|
+
weak: weakQuality,
|
|
614
|
+
unknown: unknownQuality
|
|
556
615
|
}
|
|
557
616
|
};
|
|
558
617
|
}
|