@foundrynorth/compass-schema 1.0.0 → 1.0.2
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/analyzeTypes.d.ts +1059 -0
- package/dist/analyzeTypes.d.ts.map +1 -0
- package/dist/analyzeTypes.js +156 -0
- package/dist/analyzeTypes.js.map +1 -0
- package/dist/schema.d.ts +1517 -1388
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +43 -0
- package/dist/schema.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,1059 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* SERVICE AREA TYPES
|
|
4
|
+
* For home service businesses (HVAC, plumbing, electrical, etc.),
|
|
5
|
+
* competition is based on service area overlap rather than physical distance.
|
|
6
|
+
*/
|
|
7
|
+
export type BusinessType = 'retail' | 'restaurant' | 'service_business' | 'other';
|
|
8
|
+
export interface ServiceArea {
|
|
9
|
+
zips?: string[];
|
|
10
|
+
cities?: string[];
|
|
11
|
+
namedRegions?: string[];
|
|
12
|
+
radiusMiles?: number;
|
|
13
|
+
coverageScore?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ServiceAreaOverlap {
|
|
16
|
+
overlapZips: string[];
|
|
17
|
+
overlapCities: string[];
|
|
18
|
+
overlapRatio: number;
|
|
19
|
+
competitorTier: 'primary' | 'secondary' | 'peripheral';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* CANONICAL COMPETITOR TYPE
|
|
23
|
+
* Single source of truth for competitor representation across the platform.
|
|
24
|
+
* Use this type for:
|
|
25
|
+
* - Enrichment persistence
|
|
26
|
+
* - Prompt construction
|
|
27
|
+
* - UI display on competitor cards
|
|
28
|
+
*
|
|
29
|
+
* This type normalizes competitors from all sources (Google Places, DataForSEO, Perplexity)
|
|
30
|
+
* into a consistent shape for the entire application.
|
|
31
|
+
*/
|
|
32
|
+
export interface CanonicalCompetitor {
|
|
33
|
+
id: string;
|
|
34
|
+
name: string;
|
|
35
|
+
domain?: string;
|
|
36
|
+
placeId?: string;
|
|
37
|
+
location?: {
|
|
38
|
+
lat: number;
|
|
39
|
+
lng: number;
|
|
40
|
+
city?: string;
|
|
41
|
+
state?: string;
|
|
42
|
+
address?: string;
|
|
43
|
+
};
|
|
44
|
+
category?: string;
|
|
45
|
+
sources: string[];
|
|
46
|
+
metrics?: {
|
|
47
|
+
searchShare?: number;
|
|
48
|
+
estClicks?: number;
|
|
49
|
+
estSpend?: number;
|
|
50
|
+
rating?: number;
|
|
51
|
+
reviewCount?: number;
|
|
52
|
+
};
|
|
53
|
+
isChain?: boolean;
|
|
54
|
+
chainId?: string;
|
|
55
|
+
chainName?: string;
|
|
56
|
+
chainType?: 'national' | 'regional' | 'local';
|
|
57
|
+
locations?: ChainLocation[];
|
|
58
|
+
locationCount?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Selection mode for multi-location chains:
|
|
61
|
+
* - 'single': Only this specific location
|
|
62
|
+
* - 'dma': All locations in the same DMA as prospect
|
|
63
|
+
* - 'overlap': All locations overlapping with prospect's service area
|
|
64
|
+
* - 'all': All locations nationwide (legacy 'chain' mode)
|
|
65
|
+
*/
|
|
66
|
+
selectionMode?: ChainSelectionMode;
|
|
67
|
+
selectedLocationIds?: string[];
|
|
68
|
+
locationsByMode?: {
|
|
69
|
+
dma?: number;
|
|
70
|
+
overlap?: number;
|
|
71
|
+
all: number;
|
|
72
|
+
};
|
|
73
|
+
aggregatedMetrics?: {
|
|
74
|
+
totalReviews?: number;
|
|
75
|
+
avgRating?: number;
|
|
76
|
+
serviceAreaUnion?: ServiceArea;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Chain selection mode for multi-location businesses
|
|
81
|
+
* Determines how many locations to include in analysis
|
|
82
|
+
*/
|
|
83
|
+
export type ChainSelectionMode = 'single' | 'dma' | 'overlap' | 'all';
|
|
84
|
+
/**
|
|
85
|
+
* Individual location within a chain
|
|
86
|
+
* Used when a competitor or prospect has multiple locations in the market
|
|
87
|
+
*/
|
|
88
|
+
export interface ChainLocation {
|
|
89
|
+
placeId: string;
|
|
90
|
+
name: string;
|
|
91
|
+
address?: string;
|
|
92
|
+
city?: string;
|
|
93
|
+
state?: string;
|
|
94
|
+
lat?: number;
|
|
95
|
+
lng?: number;
|
|
96
|
+
distance_km?: number;
|
|
97
|
+
rating?: number;
|
|
98
|
+
reviewCount?: number;
|
|
99
|
+
isPrimary?: boolean;
|
|
100
|
+
dmaCode?: string;
|
|
101
|
+
dmaName?: string;
|
|
102
|
+
inProspectDma?: boolean;
|
|
103
|
+
overlapsProspect?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* COMPETITOR CREATIVE TYPES
|
|
107
|
+
* Unified interface for ad creatives from Google Ads and Facebook Ads
|
|
108
|
+
* Used for UI display and media planning prompts
|
|
109
|
+
*/
|
|
110
|
+
export interface CompetitorCreative {
|
|
111
|
+
source: 'google_ads' | 'facebook_ads';
|
|
112
|
+
platform: 'google' | 'facebook' | 'instagram' | 'audience_network';
|
|
113
|
+
accountName?: string;
|
|
114
|
+
accountId?: string;
|
|
115
|
+
headline?: string;
|
|
116
|
+
primaryText?: string;
|
|
117
|
+
description?: string;
|
|
118
|
+
cta?: string;
|
|
119
|
+
ctaType?: string;
|
|
120
|
+
landingUrl?: string;
|
|
121
|
+
imageUrl?: string;
|
|
122
|
+
imageUrls?: string[] | Array<{
|
|
123
|
+
original: string;
|
|
124
|
+
resized: string;
|
|
125
|
+
}>;
|
|
126
|
+
videoUrl?: string;
|
|
127
|
+
format?: string;
|
|
128
|
+
impressions?: number | null;
|
|
129
|
+
region?: string | null;
|
|
130
|
+
startDate?: string | null;
|
|
131
|
+
endDate?: string | null;
|
|
132
|
+
pageProfileUrl?: string;
|
|
133
|
+
pageProfilePicture?: string;
|
|
134
|
+
pageLikeCount?: number;
|
|
135
|
+
pageCategories?: string[];
|
|
136
|
+
adLibraryUrl?: string;
|
|
137
|
+
sourceUrl?: string;
|
|
138
|
+
raw?: unknown;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Facebook Ad structure (from curious_coder/facebook-ads-library-scraper)
|
|
142
|
+
* Extended to include all fields from the actual API response
|
|
143
|
+
*/
|
|
144
|
+
export interface FacebookAdCreative {
|
|
145
|
+
adId?: string;
|
|
146
|
+
adArchiveId?: string;
|
|
147
|
+
adImage?: string;
|
|
148
|
+
adImages?: Array<{
|
|
149
|
+
original: string;
|
|
150
|
+
resized: string;
|
|
151
|
+
}>;
|
|
152
|
+
adCopy?: string;
|
|
153
|
+
headline?: string;
|
|
154
|
+
callToAction?: string;
|
|
155
|
+
ctaType?: string;
|
|
156
|
+
ctaLink?: string;
|
|
157
|
+
publisherPlatforms?: string[];
|
|
158
|
+
startDate?: string;
|
|
159
|
+
endDate?: string;
|
|
160
|
+
status?: string;
|
|
161
|
+
pageName?: string;
|
|
162
|
+
pageProfileUrl?: string;
|
|
163
|
+
pageProfilePicture?: string;
|
|
164
|
+
pageLikeCount?: number;
|
|
165
|
+
pageCategories?: string[];
|
|
166
|
+
displayFormat?: string;
|
|
167
|
+
adLibraryUrl?: string;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Summary of creatives for a competitor
|
|
171
|
+
*/
|
|
172
|
+
export interface CompetitorCreativeSummary {
|
|
173
|
+
googleAdsCount: number;
|
|
174
|
+
facebookAdsCount: number;
|
|
175
|
+
totalCount: number;
|
|
176
|
+
creatives: CompetitorCreative[];
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* CRITICAL CHANGE: Known competitors are now REQUIRED (1-5)
|
|
180
|
+
* Discovery is OPTIONAL and defaults to OFF
|
|
181
|
+
*/
|
|
182
|
+
export declare const KnownCompetitorSchema: z.ZodObject<{
|
|
183
|
+
name: z.ZodString;
|
|
184
|
+
placeId: z.ZodOptional<z.ZodString>;
|
|
185
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
186
|
+
address: z.ZodOptional<z.ZodString>;
|
|
187
|
+
website: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
188
|
+
}, "strip", z.ZodTypeAny, {
|
|
189
|
+
name: string;
|
|
190
|
+
placeId?: string | undefined;
|
|
191
|
+
notes?: string | undefined;
|
|
192
|
+
address?: string | undefined;
|
|
193
|
+
website?: string | undefined;
|
|
194
|
+
}, {
|
|
195
|
+
name: string;
|
|
196
|
+
placeId?: string | undefined;
|
|
197
|
+
notes?: string | undefined;
|
|
198
|
+
address?: string | undefined;
|
|
199
|
+
website?: string | undefined;
|
|
200
|
+
}>;
|
|
201
|
+
export declare const AnalyzePlanInputSchema: z.ZodObject<{
|
|
202
|
+
name: z.ZodOptional<z.ZodString>;
|
|
203
|
+
placeId: z.ZodOptional<z.ZodString>;
|
|
204
|
+
brand: z.ZodOptional<z.ZodString>;
|
|
205
|
+
businessName: z.ZodOptional<z.ZodString>;
|
|
206
|
+
city: z.ZodOptional<z.ZodString>;
|
|
207
|
+
state: z.ZodOptional<z.ZodString>;
|
|
208
|
+
geo: z.ZodOptional<z.ZodString>;
|
|
209
|
+
targetGeo: z.ZodOptional<z.ZodString>;
|
|
210
|
+
lat: z.ZodOptional<z.ZodNumber>;
|
|
211
|
+
lng: z.ZodOptional<z.ZodNumber>;
|
|
212
|
+
chainDetected: z.ZodOptional<z.ZodBoolean>;
|
|
213
|
+
selectedAreas: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
214
|
+
businessDescription: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodString>>, string, string | undefined>;
|
|
215
|
+
url: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
216
|
+
knownCompetitors: z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
217
|
+
name: z.ZodString;
|
|
218
|
+
placeId: z.ZodOptional<z.ZodString>;
|
|
219
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
220
|
+
address: z.ZodOptional<z.ZodString>;
|
|
221
|
+
website: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, string | undefined>;
|
|
222
|
+
}, "strip", z.ZodTypeAny, {
|
|
223
|
+
name: string;
|
|
224
|
+
placeId?: string | undefined;
|
|
225
|
+
notes?: string | undefined;
|
|
226
|
+
address?: string | undefined;
|
|
227
|
+
website?: string | undefined;
|
|
228
|
+
}, {
|
|
229
|
+
name: string;
|
|
230
|
+
placeId?: string | undefined;
|
|
231
|
+
notes?: string | undefined;
|
|
232
|
+
address?: string | undefined;
|
|
233
|
+
website?: string | undefined;
|
|
234
|
+
}>, "many">, {
|
|
235
|
+
name: string;
|
|
236
|
+
placeId?: string | undefined;
|
|
237
|
+
notes?: string | undefined;
|
|
238
|
+
address?: string | undefined;
|
|
239
|
+
website?: string | undefined;
|
|
240
|
+
}[], {
|
|
241
|
+
name: string;
|
|
242
|
+
placeId?: string | undefined;
|
|
243
|
+
notes?: string | undefined;
|
|
244
|
+
address?: string | undefined;
|
|
245
|
+
website?: string | undefined;
|
|
246
|
+
}[]>;
|
|
247
|
+
discoverAdditional: z.ZodDefault<z.ZodBoolean>;
|
|
248
|
+
maxAdditionalCompetitors: z.ZodDefault<z.ZodNumber>;
|
|
249
|
+
marketId: z.ZodOptional<z.ZodString>;
|
|
250
|
+
marketCenter: z.ZodOptional<z.ZodObject<{
|
|
251
|
+
lat: z.ZodNumber;
|
|
252
|
+
lng: z.ZodNumber;
|
|
253
|
+
}, "strip", z.ZodTypeAny, {
|
|
254
|
+
lat: number;
|
|
255
|
+
lng: number;
|
|
256
|
+
}, {
|
|
257
|
+
lat: number;
|
|
258
|
+
lng: number;
|
|
259
|
+
}>>;
|
|
260
|
+
serviceAreaConfig: z.ZodOptional<z.ZodObject<{
|
|
261
|
+
type: z.ZodDefault<z.ZodEnum<["physical", "hq_radius", "metro", "custom_zips"]>>;
|
|
262
|
+
radiusMiles: z.ZodOptional<z.ZodNumber>;
|
|
263
|
+
zipCodes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
264
|
+
}, "strip", z.ZodTypeAny, {
|
|
265
|
+
type: "physical" | "hq_radius" | "metro" | "custom_zips";
|
|
266
|
+
radiusMiles?: number | undefined;
|
|
267
|
+
zipCodes?: string[] | undefined;
|
|
268
|
+
}, {
|
|
269
|
+
type?: "physical" | "hq_radius" | "metro" | "custom_zips" | undefined;
|
|
270
|
+
radiusMiles?: number | undefined;
|
|
271
|
+
zipCodes?: string[] | undefined;
|
|
272
|
+
}>>;
|
|
273
|
+
prospectPlaceId: z.ZodOptional<z.ZodString>;
|
|
274
|
+
prospectBrandName: z.ZodOptional<z.ZodString>;
|
|
275
|
+
prospectChainMode: z.ZodOptional<z.ZodEnum<["single", "dma", "overlap", "all"]>>;
|
|
276
|
+
prospectLocationCount: z.ZodOptional<z.ZodNumber>;
|
|
277
|
+
prospectLocations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
278
|
+
placeId: z.ZodString;
|
|
279
|
+
name: z.ZodOptional<z.ZodString>;
|
|
280
|
+
address: z.ZodOptional<z.ZodString>;
|
|
281
|
+
city: z.ZodOptional<z.ZodString>;
|
|
282
|
+
state: z.ZodOptional<z.ZodString>;
|
|
283
|
+
lat: z.ZodOptional<z.ZodNumber>;
|
|
284
|
+
lng: z.ZodOptional<z.ZodNumber>;
|
|
285
|
+
}, "strip", z.ZodTypeAny, {
|
|
286
|
+
placeId: string;
|
|
287
|
+
name?: string | undefined;
|
|
288
|
+
address?: string | undefined;
|
|
289
|
+
city?: string | undefined;
|
|
290
|
+
state?: string | undefined;
|
|
291
|
+
lat?: number | undefined;
|
|
292
|
+
lng?: number | undefined;
|
|
293
|
+
}, {
|
|
294
|
+
placeId: string;
|
|
295
|
+
name?: string | undefined;
|
|
296
|
+
address?: string | undefined;
|
|
297
|
+
city?: string | undefined;
|
|
298
|
+
state?: string | undefined;
|
|
299
|
+
lat?: number | undefined;
|
|
300
|
+
lng?: number | undefined;
|
|
301
|
+
}>, "many">>;
|
|
302
|
+
budget: z.ZodOptional<z.ZodObject<{
|
|
303
|
+
min: z.ZodNumber;
|
|
304
|
+
max: z.ZodNumber;
|
|
305
|
+
}, "strip", z.ZodTypeAny, {
|
|
306
|
+
min: number;
|
|
307
|
+
max: number;
|
|
308
|
+
}, {
|
|
309
|
+
min: number;
|
|
310
|
+
max: number;
|
|
311
|
+
}>>;
|
|
312
|
+
planTotalBudget: z.ZodOptional<z.ZodNumber>;
|
|
313
|
+
planDurationMonths: z.ZodOptional<z.ZodNumber>;
|
|
314
|
+
goals: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
315
|
+
category: z.ZodOptional<z.ZodString>;
|
|
316
|
+
analysisDepth: z.ZodDefault<z.ZodOptional<z.ZodEnum<["standard", "deep"]>>>;
|
|
317
|
+
hubspotCompanyId: z.ZodOptional<z.ZodString>;
|
|
318
|
+
hubspotUrl: z.ZodOptional<z.ZodString>;
|
|
319
|
+
}, "strip", z.ZodTypeAny, {
|
|
320
|
+
businessDescription: string;
|
|
321
|
+
knownCompetitors: {
|
|
322
|
+
name: string;
|
|
323
|
+
placeId?: string | undefined;
|
|
324
|
+
notes?: string | undefined;
|
|
325
|
+
address?: string | undefined;
|
|
326
|
+
website?: string | undefined;
|
|
327
|
+
}[];
|
|
328
|
+
discoverAdditional: boolean;
|
|
329
|
+
maxAdditionalCompetitors: number;
|
|
330
|
+
analysisDepth: "standard" | "deep";
|
|
331
|
+
name?: string | undefined;
|
|
332
|
+
placeId?: string | undefined;
|
|
333
|
+
brand?: string | undefined;
|
|
334
|
+
businessName?: string | undefined;
|
|
335
|
+
city?: string | undefined;
|
|
336
|
+
state?: string | undefined;
|
|
337
|
+
geo?: string | undefined;
|
|
338
|
+
targetGeo?: string | undefined;
|
|
339
|
+
lat?: number | undefined;
|
|
340
|
+
lng?: number | undefined;
|
|
341
|
+
chainDetected?: boolean | undefined;
|
|
342
|
+
selectedAreas?: string[] | undefined;
|
|
343
|
+
url?: string | undefined;
|
|
344
|
+
marketId?: string | undefined;
|
|
345
|
+
marketCenter?: {
|
|
346
|
+
lat: number;
|
|
347
|
+
lng: number;
|
|
348
|
+
} | undefined;
|
|
349
|
+
serviceAreaConfig?: {
|
|
350
|
+
type: "physical" | "hq_radius" | "metro" | "custom_zips";
|
|
351
|
+
radiusMiles?: number | undefined;
|
|
352
|
+
zipCodes?: string[] | undefined;
|
|
353
|
+
} | undefined;
|
|
354
|
+
prospectPlaceId?: string | undefined;
|
|
355
|
+
prospectBrandName?: string | undefined;
|
|
356
|
+
prospectChainMode?: "single" | "dma" | "overlap" | "all" | undefined;
|
|
357
|
+
prospectLocationCount?: number | undefined;
|
|
358
|
+
prospectLocations?: {
|
|
359
|
+
placeId: string;
|
|
360
|
+
name?: string | undefined;
|
|
361
|
+
address?: string | undefined;
|
|
362
|
+
city?: string | undefined;
|
|
363
|
+
state?: string | undefined;
|
|
364
|
+
lat?: number | undefined;
|
|
365
|
+
lng?: number | undefined;
|
|
366
|
+
}[] | undefined;
|
|
367
|
+
budget?: {
|
|
368
|
+
min: number;
|
|
369
|
+
max: number;
|
|
370
|
+
} | undefined;
|
|
371
|
+
planTotalBudget?: number | undefined;
|
|
372
|
+
planDurationMonths?: number | undefined;
|
|
373
|
+
goals?: string[] | undefined;
|
|
374
|
+
category?: string | undefined;
|
|
375
|
+
hubspotCompanyId?: string | undefined;
|
|
376
|
+
hubspotUrl?: string | undefined;
|
|
377
|
+
}, {
|
|
378
|
+
knownCompetitors: {
|
|
379
|
+
name: string;
|
|
380
|
+
placeId?: string | undefined;
|
|
381
|
+
notes?: string | undefined;
|
|
382
|
+
address?: string | undefined;
|
|
383
|
+
website?: string | undefined;
|
|
384
|
+
}[];
|
|
385
|
+
name?: string | undefined;
|
|
386
|
+
placeId?: string | undefined;
|
|
387
|
+
brand?: string | undefined;
|
|
388
|
+
businessName?: string | undefined;
|
|
389
|
+
city?: string | undefined;
|
|
390
|
+
state?: string | undefined;
|
|
391
|
+
geo?: string | undefined;
|
|
392
|
+
targetGeo?: string | undefined;
|
|
393
|
+
lat?: number | undefined;
|
|
394
|
+
lng?: number | undefined;
|
|
395
|
+
chainDetected?: boolean | undefined;
|
|
396
|
+
selectedAreas?: string[] | undefined;
|
|
397
|
+
businessDescription?: string | undefined;
|
|
398
|
+
url?: string | undefined;
|
|
399
|
+
discoverAdditional?: boolean | undefined;
|
|
400
|
+
maxAdditionalCompetitors?: number | undefined;
|
|
401
|
+
marketId?: string | undefined;
|
|
402
|
+
marketCenter?: {
|
|
403
|
+
lat: number;
|
|
404
|
+
lng: number;
|
|
405
|
+
} | undefined;
|
|
406
|
+
serviceAreaConfig?: {
|
|
407
|
+
type?: "physical" | "hq_radius" | "metro" | "custom_zips" | undefined;
|
|
408
|
+
radiusMiles?: number | undefined;
|
|
409
|
+
zipCodes?: string[] | undefined;
|
|
410
|
+
} | undefined;
|
|
411
|
+
prospectPlaceId?: string | undefined;
|
|
412
|
+
prospectBrandName?: string | undefined;
|
|
413
|
+
prospectChainMode?: "single" | "dma" | "overlap" | "all" | undefined;
|
|
414
|
+
prospectLocationCount?: number | undefined;
|
|
415
|
+
prospectLocations?: {
|
|
416
|
+
placeId: string;
|
|
417
|
+
name?: string | undefined;
|
|
418
|
+
address?: string | undefined;
|
|
419
|
+
city?: string | undefined;
|
|
420
|
+
state?: string | undefined;
|
|
421
|
+
lat?: number | undefined;
|
|
422
|
+
lng?: number | undefined;
|
|
423
|
+
}[] | undefined;
|
|
424
|
+
budget?: {
|
|
425
|
+
min: number;
|
|
426
|
+
max: number;
|
|
427
|
+
} | undefined;
|
|
428
|
+
planTotalBudget?: number | undefined;
|
|
429
|
+
planDurationMonths?: number | undefined;
|
|
430
|
+
goals?: string[] | undefined;
|
|
431
|
+
category?: string | undefined;
|
|
432
|
+
analysisDepth?: "standard" | "deep" | undefined;
|
|
433
|
+
hubspotCompanyId?: string | undefined;
|
|
434
|
+
hubspotUrl?: string | undefined;
|
|
435
|
+
}>;
|
|
436
|
+
export type KnownCompetitor = z.infer<typeof KnownCompetitorSchema>;
|
|
437
|
+
export type AnalyzePlanInput = z.infer<typeof AnalyzePlanInputSchema>;
|
|
438
|
+
/**
|
|
439
|
+
* Validation state after processing
|
|
440
|
+
*/
|
|
441
|
+
export interface ValidatedCompetitor extends KnownCompetitor {
|
|
442
|
+
validated: boolean | 'needs_clarification';
|
|
443
|
+
status: 'confirmed' | 'not_found' | 'multiple_matches';
|
|
444
|
+
confidence: number;
|
|
445
|
+
placeId?: string;
|
|
446
|
+
verifiedName?: string;
|
|
447
|
+
verifiedAddress?: string;
|
|
448
|
+
geometry?: {
|
|
449
|
+
lat: number;
|
|
450
|
+
lng: number;
|
|
451
|
+
};
|
|
452
|
+
category?: string;
|
|
453
|
+
rating?: number;
|
|
454
|
+
reviewCount?: number;
|
|
455
|
+
options?: Array<{
|
|
456
|
+
placeId: string;
|
|
457
|
+
name: string;
|
|
458
|
+
address: string;
|
|
459
|
+
distance?: number;
|
|
460
|
+
}>;
|
|
461
|
+
suggestion?: string;
|
|
462
|
+
source: 'user_provided' | 'ai_suggested';
|
|
463
|
+
originalName?: string;
|
|
464
|
+
businessType?: BusinessType;
|
|
465
|
+
serviceArea?: ServiceArea | null;
|
|
466
|
+
serviceAreaOverlap?: ServiceAreaOverlap | null;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Base business profile (minimal required fields)
|
|
470
|
+
*/
|
|
471
|
+
export interface BaseBusinessProfile {
|
|
472
|
+
name: string;
|
|
473
|
+
placeId: string | null;
|
|
474
|
+
address: string;
|
|
475
|
+
category?: string;
|
|
476
|
+
rating?: number;
|
|
477
|
+
reviewCount?: number;
|
|
478
|
+
phone?: string;
|
|
479
|
+
website?: string;
|
|
480
|
+
seeded?: boolean;
|
|
481
|
+
source?: string;
|
|
482
|
+
validationBypassed?: boolean;
|
|
483
|
+
bypassedAI?: boolean;
|
|
484
|
+
lat?: number;
|
|
485
|
+
lng?: number;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Enrichment augmentation data (additional fields added during enrichment)
|
|
489
|
+
*/
|
|
490
|
+
export interface EnrichmentAugmentation {
|
|
491
|
+
digitalMaturity?: number;
|
|
492
|
+
maturityStage?: string;
|
|
493
|
+
strengths?: string[];
|
|
494
|
+
gaps?: string[];
|
|
495
|
+
competitiveContext?: any;
|
|
496
|
+
comparativeInsights?: any;
|
|
497
|
+
[key: string]: any;
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Validated business (base profile + enrichment data)
|
|
501
|
+
*/
|
|
502
|
+
export interface ValidatedBusiness extends BaseBusinessProfile, Partial<EnrichmentAugmentation> {
|
|
503
|
+
validated: boolean;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Fully enriched business (all enrichment fields guaranteed)
|
|
507
|
+
*/
|
|
508
|
+
export interface FullyEnrichedBusiness extends ValidatedBusiness {
|
|
509
|
+
digitalMaturity: number;
|
|
510
|
+
maturityStage: string;
|
|
511
|
+
strengths: string[];
|
|
512
|
+
gaps: string[];
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Enrichment result structure
|
|
516
|
+
*/
|
|
517
|
+
export interface EnrichedBusiness {
|
|
518
|
+
placeId: string;
|
|
519
|
+
name: string;
|
|
520
|
+
address: string;
|
|
521
|
+
city: string;
|
|
522
|
+
state: string;
|
|
523
|
+
lat: number;
|
|
524
|
+
lng: number;
|
|
525
|
+
category: string;
|
|
526
|
+
businessType?: BusinessType;
|
|
527
|
+
serviceArea?: ServiceArea | null;
|
|
528
|
+
serviceAreaOverlap?: ServiceAreaOverlap | null;
|
|
529
|
+
rating: number;
|
|
530
|
+
reviewCount: number;
|
|
531
|
+
metaAds: {
|
|
532
|
+
present: boolean;
|
|
533
|
+
activeCount: number;
|
|
534
|
+
totalSeen: number;
|
|
535
|
+
oldestAdDate: string | null;
|
|
536
|
+
impressionsRange: string | null;
|
|
537
|
+
creative?: {
|
|
538
|
+
themes: string[];
|
|
539
|
+
messaging: string[];
|
|
540
|
+
visualStyle: string;
|
|
541
|
+
offers: string[];
|
|
542
|
+
ctaTypes: string[];
|
|
543
|
+
ads?: Array<{
|
|
544
|
+
adId?: string;
|
|
545
|
+
adImage?: string;
|
|
546
|
+
adCopy?: string;
|
|
547
|
+
headline?: string;
|
|
548
|
+
callToAction?: string;
|
|
549
|
+
publisherPlatforms?: string[];
|
|
550
|
+
startDate?: string;
|
|
551
|
+
status?: string;
|
|
552
|
+
}>;
|
|
553
|
+
};
|
|
554
|
+
audience?: {
|
|
555
|
+
platforms: string[];
|
|
556
|
+
ageRanges: string[];
|
|
557
|
+
genders: string[];
|
|
558
|
+
interests: string[];
|
|
559
|
+
};
|
|
560
|
+
estimatedSpend?: {
|
|
561
|
+
monthly: number;
|
|
562
|
+
confidence: 'low' | 'medium' | 'high';
|
|
563
|
+
basis: string;
|
|
564
|
+
};
|
|
565
|
+
};
|
|
566
|
+
website: {
|
|
567
|
+
url: string | null;
|
|
568
|
+
accessible: boolean;
|
|
569
|
+
seo?: {
|
|
570
|
+
titleTag: string;
|
|
571
|
+
metaDescription: string;
|
|
572
|
+
h1Tags: string[];
|
|
573
|
+
contentWordCount: number;
|
|
574
|
+
keywordDensity: {
|
|
575
|
+
[keyword: string]: number;
|
|
576
|
+
};
|
|
577
|
+
mobileSpeed: number;
|
|
578
|
+
desktopSpeed: number;
|
|
579
|
+
coreWebVitals: boolean;
|
|
580
|
+
httpsEnabled: boolean;
|
|
581
|
+
structuredData: string[];
|
|
582
|
+
nabConsistency: boolean;
|
|
583
|
+
localKeywords: string[];
|
|
584
|
+
locationPages: number;
|
|
585
|
+
};
|
|
586
|
+
conversion?: {
|
|
587
|
+
hasBooking: boolean;
|
|
588
|
+
hasContactForm: boolean;
|
|
589
|
+
hasPhoneNumber: boolean;
|
|
590
|
+
hasChatWidget: boolean;
|
|
591
|
+
ctaCount: number;
|
|
592
|
+
ctaTypes: string[];
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
technologies: string[];
|
|
596
|
+
sophistication: 'basic' | 'intermediate' | 'advanced';
|
|
597
|
+
hasEcommerce: boolean;
|
|
598
|
+
hasAnalytics: boolean;
|
|
599
|
+
hasBlog: boolean;
|
|
600
|
+
mobileOptimized: boolean;
|
|
601
|
+
loadTime: number;
|
|
602
|
+
social: {
|
|
603
|
+
facebook?: {
|
|
604
|
+
url: string;
|
|
605
|
+
followers: number;
|
|
606
|
+
postsPerWeek: number;
|
|
607
|
+
engagementRate: number;
|
|
608
|
+
lastPostDate: string;
|
|
609
|
+
};
|
|
610
|
+
instagram?: {
|
|
611
|
+
url: string;
|
|
612
|
+
followers: number;
|
|
613
|
+
postsPerWeek: number;
|
|
614
|
+
engagementRate: number;
|
|
615
|
+
lastPostDate: string;
|
|
616
|
+
};
|
|
617
|
+
linkedin?: {
|
|
618
|
+
url: string;
|
|
619
|
+
followers: number;
|
|
620
|
+
};
|
|
621
|
+
};
|
|
622
|
+
googleAds?: {
|
|
623
|
+
active: boolean;
|
|
624
|
+
adCount: number;
|
|
625
|
+
advertiserName?: string;
|
|
626
|
+
lastScrapedAt?: Date;
|
|
627
|
+
fromCache?: boolean;
|
|
628
|
+
ads?: Array<{
|
|
629
|
+
id: string;
|
|
630
|
+
adType: 'image' | 'video' | 'text';
|
|
631
|
+
imageUrl?: string;
|
|
632
|
+
videoUrl?: string;
|
|
633
|
+
headline?: string;
|
|
634
|
+
description?: string;
|
|
635
|
+
landingPage?: string;
|
|
636
|
+
advertiser?: string;
|
|
637
|
+
format?: string;
|
|
638
|
+
lastShownDate?: string;
|
|
639
|
+
regions?: string[];
|
|
640
|
+
}>;
|
|
641
|
+
};
|
|
642
|
+
creatives?: CompetitorCreative[];
|
|
643
|
+
strategy: {
|
|
644
|
+
channelPresence: {
|
|
645
|
+
[channel: string]: {
|
|
646
|
+
active: boolean;
|
|
647
|
+
evidence: string;
|
|
648
|
+
investmentLevel: 'low' | 'medium' | 'high';
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
messaging: {
|
|
652
|
+
primaryThemes: string[];
|
|
653
|
+
positioning: string;
|
|
654
|
+
tone: 'professional' | 'casual' | 'playful' | 'authoritative';
|
|
655
|
+
};
|
|
656
|
+
sophistication: {
|
|
657
|
+
level: 'beginner' | 'intermediate' | 'advanced';
|
|
658
|
+
signals: string[];
|
|
659
|
+
investmentLevel: 'low' | 'medium' | 'high';
|
|
660
|
+
};
|
|
661
|
+
strengths: string[];
|
|
662
|
+
gaps: string[];
|
|
663
|
+
};
|
|
664
|
+
digitalMaturity: number;
|
|
665
|
+
maturityStage: 'Foundation' | 'Growth' | 'Advanced';
|
|
666
|
+
source: 'prospect' | 'user_provided' | 'ai_suggested';
|
|
667
|
+
validated: boolean;
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Prospect context for media planning
|
|
671
|
+
* Contains essential business information extracted from enrichment
|
|
672
|
+
*/
|
|
673
|
+
export interface PlanningProspect {
|
|
674
|
+
name: string;
|
|
675
|
+
category: string;
|
|
676
|
+
serviceAreaDescription: string;
|
|
677
|
+
websiteSummary?: string;
|
|
678
|
+
keyValueProps?: string[];
|
|
679
|
+
currentChannels?: string[];
|
|
680
|
+
weaknesses?: string[];
|
|
681
|
+
gaps?: string[];
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* Competitor context for media planning
|
|
685
|
+
* Summarized competitive intelligence including creative themes
|
|
686
|
+
*/
|
|
687
|
+
export interface PlanningCompetitor {
|
|
688
|
+
name: string;
|
|
689
|
+
category: string;
|
|
690
|
+
serviceAreaDescription?: string;
|
|
691
|
+
adSpendLevel?: 'low' | 'medium' | 'high';
|
|
692
|
+
googleAdsThemes?: string[];
|
|
693
|
+
metaAdsThemes?: string[];
|
|
694
|
+
landingPagePatterns?: string[];
|
|
695
|
+
activeChannels?: string[];
|
|
696
|
+
investmentLevel?: 'low' | 'medium' | 'high';
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Performance benchmarks for a product/channel
|
|
700
|
+
* Used to inform realistic KPI expectations
|
|
701
|
+
*/
|
|
702
|
+
export interface PlanningBenchmark {
|
|
703
|
+
productCode: string;
|
|
704
|
+
mappedIndustry: string;
|
|
705
|
+
cpm?: number;
|
|
706
|
+
ctr?: number;
|
|
707
|
+
cvr?: number;
|
|
708
|
+
viewability?: number;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* PlanningContext DTO
|
|
712
|
+
* Single structured object passed to media plan LLM
|
|
713
|
+
*
|
|
714
|
+
* This replaces scattered string concatenation with a clean JSON structure
|
|
715
|
+
* that the model can reference systematically when generating plans.
|
|
716
|
+
*/
|
|
717
|
+
export interface PlanningContext {
|
|
718
|
+
prospect: PlanningProspect;
|
|
719
|
+
competitors: PlanningCompetitor[];
|
|
720
|
+
performanceBenchmarks: PlanningBenchmark[];
|
|
721
|
+
competitorCount: number;
|
|
722
|
+
advertisingIntensity: 'low' | 'medium' | 'high';
|
|
723
|
+
marketGaps: string[];
|
|
724
|
+
creativeStrategySummary?: string;
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Local Market Score - single 0-100 number for quick comparison
|
|
728
|
+
* Distills the 5-pillar Local Authority Score into a sales-friendly format
|
|
729
|
+
*/
|
|
730
|
+
export interface LocalMarketScore {
|
|
731
|
+
/** Single 0-100 score for quick comparison */
|
|
732
|
+
score: number;
|
|
733
|
+
/** Classification for sales conversations */
|
|
734
|
+
tier: 'Leader' | 'Competitive' | 'Emerging' | 'At Risk';
|
|
735
|
+
/** Color code for UI (red, orange, yellow, gray) */
|
|
736
|
+
tierColor: 'red' | 'orange' | 'yellow' | 'gray';
|
|
737
|
+
/** One-line summary for sales pitch */
|
|
738
|
+
summary: string;
|
|
739
|
+
/** Component breakdown for details */
|
|
740
|
+
breakdown: {
|
|
741
|
+
localPack: {
|
|
742
|
+
score: number;
|
|
743
|
+
maxScore: 40;
|
|
744
|
+
label: string;
|
|
745
|
+
};
|
|
746
|
+
reviews: {
|
|
747
|
+
score: number;
|
|
748
|
+
maxScore: 30;
|
|
749
|
+
label: string;
|
|
750
|
+
};
|
|
751
|
+
gbpProfile: {
|
|
752
|
+
score: number;
|
|
753
|
+
maxScore: 20;
|
|
754
|
+
label: string;
|
|
755
|
+
};
|
|
756
|
+
digital: {
|
|
757
|
+
score: number;
|
|
758
|
+
maxScore: 10;
|
|
759
|
+
label: string;
|
|
760
|
+
};
|
|
761
|
+
};
|
|
762
|
+
/** Calculated timestamp */
|
|
763
|
+
calculatedAt: string;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Revenue Impact - quantifies the dollar value of local search position
|
|
767
|
+
*/
|
|
768
|
+
export interface RevenueImpact {
|
|
769
|
+
/** Monthly revenue prospect is currently capturing */
|
|
770
|
+
currentMonthlyRevenue: number;
|
|
771
|
+
/** Monthly revenue the market leader captures */
|
|
772
|
+
leaderMonthlyRevenue: number;
|
|
773
|
+
/** Monthly revenue gap (what prospect is missing) */
|
|
774
|
+
monthlyRevenueGap: number;
|
|
775
|
+
/** Annual revenue opportunity */
|
|
776
|
+
annualOpportunity: number;
|
|
777
|
+
/** Benchmarks used for calculation */
|
|
778
|
+
benchmarks: {
|
|
779
|
+
avgTicket: number;
|
|
780
|
+
conversionRate: number;
|
|
781
|
+
category: string;
|
|
782
|
+
};
|
|
783
|
+
/** Position-based CTR breakdown */
|
|
784
|
+
positionAnalysis: {
|
|
785
|
+
prospectCTR: number;
|
|
786
|
+
leaderCTR: number;
|
|
787
|
+
ctrGap: number;
|
|
788
|
+
};
|
|
789
|
+
/** Funnel metrics for transparency */
|
|
790
|
+
funnel: {
|
|
791
|
+
monthlySearchVolume: number;
|
|
792
|
+
prospectClicks: number;
|
|
793
|
+
prospectConversions: number;
|
|
794
|
+
leaderClicks: number;
|
|
795
|
+
leaderConversions: number;
|
|
796
|
+
};
|
|
797
|
+
/** Sales-ready talking points */
|
|
798
|
+
insights: {
|
|
799
|
+
headline: string;
|
|
800
|
+
comparison: string;
|
|
801
|
+
opportunity: string;
|
|
802
|
+
};
|
|
803
|
+
/** Calculation timestamp */
|
|
804
|
+
calculatedAt: string;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Single comparison point in a head-to-head analysis
|
|
808
|
+
*/
|
|
809
|
+
export interface WinLossItem {
|
|
810
|
+
/** What's being compared */
|
|
811
|
+
metric: string;
|
|
812
|
+
/** Prospect's value/status */
|
|
813
|
+
prospectValue: string | number;
|
|
814
|
+
/** Competitor's value/status */
|
|
815
|
+
competitorValue: string | number;
|
|
816
|
+
/** Whether prospect wins this comparison */
|
|
817
|
+
prospectWins: boolean;
|
|
818
|
+
/** Impact level for prioritization */
|
|
819
|
+
impact: 'high' | 'medium' | 'low';
|
|
820
|
+
/** One-sentence insight for sales pitch */
|
|
821
|
+
insight: string;
|
|
822
|
+
/** Actionable recommendation */
|
|
823
|
+
action?: string;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Head-to-head comparison with a single competitor
|
|
827
|
+
*/
|
|
828
|
+
export interface HeadToHeadComparison {
|
|
829
|
+
/** Competitor being compared */
|
|
830
|
+
competitorName: string;
|
|
831
|
+
/** List of wins for prospect */
|
|
832
|
+
wins: WinLossItem[];
|
|
833
|
+
/** List of losses for prospect */
|
|
834
|
+
losses: WinLossItem[];
|
|
835
|
+
/** Overall assessment */
|
|
836
|
+
summary: {
|
|
837
|
+
winCount: number;
|
|
838
|
+
lossCount: number;
|
|
839
|
+
verdict: 'prospect_leads' | 'competitor_leads' | 'close_match';
|
|
840
|
+
headline: string;
|
|
841
|
+
};
|
|
842
|
+
/** Priority actions based on losses */
|
|
843
|
+
priorityActions: string[];
|
|
844
|
+
/** Calculated at timestamp */
|
|
845
|
+
calculatedAt: string;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Full competitive narrative across all competitors
|
|
849
|
+
*/
|
|
850
|
+
export interface CompetitiveNarrative {
|
|
851
|
+
/** Prospect name */
|
|
852
|
+
prospectName: string;
|
|
853
|
+
/** Head-to-head comparisons with each competitor */
|
|
854
|
+
headToHead: HeadToHeadComparison[];
|
|
855
|
+
/** Market-level summary */
|
|
856
|
+
marketSummary: {
|
|
857
|
+
/** Where prospect wins vs the market */
|
|
858
|
+
marketWins: string[];
|
|
859
|
+
/** Where prospect loses vs the market */
|
|
860
|
+
marketLosses: string[];
|
|
861
|
+
/** Overall market position */
|
|
862
|
+
position: 'leader' | 'competitive' | 'challenger' | 'at_risk';
|
|
863
|
+
/** Executive summary paragraph */
|
|
864
|
+
executiveSummary: string;
|
|
865
|
+
};
|
|
866
|
+
/** Top 3 actions to take */
|
|
867
|
+
topActions: Array<{
|
|
868
|
+
action: string;
|
|
869
|
+
impact: string;
|
|
870
|
+
competitor: string;
|
|
871
|
+
}>;
|
|
872
|
+
/** Calculated at timestamp */
|
|
873
|
+
calculatedAt: string;
|
|
874
|
+
}
|
|
875
|
+
/** Prospect enrichment from business lookup + website analysis */
|
|
876
|
+
export interface IntelligenceProspect extends PlanningProspect {
|
|
877
|
+
url?: string;
|
|
878
|
+
placeId?: string;
|
|
879
|
+
address?: string;
|
|
880
|
+
city?: string;
|
|
881
|
+
state?: string;
|
|
882
|
+
phone?: string;
|
|
883
|
+
rating?: number;
|
|
884
|
+
reviewCount?: number;
|
|
885
|
+
techStack?: string[];
|
|
886
|
+
digitalMaturityScore?: number;
|
|
887
|
+
pixelsDetected?: string[];
|
|
888
|
+
socialProfiles?: Record<string, string>;
|
|
889
|
+
/** Nielsen DMA code for market intelligence lookups */
|
|
890
|
+
dmaCode?: string;
|
|
891
|
+
}
|
|
892
|
+
/** Ad intelligence for a single competitor or prospect */
|
|
893
|
+
export interface AdIntelligenceEntry {
|
|
894
|
+
entityName: string;
|
|
895
|
+
googleAdsCount?: number;
|
|
896
|
+
googleAdsThemes?: string[];
|
|
897
|
+
metaAdsCount?: number;
|
|
898
|
+
metaAdsThemes?: string[];
|
|
899
|
+
estimatedMonthlySpend?: number;
|
|
900
|
+
activeChannels?: string[];
|
|
901
|
+
creativeExamples?: Array<{
|
|
902
|
+
platform: string;
|
|
903
|
+
headline?: string;
|
|
904
|
+
description?: string;
|
|
905
|
+
imageUrl?: string;
|
|
906
|
+
}>;
|
|
907
|
+
}
|
|
908
|
+
/** Pixel / tracking detection results */
|
|
909
|
+
export interface PixelDetectionResult {
|
|
910
|
+
url: string;
|
|
911
|
+
pixelsFound: string[];
|
|
912
|
+
analyticsTools: string[];
|
|
913
|
+
adPlatforms: string[];
|
|
914
|
+
tagManagers: string[];
|
|
915
|
+
remarketingActive: boolean;
|
|
916
|
+
}
|
|
917
|
+
/** Domain SEO overview */
|
|
918
|
+
export interface DomainOverviewResult {
|
|
919
|
+
domain: string;
|
|
920
|
+
organicKeywords?: number;
|
|
921
|
+
organicTraffic?: number;
|
|
922
|
+
domainAuthority?: number;
|
|
923
|
+
backlinks?: number;
|
|
924
|
+
topKeywords?: Array<{
|
|
925
|
+
keyword: string;
|
|
926
|
+
position: number;
|
|
927
|
+
volume: number;
|
|
928
|
+
}>;
|
|
929
|
+
}
|
|
930
|
+
/** SEO keyword/ranking analysis (distinct from domain-level overview) */
|
|
931
|
+
export interface SeoAnalysisResult {
|
|
932
|
+
domain: string;
|
|
933
|
+
totalKeywordsTracked?: number;
|
|
934
|
+
keywordsInTop10?: number;
|
|
935
|
+
keywordsInTop3?: number;
|
|
936
|
+
organicTraffic?: number;
|
|
937
|
+
topKeywords?: Array<{
|
|
938
|
+
keyword: string;
|
|
939
|
+
position: number;
|
|
940
|
+
volume: number;
|
|
941
|
+
difficulty?: number;
|
|
942
|
+
}>;
|
|
943
|
+
contentGaps?: string[];
|
|
944
|
+
technicalIssues?: string[];
|
|
945
|
+
}
|
|
946
|
+
/** Landing page audit results */
|
|
947
|
+
export interface LandingPageAuditResult {
|
|
948
|
+
url: string;
|
|
949
|
+
mobileScore?: number;
|
|
950
|
+
desktopScore?: number;
|
|
951
|
+
loadTimeMs?: number;
|
|
952
|
+
hasCtaAboveFold?: boolean;
|
|
953
|
+
hasForm?: boolean;
|
|
954
|
+
hasPhoneNumber?: boolean;
|
|
955
|
+
issues?: string[];
|
|
956
|
+
}
|
|
957
|
+
/** Social media presence data */
|
|
958
|
+
export interface SocialDataResult {
|
|
959
|
+
profiles: Array<{
|
|
960
|
+
platform: string;
|
|
961
|
+
url?: string;
|
|
962
|
+
followers?: number;
|
|
963
|
+
postsPerWeek?: number;
|
|
964
|
+
engagementRate?: number;
|
|
965
|
+
}>;
|
|
966
|
+
}
|
|
967
|
+
/** Strategy brief output */
|
|
968
|
+
export interface StrategyBriefResult {
|
|
969
|
+
executiveSummary: string;
|
|
970
|
+
marketPosition: string;
|
|
971
|
+
recommendedChannels: Array<{
|
|
972
|
+
channel: string;
|
|
973
|
+
productCode?: string;
|
|
974
|
+
rationale: string;
|
|
975
|
+
priority: 'high' | 'medium' | 'low';
|
|
976
|
+
}>;
|
|
977
|
+
competitiveGaps: string[];
|
|
978
|
+
targetAudience?: string;
|
|
979
|
+
messagingThemes?: string[];
|
|
980
|
+
}
|
|
981
|
+
/** Market context from market intelligence tables */
|
|
982
|
+
export interface MarketContextResult {
|
|
983
|
+
seasonality?: {
|
|
984
|
+
type: string;
|
|
985
|
+
score: number;
|
|
986
|
+
peakMonths: string[];
|
|
987
|
+
monthlyMultipliers: Record<string, number>;
|
|
988
|
+
};
|
|
989
|
+
benchmarks?: {
|
|
990
|
+
avgTicket: number;
|
|
991
|
+
conversionRate: number;
|
|
992
|
+
marketSaturationScore?: number;
|
|
993
|
+
yoyGrowthRate?: number;
|
|
994
|
+
};
|
|
995
|
+
marketScore?: {
|
|
996
|
+
overallScore: number;
|
|
997
|
+
timingScore: number;
|
|
998
|
+
competitionScore: number;
|
|
999
|
+
economicScore?: number;
|
|
1000
|
+
recommendation: string;
|
|
1001
|
+
rationale?: string;
|
|
1002
|
+
};
|
|
1003
|
+
competitiveLandscape?: {
|
|
1004
|
+
totalBusinesses?: number;
|
|
1005
|
+
advertisingPenetration?: number;
|
|
1006
|
+
marketConcentration?: string;
|
|
1007
|
+
opportunityScore?: number;
|
|
1008
|
+
};
|
|
1009
|
+
economicIndicators?: {
|
|
1010
|
+
consumerConfidenceIndex?: number;
|
|
1011
|
+
unemploymentRate?: number;
|
|
1012
|
+
medianHouseholdIncome?: number;
|
|
1013
|
+
economicHealthScore?: number;
|
|
1014
|
+
economicHealthTrend?: string;
|
|
1015
|
+
};
|
|
1016
|
+
searchVolume?: {
|
|
1017
|
+
monthlySearchVolume?: number;
|
|
1018
|
+
topKeywords?: Array<{
|
|
1019
|
+
keyword: string;
|
|
1020
|
+
volume: number;
|
|
1021
|
+
}>;
|
|
1022
|
+
yoyChangePct?: number;
|
|
1023
|
+
trendDirection?: string;
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* IntelligenceData — the single structured blob written to plans.intelligenceData.
|
|
1028
|
+
*
|
|
1029
|
+
* fn-v2 analyze-location.ts writes this after all enrichment tools complete.
|
|
1030
|
+
* All fields are optional — data fills in progressively as tools complete.
|
|
1031
|
+
*/
|
|
1032
|
+
export interface IntelligenceData {
|
|
1033
|
+
/** Enriched prospect profile */
|
|
1034
|
+
prospect?: IntelligenceProspect;
|
|
1035
|
+
/** Enriched competitor profiles */
|
|
1036
|
+
competitors?: PlanningCompetitor[];
|
|
1037
|
+
/** Ad intelligence (prospect + competitors) */
|
|
1038
|
+
adIntelligence?: AdIntelligenceEntry[];
|
|
1039
|
+
/** Pixel / tracking detection for prospect site */
|
|
1040
|
+
pixelDetection?: PixelDetectionResult;
|
|
1041
|
+
/** Domain SEO overview for prospect */
|
|
1042
|
+
domainOverview?: DomainOverviewResult;
|
|
1043
|
+
/** Landing page audit for prospect */
|
|
1044
|
+
landingPageAudit?: LandingPageAuditResult;
|
|
1045
|
+
/** SEO keyword/ranking analysis */
|
|
1046
|
+
seoData?: SeoAnalysisResult;
|
|
1047
|
+
/** Social media presence */
|
|
1048
|
+
socialData?: SocialDataResult;
|
|
1049
|
+
/** Strategy brief from LLM analysis */
|
|
1050
|
+
strategyBrief?: StrategyBriefResult;
|
|
1051
|
+
/** Market intelligence (seasonality, benchmarks, scores) */
|
|
1052
|
+
marketContext?: MarketContextResult;
|
|
1053
|
+
/** Metadata */
|
|
1054
|
+
enrichedAt?: string;
|
|
1055
|
+
enrichmentVersion?: string;
|
|
1056
|
+
toolsCompleted?: string[];
|
|
1057
|
+
toolsFailed?: string[];
|
|
1058
|
+
}
|
|
1059
|
+
//# sourceMappingURL=analyzeTypes.d.ts.map
|