nosible 0.1.5

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.
Files changed (44) hide show
  1. package/README.md +438 -0
  2. package/dist/index.cjs +1841 -0
  3. package/dist/index.cjs.map +29 -0
  4. package/dist/index.js +1815 -0
  5. package/dist/index.js.map +29 -0
  6. package/package.json +63 -0
  7. package/src/api/api.test.ts +366 -0
  8. package/src/api/index.ts +179 -0
  9. package/src/api/schemas.ts +152 -0
  10. package/src/client.test.ts +685 -0
  11. package/src/client.ts +762 -0
  12. package/src/index.ts +4 -0
  13. package/src/scrape/types.ts +119 -0
  14. package/src/scrape/webPageData.test.ts +302 -0
  15. package/src/scrape/webPageData.ts +103 -0
  16. package/src/search/analyze.test.ts +396 -0
  17. package/src/search/analyze.ts +151 -0
  18. package/src/search/bulkSearch.ts +62 -0
  19. package/src/search/result.test.ts +423 -0
  20. package/src/search/result.ts +391 -0
  21. package/src/search/result.types.ts +32 -0
  22. package/src/search/resultFactory.ts +21 -0
  23. package/src/search/resultSet.io.test.ts +320 -0
  24. package/src/search/resultSet.test.ts +368 -0
  25. package/src/search/resultSet.ts +387 -0
  26. package/src/search/resultSet.types.ts +3 -0
  27. package/src/search/search.test.ts +299 -0
  28. package/src/search/search.ts +187 -0
  29. package/src/search/searchSet.io.test.ts +321 -0
  30. package/src/search/searchSet.ts +122 -0
  31. package/src/search/sqlFilter.test.ts +129 -0
  32. package/src/search/sqlFilter.ts +147 -0
  33. package/src/test-utils/mocks.ts +159 -0
  34. package/src/topicTrend/topicTrend.ts +53 -0
  35. package/src/utils/browser.test.ts +209 -0
  36. package/src/utils/browser.ts +21 -0
  37. package/src/utils/fernet.ts +47 -0
  38. package/src/utils/file.test.ts +81 -0
  39. package/src/utils/file.ts +195 -0
  40. package/src/utils/index.ts +7 -0
  41. package/src/utils/llm.test.ts +279 -0
  42. package/src/utils/llm.ts +244 -0
  43. package/src/utils/userPlan.test.ts +332 -0
  44. package/src/utils/userPlan.ts +211 -0
