nosible 0.2.13 → 0.2.16
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 +1 -1
- package/dist/index.cjs +202 -257
- package/dist/index.cjs.map +10 -10
- package/dist/index.js +202 -273
- package/dist/index.js.map +10 -10
- package/package.json +2 -2
- package/src/api/index.ts +16 -8
- package/src/api/schemas.ts +20 -0
- package/src/apiKey.test.ts +125 -0
- package/src/client.test.ts +142 -34
- package/src/client.ts +258 -115
- package/src/index.ts +8 -0
- package/src/scrape/types.ts +118 -24
- package/src/scrape/webPageData.ts +39 -4
- package/src/search/resultSet.io.test.ts +16 -16
- package/src/topicTrend/topicTrend.ts +3 -5
- package/src/utils/llm.test.ts +13 -13
- package/src/utils/rateLimiters.test.ts +652 -0
- package/src/utils/rateLimiters.ts +73 -0
- package/src/utils/userPlan.test.ts +0 -118
- package/src/utils/userPlan.ts +0 -217
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -42,6 +42,10 @@ var __export = (target, all) => {
|
|
|
42
42
|
// src/index.ts
|
|
43
43
|
var exports_src = {};
|
|
44
44
|
__export(exports_src, {
|
|
45
|
+
userSearchParamsSchema: () => userSearchParamsSchema,
|
|
46
|
+
snippetSchema: () => snippetSchema,
|
|
47
|
+
scrapeResponseSchema: () => scrapeResponseSchema,
|
|
48
|
+
scrapeFullResSchema: () => scrapeFullResSchema,
|
|
45
49
|
WebPageData: () => WebPageData,
|
|
46
50
|
TopicTrend: () => TopicTrend,
|
|
47
51
|
SearchSet: () => SearchSet,
|
|
@@ -176,16 +180,16 @@ var snippetStatisticsSchema = import_zod2.z.object({
|
|
|
176
180
|
images: import_zod2.z.number().optional()
|
|
177
181
|
});
|
|
178
182
|
var snippetSchema = import_zod2.z.object({
|
|
179
|
-
url_hash: import_zod2.z.string(),
|
|
180
|
-
snippet_hash: import_zod2.z.string(),
|
|
181
|
-
prev_snippet_hash: import_zod2.z.string().nullable(),
|
|
182
|
-
next_snippet_hash: import_zod2.z.string().nullable(),
|
|
183
|
-
content: import_zod2.z.string(),
|
|
184
|
-
words: import_zod2.z.string().optional(),
|
|
185
|
-
language: import_zod2.z.string(),
|
|
186
|
-
statistics: snippetStatisticsSchema.optional(),
|
|
187
|
-
links: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.string()).optional(),
|
|
188
|
-
images: import_zod2.z.array(import_zod2.z.string()).optional()
|
|
183
|
+
url_hash: import_zod2.z.string().describe("Hash of the URL from which the snippet was extracted"),
|
|
184
|
+
snippet_hash: import_zod2.z.string().describe("A unique hash for the snippet"),
|
|
185
|
+
prev_snippet_hash: import_zod2.z.string().nullable().describe("Hash of the previous snippet in sequence"),
|
|
186
|
+
next_snippet_hash: import_zod2.z.string().nullable().describe("Hash of the next snippet in sequence"),
|
|
187
|
+
content: import_zod2.z.string().describe("The text content of the snippet"),
|
|
188
|
+
words: import_zod2.z.string().optional().describe("The words in the snippet"),
|
|
189
|
+
language: import_zod2.z.string().describe("The language of the snippet"),
|
|
190
|
+
statistics: snippetStatisticsSchema.optional().describe("Statistical information about the snippet"),
|
|
191
|
+
links: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.string()).optional().describe("List of links associated with the snippet"),
|
|
192
|
+
images: import_zod2.z.array(import_zod2.z.string()).optional().describe("List of image URLs associated with the snippet")
|
|
189
193
|
});
|
|
190
194
|
var scrapeStatisticsSchema = import_zod2.z.object({
|
|
191
195
|
snippets: import_zod2.z.number(),
|
|
@@ -252,20 +256,20 @@ var structuredSchema = import_zod2.z.object({
|
|
|
252
256
|
});
|
|
253
257
|
var urlTreeSchema = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.any());
|
|
254
258
|
var scrapeResponseSchema = import_zod2.z.object({
|
|
255
|
-
request: scrapeRequestSchema,
|
|
256
|
-
page: scrapePageSchema,
|
|
257
|
-
statistics: scrapeStatisticsSchema,
|
|
258
|
-
languages: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.number()),
|
|
259
|
-
snippets: import_zod2.z.record(import_zod2.z.string(), snippetSchema),
|
|
260
|
-
full_text: import_zod2.z.string(),
|
|
261
|
-
metadata: metadataSchema,
|
|
262
|
-
structured: import_zod2.z.array(structuredSchema),
|
|
263
|
-
url_tree: urlTreeSchema
|
|
259
|
+
request: scrapeRequestSchema.describe("Details of the original scrape request including URL components and proxy settings"),
|
|
260
|
+
page: scrapePageSchema.describe("Basic page metadata like title, description, author, and dates"),
|
|
261
|
+
statistics: scrapeStatisticsSchema.describe("Content statistics including counts of snippets, words, characters, images, and other media"),
|
|
262
|
+
languages: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.number()).describe("Language detection results with language codes and confidence scores"),
|
|
263
|
+
snippets: import_zod2.z.record(import_zod2.z.string(), snippetSchema).describe("Page content broken into manageable snippets with metadata"),
|
|
264
|
+
full_text: import_zod2.z.string().describe("Complete extracted text content from the page"),
|
|
265
|
+
metadata: metadataSchema.describe("Additional metadata extracted from the page as key-value pairs"),
|
|
266
|
+
structured: import_zod2.z.array(structuredSchema).describe("Structured data extracted from the page (JSON-LD, microdata, etc.)"),
|
|
267
|
+
url_tree: urlTreeSchema.describe("Hierarchical structure of URLs found on the page")
|
|
264
268
|
});
|
|
265
269
|
var scrapeFullResSchema = import_zod2.z.object({
|
|
266
|
-
message: import_zod2.z.string(),
|
|
267
|
-
added_to_batch: import_zod2.z.boolean(),
|
|
268
|
-
response: scrapeResponseSchema
|
|
270
|
+
message: import_zod2.z.string().describe("Status message describing the scrape operation result"),
|
|
271
|
+
added_to_batch: import_zod2.z.boolean().describe("Indicates if this scrape was added to a batch for processing"),
|
|
272
|
+
response: scrapeResponseSchema.describe("The complete scrape response containing all extracted data")
|
|
269
273
|
});
|
|
270
274
|
|
|
271
275
|
// src/api/schemas.ts
|
|
@@ -382,6 +386,22 @@ var bulkResSchema = import_zod3.default.object({
|
|
|
382
386
|
decrypt_using: import_zod3.default.string(),
|
|
383
387
|
download_from: import_zod3.default.string()
|
|
384
388
|
});
|
|
389
|
+
var limitItemSchema = import_zod3.default.object({
|
|
390
|
+
name: import_zod3.default.string(),
|
|
391
|
+
query_type: import_zod3.default.string(),
|
|
392
|
+
duration_seconds: import_zod3.default.number(),
|
|
393
|
+
duration_string: import_zod3.default.string(),
|
|
394
|
+
limit: import_zod3.default.number(),
|
|
395
|
+
limited: import_zod3.default.boolean(),
|
|
396
|
+
remaining: import_zod3.default.number(),
|
|
397
|
+
period_start: import_zod3.default.string().nullable(),
|
|
398
|
+
period_end: import_zod3.default.string().nullable()
|
|
399
|
+
});
|
|
400
|
+
var limitsResponseSchema = import_zod3.default.object({
|
|
401
|
+
api_key_id: import_zod3.default.string(),
|
|
402
|
+
subscription_id: import_zod3.default.string(),
|
|
403
|
+
limits: import_zod3.default.array(limitItemSchema)
|
|
404
|
+
});
|
|
385
405
|
|
|
386
406
|
// src/utils/llm.ts
|
|
387
407
|
var defaultModel = "google/gemini-2.0-flash-001";
|
|
@@ -572,21 +592,28 @@ var callNosibleApi = async ({
|
|
|
572
592
|
baseUrl = "https://www.nosible.ai/search/v2",
|
|
573
593
|
endpoint,
|
|
574
594
|
body,
|
|
575
|
-
apiKey
|
|
595
|
+
apiKey,
|
|
596
|
+
method = "POST"
|
|
576
597
|
}) => {
|
|
577
598
|
const now = Date.now();
|
|
578
|
-
|
|
579
|
-
|
|
599
|
+
if (method === "POST" && body && process.env.NOSIBLE_DEBUG) {
|
|
600
|
+
console.debug("Body: ", JSON.stringify(body, null, 2));
|
|
601
|
+
}
|
|
602
|
+
if (process.env.NOSIBLE_DEBUG) {
|
|
603
|
+
console.time(`Request-${endpoint}(${now})`);
|
|
604
|
+
}
|
|
580
605
|
const response = await fetch(`${baseUrl}/${endpoint}`, {
|
|
581
|
-
method
|
|
606
|
+
method,
|
|
582
607
|
headers: {
|
|
583
608
|
Accept: "application/json",
|
|
584
|
-
"Content-Type": "application/json",
|
|
609
|
+
...method === "POST" ? { "Content-Type": "application/json" } : {},
|
|
585
610
|
"Api-Key": `${apiKey}`
|
|
586
611
|
},
|
|
587
|
-
body: JSON.stringify(body)
|
|
612
|
+
...method === "POST" && body ? { body: JSON.stringify(body) } : {}
|
|
588
613
|
});
|
|
589
|
-
|
|
614
|
+
if (process.env.NOSIBLE_DEBUG) {
|
|
615
|
+
console.timeEnd(`Request-${endpoint}(${now})`);
|
|
616
|
+
}
|
|
590
617
|
if (!response.ok) {
|
|
591
618
|
console.error("Status: " + response.status);
|
|
592
619
|
if (response.status === 401) {
|
|
@@ -911,8 +938,11 @@ class WebPageData {
|
|
|
911
938
|
}
|
|
912
939
|
static async fromJson(client, inputPath) {
|
|
913
940
|
const data = await importJson({ filePath: inputPath });
|
|
914
|
-
const validData = scrapeResponseSchema.
|
|
915
|
-
|
|
941
|
+
const validData = scrapeResponseSchema.safeParse(data);
|
|
942
|
+
if (!validData.success) {
|
|
943
|
+
throw new Error("Provided JSON is not a valid scrape response");
|
|
944
|
+
}
|
|
945
|
+
return new WebPageData(client, validData.data);
|
|
916
946
|
}
|
|
917
947
|
async writeJson(outputPath) {
|
|
918
948
|
return await exportJson(this.data, outputPath);
|
|
@@ -967,175 +997,35 @@ class TopicTrend {
|
|
|
967
997
|
}
|
|
968
998
|
}
|
|
969
999
|
|
|
970
|
-
// src/utils/
|
|
1000
|
+
// src/utils/rateLimiters.ts
|
|
971
1001
|
var import_bottleneck = __toESM(require("bottleneck"));
|
|
972
|
-
var
|
|
973
|
-
{
|
|
974
|
-
|
|
975
|
-
name: "Legacy",
|
|
976
|
-
rateLimits: {
|
|
977
|
-
scrapeUrl: { minute: 60, month: 1400 },
|
|
978
|
-
bulk: { minute: 60, month: 1400 },
|
|
979
|
-
fast: { minute: 60, month: 14000 }
|
|
980
|
-
}
|
|
981
|
-
},
|
|
982
|
-
{
|
|
983
|
-
key: "pro",
|
|
984
|
-
name: "Legacy",
|
|
985
|
-
rateLimits: {
|
|
986
|
-
scrapeUrl: { minute: 60, month: 6700 },
|
|
987
|
-
bulk: { minute: 60, month: 6700 },
|
|
988
|
-
fast: { minute: 60, month: 67000 }
|
|
989
|
-
}
|
|
990
|
-
},
|
|
991
|
-
{
|
|
992
|
-
key: "pro+",
|
|
993
|
-
name: "Legacy",
|
|
994
|
-
rateLimits: {
|
|
995
|
-
scrapeUrl: { minute: 60, month: 32000 },
|
|
996
|
-
bulk: { minute: 60, month: 32000 },
|
|
997
|
-
fast: { minute: 60, month: 320000 }
|
|
998
|
-
}
|
|
999
|
-
},
|
|
1000
|
-
{
|
|
1001
|
-
key: "bus",
|
|
1002
|
-
name: "Legacy",
|
|
1003
|
-
rateLimits: {
|
|
1004
|
-
scrapeUrl: { minute: 60, month: 200000 },
|
|
1005
|
-
bulk: { minute: 60, month: 200000 },
|
|
1006
|
-
fast: { minute: 60, month: 2000000 }
|
|
1007
|
-
}
|
|
1008
|
-
},
|
|
1009
|
-
{
|
|
1010
|
-
key: "bus+",
|
|
1011
|
-
name: "Legacy",
|
|
1012
|
-
rateLimits: {
|
|
1013
|
-
scrapeUrl: { minute: 60, month: 500000 },
|
|
1014
|
-
bulk: { minute: 60, month: 500000 },
|
|
1015
|
-
fast: { minute: 120, month: 5000000 }
|
|
1016
|
-
}
|
|
1017
|
-
},
|
|
1018
|
-
{
|
|
1019
|
-
key: "prod",
|
|
1020
|
-
name: "Legacy",
|
|
1021
|
-
rateLimits: {
|
|
1022
|
-
scrapeUrl: { minute: 60, month: 1500000 },
|
|
1023
|
-
bulk: { minute: 60, month: 1500000 },
|
|
1024
|
-
fast: { minute: 360, month: 15000000 }
|
|
1025
|
-
}
|
|
1026
|
-
},
|
|
1027
|
-
{
|
|
1028
|
-
key: "chat",
|
|
1029
|
-
name: "Legacy",
|
|
1030
|
-
rateLimits: {
|
|
1031
|
-
scrapeUrl: { minute: 60, month: 1500000 },
|
|
1032
|
-
bulk: { minute: 60, month: 1500000 },
|
|
1033
|
-
fast: { minute: 360, month: 15000000 }
|
|
1034
|
-
}
|
|
1035
|
-
},
|
|
1036
|
-
{
|
|
1037
|
-
key: "self",
|
|
1038
|
-
name: "Legacy",
|
|
1039
|
-
rateLimits: {
|
|
1040
|
-
scrapeUrl: { minute: 6000, month: 1500000 },
|
|
1041
|
-
bulk: { minute: 6000, month: 1500000 },
|
|
1042
|
-
fast: { minute: 36000, month: 15000000 }
|
|
1043
|
-
}
|
|
1044
|
-
},
|
|
1045
|
-
{
|
|
1046
|
-
key: "test",
|
|
1047
|
-
name: "Free",
|
|
1048
|
-
rateLimits: {
|
|
1049
|
-
scrapeUrl: { minute: 60, month: 300 },
|
|
1050
|
-
bulk: { minute: 60, month: 300 },
|
|
1051
|
-
fast: { minute: 60, month: 3000 }
|
|
1052
|
-
}
|
|
1053
|
-
},
|
|
1054
|
-
{
|
|
1055
|
-
key: "cons",
|
|
1056
|
-
name: "Consumer",
|
|
1057
|
-
rateLimits: {
|
|
1058
|
-
scrapeUrl: { minute: 60, month: 3000 },
|
|
1059
|
-
bulk: { minute: 60, month: 3000 },
|
|
1060
|
-
fast: { minute: 120, month: 30000 }
|
|
1061
|
-
}
|
|
1062
|
-
},
|
|
1063
|
-
{
|
|
1064
|
-
key: "stup",
|
|
1065
|
-
name: "Startup",
|
|
1066
|
-
rateLimits: {
|
|
1067
|
-
scrapeUrl: { minute: 60, month: 30000 },
|
|
1068
|
-
bulk: { minute: 60, month: 30000 },
|
|
1069
|
-
fast: { minute: 360, month: 300000 }
|
|
1070
|
-
}
|
|
1071
|
-
},
|
|
1072
|
-
{
|
|
1073
|
-
key: "busn",
|
|
1074
|
-
name: "Business",
|
|
1075
|
-
rateLimits: {
|
|
1076
|
-
scrapeUrl: { minute: 60, month: 300000 },
|
|
1077
|
-
bulk: { minute: 60, month: 300000 },
|
|
1078
|
-
fast: { minute: 360, month: 3000000 }
|
|
1079
|
-
}
|
|
1080
|
-
},
|
|
1081
|
-
{
|
|
1082
|
-
key: "ent",
|
|
1083
|
-
name: "Enterprise",
|
|
1084
|
-
rateLimits: {
|
|
1085
|
-
scrapeUrl: { minute: 60, month: 1500000 },
|
|
1086
|
-
bulk: { minute: 60, month: 1500000 },
|
|
1087
|
-
fast: { minute: 360, month: 15000000 }
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
];
|
|
1091
|
-
var getUserPlan = (apiKey) => {
|
|
1092
|
-
if (apiKey.split("|").length > 2) {
|
|
1093
|
-
throw new Error("Invalid API key");
|
|
1002
|
+
var createChainedLimiter = (limitItems) => {
|
|
1003
|
+
if (limitItems.length === 0) {
|
|
1004
|
+
return new import_bottleneck.default;
|
|
1094
1005
|
}
|
|
1095
|
-
const
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1098
|
-
|
|
1006
|
+
const sortedLimits = [...limitItems].sort((a, b) => a.duration_seconds - b.duration_seconds);
|
|
1007
|
+
const limiters = sortedLimits.map((limitItem) => {
|
|
1008
|
+
const intervalMs = limitItem.duration_seconds * 1000;
|
|
1009
|
+
return new import_bottleneck.default({
|
|
1010
|
+
reservoir: limitItem.remaining ?? limitItem.limit,
|
|
1011
|
+
reservoirRefreshAmount: limitItem.limit,
|
|
1012
|
+
reservoirRefreshInterval: intervalMs
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
let chainedLimiter = limiters[0];
|
|
1016
|
+
for (let i = 1;i < limiters.length; i++) {
|
|
1017
|
+
chainedLimiter = chainedLimiter.chain(limiters[i]);
|
|
1099
1018
|
}
|
|
1100
|
-
return
|
|
1019
|
+
return chainedLimiter;
|
|
1101
1020
|
};
|
|
1102
|
-
var
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
});
|
|
1111
|
-
const scrapeUrlMonthLimiter = new import_bottleneck.default({
|
|
1112
|
-
reservoir: plan.rateLimits.scrapeUrl.month,
|
|
1113
|
-
reservoirRefreshAmount: plan.rateLimits.scrapeUrl.month,
|
|
1114
|
-
reservoirRefreshInterval: monthMs
|
|
1115
|
-
});
|
|
1116
|
-
const scrapeUrlLimiter = scrapeUrlMinLimiter.chain(scrapeUrlMonthLimiter);
|
|
1117
|
-
const bulkMinLimiter = new import_bottleneck.default({
|
|
1118
|
-
reservoir: plan.rateLimits.bulk.minute,
|
|
1119
|
-
reservoirRefreshAmount: plan.rateLimits.bulk.minute,
|
|
1120
|
-
reservoirRefreshInterval: minMs
|
|
1121
|
-
});
|
|
1122
|
-
const bulkMonthLimiter = new import_bottleneck.default({
|
|
1123
|
-
reservoir: plan.rateLimits.bulk.month,
|
|
1124
|
-
reservoirRefreshAmount: plan.rateLimits.bulk.month,
|
|
1125
|
-
reservoirRefreshInterval: monthMs
|
|
1126
|
-
});
|
|
1127
|
-
const bulkLimiter = bulkMinLimiter.chain(bulkMonthLimiter);
|
|
1128
|
-
const fastMinLimiter = new import_bottleneck.default({
|
|
1129
|
-
reservoir: plan.rateLimits.fast.minute,
|
|
1130
|
-
reservoirRefreshAmount: plan.rateLimits.fast.minute,
|
|
1131
|
-
reservoirRefreshInterval: minMs
|
|
1132
|
-
});
|
|
1133
|
-
const fastMonthLimiter = new import_bottleneck.default({
|
|
1134
|
-
reservoir: plan.rateLimits.fast.month,
|
|
1135
|
-
reservoirRefreshAmount: plan.rateLimits.fast.month,
|
|
1136
|
-
reservoirRefreshInterval: monthMs
|
|
1137
|
-
});
|
|
1138
|
-
const fastLimiter = fastMinLimiter.chain(fastMonthLimiter);
|
|
1021
|
+
var createLimiters = (limitsResponse) => {
|
|
1022
|
+
const { limits } = limitsResponse;
|
|
1023
|
+
const visitLimits = limits.filter((l) => l.query_type === "visit");
|
|
1024
|
+
const slowLimits = limits.filter((l) => l.query_type === "slow");
|
|
1025
|
+
const fastLimits = limits.filter((l) => l.query_type === "fast");
|
|
1026
|
+
const scrapeUrlLimiter = createChainedLimiter(visitLimits);
|
|
1027
|
+
const bulkLimiter = createChainedLimiter(slowLimits);
|
|
1028
|
+
const fastLimiter = createChainedLimiter(fastLimits);
|
|
1139
1029
|
return {
|
|
1140
1030
|
scrapeUrl: scrapeUrlLimiter,
|
|
1141
1031
|
bulk: bulkLimiter,
|
|
@@ -1701,23 +1591,26 @@ var nosibleClientParams = import_zod7.default.union([
|
|
|
1701
1591
|
openAiBaseURL: import_zod7.default.string().optional(),
|
|
1702
1592
|
sentimentModel: import_zod7.default.string().optional(),
|
|
1703
1593
|
expansionsModel: import_zod7.default.string().optional(),
|
|
1704
|
-
searchDefaults: clientDefaultSearchParamsSchema.optional()
|
|
1594
|
+
searchDefaults: clientDefaultSearchParamsSchema.optional(),
|
|
1595
|
+
rateLimit: import_zod7.default.boolean().optional()
|
|
1705
1596
|
})
|
|
1706
1597
|
]);
|
|
1707
1598
|
|
|
1708
1599
|
class NosibleClient {
|
|
1709
1600
|
baseUrl = "https://www.nosible.ai/search/v2";
|
|
1710
1601
|
apiKey;
|
|
1602
|
+
api_key_version;
|
|
1711
1603
|
llmClient;
|
|
1712
1604
|
expansionsModel = "openai/gpt-4o";
|
|
1713
1605
|
searchDefaults;
|
|
1714
|
-
|
|
1606
|
+
rateLimit = true;
|
|
1715
1607
|
limiters;
|
|
1716
1608
|
constructor(params) {
|
|
1717
1609
|
let apiKeyToUse;
|
|
1718
1610
|
let llmApiKeyToUse;
|
|
1719
1611
|
let openAiBaseURL;
|
|
1720
1612
|
let searchDefaultsToUse;
|
|
1613
|
+
let rateLimitToUse = true;
|
|
1721
1614
|
if (typeof params === "string") {
|
|
1722
1615
|
apiKeyToUse = params;
|
|
1723
1616
|
} else if (params) {
|
|
@@ -1725,6 +1618,7 @@ class NosibleClient {
|
|
|
1725
1618
|
llmApiKeyToUse = params.llmApiKey || process.env.LLM_API_KEY;
|
|
1726
1619
|
openAiBaseURL = params.openAiBaseURL;
|
|
1727
1620
|
searchDefaultsToUse = params.searchDefaults;
|
|
1621
|
+
rateLimitToUse = params.rateLimit ?? false;
|
|
1728
1622
|
} else {
|
|
1729
1623
|
apiKeyToUse = process.env.NOSIBLE_API_KEY;
|
|
1730
1624
|
llmApiKeyToUse = process.env.LLM_API_KEY;
|
|
@@ -1742,8 +1636,7 @@ class NosibleClient {
|
|
|
1742
1636
|
}
|
|
1743
1637
|
this.apiKey = apiKeyToUse;
|
|
1744
1638
|
this.searchDefaults = searchDefaultsToUse;
|
|
1745
|
-
this.
|
|
1746
|
-
this.limiters = getLimiters(this.apiKey);
|
|
1639
|
+
this.rateLimit = rateLimitToUse;
|
|
1747
1640
|
}
|
|
1748
1641
|
mergeWithDefaultSearch(params) {
|
|
1749
1642
|
if (!this.searchDefaults) {
|
|
@@ -1758,43 +1651,70 @@ class NosibleClient {
|
|
|
1758
1651
|
if (!apiKey) {
|
|
1759
1652
|
throw new Error("API key is required");
|
|
1760
1653
|
}
|
|
1654
|
+
if (apiKey.startsWith("nos_sk_")) {
|
|
1655
|
+
this.api_key_version = "v2";
|
|
1656
|
+
this.apiKey = apiKey;
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1761
1659
|
const splits = apiKey.split("|");
|
|
1762
1660
|
if (splits.length !== 2) {
|
|
1763
|
-
throw new Error("API key is invalid - expected format '
|
|
1661
|
+
throw new Error("API key is invalid - expected format 'plan_id|secret_key' (v1) or 'nos_sk_...' (v2)");
|
|
1764
1662
|
}
|
|
1663
|
+
this.api_key_version = "v1";
|
|
1765
1664
|
this.apiKey = apiKey;
|
|
1766
1665
|
}
|
|
1767
|
-
async _request(endpoint, body) {
|
|
1666
|
+
async _request(endpoint, body, method = "POST") {
|
|
1768
1667
|
return callNosibleApi({
|
|
1769
1668
|
endpoint,
|
|
1770
1669
|
body,
|
|
1771
|
-
apiKey: this.apiKey
|
|
1670
|
+
apiKey: this.apiKey,
|
|
1671
|
+
method
|
|
1772
1672
|
});
|
|
1773
1673
|
}
|
|
1674
|
+
async _initializeLimiters() {
|
|
1675
|
+
if (!this.rateLimit || this.limiters) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const limitsResponse = await this.limits();
|
|
1679
|
+
this.limiters = createLimiters(limitsResponse);
|
|
1680
|
+
}
|
|
1681
|
+
async limits() {
|
|
1682
|
+
const response = await this._request("limits", undefined, "GET");
|
|
1683
|
+
return response;
|
|
1684
|
+
}
|
|
1774
1685
|
async fastSearch(params) {
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
search
|
|
1686
|
+
if (this.rateLimit && !this.limiters) {
|
|
1687
|
+
await this._initializeLimiters();
|
|
1688
|
+
}
|
|
1689
|
+
const executeSearch = async () => {
|
|
1690
|
+
let search;
|
|
1691
|
+
const hasSearchInstance = (params2) => {
|
|
1692
|
+
return "search" in params2;
|
|
1693
|
+
};
|
|
1694
|
+
if (hasSearchInstance(params)) {
|
|
1695
|
+
if (params.search instanceof Search) {
|
|
1696
|
+
search = params.search;
|
|
1697
|
+
} else {
|
|
1698
|
+
throw new Error("Invalid search parameter provided.");
|
|
1699
|
+
}
|
|
1782
1700
|
} else {
|
|
1783
|
-
|
|
1701
|
+
const mergedParams = this.mergeWithDefaultSearch(params);
|
|
1702
|
+
search = new Search(mergedParams);
|
|
1784
1703
|
}
|
|
1785
|
-
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1704
|
+
const body = await search.searchBody({ client: this });
|
|
1705
|
+
const validatedBody = validateFastSearchParams(body);
|
|
1706
|
+
const { message, query, response } = await this._request("fast-search", validatedBody);
|
|
1707
|
+
const results = response.map((result) => {
|
|
1708
|
+
const fullResult = { ...query, ...result };
|
|
1709
|
+
return fullResult;
|
|
1710
|
+
});
|
|
1711
|
+
const resultSet = createResultSet(this, results);
|
|
1712
|
+
return resultSet;
|
|
1713
|
+
};
|
|
1714
|
+
if (this.rateLimit && this.limiters) {
|
|
1715
|
+
return this.limiters.fast.schedule(executeSearch);
|
|
1788
1716
|
}
|
|
1789
|
-
|
|
1790
|
-
const validatedBody = validateFastSearchParams(body);
|
|
1791
|
-
const { message, query, response } = await this._request("fast-search", validatedBody);
|
|
1792
|
-
const results = response.map((result) => {
|
|
1793
|
-
const fullResult = { ...query, ...result };
|
|
1794
|
-
return fullResult;
|
|
1795
|
-
});
|
|
1796
|
-
const resultSet = createResultSet(this, results);
|
|
1797
|
-
return resultSet;
|
|
1717
|
+
return executeSearch();
|
|
1798
1718
|
}
|
|
1799
1719
|
async fastSearches(params) {
|
|
1800
1720
|
let searchSet;
|
|
@@ -1810,49 +1730,74 @@ class NosibleClient {
|
|
|
1810
1730
|
return results;
|
|
1811
1731
|
}
|
|
1812
1732
|
async aiSearch(params) {
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1733
|
+
if (this.rateLimit && !this.limiters) {
|
|
1734
|
+
await this._initializeLimiters();
|
|
1735
|
+
}
|
|
1736
|
+
const executeSearch = async () => {
|
|
1737
|
+
const json = await this._request("search", params);
|
|
1738
|
+
const results = json.response.map((result) => {
|
|
1739
|
+
const fullResult = { ...json.query, ...result };
|
|
1740
|
+
return fullResult;
|
|
1741
|
+
});
|
|
1742
|
+
const resultSet = createResultSet(this, results);
|
|
1743
|
+
return resultSet;
|
|
1744
|
+
};
|
|
1745
|
+
if (this.rateLimit && this.limiters) {
|
|
1746
|
+
return this.limiters.fast.schedule(executeSearch);
|
|
1747
|
+
}
|
|
1748
|
+
return executeSearch();
|
|
1820
1749
|
}
|
|
1821
1750
|
async bulkSearch(params) {
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
search
|
|
1751
|
+
if (this.rateLimit && !this.limiters) {
|
|
1752
|
+
await this._initializeLimiters();
|
|
1753
|
+
}
|
|
1754
|
+
const executeSearch = async () => {
|
|
1755
|
+
let search;
|
|
1756
|
+
const hasSearchInstance = (params2) => {
|
|
1757
|
+
return "search" in params2;
|
|
1758
|
+
};
|
|
1759
|
+
if (hasSearchInstance(params)) {
|
|
1760
|
+
if (params.search instanceof Search) {
|
|
1761
|
+
search = params.search;
|
|
1762
|
+
} else {
|
|
1763
|
+
throw new Error("Invalid search parameter provided.");
|
|
1764
|
+
}
|
|
1829
1765
|
} else {
|
|
1830
|
-
|
|
1766
|
+
const mergedParams = this.mergeWithDefaultSearch(params);
|
|
1767
|
+
search = new Search(mergedParams);
|
|
1831
1768
|
}
|
|
1832
|
-
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1769
|
+
const body = await search.searchBody({ client: this });
|
|
1770
|
+
const validatedBody = validateBulkSearchParams(body);
|
|
1771
|
+
const fileLocation = await this._request("bulk-search", validatedBody);
|
|
1772
|
+
const json = await bulkSearchDownload({
|
|
1773
|
+
downloadFrom: fileLocation.download_from,
|
|
1774
|
+
decryptUsing: fileLocation.decrypt_using
|
|
1775
|
+
});
|
|
1776
|
+
const results = json.response.map((result) => {
|
|
1777
|
+
const fullResult = { ...json.query, ...result };
|
|
1778
|
+
return fullResult;
|
|
1779
|
+
});
|
|
1780
|
+
const resultSet = createResultSet(this, results);
|
|
1781
|
+
return resultSet;
|
|
1782
|
+
};
|
|
1783
|
+
if (this.rateLimit && this.limiters) {
|
|
1784
|
+
return this.limiters.bulk.schedule(executeSearch);
|
|
1835
1785
|
}
|
|
1836
|
-
|
|
1837
|
-
const validatedBody = validateBulkSearchParams(body);
|
|
1838
|
-
const fileLocation = await this._request("bulk-search", validatedBody);
|
|
1839
|
-
const json = await bulkSearchDownload({
|
|
1840
|
-
downloadFrom: fileLocation.download_from,
|
|
1841
|
-
decryptUsing: fileLocation.decrypt_using
|
|
1842
|
-
});
|
|
1843
|
-
const results = json.response.map((result) => {
|
|
1844
|
-
const fullResult = { ...json.query, ...result };
|
|
1845
|
-
return fullResult;
|
|
1846
|
-
});
|
|
1847
|
-
const resultSet = createResultSet(this, results);
|
|
1848
|
-
return resultSet;
|
|
1786
|
+
return executeSearch();
|
|
1849
1787
|
}
|
|
1850
1788
|
async scrapeUrl(url) {
|
|
1851
|
-
|
|
1789
|
+
if (this.rateLimit && !this.limiters) {
|
|
1790
|
+
await this._initializeLimiters();
|
|
1791
|
+
}
|
|
1792
|
+
const executeScrape = async () => {
|
|
1852
1793
|
const json = await this._request("scrape-url", { url });
|
|
1853
1794
|
const webPage = new WebPageData(this, json.response);
|
|
1854
1795
|
return webPage;
|
|
1855
|
-
}
|
|
1796
|
+
};
|
|
1797
|
+
if (this.rateLimit && this.limiters) {
|
|
1798
|
+
return this.limiters.scrapeUrl.schedule(executeScrape);
|
|
1799
|
+
}
|
|
1800
|
+
return executeScrape();
|
|
1856
1801
|
}
|
|
1857
1802
|
async topicTrend(query, sqlFilter) {
|
|
1858
1803
|
const body = { query, sql_filter: sqlFilter };
|
|
@@ -1862,4 +1807,4 @@ class NosibleClient {
|
|
|
1862
1807
|
}
|
|
1863
1808
|
}
|
|
1864
1809
|
|
|
1865
|
-
//# debugId=
|
|
1810
|
+
//# debugId=71464A07E20B4B6E64756E2164756E21
|