@graphext/cuery 0.7.0 → 0.8.1

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.
@@ -1,182 +0,0 @@
1
- import * as dntShim from "../../../_dnt.shims.js";
2
- import { withRetries } from '../../helpers/async.js';
3
- import { extractDomain } from '../../helpers/urls.js';
4
- export const HASDATA_CONCURRENCY = 29;
5
- export const HASDATA_RETRY_CONFIG = {
6
- maxRetries: 3,
7
- initialDelay: 1000,
8
- maxDelay: 8000,
9
- backoffMultiplier: 2,
10
- statusCodes: [429, 500]
11
- };
12
- export function getHasDataApiKey() {
13
- const apiKey = dntShim.Deno.env.get('HASDATA_API_KEY');
14
- if (!apiKey) {
15
- throw new Error('HASDATA_API_KEY environment variable is required');
16
- }
17
- return apiKey;
18
- }
19
- export async function fetchHasDataWithRetry(url, retryConfig = HASDATA_RETRY_CONFIG) {
20
- const headers = {
21
- 'x-api-key': getHasDataApiKey()
22
- };
23
- const response = await withRetries(async () => fetch(url, {
24
- headers,
25
- signal: dntShim.dntGlobalThis.abortSignal
26
- }), retryConfig);
27
- if (!response.ok) {
28
- const status = response.status;
29
- let errorMessage;
30
- if (status === 401) {
31
- errorMessage = 'HasData API error (401): Invalid API key';
32
- }
33
- else if (status === 403) {
34
- errorMessage = 'HasData API error (403): API credits exhausted';
35
- }
36
- else if (status === 404) {
37
- errorMessage = 'HasData API error (404): Page not found';
38
- }
39
- else {
40
- errorMessage = `HasData API error: ${status} ${response.statusText}`;
41
- }
42
- console.error(errorMessage);
43
- throw new Error(errorMessage);
44
- }
45
- return response;
46
- }
47
- function removeCSSChunks(text) {
48
- if (!text) {
49
- return '';
50
- }
51
- // Remove CSS blocks that start with :root (anchored pattern - safe)
52
- text = text.replace(/:root\{[^}]*\}(?:@supports[^}]*\{[^}]*\{[^}]*\}\})?(?:\.[a-zA-Z0-9_-]+\{[^}]*\})*\.?/g, '');
53
- // Remove standalone @supports blocks (less common but safe anchor)
54
- text = text.replace(/@supports[^\{]*\{(?:[^{}]|\{[^}]*\})*\}/g, '');
55
- // Only remove class blocks if they appear in suspicious patterns (3+ consecutive)
56
- text = text.replace(/(?:\.[a-zA-Z0-9_-]+\{[^}]*\}){3,}/g, '');
57
- return text;
58
- }
59
- function cleanText(text) {
60
- if (!text) {
61
- return '';
62
- }
63
- text = removeCSSChunks(text);
64
- text = text.replace(/\u00a0/g, ' ');
65
- text = text.replace(/[ \t]+/g, ' ');
66
- const lines = text.split('\n').map(line => line.trim());
67
- const cleaned = [];
68
- for (const line of lines) {
69
- if (line || (cleaned.length > 0 && cleaned[cleaned.length - 1])) {
70
- cleaned.push(line);
71
- }
72
- }
73
- return cleaned.join('\n').trim();
74
- }
75
- function* iterListItems(items, indent = 0) {
76
- const prefix = ' '.repeat(indent) + '- ';
77
- for (const obj of items) {
78
- const title = obj.title || '';
79
- const snippet = obj.snippet || '';
80
- let line;
81
- if (title && snippet && title.endsWith(':')) {
82
- line = `${title} ${snippet}`.trim();
83
- }
84
- else {
85
- line = [title, snippet].filter(p => p).join(' ').trim();
86
- }
87
- if (line) {
88
- yield prefix + cleanText(line);
89
- }
90
- if (obj.list && Array.isArray(obj.list)) {
91
- yield* iterListItems(obj.list, indent + 1);
92
- }
93
- }
94
- }
95
- function formatTable(block) {
96
- const rows = block.rows || [];
97
- if (rows.length === 0) {
98
- return '';
99
- }
100
- const out = [];
101
- const header = rows[0].map(cell => removeCSSChunks(cell));
102
- out.push('| ' + header.join(' | ') + ' |');
103
- out.push('| ' + header.map(() => '---').join(' | ') + ' |');
104
- for (let i = 1; i < rows.length; i++) {
105
- const cleanedRow = rows[i].map(cell => removeCSSChunks(cell));
106
- out.push('| ' + cleanedRow.join(' | ') + ' |');
107
- }
108
- return out.join('\n');
109
- }
110
- function formatCode(block) {
111
- const lang = block.language || '';
112
- const snippet = block.snippet || '';
113
- if (!snippet) {
114
- return '';
115
- }
116
- const header = `[Code${lang ? ': ' + lang : ''}]`;
117
- return `${header}\n${snippet.trim()}`;
118
- }
119
- function parseAIResult(data, { allowNestedOverview = true } = {}) {
120
- const textBlocks = data.textBlocks || (allowNestedOverview ? data.aiOverview?.textBlocks : []) || [];
121
- const parts = [];
122
- const handlers = {
123
- paragraph: (b) => cleanText(b.snippet || ''),
124
- list: (b) => Array.from(iterListItems(b.list || [])).join('\n'),
125
- table: formatTable,
126
- code: formatCode
127
- };
128
- for (const block of textBlocks) {
129
- const btype = block.type || (block.snippet ? 'paragraph' : null);
130
- if (!btype || btype === 'carousel') {
131
- continue;
132
- }
133
- const handler = handlers[btype];
134
- if (handler) {
135
- const rendered = handler(block);
136
- if (rendered) {
137
- parts.push(rendered);
138
- }
139
- }
140
- else {
141
- const snippet = block.snippet || '';
142
- if (snippet) {
143
- parts.push(cleanText(snippet));
144
- }
145
- }
146
- }
147
- const deduped = [];
148
- for (const p of parts) {
149
- if (deduped.length === 0 || deduped[deduped.length - 1] !== p) {
150
- deduped.push(p);
151
- }
152
- }
153
- let answer = cleanText(deduped.join('\n\n'));
154
- if (answer.length > 16000) {
155
- console.warn('Warning: AI answer truncated to 16000 characters');
156
- answer = answer.slice(0, 16000);
157
- }
158
- const refs = data.references || (allowNestedOverview ? data.aiOverview?.references : []) || [];
159
- const sources = [];
160
- for (const r of refs) {
161
- const link = r.link || r.url;
162
- const title = [r.title, r.source, r.snippet].filter(Boolean).join(' - ');
163
- if (link) {
164
- sources.push({
165
- title,
166
- url: link,
167
- domain: extractDomain(link)
168
- });
169
- }
170
- }
171
- return { answer, sources };
172
- }
173
- export function parseAIO(aio) {
174
- return parseAIResult(aio, {
175
- allowNestedOverview: true
176
- });
177
- }
178
- export function parseAIM(aim) {
179
- return parseAIResult(aim, {
180
- allowNestedOverview: false
181
- });
182
- }
@@ -1,73 +0,0 @@
1
- import { type JobId } from './apis/chatgptScraper/index.js';
2
- import { type Entity } from './schemas/entity.schema.js';
3
- import { type Funnel } from './schemas/funnel.schema.js';
4
- import type { ModelIdentifier } from './schemas/models.schema.js';
5
- import { type Persona } from './schemas/persona.schema.js';
6
- import { type BrandContext } from './schemas/brand.schema.js';
7
- import { type Source, type EnrichedSource, type SearchSource } from './schemas/sources.schema.js';
8
- export type ModelResponse = {
9
- prompt: string;
10
- model: string;
11
- answer: string;
12
- sources: Array<Source>;
13
- searchQueries: Array<string>;
14
- searchSources: Array<SearchSource>;
15
- };
16
- export type EnrichedModelResponse = {
17
- prompt: string;
18
- model: string;
19
- answer: string;
20
- sources: Array<EnrichedSource>;
21
- fanout?: Array<string>;
22
- rankedBrandsInAnswer: Array<string>;
23
- rankedBrandsInSourceTitles: Array<string>;
24
- rankedBrandsInSourceDomains: Array<string>;
25
- };
26
- export declare function queryModel(prompts: Array<string | JobId>, model: ModelIdentifier, searchCountry?: string | null): Promise<Array<ModelResponse>>;
27
- export declare function classifyIntent(texts: Array<string>): Promise<Array<string | null>>;
28
- export declare function extractTopics(params: {
29
- records: Array<Record<string, unknown>>;
30
- maxSamples?: number;
31
- instructions?: string;
32
- language?: string;
33
- model?: string;
34
- }): Promise<{
35
- topics: {
36
- topic: string;
37
- subtopics: string[];
38
- }[];
39
- } | null>;
40
- export declare function generatePersonas(params: {
41
- sector: string;
42
- market: string;
43
- language: string;
44
- brand?: string;
45
- brandDomain?: string;
46
- count?: number;
47
- briefing?: string;
48
- instructions?: string;
49
- userLanguage?: string;
50
- personas?: Array<Persona>;
51
- model?: string;
52
- }): Promise<{
53
- personas: {
54
- name: string;
55
- description: string;
56
- keywordSeeds: string[];
57
- }[];
58
- explanation: string;
59
- } | null>;
60
- export declare function extractAndAssignTopics(texts: Array<string>): Promise<Array<{
61
- topic: string | null;
62
- subtopic: string | null;
63
- }>>;
64
- export declare function assignFunnelStages(texts: Array<string>, funnel: Funnel): Promise<Array<{
65
- funnelStage: string | null;
66
- funnelCategory: string | null;
67
- }>>;
68
- export declare function classifyIntoPersonas(texts: Array<string>, personas: Array<Persona>): Promise<Array<Array<string> | null>>;
69
- export declare function classifyBrandedNonBranded(texts: Array<string>): Promise<Array<string | null>>;
70
- export declare function extractEntities(texts: Array<string>): Promise<Array<Array<Entity> | null>>;
71
- export declare function scorePurchaseProbability(texts: Array<string>, numDays?: number): Promise<Array<number | null>>;
72
- export declare function scoreRelevance(prompts: Array<string>, context: BrandContext): Promise<Array<number | null>>;
73
- //# sourceMappingURL=api.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/src/api.ts"],"names":[],"mappings":"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"}
package/script/src/api.js DELETED
@@ -1,221 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.queryModel = queryModel;
4
- exports.classifyIntent = classifyIntent;
5
- exports.extractTopics = extractTopics;
6
- exports.generatePersonas = generatePersonas;
7
- exports.extractAndAssignTopics = extractAndAssignTopics;
8
- exports.assignFunnelStages = assignFunnelStages;
9
- exports.classifyIntoPersonas = classifyIntoPersonas;
10
- exports.classifyBrandedNonBranded = classifyBrandedNonBranded;
11
- exports.extractEntities = extractEntities;
12
- exports.scorePurchaseProbability = scorePurchaseProbability;
13
- exports.scoreRelevance = scoreRelevance;
14
- /* eslint no-console: ["warn", { allow: ["log", "warn", "error"] }] */
15
- const aim_js_1 = require("./apis/hasdata/aim.js");
16
- const aio_js_1 = require("./apis/hasdata/aio.js");
17
- const index_js_1 = require("./apis/chatgptScraper/index.js");
18
- const classifier_js_1 = require("./tools/classifier.js");
19
- const entities_js_1 = require("./tools/entities.js");
20
- const funnel_js_1 = require("./tools/funnel.js");
21
- const utils_js_1 = require("./helpers/utils.js");
22
- const scorer_js_1 = require("./tools/scorer.js");
23
- const topics_js_1 = require("./tools/topics.js");
24
- const personas_js_1 = require("./tools/personas.js");
25
- const utils_js_2 = require("./helpers/utils.js");
26
- async function queryModel(prompts, model, searchCountry = null) {
27
- let searchResults;
28
- if (model === 'google/ai-overview') {
29
- searchResults = await (0, aio_js_1.fetchAIOBatch)(prompts, searchCountry, null);
30
- }
31
- else if (model === 'google/ai-mode') {
32
- searchResults = await (0, aim_js_1.fetchAIMBatch)(prompts, searchCountry, null);
33
- }
34
- else if (model.startsWith('openai/') || model.includes('chatgpt')) {
35
- // JobIds are alphanumeric strings without spaces
36
- // (e.g., "7420410504197219329" for Oxylabs, "s_xxxxx" for Brightdata)
37
- // Prompts are natural language text with spaces
38
- const firstPrompt = prompts[0];
39
- const isJobIdArray = prompts.length === 0 || firstPrompt == null || !firstPrompt.includes(' ');
40
- if (isJobIdArray) {
41
- searchResults = await (0, index_js_1.downloadGPTSnapshots)(prompts);
42
- }
43
- else {
44
- searchResults = await (0, index_js_1.scrapeGPTBatch)({
45
- prompts: prompts,
46
- countryISOCode: searchCountry
47
- });
48
- }
49
- }
50
- else {
51
- throw new Error(`Unsupported model: ${model}`);
52
- }
53
- // Convert model identifier to column name (e.g., "google/ai-overview" -> "ai_overview")
54
- const modelColumnName = (0, utils_js_1.cleanColumnName)(model.split('/').pop() ?? model);
55
- const responses = searchResults.map((sr, i) => ({
56
- answer: sr.answer,
57
- sources: sr.sources,
58
- prompt: sr.prompt ?? (typeof prompts[i] === 'string' ? prompts[i] : ''),
59
- model: modelColumnName,
60
- searchQueries: sr.searchQueries ?? [],
61
- searchSources: sr.searchSources ?? []
62
- }));
63
- return responses;
64
- }
65
- const intentLabels = {
66
- 'informational': 'The user is seeking information or answers to questions about a topic.',
67
- 'navigational': 'The user is trying to find a specific website or page.',
68
- 'transactional': 'The user intends to complete a transaction, such as making a purchase or signing up for a service.'
69
- };
70
- async function classifyIntent(texts) {
71
- console.log('Classifying prompt intents...');
72
- const classifier = new classifier_js_1.Classifier({
73
- labels: intentLabels,
74
- instructions: 'Classify the search query into one of the following intents: informational, navigational, transactional.'
75
- }, { model: 'gpt-4.1-mini' });
76
- const textRecords = texts.map(text => ({ text }));
77
- const intents = await classifier.batch(textRecords);
78
- return intents.toArray();
79
- }
80
- async function extractTopics(params) {
81
- const extractor = new topics_js_1.TopicExtractor({
82
- maxSamples: params.maxSamples,
83
- instructions: params.instructions,
84
- language: params.language
85
- }, { model: params.model ?? 'gpt-4.1-mini' });
86
- const result = await extractor.invoke(params.records);
87
- return result.parsed;
88
- }
89
- async function generatePersonas(params) {
90
- const generator = new personas_js_1.PersonaGenerator({
91
- sector: params.sector,
92
- market: params.market,
93
- language: params.language,
94
- brand: params.brand,
95
- brandDomain: params.brandDomain,
96
- count: params.count,
97
- briefing: params.briefing,
98
- instructions: params.instructions,
99
- userLanguage: params.userLanguage
100
- }, { model: params.model ?? 'gpt-4.1' });
101
- const result = await generator.invoke(params.personas ?? null);
102
- return result.parsed;
103
- }
104
- async function extractAndAssignTopics(texts) {
105
- console.log('Identifying topics...');
106
- const textRecords = texts.map(text => ({ text }));
107
- const extractor = new topics_js_1.TopicExtractor({ maxSamples: 500 }, { model: 'gpt-4.1' });
108
- const taxonomyResult = await extractor.invoke(textRecords);
109
- const taxonomy = taxonomyResult.parsed;
110
- if (taxonomy == null) {
111
- return texts.map(() => ({ topic: null, subtopic: null }));
112
- }
113
- console.log('Assigning topics...');
114
- const assigner = new topics_js_1.TopicAssigner({ taxonomy }, { model: 'gpt-4.1-mini' });
115
- const topicLabels = await assigner.batch(texts);
116
- return topicLabels.toArray().map(label => ({
117
- topic: label?.topic ?? null,
118
- subtopic: label?.subtopic ?? null
119
- }));
120
- }
121
- async function assignFunnelStages(texts, funnel) {
122
- console.log('Assigning funnel stages...');
123
- const funnelTopics = (0, funnel_js_1.funnelToTopics)(funnel);
124
- const assigner = new topics_js_1.TopicAssigner({ taxonomy: funnelTopics }, { model: 'gpt-4.1-mini' });
125
- const funnelLabels = await assigner.batch(texts);
126
- return funnelLabels.toArray().map(label => ({
127
- funnelStage: label?.topic ?? null,
128
- funnelCategory: label?.subtopic ?? null
129
- }));
130
- }
131
- async function classifyIntoPersonas(texts, personas) {
132
- console.log('Classifying prompts into personas...');
133
- const personaLabels = {};
134
- personas.forEach(persona => {
135
- personaLabels[persona.name] = persona.description;
136
- });
137
- const labeler = new classifier_js_1.Labeler({
138
- labels: personaLabels,
139
- instructions: 'Classify the search query into one or more customer personas based on the language, intent, and context.'
140
- }, { model: 'gpt-4.1-mini' });
141
- const textRecords = texts.map(text => ({ text }));
142
- const personaAssignments = await labeler.batch(textRecords);
143
- return personaAssignments.toArray();
144
- }
145
- async function classifyBrandedNonBranded(texts) {
146
- console.log('Classifying prompts into branded/non-branded...');
147
- const brandedLabels = {
148
- 'branded': 'The query explicitly mentions a specific brand name, company name, product name, or trademark. It shows clear brand awareness and intent to find information about that particular brand.',
149
- 'non-branded': 'The query is generic and does not mention any specific brand names. It focuses on product categories, features, problems, or general information without brand specificity.'
150
- };
151
- const classifier = new classifier_js_1.Classifier({
152
- labels: brandedLabels,
153
- instructions: 'Classify whether the search query mentions specific brands or is generic/category-based.'
154
- }, { model: 'gpt-4.1-mini' });
155
- const textRecords = texts.map(text => ({ text }));
156
- const brandedClassifications = await classifier.batch(textRecords);
157
- return brandedClassifications.toArray();
158
- }
159
- async function extractEntities(texts) {
160
- const extractor = new entities_js_1.EntityExtractor({
161
- entityDefinitions: {
162
- brands: 'Any brand or companies mentioned',
163
- products: 'Any products or services mentioned',
164
- features: 'Specific features or attributes of products/services',
165
- issues: 'Problems or issues mentioned'
166
- }
167
- }, { model: 'gpt-4.1-mini' });
168
- const result = await extractor.batch(texts);
169
- return result.toArray();
170
- }
171
- async function scorePurchaseProbability(texts, numDays = 30) {
172
- console.log('Scoring purchase probability...');
173
- const records = texts.map(text => ({ text }));
174
- const description = (0, utils_js_2.dedent)(`
175
- A score from 0 to 100 indicating the likelihood that the user intends to make a purchase
176
- in the next ${numDays} days. The input record is a text query representing a search
177
- that a user may perform using a traditional search engine or LLM chat.
178
- `);
179
- const scorer = new scorer_js_1.Scorer({ name: 'Purchase Probability', description: description, type: 'integer', min: 0, max: 100 }, { model: 'gpt-4.1-mini' });
180
- const scores = await scorer.batch(records);
181
- return scores.toArray();
182
- }
183
- async function scoreRelevance(prompts, context) {
184
- console.log('Scoring prompt relevance...');
185
- const contextParts = [];
186
- if (context.shortName) {
187
- contextParts.push(`Brand: ${context.shortName}`);
188
- }
189
- if (context.sector) {
190
- contextParts.push(`Sector: ${context.sector}`);
191
- }
192
- if (context.country) {
193
- contextParts.push(`Market: ${context.country}`);
194
- }
195
- if (context.briefing) {
196
- contextParts.push(`Brief: ${context.briefing}`);
197
- }
198
- const contextDescription = contextParts.length > 0
199
- ? `Analysis context:\n${contextParts.join('\n')}`
200
- : '';
201
- const records = prompts.map(prompt => ({ prompt }));
202
- const description = (0, utils_js_2.dedent)(`
203
- A score from 0.0 to 1.0 measuring how relevant this search keyword/prompt is
204
- for the specified SEO analysis context. Consider whether it is specific enough
205
- for the following brand, sector, market and research brief.
206
-
207
- ${contextDescription}
208
-
209
- A score of 0.0 means the keyword/prompt is completely irrelevant to the context,
210
- e.g. "cheap shoes" for a brand selling enterprise software. A low score can mean
211
- the keyword/prompt is too generic, not related to the sector/market, or targets
212
- a different audience. E.g. "data analysis" might be too broad in the context of
213
- a social network scheduler tool. A medium or high score means the keyword is relevant,
214
- e.g. "social media data analysis" for the same brand and analysis brief. A score of
215
- 1.0 means the keyword/prompt is highly relevant and specific to the context, e.g.
216
- "best CRM software for small business" for a brand selling CRM software.
217
- `);
218
- const scorer = new scorer_js_1.Scorer({ name: 'Prompt Relevance', description: description, type: 'number', min: 0.0, max: 1.0 }, { model: 'gpt-4.1-mini' });
219
- const scores = await scorer.batch(records);
220
- return scores.toArray();
221
- }
@@ -1,4 +0,0 @@
1
- import type { AIOParsed } from './helpers.js';
2
- export declare function fetchAIM(prompt: string, country?: string | null, language?: string | null, location?: string | null): Promise<AIOParsed>;
3
- export declare function fetchAIMBatch(prompts: Array<string>, country?: string | null, language?: string | null, location?: string | null, maxConcurrency?: number): Promise<Array<AIOParsed>>;
4
- //# sourceMappingURL=aim.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aim.d.ts","sourceRoot":"","sources":["../../../../src/src/apis/hasdata/aim.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,SAAS,EACT,MAAM,cAAc,CAAC;AAOtB,wBAAsB,QAAQ,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,QAAQ,GAAE,MAAM,GAAG,IAAW,GAC5B,OAAO,CAAC,SAAS,CAAC,CA4BpB;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,EACtB,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,cAAc,GAAE,MAA4B,GAC1C,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAM3B"}
@@ -1,36 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchAIM = fetchAIM;
4
- exports.fetchAIMBatch = fetchAIMBatch;
5
- /* eslint no-console: ["warn", { allow: ["log", "warn", "error"] }] */
6
- const async_js_1 = require("../../helpers/async.js");
7
- const helpers_js_1 = require("./helpers.js");
8
- async function fetchAIM(prompt, country = null, language = null, location = null) {
9
- const aimEndpoint = 'https://api.hasdata.com/scrape/google/ai-mode';
10
- const params = { q: prompt };
11
- if (location) {
12
- params.location = location;
13
- }
14
- if (country) {
15
- params.gl = country.toLowerCase();
16
- }
17
- if (language) {
18
- params.hl = language.toLowerCase();
19
- }
20
- const url = new URL(aimEndpoint);
21
- for (const [key, value] of Object.entries(params)) {
22
- url.searchParams.set(key, value);
23
- }
24
- try {
25
- const response = await (0, helpers_js_1.fetchHasDataWithRetry)(url.toString());
26
- const content = await response.json();
27
- return (0, helpers_js_1.parseAIM)(content);
28
- }
29
- catch (error) {
30
- console.error('HasData AI Mode API error:', error);
31
- return { answer: '', sources: [] };
32
- }
33
- }
34
- async function fetchAIMBatch(prompts, country = null, language = null, location = null, maxConcurrency = helpers_js_1.HASDATA_CONCURRENCY) {
35
- return (0, async_js_1.mapParallel)(prompts, maxConcurrency, async (prompt) => fetchAIM(prompt, country, language, location));
36
- }
@@ -1,4 +0,0 @@
1
- import { type AIOParsed } from './helpers.js';
2
- export declare function fetchAIO(prompt: string, country?: string | null, language?: string | null): Promise<AIOParsed>;
3
- export declare function fetchAIOBatch(prompts: Array<string>, country?: string | null, language?: string | null, maxConcurrency?: number): Promise<Array<AIOParsed>>;
4
- //# sourceMappingURL=aio.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aio.d.ts","sourceRoot":"","sources":["../../../../src/src/apis/hasdata/aio.ts"],"names":[],"mappings":"AAGA,OAAO,EAIN,KAAK,SAAS,EAEd,MAAM,cAAc,CAAC;AAStB,wBAAsB,QAAQ,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,QAAQ,GAAE,MAAM,GAAG,IAAW,GAC5B,OAAO,CAAC,SAAS,CAAC,CAiCpB;AAED,wBAAsB,aAAa,CAClC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,EACtB,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,cAAc,GAAE,MAA4B,GAC1C,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAM3B"}
@@ -1,46 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetchAIO = fetchAIO;
4
- exports.fetchAIOBatch = fetchAIOBatch;
5
- /* eslint no-console: ["warn", { allow: ["log", "warn", "error"] }] */
6
- const async_js_1 = require("../../helpers/async.js");
7
- const helpers_js_1 = require("./helpers.js");
8
- function aioRequestUrl(aio) {
9
- if (aio.pageToken && aio.hasdataLink) {
10
- return aio.hasdataLink;
11
- }
12
- return null;
13
- }
14
- async function fetchAIO(prompt, country = null, language = null) {
15
- const serpEndpoint = 'https://api.hasdata.com/scrape/google/serp';
16
- const params = { q: prompt };
17
- if (country) {
18
- params.gl = country.toLowerCase();
19
- }
20
- if (language) {
21
- params.hl = language.toLowerCase();
22
- }
23
- const url = new URL(serpEndpoint);
24
- for (const [key, value] of Object.entries(params)) {
25
- url.searchParams.set(key, value);
26
- }
27
- try {
28
- let response = await (0, helpers_js_1.fetchHasDataWithRetry)(url.toString());
29
- let content = await response.json();
30
- let aio = content.aiOverview || {};
31
- const aioUrlString = aioRequestUrl(aio);
32
- if (aioUrlString) {
33
- response = await (0, helpers_js_1.fetchHasDataWithRetry)(aioUrlString);
34
- content = await response.json();
35
- aio = content.aiOverview || {};
36
- }
37
- return (0, helpers_js_1.parseAIO)(aio);
38
- }
39
- catch (error) {
40
- console.error('HasData API error:', error);
41
- return { answer: '', sources: [] };
42
- }
43
- }
44
- async function fetchAIOBatch(prompts, country = null, language = null, maxConcurrency = helpers_js_1.HASDATA_CONCURRENCY) {
45
- return (0, async_js_1.mapParallel)(prompts, maxConcurrency, async (prompt) => fetchAIO(prompt, country, language));
46
- }
@@ -1,55 +0,0 @@
1
- import { type RetryConfig } from '../../helpers/async.js';
2
- import type { Source } from '../../schemas/sources.schema.js';
3
- export declare const HASDATA_CONCURRENCY = 29;
4
- export declare const HASDATA_RETRY_CONFIG: RetryConfig;
5
- export declare function getHasDataApiKey(): string;
6
- export declare function fetchHasDataWithRetry(url: string, retryConfig?: RetryConfig): Promise<Response>;
7
- interface ListItem {
8
- title?: string;
9
- snippet?: string;
10
- list?: Array<ListItem>;
11
- }
12
- interface TextBlock {
13
- type?: string;
14
- snippet?: string;
15
- snippetHighlightedWords?: Array<string>;
16
- referenceIndexes?: Array<number>;
17
- list?: Array<ListItem>;
18
- rows?: Array<Array<string>>;
19
- thumbnail?: string;
20
- language?: string;
21
- }
22
- interface Reference {
23
- index?: number;
24
- title?: string;
25
- link?: string;
26
- url?: string;
27
- snippet?: string;
28
- source?: string;
29
- }
30
- export interface AIOverview {
31
- textBlocks?: Array<TextBlock>;
32
- references?: Array<Reference>;
33
- aiOverview?: AIOverview;
34
- pageToken?: string;
35
- hasdataLink?: string;
36
- }
37
- interface RequestMetadata {
38
- id?: string;
39
- status?: string;
40
- html?: string;
41
- url?: string;
42
- }
43
- export interface AIMode {
44
- requestMetadata?: RequestMetadata;
45
- textBlocks?: Array<TextBlock>;
46
- references?: Array<Reference>;
47
- }
48
- export interface AIOParsed {
49
- answer: string;
50
- sources: Array<Source>;
51
- }
52
- export declare function parseAIO(aio: AIOverview): AIOParsed;
53
- export declare function parseAIM(aim: AIMode): AIOParsed;
54
- export {};
55
- //# sourceMappingURL=helpers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../src/src/apis/hasdata/helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iCAAiC,CAAC;AAG9D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,eAAO,MAAM,oBAAoB,EAAE,WAMlC,CAAC;AAEF,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,wBAAsB,qBAAqB,CAC1C,GAAG,EAAE,MAAM,EACX,WAAW,GAAE,WAAkC,GAC7C,OAAO,CAAC,QAAQ,CAAC,CAgCnB;AAED,UAAU,QAAQ;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;CACvB;AAED,UAAU,SAAS;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvB,IAAI,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,SAAS;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IAC1B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,eAAe;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,MAAM;IACtB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACvB;AAwJD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,SAAS,CAInD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAI/C"}