@lynxflow/seo-engine 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +333 -82
- package/connectors/cloudflare-worker/worker.js +54 -26
- package/connectors/laravel/LynxSeoController.php +8 -8
- package/connectors/wordpress/lynxseo-connector.php +296 -53
- package/dist/ai-copilot-client.d.ts +36 -0
- package/dist/analytics-client.d.ts +53 -0
- package/dist/auth-key.d.ts +57 -0
- package/dist/backlinks-client.d.ts +31 -0
- package/dist/engine.d.ts +73 -0
- package/dist/i18n-dictionary.d.ts +30 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +1265 -169
- package/dist/index.mjs +1463 -0
- package/dist/indexnow-client.d.ts +25 -0
- package/dist/lago-token-meter.d.ts +36 -0
- package/dist/schema-builder.d.ts +27 -0
- package/dist/serp-client.d.ts +42 -0
- package/dist/site-auditor.d.ts +29 -0
- package/dist/src/ai-copilot-client.d.ts +36 -0
- package/dist/src/analytics-client.d.ts +53 -0
- package/dist/src/auth-key.d.ts +57 -0
- package/dist/src/backlinks-client.d.ts +31 -0
- package/dist/src/engine.d.ts +72 -0
- package/dist/src/i18n-dictionary.d.ts +30 -0
- package/dist/src/index.d.ts +42 -0
- package/dist/src/indexnow-client.d.ts +25 -0
- package/dist/src/lago-token-meter.d.ts +36 -0
- package/dist/src/schema-builder.d.ts +27 -0
- package/dist/src/serp-client.d.ts +42 -0
- package/dist/src/site-auditor.d.ts +29 -0
- package/dist/src/token-quota-manager.d.ts +37 -0
- package/dist/src/types.d.ts +145 -0
- package/dist/token-quota-manager.d.ts +37 -0
- package/dist/types.d.ts +162 -0
- package/lynxflow-seo-engine-1.2.0.tgz +0 -0
- package/package.json +1 -1
- package/src/ai-copilot-client.ts +84 -0
- package/src/analytics-client.ts +141 -0
- package/src/auth-key.ts +203 -0
- package/src/backlinks-client.ts +85 -0
- package/src/engine.ts +499 -85
- package/src/i18n-dictionary.ts +362 -0
- package/src/index.ts +18 -4
- package/src/indexnow-client.ts +94 -0
- package/src/lago-token-meter.ts +7 -2
- package/src/schema-builder.ts +87 -0
- package/src/serp-client.ts +89 -0
- package/src/site-auditor.ts +83 -0
- package/src/types.ts +148 -28
- package/tsconfig.json +14 -0
- package/lynxflow-seo-engine-1.0.0.tgz +0 -0
- package/src/licensing.ts +0 -138
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🪙 Token Quota & Credit Billing Bridge
|
|
3
|
+
*
|
|
4
|
+
* Synchronizes with LynxFlow's official TokenBillingService & CreditService (1$ = 100 credits).
|
|
5
|
+
* Manages tenant monthly quotas, OpenRouter raw provider costs, and credit balances.
|
|
6
|
+
*/
|
|
7
|
+
export interface TokenUsageRecord {
|
|
8
|
+
promptTokens: number;
|
|
9
|
+
completionTokens: number;
|
|
10
|
+
totalTokens: number;
|
|
11
|
+
creditsCharged: number;
|
|
12
|
+
model: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CreditQuotaStatus {
|
|
16
|
+
tier: "starter" | "growth" | "enterprise";
|
|
17
|
+
monthlyCreditBudget: number;
|
|
18
|
+
usedCredits: number;
|
|
19
|
+
remainingCredits: number;
|
|
20
|
+
isQuotaExceeded: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class TokenQuotaManager {
|
|
23
|
+
private tier;
|
|
24
|
+
private monthlyCreditBudget;
|
|
25
|
+
private usedCredits;
|
|
26
|
+
constructor(tier?: "starter" | "growth" | "enterprise");
|
|
27
|
+
private resolveBudget;
|
|
28
|
+
/**
|
|
29
|
+
* Records token usage and calculates LynxFlow Credit cost (1$ = 100 credits).
|
|
30
|
+
* Only charges credits when AI call succeeds (Issue #222 fix).
|
|
31
|
+
*/
|
|
32
|
+
consumeTokens(promptTokens: number, completionTokens: number, model: string, rawProviderCostUsd?: number): TokenUsageRecord;
|
|
33
|
+
/**
|
|
34
|
+
* Gets current credit quota status.
|
|
35
|
+
*/
|
|
36
|
+
getStatus(): CreditQuotaStatus;
|
|
37
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🌟 Types & Interfaces for @lynxflow/seo-engine Universal SDK
|
|
3
|
+
*
|
|
4
|
+
* Full Institutional pSEO Type System matching LynxFlow's 8 Programmatic Matrices:
|
|
5
|
+
* 1. 📍 Local & GEO : [Pillar/Service] × [City/Country]
|
|
6
|
+
* 2. 🥊 VS & Comparatifs : [Brand/Service] vs [Competitor]
|
|
7
|
+
* 3. 🔄 Alternatives : [Competitor] ➔ [Brand/Service]
|
|
8
|
+
* 4. 🏢 Secteurs & Métiers : [Service] × [Industry Vertical] (with localized ROI)
|
|
9
|
+
* 5. 🔌 Intégrations : [Service] × [Third-party Software]
|
|
10
|
+
* 6. 👤 Personas & Décideurs : [Service] × [Decision Maker Role]
|
|
11
|
+
* 7. 🎯 Cas d'Usage : [Problem Statement] ➔ [AI Workflow Solution]
|
|
12
|
+
* 8. 🧮 Simulateurs & Outils : Interactive ROI & Time-Savings Calculators
|
|
13
|
+
*/
|
|
14
|
+
export interface LynxServiceDefinition {
|
|
15
|
+
slug: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
pricePerMonth: number;
|
|
19
|
+
category?: string;
|
|
20
|
+
features?: string[];
|
|
21
|
+
faqs?: {
|
|
22
|
+
question: string;
|
|
23
|
+
answer: string;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
export interface PseoIndustry {
|
|
27
|
+
slug: string;
|
|
28
|
+
name: string;
|
|
29
|
+
singular: string;
|
|
30
|
+
category: string;
|
|
31
|
+
badge?: string;
|
|
32
|
+
painPoints?: string[];
|
|
33
|
+
targetSolutions?: string[];
|
|
34
|
+
roiMonthlySavings?: number;
|
|
35
|
+
hoursSavedWeekly?: number;
|
|
36
|
+
conversionBoost?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface PseoCompetitor {
|
|
39
|
+
slug: string;
|
|
40
|
+
name: string;
|
|
41
|
+
badge?: string;
|
|
42
|
+
category: string;
|
|
43
|
+
priceRange?: string;
|
|
44
|
+
strengths?: string[];
|
|
45
|
+
weaknesses?: string[];
|
|
46
|
+
whySwitchToUs?: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface PseoIntegration {
|
|
49
|
+
slug: string;
|
|
50
|
+
name: string;
|
|
51
|
+
category: string;
|
|
52
|
+
badge?: string;
|
|
53
|
+
syncFeatures?: string[];
|
|
54
|
+
setupTimeMinutes?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface PseoUseCase {
|
|
57
|
+
slug: string;
|
|
58
|
+
title: string;
|
|
59
|
+
problem: string;
|
|
60
|
+
aiSolution: string;
|
|
61
|
+
measurableOutcome?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface PseoPersona {
|
|
64
|
+
slug: string;
|
|
65
|
+
role: string;
|
|
66
|
+
department: string;
|
|
67
|
+
topResponsibilities?: string[];
|
|
68
|
+
timeWastersAutomated?: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface PseoCity {
|
|
71
|
+
slug: string;
|
|
72
|
+
name: string;
|
|
73
|
+
region: string;
|
|
74
|
+
country: string;
|
|
75
|
+
department?: string;
|
|
76
|
+
densityTier?: "major" | "secondary" | "regional";
|
|
77
|
+
}
|
|
78
|
+
export interface PseoCountry {
|
|
79
|
+
code: string;
|
|
80
|
+
name: string;
|
|
81
|
+
currency: string;
|
|
82
|
+
currencySymbol: string;
|
|
83
|
+
locale: string;
|
|
84
|
+
economicMultiplier: number;
|
|
85
|
+
}
|
|
86
|
+
export interface LynxSeoConfig {
|
|
87
|
+
apiKey?: string;
|
|
88
|
+
licenseKey?: string;
|
|
89
|
+
domain: string;
|
|
90
|
+
brandName: string;
|
|
91
|
+
category?: string;
|
|
92
|
+
defaultLocale?: string;
|
|
93
|
+
supportedLocales?: string[];
|
|
94
|
+
currency?: string;
|
|
95
|
+
currencySymbol?: string;
|
|
96
|
+
services: LynxServiceDefinition[];
|
|
97
|
+
industries?: PseoIndustry[];
|
|
98
|
+
competitors?: PseoCompetitor[];
|
|
99
|
+
integrations?: PseoIntegration[];
|
|
100
|
+
personas?: PseoPersona[];
|
|
101
|
+
useCases?: PseoUseCase[];
|
|
102
|
+
cities?: PseoCity[];
|
|
103
|
+
}
|
|
104
|
+
export interface LynxResolvedPage {
|
|
105
|
+
url: string;
|
|
106
|
+
title: string;
|
|
107
|
+
description: string;
|
|
108
|
+
h1: string;
|
|
109
|
+
directAnswer: string;
|
|
110
|
+
locale?: string;
|
|
111
|
+
hreflangs?: {
|
|
112
|
+
lang: string;
|
|
113
|
+
url: string;
|
|
114
|
+
}[];
|
|
115
|
+
faqs: {
|
|
116
|
+
question: string;
|
|
117
|
+
answer: string;
|
|
118
|
+
}[];
|
|
119
|
+
jsonLd: Record<string, unknown>;
|
|
120
|
+
htmlBody: string;
|
|
121
|
+
markdownBody: string;
|
|
122
|
+
executionTimeMs: number;
|
|
123
|
+
localizedRoi?: {
|
|
124
|
+
monthlySavingsFormatted: string;
|
|
125
|
+
hoursSavedWeekly: number;
|
|
126
|
+
annualSavingsFormatted: string;
|
|
127
|
+
currencySymbol: string;
|
|
128
|
+
};
|
|
129
|
+
service?: LynxServiceDefinition;
|
|
130
|
+
industry?: PseoIndustry;
|
|
131
|
+
competitor?: PseoCompetitor;
|
|
132
|
+
integration?: PseoIntegration;
|
|
133
|
+
meta?: {
|
|
134
|
+
title: string;
|
|
135
|
+
description: string;
|
|
136
|
+
h1: string;
|
|
137
|
+
canonical: string;
|
|
138
|
+
openGraphImageUrl?: string;
|
|
139
|
+
};
|
|
140
|
+
debug?: {
|
|
141
|
+
licenseStatus: "active" | "trial" | "expired" | "offline_mode";
|
|
142
|
+
generationMs: number;
|
|
143
|
+
matrixType: string;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🪙 Token Quota & Credit Billing Bridge
|
|
3
|
+
*
|
|
4
|
+
* Synchronizes with LynxFlow's official TokenBillingService & CreditService (1$ = 100 credits).
|
|
5
|
+
* Manages tenant monthly quotas, OpenRouter raw provider costs, and credit balances.
|
|
6
|
+
*/
|
|
7
|
+
export interface TokenUsageRecord {
|
|
8
|
+
promptTokens: number;
|
|
9
|
+
completionTokens: number;
|
|
10
|
+
totalTokens: number;
|
|
11
|
+
creditsCharged: number;
|
|
12
|
+
model: string;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CreditQuotaStatus {
|
|
16
|
+
tier: "starter" | "growth" | "enterprise";
|
|
17
|
+
monthlyCreditBudget: number;
|
|
18
|
+
usedCredits: number;
|
|
19
|
+
remainingCredits: number;
|
|
20
|
+
isQuotaExceeded: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class TokenQuotaManager {
|
|
23
|
+
private tier;
|
|
24
|
+
private monthlyCreditBudget;
|
|
25
|
+
private usedCredits;
|
|
26
|
+
constructor(tier?: "starter" | "growth" | "enterprise");
|
|
27
|
+
private resolveBudget;
|
|
28
|
+
/**
|
|
29
|
+
* Records token usage and calculates LynxFlow Credit cost (1$ = 100 credits).
|
|
30
|
+
* Only charges credits when AI call succeeds (Issue #222 fix).
|
|
31
|
+
*/
|
|
32
|
+
consumeTokens(promptTokens: number, completionTokens: number, model: string, rawProviderCostUsd?: number): TokenUsageRecord;
|
|
33
|
+
/**
|
|
34
|
+
* Gets current credit quota status.
|
|
35
|
+
*/
|
|
36
|
+
getStatus(): CreditQuotaStatus;
|
|
37
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🌟 Types & Interfaces for @lynxflow/seo-engine Universal SDK
|
|
3
|
+
*
|
|
4
|
+
* Full Institutional pSEO Type System matching LynxFlow's 8 Programmatic Matrices:
|
|
5
|
+
* 1. 📍 Local & GEO : [Pillar/Service] × [City/Country]
|
|
6
|
+
* 2. 🥊 VS & Comparatifs : [Brand/Service] vs [Competitor]
|
|
7
|
+
* 3. 🔄 Alternatives : [Competitor] ➔ [Brand/Service]
|
|
8
|
+
* 4. 🏢 Secteurs & Métiers : [Service] × [Industry Vertical] (with localized ROI)
|
|
9
|
+
* 5. 🔌 Intégrations : [Service] × [Third-party Software]
|
|
10
|
+
* 6. 👤 Personas & Décideurs : [Service] × [Decision Maker Role]
|
|
11
|
+
* 7. 🎯 Cas d'Usage : [Problem Statement] ➔ [AI Workflow Solution]
|
|
12
|
+
* 8. 🧮 Simulateurs & Outils : Interactive ROI & Time-Savings Calculators
|
|
13
|
+
*/
|
|
14
|
+
export interface LynxServiceDefinition {
|
|
15
|
+
slug: string;
|
|
16
|
+
name: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
pricePerMonth: number;
|
|
19
|
+
category?: string;
|
|
20
|
+
features?: string[];
|
|
21
|
+
faqs?: {
|
|
22
|
+
question: string;
|
|
23
|
+
answer: string;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
export interface PseoIndustry {
|
|
27
|
+
slug: string;
|
|
28
|
+
name: string;
|
|
29
|
+
singular: string;
|
|
30
|
+
category: string;
|
|
31
|
+
badge?: string;
|
|
32
|
+
painPoints?: string[];
|
|
33
|
+
targetSolutions?: string[];
|
|
34
|
+
roiMonthlySavings?: number;
|
|
35
|
+
hoursSavedWeekly?: number;
|
|
36
|
+
conversionBoost?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface PseoCompetitor {
|
|
39
|
+
slug: string;
|
|
40
|
+
name: string;
|
|
41
|
+
badge?: string;
|
|
42
|
+
category: string;
|
|
43
|
+
priceRange?: string;
|
|
44
|
+
strengths?: string[];
|
|
45
|
+
weaknesses?: string[];
|
|
46
|
+
whySwitchToUs?: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface PseoIntegration {
|
|
49
|
+
slug: string;
|
|
50
|
+
name: string;
|
|
51
|
+
category: string;
|
|
52
|
+
badge?: string;
|
|
53
|
+
syncFeatures?: string[];
|
|
54
|
+
setupTimeMinutes?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface PseoUseCase {
|
|
57
|
+
slug: string;
|
|
58
|
+
title: string;
|
|
59
|
+
problem: string;
|
|
60
|
+
aiSolution: string;
|
|
61
|
+
measurableOutcome?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface PseoPersona {
|
|
64
|
+
slug: string;
|
|
65
|
+
role: string;
|
|
66
|
+
department: string;
|
|
67
|
+
topResponsibilities?: string[];
|
|
68
|
+
timeWastersAutomated?: string[];
|
|
69
|
+
}
|
|
70
|
+
export interface PseoCity {
|
|
71
|
+
slug: string;
|
|
72
|
+
name: string;
|
|
73
|
+
region: string;
|
|
74
|
+
country: string;
|
|
75
|
+
department?: string;
|
|
76
|
+
densityTier?: "major" | "secondary" | "regional";
|
|
77
|
+
}
|
|
78
|
+
export interface PseoCountry {
|
|
79
|
+
code: string;
|
|
80
|
+
name: string;
|
|
81
|
+
currency: string;
|
|
82
|
+
currencySymbol: string;
|
|
83
|
+
locale: string;
|
|
84
|
+
economicMultiplier: number;
|
|
85
|
+
}
|
|
86
|
+
export interface LynxSeoConfig {
|
|
87
|
+
apiKey?: string;
|
|
88
|
+
licenseKey?: string;
|
|
89
|
+
domain: string;
|
|
90
|
+
brandName: string;
|
|
91
|
+
category?: string;
|
|
92
|
+
defaultLocale?: string;
|
|
93
|
+
supportedLocales?: string[];
|
|
94
|
+
currency?: string;
|
|
95
|
+
currencySymbol?: string;
|
|
96
|
+
services: LynxServiceDefinition[];
|
|
97
|
+
industries?: PseoIndustry[];
|
|
98
|
+
competitors?: PseoCompetitor[];
|
|
99
|
+
integrations?: PseoIntegration[];
|
|
100
|
+
personas?: PseoPersona[];
|
|
101
|
+
useCases?: PseoUseCase[];
|
|
102
|
+
cities?: PseoCity[];
|
|
103
|
+
}
|
|
104
|
+
export interface LynxResolvedPage {
|
|
105
|
+
url: string;
|
|
106
|
+
title: string;
|
|
107
|
+
description: string;
|
|
108
|
+
h1: string;
|
|
109
|
+
directAnswer: string;
|
|
110
|
+
locale?: string;
|
|
111
|
+
hreflangs?: {
|
|
112
|
+
lang: string;
|
|
113
|
+
url: string;
|
|
114
|
+
}[];
|
|
115
|
+
faqs: {
|
|
116
|
+
question: string;
|
|
117
|
+
answer: string;
|
|
118
|
+
}[];
|
|
119
|
+
jsonLd: Record<string, unknown>;
|
|
120
|
+
htmlBody: string;
|
|
121
|
+
markdownBody: string;
|
|
122
|
+
executionTimeMs: number;
|
|
123
|
+
localizedRoi?: {
|
|
124
|
+
monthlySavingsFormatted: string;
|
|
125
|
+
hoursSavedWeekly: number;
|
|
126
|
+
annualSavingsFormatted: string;
|
|
127
|
+
currencySymbol: string;
|
|
128
|
+
};
|
|
129
|
+
service?: LynxServiceDefinition;
|
|
130
|
+
industry?: PseoIndustry;
|
|
131
|
+
competitor?: PseoCompetitor;
|
|
132
|
+
integration?: PseoIntegration;
|
|
133
|
+
meta?: {
|
|
134
|
+
title: string;
|
|
135
|
+
description: string;
|
|
136
|
+
h1: string;
|
|
137
|
+
canonical: string;
|
|
138
|
+
openGraphImageUrl?: string;
|
|
139
|
+
};
|
|
140
|
+
fullUrl?: string;
|
|
141
|
+
schemaJsonLd?: Record<string, unknown>;
|
|
142
|
+
content?: {
|
|
143
|
+
directAnswerGeoHtml?: string;
|
|
144
|
+
heroHeadline?: string;
|
|
145
|
+
heroSubheadline?: string;
|
|
146
|
+
markdownBody?: string;
|
|
147
|
+
faqList?: {
|
|
148
|
+
question: string;
|
|
149
|
+
answer: string;
|
|
150
|
+
}[];
|
|
151
|
+
};
|
|
152
|
+
pricing?: {
|
|
153
|
+
priceNumber?: number;
|
|
154
|
+
priceFormatted?: string;
|
|
155
|
+
currency?: string;
|
|
156
|
+
};
|
|
157
|
+
debug?: {
|
|
158
|
+
licenseStatus?: "active" | "trial" | "expired" | "offline_mode";
|
|
159
|
+
generationMs?: number;
|
|
160
|
+
matrixType?: string;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🤖 LynxFlow AI Content Copilot Client
|
|
3
|
+
*
|
|
4
|
+
* Generates automated long-form blog articles, landing page copywriting,
|
|
5
|
+
* FAQ expansions, and PAS framework marketing copy.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface GenerateArticleParams {
|
|
9
|
+
topic: string;
|
|
10
|
+
targetKeywords: string[];
|
|
11
|
+
targetAudience?: string;
|
|
12
|
+
tone?: "professional" | "persuasive" | "educational";
|
|
13
|
+
wordCount?: number;
|
|
14
|
+
locale?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface GeneratedArticleResult {
|
|
18
|
+
title: string;
|
|
19
|
+
metaDescription: string;
|
|
20
|
+
h1: string;
|
|
21
|
+
markdownContent: string;
|
|
22
|
+
htmlContent: string;
|
|
23
|
+
directAnswerSnippet: string;
|
|
24
|
+
faqs: { question: string; answer: string }[];
|
|
25
|
+
tokensConsumed: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class AiCopilotClient {
|
|
29
|
+
private apiKey: string;
|
|
30
|
+
private endpoint: string;
|
|
31
|
+
|
|
32
|
+
constructor(apiKey: string, endpoint = "https://lynxintel.io/api/v1/ai") {
|
|
33
|
+
this.apiKey = apiKey;
|
|
34
|
+
this.endpoint = endpoint;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generates a complete SEO article with headings, FAQs, and Schema-ready JSON.
|
|
39
|
+
*/
|
|
40
|
+
async generateArticle(params: GenerateArticleParams): Promise<GeneratedArticleResult> {
|
|
41
|
+
try {
|
|
42
|
+
if (!this.apiKey || this.apiKey.startsWith("demo_")) {
|
|
43
|
+
const title = `Guide Complet : ${params.topic} en 2026`;
|
|
44
|
+
const metaDescription = `Découvrez comment maîtriser ${params.topic} avec les meilleures pratiques, outils et stratégies pour accélérer vos résultats.`;
|
|
45
|
+
const h1 = `Tout Savoir sur ${params.topic}`;
|
|
46
|
+
const directAnswer = `${params.topic} permet aux entreprises d'optimiser leurs performances grâce à des processus automatisés et une intégration fluide.`;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
title,
|
|
50
|
+
metaDescription,
|
|
51
|
+
h1,
|
|
52
|
+
directAnswerSnippet: directAnswer,
|
|
53
|
+
markdownContent: `# ${h1}\n\n${metaDescription}\n\n## 1. Pourquoi ${params.topic} est incontournable\n\nOptimiser votre stratégie avec ${params.targetKeywords.join(", ")}.\n\n## 2. Étapes de Mise en Place\n\n- Configuration initiale rapide\n- Déploiement automatisé\n- Mesure du ROI`,
|
|
54
|
+
htmlContent: `<h1>${h1}</h1><p>${metaDescription}</p><h2>1. Pourquoi ${params.topic} est incontournable</h2><p>Optimiser votre stratégie avec ${params.targetKeywords.join(", ")}.</p>`,
|
|
55
|
+
faqs: [
|
|
56
|
+
{ question: `Combien de temps pour mettre en place ${params.topic} ?`, answer: "La mise en place prend généralement moins de 15 minutes." },
|
|
57
|
+
{ question: "Quels sont les bénéfices immédiats ?", answer: "Gain de temps, réduction des coûts et automatisation des tâches chronophages." },
|
|
58
|
+
],
|
|
59
|
+
tokensConsumed: 450,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const res = await fetch(`${this.endpoint}/generate-article`, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` },
|
|
66
|
+
body: JSON.stringify(params),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!res.ok) throw new Error("AI Copilot API error");
|
|
70
|
+
return (await res.json()) as GeneratedArticleResult;
|
|
71
|
+
} catch {
|
|
72
|
+
return {
|
|
73
|
+
title: params.topic,
|
|
74
|
+
metaDescription: `Guide sur ${params.topic}`,
|
|
75
|
+
h1: params.topic,
|
|
76
|
+
directAnswerSnippet: `${params.topic} est essentiel en 2026.`,
|
|
77
|
+
markdownContent: `# ${params.topic}`,
|
|
78
|
+
htmlContent: `<h1>${params.topic}</h1>`,
|
|
79
|
+
faqs: [],
|
|
80
|
+
tokensConsumed: 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📊 LynxFlow Embedded Analytics & Web Traffic Client
|
|
3
|
+
*
|
|
4
|
+
* Tracks pageviews, visitors, sessions, Core Web Vitals, and AI Crawler Bots
|
|
5
|
+
* (ChatGPT-User, PerplexityBot, ClaudeBot, Googlebot) directly from client sites.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface AnalyticsEventPayload {
|
|
9
|
+
path: string;
|
|
10
|
+
referrer?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
country?: string;
|
|
13
|
+
durationMs?: number;
|
|
14
|
+
isAiBot?: boolean;
|
|
15
|
+
botName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AnalyticsSummaryReport {
|
|
19
|
+
totalPageViews: number;
|
|
20
|
+
uniqueVisitors: number;
|
|
21
|
+
bounceRate: number; // 0.0 to 1.0
|
|
22
|
+
avgTimeOnPageSec: number;
|
|
23
|
+
topPages: { path: string; views: number }[];
|
|
24
|
+
aiBotVisits: { bot: string; hits: number }[];
|
|
25
|
+
trafficByCountry: { country: string; visitors: number }[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class LynxAnalyticsClient {
|
|
29
|
+
private apiKey: string;
|
|
30
|
+
private endpoint: string;
|
|
31
|
+
|
|
32
|
+
constructor(apiKey: string, endpoint = "https://lynxintel.io/api/v1/analytics") {
|
|
33
|
+
this.apiKey = apiKey;
|
|
34
|
+
this.endpoint = endpoint;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Identifies if a User-Agent belongs to an AI search crawler.
|
|
39
|
+
*/
|
|
40
|
+
static detectAiBot(userAgent: string): { isAiBot: boolean; botName?: string } {
|
|
41
|
+
const ua = (userAgent || "").toLowerCase();
|
|
42
|
+
if (ua.includes("chatgpt-user") || ua.includes("oai-searchbot") || ua.includes("gptbot")) {
|
|
43
|
+
return { isAiBot: true, botName: "ChatGPT Search (OpenAI)" };
|
|
44
|
+
}
|
|
45
|
+
if (ua.includes("perplexitybot")) {
|
|
46
|
+
return { isAiBot: true, botName: "Perplexity AI" };
|
|
47
|
+
}
|
|
48
|
+
if (ua.includes("claudebot") || ua.includes("anthropic-ai")) {
|
|
49
|
+
return { isAiBot: true, botName: "Claude (Anthropic)" };
|
|
50
|
+
}
|
|
51
|
+
if (ua.includes("google-extended") || ua.includes("googlebot")) {
|
|
52
|
+
return { isAiBot: true, botName: "Googlebot / AI Overviews" };
|
|
53
|
+
}
|
|
54
|
+
if (ua.includes("bingbot")) {
|
|
55
|
+
return { isAiBot: true, botName: "Bingbot (Microsoft)" };
|
|
56
|
+
}
|
|
57
|
+
return { isAiBot: false };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tracks a live pageview or visitor interaction.
|
|
62
|
+
*/
|
|
63
|
+
async trackPageView(event: AnalyticsEventPayload): Promise<boolean> {
|
|
64
|
+
try {
|
|
65
|
+
const aiInfo = event.userAgent ? LynxAnalyticsClient.detectAiBot(event.userAgent) : { isAiBot: false };
|
|
66
|
+
|
|
67
|
+
const payload = {
|
|
68
|
+
apiKey: this.apiKey,
|
|
69
|
+
timestamp: Date.now(),
|
|
70
|
+
...event,
|
|
71
|
+
isAiBot: event.isAiBot ?? aiInfo.isAiBot,
|
|
72
|
+
botName: event.botName ?? aiInfo.botName,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (!this.apiKey || this.apiKey.startsWith("demo_") || this.apiKey.startsWith("lynx_starter_")) {
|
|
76
|
+
// Offline / local in-memory fallback
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const res = await fetch(`${this.endpoint}/track`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` },
|
|
83
|
+
body: JSON.stringify(payload),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return res.ok;
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Retrieves analytics summary and AI search traffic report for the site.
|
|
94
|
+
*/
|
|
95
|
+
async getSummary(period: "24h" | "7d" | "30d" | "all" = "30d"): Promise<AnalyticsSummaryReport> {
|
|
96
|
+
try {
|
|
97
|
+
if (!this.apiKey || this.apiKey.startsWith("demo_")) {
|
|
98
|
+
// Mocked realistic default report for offline/demo usage
|
|
99
|
+
return {
|
|
100
|
+
totalPageViews: 14820,
|
|
101
|
+
uniqueVisitors: 6340,
|
|
102
|
+
bounceRate: 0.38,
|
|
103
|
+
avgTimeOnPageSec: 84,
|
|
104
|
+
topPages: [
|
|
105
|
+
{ path: "/solutions/crm-pipeline/paris", views: 2450 },
|
|
106
|
+
{ path: "/comparatif/crm-pipeline-vs-hubspot", views: 1890 },
|
|
107
|
+
{ path: "/secteurs/crm-pipeline-pour-avocats", views: 1420 },
|
|
108
|
+
],
|
|
109
|
+
aiBotVisits: [
|
|
110
|
+
{ bot: "ChatGPT Search (OpenAI)", hits: 412 },
|
|
111
|
+
{ bot: "Perplexity AI", hits: 308 },
|
|
112
|
+
{ bot: "Googlebot / AI Overviews", hits: 1250 },
|
|
113
|
+
],
|
|
114
|
+
trafficByCountry: [
|
|
115
|
+
{ country: "FR", visitors: 4200 },
|
|
116
|
+
{ country: "BE", visitors: 850 },
|
|
117
|
+
{ country: "CH", visitors: 690 },
|
|
118
|
+
{ country: "CA", visitors: 600 },
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const res = await fetch(`${this.endpoint}/summary?period=${period}`, {
|
|
124
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!res.ok) throw new Error("Failed to fetch analytics summary");
|
|
128
|
+
return (await res.json()) as AnalyticsSummaryReport;
|
|
129
|
+
} catch {
|
|
130
|
+
return {
|
|
131
|
+
totalPageViews: 0,
|
|
132
|
+
uniqueVisitors: 0,
|
|
133
|
+
bounceRate: 0,
|
|
134
|
+
avgTimeOnPageSec: 0,
|
|
135
|
+
topPages: [],
|
|
136
|
+
aiBotVisits: [],
|
|
137
|
+
trafficByCountry: [],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|