@@ -0,0 +1,366 @@
1
+ import {describe, it, expect, beforeEach, afterEach, jest} from "bun:test";
2
+ import {callNosibleApi} from ".";
3
+
4
+ describe("callNosibleApi mock responses", () => {
5
+ const mockApiKey = "test-api-key";
6
+ const mockEndpoint = "test-endpoint";
7
+ const mockBody = {test: "data"};
8
+
9
+ // Store the original fetch function
10
+ const originalFetch = globalThis.fetch;
11
+
12
+ beforeEach(() => {
13
+ // Reset fetch mock before each test
14
+ });
15
+
16
+ afterEach(() => {
17
+ // Restore the original fetch function after each test
18
+ globalThis.fetch = originalFetch;
19
+ });
20
+
21
+ it("should handle 200 OK response correctly", async () => {
22
+ // Mock a successful response
23
+ const mockResponse = Promise.resolve({
24
+ ok: true,
25
+ status: 200,
26
+ json: () =>
27
+ Promise.resolve({
28
+ data: "test response data",
29
+ message: "success",
30
+ }),
31
+ });
32
+
33
+ // Replace global fetch with our mock
34
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
35
+
36
+ const result = await callNosibleApi({
37
+ endpoint: mockEndpoint,
38
+ body: mockBody,
39
+ apiKey: mockApiKey,
40
+ });
41
+
42
+ expect(result).toEqual({
43
+ data: "test response data",
44
+ message: "success",
45
+ });
46
+
47
+ // Verify fetch was called with correct parameters
48
+ expect(globalThis.fetch).toHaveBeenCalledWith(
49
+ `https://www.nosible.ai/search/v2/${mockEndpoint}`,
50
+ {
51
+ method: "POST",
52
+ headers: {
53
+ Accept: "application/json",
54
+ "Content-Type": "application/json",
55
+ "Api-Key": mockApiKey,
56
+ },
57
+ body: JSON.stringify(mockBody),
58
+ }
59
+ );
60
+ });
61
+
62
+ it("should handle 401 Unauthorized response correctly", async () => {
63
+ // Mock a 401 response
64
+ const mockResponse = Promise.resolve({
65
+ ok: false,
66
+ status: 401,
67
+ json: () => Promise.resolve({}),
68
+ text: () => Promise.resolve("Unauthorized"),
69
+ });
70
+
71
+ // Replace global fetch with our mock
72
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
73
+
74
+ // Expect the function to throw an error
75
+ await expect(
76
+ callNosibleApi({
77
+ endpoint: mockEndpoint,
78
+ body: mockBody,
79
+ apiKey: mockApiKey,
80
+ })
81
+ ).rejects.toThrow("Your API key is not valid.");
82
+
83
+ // Verify fetch was called with correct parameters
84
+ expect(globalThis.fetch).toHaveBeenCalledWith(
85
+ `https://www.nosible.ai/search/v2/${mockEndpoint}`,
86
+ {
87
+ method: "POST",
88
+ headers: {
89
+ Accept: "application/json",
90
+ "Content-Type": "application/json",
91
+ "Api-Key": mockApiKey,
92
+ },
93
+ body: JSON.stringify(mockBody),
94
+ }
95
+ );
96
+ });
97
+
98
+ it("should handle 422 with string_too_short error correctly", async () => {
99
+ // Mock a 422 response with string_too_short error
100
+ const mockResponse = Promise.resolve({
101
+ ok: false,
102
+ status: 422,
103
+ headers: {
104
+ get: (name: string) => {
105
+ if (name.toLowerCase() === "content-type") return "application/json";
106
+ return "";
107
+ },
108
+ },
109
+ json: () =>
110
+ Promise.resolve([
111
+ {
112
+ type: "string_too_short",
113
+ message: "API key is too short",
114
+ },
115
+ ]),
116
+ text: () => Promise.resolve("Bad Request"),
117
+ });
118
+
119
+ // Replace global fetch with our mock
120
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
121
+
122
+ // Expect the function to throw an error
123
+ await expect(
124
+ callNosibleApi({
125
+ endpoint: mockEndpoint,
126
+ body: mockBody,
127
+ apiKey: mockApiKey,
128
+ })
129
+ ).rejects.toThrow("Your API key is not valid: Too Short.");
130
+ });
131
+
132
+ it("should handle 422 with general bad request correctly", async () => {
133
+ // Mock a 422 response with general error
134
+ const mockResponse = Promise.resolve({
135
+ ok: false,
136
+ status: 422,
137
+ headers: {
138
+ get: (name: string) => {
139
+ if (name.toLowerCase() === "content-type") return "application/json";
140
+ return "";
141
+ },
142
+ },
143
+ json: () =>
144
+ Promise.resolve([
145
+ {
146
+ message: "Bad request",
147
+ },
148
+ ]),
149
+ text: () => Promise.resolve("Bad Request"),
150
+ });
151
+
152
+ // Replace global fetch with our mock
153
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
154
+
155
+ // Expect the function to throw an error
156
+ await expect(
157
+ callNosibleApi({
158
+ endpoint: mockEndpoint,
159
+ body: mockBody,
160
+ apiKey: mockApiKey,
161
+ })
162
+ ).rejects.toThrow("You made a bad request.");
163
+ });
164
+
165
+ it("should handle 429 rate limit error correctly", async () => {
166
+ // Mock a 429 response
167
+ const mockResponse = Promise.resolve({
168
+ ok: false,
169
+ status: 429,
170
+ headers: {
171
+ get: () => "",
172
+ },
173
+ json: () => Promise.resolve({}),
174
+ text: () => Promise.resolve("Rate Limit Exceeded"),
175
+ });
176
+
177
+ // Replace global fetch with our mock
178
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
179
+
180
+ // Expect the function to throw an error
181
+ await expect(
182
+ callNosibleApi({
183
+ endpoint: mockEndpoint,
184
+ body: mockBody,
185
+ apiKey: mockApiKey,
186
+ })
187
+ ).rejects.toThrow("You have hit your rate limit.");
188
+ });
189
+
190
+ it("should handle 409 concurrent searches error correctly", async () => {
191
+ // Mock a 409 response
192
+ const mockResponse = Promise.resolve({
193
+ ok: false,
194
+ status: 409,
195
+ headers: {
196
+ get: () => "",
197
+ },
198
+ json: () => Promise.resolve({}),
199
+ text: () => Promise.resolve("Too Many Requests"),
200
+ });
201
+
202
+ // Replace global fetch with our mock
203
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
204
+
205
+ // Expect the function to throw an error
206
+ await expect(
207
+ callNosibleApi({
208
+ endpoint: mockEndpoint,
209
+ body: mockBody,
210
+ apiKey: mockApiKey,
211
+ })
212
+ ).rejects.toThrow("Too many concurrent searches.");
213
+ });
214
+
215
+ it("should handle 500 internal server error correctly", async () => {
216
+ // Mock a 500 response
217
+ const mockResponse = Promise.resolve({
218
+ ok: false,
219
+ status: 500,
220
+ headers: {
221
+ get: () => "",
222
+ },
223
+ json: () => Promise.resolve({}),
224
+ text: () => Promise.resolve("Internal Server Error"),
225
+ });
226
+
227
+ // Replace global fetch with our mock
228
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
229
+
230
+ // Expect the function to throw an error
231
+ await expect(
232
+ callNosibleApi({
233
+ endpoint: mockEndpoint,
234
+ body: mockBody,
235
+ apiKey: mockApiKey,
236
+ })
237
+ ).rejects.toThrow("An unexpected error occurred.");
238
+ });
239
+
240
+ it("should handle 502 bad gateway error correctly", async () => {
241
+ // Mock a 502 response
242
+ const mockResponse = Promise.resolve({
243
+ ok: false,
244
+ status: 502,
245
+ headers: {
246
+ get: () => "",
247
+ },
248
+ json: () => Promise.resolve({}),
249
+ text: () => Promise.resolve("Bad Gateway"),
250
+ });
251
+
252
+ // Replace global fetch with our mock
253
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
254
+
255
+ // Expect the function to throw an error
256
+ await expect(
257
+ callNosibleApi({
258
+ endpoint: mockEndpoint,
259
+ body: mockBody,
260
+ apiKey: mockApiKey,
261
+ })
262
+ ).rejects.toThrow("NOSIBLE is currently restarting.");
263
+ });
264
+
265
+ it("should handle 504 gateway timeout error correctly", async () => {
266
+ // Mock a 504 response
267
+ const mockResponse = Promise.resolve({
268
+ ok: false,
269
+ status: 504,
270
+ headers: {
271
+ get: () => "",
272
+ },
273
+ json: () => Promise.resolve({}),
274
+ text: () => Promise.resolve("Gateway Timeout"),
275
+ });
276
+
277
+ // Replace global fetch with our mock
278
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
279
+
280
+ // Expect the function to throw an error
281
+ await expect(
282
+ callNosibleApi({
283
+ endpoint: mockEndpoint,
284
+ body: mockBody,
285
+ apiKey: mockApiKey,
286
+ })
287
+ ).rejects.toThrow("NOSIBLE is currently overloaded.");
288
+ });
289
+
290
+ it("should handle fallback for other status codes", async () => {
291
+ // Mock a 418 response (I'm a teapot)
292
+ const mockResponse = Promise.resolve({
293
+ ok: false,
294
+ status: 418,
295
+ headers: {
296
+ get: () => "",
297
+ },
298
+ json: () => Promise.resolve({}),
299
+ text: () => Promise.resolve("I'm a teapot"),
300
+ });
301
+
302
+ // Replace global fetch with our mock
303
+ globalThis.fetch = jest.fn(() => mockResponse) as any;
304
+
305
+ // Expect the function to throw an error
306
+ await expect(
307
+ callNosibleApi({
308
+ endpoint: mockEndpoint,
309
+ body: mockBody,
310
+ apiKey: mockApiKey,
311
+ })
312
+ ).rejects.toThrow("HTTP error! status: 418");
313
+ });
314
+ });
315
+
316
+ describe("callNosibleApi", () => {
317
+ const apiKey = process.env.NOSIBLE_API_KEY;
318
+
319
+ // Only run the API test if we have a valid API key
320
+ const shouldRunApiTest = apiKey && apiKey.length > 0;
321
+
322
+ it("should respond with JSON when calling the real API", async () => {
323
+ if (!shouldRunApiTest) {
324
+ // Skip the test if no API key is available
325
+ expect(true).toBe(true);
326
+ console.log("Skipping API test - no valid API key found");
327
+ return;
328
+ }
329
+
330
+ // When we have a valid API key, test the actual API call
331
+ const response = await callNosibleApi({
332
+ endpoint: "fast-search",
333
+ body: {
334
+ question: "machine learning",
335
+ sql_filter: "SELECT loc FROM engine",
336
+ algorithm: "hybrid-2",
337
+ n_results: 10,
338
+ language: "en",
339
+ continent: "North America",
340
+ country: "United States",
341
+ sector: "Information Technology",
342
+ },
343
+ apiKey: apiKey,
344
+ });
345
+
346
+ // Verify that we get a response with the expected structure
347
+ expect(response).toBeDefined();
348
+ // The response should be an object
349
+ expect(typeof response).toBe("object");
350
+ // Log the response structure for debugging
351
+ console.log("Response keys:", Object.keys(response));
352
+ // It should have the expected properties
353
+ expect(response).toHaveProperty("message");
354
+ expect(response).toHaveProperty("query");
355
+ expect(response).toHaveProperty("response");
356
+ // Response should be an array
357
+ expect(Array.isArray(response.response)).toBe(true);
358
+ // Each item in response should have required properties
359
+ if (response.response.length > 0) {
360
+ const firstItem = response.response[0];
361
+ expect(firstItem).toHaveProperty("url");
362
+ expect(firstItem).toHaveProperty("title");
363
+ expect(firstItem).toHaveProperty("content");
364
+ }
365
+ });
366
+ });
@@ -0,0 +1,179 @@
1
+ import {
2
+ algorithmEnum,
3
+ brandSafetyEnum,
4
+ continentEnum,
5
+ regionEnum,
6
+ } from "./schemas";
7
+ import {z} from "zod";
8
+
9
+ /* ------------------ SEARCH ------------------ */
10
+
11
+ // Number of results
12
+ export const fastNResults = z.number().min(10).max(100).optional().default(100);
13
+ export const searchNResults = z
14
+ .number()
15
+ .min(100)
16
+ .max(1000)
17
+ .optional()
18
+ .default(100);
19
+ export const bulkNResults = z
20
+ .number()
21
+ .min(1000)
22
+ .max(10000)
23
+ .optional()
24
+ .default(1000);
25
+
26
+ // AI search params for search only
27
+ export const aiSearchParamsSchema = z.object({
28
+ prompt: z.string().min(25).max(2500),
29
+ agent: z.string().optional(),
30
+ });
31
+ export type SearchParamsType = z.infer<typeof aiSearchParamsSchema>;
32
+
33
+ // Standard search params for fast and bulk search only
34
+ export const stdSearchParams = z.object({
35
+ question: z.string(),
36
+ expansions: z.array(z.string()).optional(),
37
+ sql_filter: z.string().optional(),
38
+ algorithm: algorithmEnum.optional(),
39
+ n_results: z.number(),
40
+ n_probes: z.number().min(1).max(10).optional(),
41
+ n_contextify: z.number().min(64).max(1024).optional(),
42
+ min_similarity: z.number().min(0).max(1).optional(),
43
+ must_include: z.array(z.string()).max(100).optional(),
44
+ must_exclude: z.array(z.string()).max(100).optional(),
45
+ brand_safety: brandSafetyEnum.optional(),
46
+ language: z.string().min(2).max(2).optional(),
47
+ continent: continentEnum.optional(),
48
+ region: regionEnum.optional(),
49
+ country: z.string().optional(),
50
+ sector: z.string().optional(),
51
+ industry_group: z.string().optional(),
52
+ industry: z.string().optional(),
53
+ sub_industry: z.string().optional(),
54
+ iab_tier_1: z.string().optional(),
55
+ iab_tier_2: z.string().optional(),
56
+ iab_tier_3: z.string().optional(),
57
+ iab_tier_4: z.string().optional(),
58
+ companies: z.array(z.string()).max(30).optional(),
59
+ });
60
+ export type StdSearchParamsType = z.infer<typeof stdSearchParams>;
61
+
62
+ // Fast search params
63
+ const fastSearchParamsSchema = z.object({
64
+ ...stdSearchParams.shape,
65
+ n_results: fastNResults, // Overrides stdSearchParams
66
+ });
67
+ export type FastSearchParamsType = z.infer<typeof fastSearchParamsSchema>;
68
+
69
+ // Bulk search params
70
+ const bulkSearchParamsSchema = z.object({
71
+ ...stdSearchParams.shape,
72
+ n_results: bulkNResults, // Overrides stdSearchParams
73
+ });
74
+ type BulkSearchParamsType = z.infer<typeof bulkSearchParamsSchema>;
75
+
76
+ // Validation Functions
77
+ export const validateAiSearchParams = (params: StdSearchParamsType) => {
78
+ const validatedParams = aiSearchParamsSchema.parse(params);
79
+ };
80
+
81
+ export const validateFastSearchParams = (params: FastSearchParamsType) => {
82
+ const validatedParams = fastSearchParamsSchema.parse(params);
83
+ return validatedParams;
84
+ };
85
+
86
+ export const validateBulkSearchParams = (params: BulkSearchParamsType) => {
87
+ const validatedParams = bulkSearchParamsSchema.parse(params);
88
+ return validatedParams;
89
+ };
90
+
91
+ /* --------------- SCRAPE URL --------------- */
92
+ const scapeUrlBody = z.object({
93
+ url: z.string(),
94
+ });
95
+ type ScrapeUrlBodyType = z.infer<typeof scapeUrlBody>;
96
+
97
+ /* ------------------ TOPIC TREND ------------------ */
98
+ const topicTrendBody = z.object({
99
+ query: z.string(),
100
+ sql_filter: z.string().optional(),
101
+ });
102
+ type TopicTrendBodyType = z.infer<typeof topicTrendBody>;
103
+
104
+ /* ------------------ API CALL ------------------ */
105
+
106
+ // Call API
107
+
108
+ export const callNosibleApi = async ({
109
+ baseUrl = "https://www.nosible.ai/search/v2",
110
+ endpoint,
111
+ body,
112
+ apiKey,
113
+ }: {
114
+ baseUrl?: string;
115
+ endpoint: string;
116
+ body: any;
117
+ apiKey: string;
118
+ }): Promise<any> => {
119
+ /** Make authenticated POST request to the API */
120
+ const response = await fetch(`${baseUrl}/${endpoint}`, {
121
+ method: "POST",
122
+ headers: {
123
+ Accept: "application/json",
124
+ "Content-Type": "application/json",
125
+ "Api-Key": `${apiKey}`,
126
+ },
127
+ body: JSON.stringify(body),
128
+ });
129
+
130
+ /** Handle error responses with detailed logging */
131
+ if (!response.ok) {
132
+ console.error("Status: " + response.status);
133
+
134
+ if (response.status === 401) {
135
+ throw new Error("Your API key is not valid.");
136
+ }
137
+
138
+ if (response.status === 422) {
139
+ const contentType = response.headers.get("Content-Type") || "";
140
+ if (contentType.startsWith("application/json")) {
141
+ const body = await response.json();
142
+ const errorBody = Array.isArray(body) ? body[0] : body;
143
+ console.error(errorBody);
144
+ if (errorBody?.type === "string_too_short") {
145
+ throw new Error("Your API key is not valid: Too Short.");
146
+ }
147
+ }
148
+ throw new Error("You made a bad request.");
149
+ }
150
+
151
+ if (response.status === 429) {
152
+ throw new Error("You have hit your rate limit.");
153
+ }
154
+
155
+ if (response.status === 409) {
156
+ throw new Error("Too many concurrent searches.");
157
+ }
158
+
159
+ if (response.status === 500) {
160
+ throw new Error("An unexpected error occurred.");
161
+ }
162
+
163
+ if (response.status === 502) {
164
+ throw new Error("NOSIBLE is currently restarting.");
165
+ }
166
+
167
+ if (response.status === 504) {
168
+ throw new Error("NOSIBLE is currently overloaded.");
169
+ }
170
+
171
+ // Fallback for other status codes
172
+ console.error(await response.text());
173
+ throw new Error(`HTTP error! status: ${response.status}`);
174
+ } else {
175
+ /** Log successful response and return parsed data */
176
+ const data = await response.json();
177
+ return data;
178
+ }
179
+ };
@@ -0,0 +1,152 @@
1
+ import z from "zod";
2
+
3
+ export const queryTypeEnum = z.enum(["fast-search", "search", "bulk-search"]);
4
+ type QueryType = z.infer<typeof queryTypeEnum>;
5
+
6
+ export const algorithmEnum = z.enum([
7
+ "string",
8
+ "lexical",
9
+ "baseline",
10
+ "hamming",
11
+ "hybrid-1",
12
+ "hybrid-2",
13
+ "hybrid-3",
14
+ "company",
15
+ ]);
16
+ export type AlgorithmEnum = z.infer<typeof algorithmEnum>;
17
+
18
+ export const brandSafetyEnum = z.enum(["safe", "sensitive", "unsafe"]);
19
+ export type BrandSafetyEnum = z.infer<typeof brandSafetyEnum>;
20
+
21
+ export const continentEnum = z.enum([
22
+ "Africa",
23
+ "Asia",
24
+ "Europe",
25
+ "North America",
26
+ "Oceania",
27
+ "South America",
28
+ "Worldwide",
29
+ ]);
30
+ export type ContinentEnum = z.infer<typeof continentEnum>;
31
+
32
+ export const regionEnum = z.enum([
33
+ "Caribbean",
34
+ "Central Africa",
35
+ "Central America",
36
+ "Central Asia",
37
+ "Central Europe",
38
+ "East Africa",
39
+ "East Asia",
40
+ "Eastern Europe",
41
+ "Middle East",
42
+ "North Africa",
43
+ "North America",
44
+ "Northern Europe",
45
+ "Oceania",
46
+ "South America",
47
+ "South Asia",
48
+ "Southeast Asia",
49
+ "Southern Africa",
50
+ "Southern Europe",
51
+ "Sub-Saharan Africa",
52
+ "The Amazon Basin",
53
+ "The Andes",
54
+ "The Arctic Region",
55
+ "The Balkans",
56
+ "The Caucasus",
57
+ "The Horn of Africa",
58
+ "The Levant",
59
+ "The Middle East",
60
+ "The Sahel",
61
+ "West Africa",
62
+ "Western Europe",
63
+ "Worldwide",
64
+ ]);
65
+
66
+ export type RegionEnum = z.infer<typeof regionEnum>;
67
+
68
+ export interface SearchResType {
69
+ message: string;
70
+ query: SearchQuery;
71
+ response: SearchResponse[];
72
+ }
73
+
74
+ export const searchQuerySchema = z.object({
75
+ instruction: z.string(),
76
+ question: z.string(),
77
+ expansions: z.array(z.string()),
78
+ brand_safety: z.null(),
79
+ language: z.string().nullable(),
80
+ continent: z.string().nullable(),
81
+ region: z.string().nullable(),
82
+ country: z.string().nullable(),
83
+ sector: z.string().nullable(),
84
+ industry_group: z.string().nullable(),
85
+ industry: z.string().nullable(),
86
+ sub_industry: z.string().nullable(),
87
+ iab_tier_1: z.string().nullable(),
88
+ iab_tier_2: z.string().nullable(),
89
+ iab_tier_3: z.string().nullable(),
90
+ iab_tier_4: z.string().nullable(),
91
+ algorithm: z.string(),
92
+ n_results: z.number(),
93
+ n_contextify: z.number(),
94
+ n_probes: z.number(),
95
+ min_similarity: z.number(),
96
+ pass_similarity: z.number(),
97
+ must_exclude: z.array(z.string()),
98
+ must_include: z.array(z.string()),
99
+ shard_selector: z.string(),
100
+ shard_reranker: z.string(),
101
+ search_lang: z.string(),
102
+ search_words: z.record(z.string(), z.number()),
103
+ search_intents: z.record(z.string(), z.any()), // Empty object type
104
+ companies: z.array(z.any()),
105
+ });
106
+
107
+ export type SearchQuery = z.infer<typeof searchQuerySchema>;
108
+
109
+ // SearchIntents is an empty object type
110
+ export type SearchIntents = Record<string, any>;
111
+
112
+ const searchResultSemanticsSchema = z.object({
113
+ origin_shard: z.number(),
114
+ chunks_total: z.number(),
115
+ chunks_matched: z.number(),
116
+ chunks_kept: z.number(),
117
+ similarity: z.number(),
118
+ bitstring: z.string(),
119
+ });
120
+ export type SearchResultSemantics = z.infer<typeof searchResultSemanticsSchema>;
121
+
122
+ export const searchResponseSchema = z.object({
123
+ url_hash: z.string(),
124
+ url: z.string(),
125
+ netloc: z.string(),
126
+ published: z.string(),
127
+ visited: z.string(),
128
+ language: z.string(),
129
+ author: z.string(),
130
+ title: z.string(),
131
+ description: z.string(),
132
+ content: z.string(),
133
+ best_chunk: z.string(),
134
+ semantics: searchResultSemanticsSchema,
135
+ });
136
+ export type SearchResponse = z.infer<typeof searchResponseSchema>;
137
+
138
+ // Bulk Search Response
139
+ const bulkResSchema = z.object({
140
+ message: z.string(),
141
+ decrypt_using: z.string(),
142
+ download_from: z.string(),
143
+ });
144
+ export type BulkResType = z.infer<typeof bulkResSchema>;
145
+
146
+ // Re-export scrape types from scrape.ts
147
+ export {
148
+ scrapeResponseSchema,
149
+ scrapeFullResSchema,
150
+ type ScrapeResponse,
151
+ type ScrapeFullRes,
152
+ } from "../scrape/types";