@endday/search-mcp 1.0.0 → 1.0.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.
- package/dist/index.js +4724 -0
- package/dist/search-mcp.js +4715 -0
- package/package.json +14 -14
- package/data/blocklist.generated.js +0 -2
- package/envs.js +0 -129
- package/index.js +0 -6
- package/mcp/search-mcp.js +0 -8
- package/src/content/extract.impl.js +0 -228
- package/src/content/extract.js +0 -1
- package/src/content/fetch.impl.js +0 -400
- package/src/content/fetch.js +0 -1
- package/src/core/crypto.js +0 -7
- package/src/core/errors.impl.js +0 -52
- package/src/core/errors.js +0 -1
- package/src/core/html.impl.js +0 -69
- package/src/core/html.js +0 -1
- package/src/mcp/config.js +0 -75
- package/src/mcp/format.js +0 -44
- package/src/mcp/index.js +0 -10
- package/src/mcp/local/content.js +0 -26
- package/src/mcp/local/search.js +0 -233
- package/src/mcp/schemas.js +0 -132
- package/src/mcp/server.js +0 -97
- package/src/mcp/tools/content.js +0 -31
- package/src/mcp/tools/jinaContent.js +0 -38
- package/src/mcp/tools/newsSearch.js +0 -22
- package/src/mcp/tools/webSearch.js +0 -57
- package/src/platform/auth.impl.js +0 -166
- package/src/platform/auth.js +0 -1
- package/src/platform/cache.impl.js +0 -166
- package/src/platform/cache.js +0 -1
- package/src/platform/health.impl.js +0 -133
- package/src/platform/health.js +0 -1
- package/src/platform/http.impl.js +0 -108
- package/src/platform/http.js +0 -1
- package/src/platform/logger.impl.js +0 -51
- package/src/platform/logger.js +0 -1
- package/src/platform/metrics.impl.js +0 -43
- package/src/platform/metrics.js +0 -1
- package/src/platform/nodeHttpClient.js +0 -104
- package/src/platform/rateLimit.impl.js +0 -141
- package/src/platform/rateLimit.js +0 -1
- package/src/platform/requestContext.impl.js +0 -10
- package/src/platform/requestContext.js +0 -1
- package/src/platform/session.impl.js +0 -198
- package/src/platform/session.js +0 -1
- package/src/platform/stateKv.impl.js +0 -18
- package/src/platform/stateKv.js +0 -1
- package/src/platform/tasks.impl.js +0 -17
- package/src/platform/tasks.js +0 -1
- package/src/routes/requestParams.impl.js +0 -12
- package/src/routes/requestParams.js +0 -1
- package/src/search/engineRegistry.impl.js +0 -117
- package/src/search/engineRegistry.js +0 -1
- package/src/search/engineRequest.impl.js +0 -377
- package/src/search/engineRequest.js +0 -1
- package/src/search/engineUtils.impl.js +0 -227
- package/src/search/engineUtils.js +0 -1
- package/src/search/engines/baidu.impl.js +0 -145
- package/src/search/engines/baidu.js +0 -2
- package/src/search/engines/bing.impl.js +0 -509
- package/src/search/engines/bing.js +0 -2
- package/src/search/engines/brave.impl.js +0 -223
- package/src/search/engines/brave.js +0 -2
- package/src/search/engines/duckduckgo.impl.js +0 -164
- package/src/search/engines/duckduckgo.js +0 -2
- package/src/search/engines/mojeek.impl.js +0 -115
- package/src/search/engines/mojeek.js +0 -2
- package/src/search/engines/qwant.impl.js +0 -188
- package/src/search/engines/qwant.js +0 -2
- package/src/search/engines/startpage.impl.js +0 -237
- package/src/search/engines/startpage.js +0 -2
- package/src/search/engines/toutiao.impl.js +0 -265
- package/src/search/engines/toutiao.js +0 -2
- package/src/search/engines/yahoo.impl.js +0 -379
- package/src/search/engines/yahoo.js +0 -2
- package/src/search/gateway.impl.js +0 -423
- package/src/search/gateway.js +0 -1
- package/src/search/ranking.impl.js +0 -381
- package/src/search/ranking.js +0 -1
- package/src/search/requestPolicy.impl.js +0 -137
- package/src/search/requestPolicy.js +0 -1
- package/src/search/upstreamSession.impl.js +0 -148
- package/src/search/upstreamSession.js +0 -1
- /package/{index.d.ts → dist/index.d.ts} +0 -0
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { Impit } from "impit/index.js";
|
|
2
|
-
|
|
3
|
-
const impitClients = new Map();
|
|
4
|
-
|
|
5
|
-
function isNodeRuntime() {
|
|
6
|
-
return typeof process !== "undefined" && !!process.versions?.node;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function isPatchedFetch(fetchImpl = globalThis.fetch) {
|
|
10
|
-
if (typeof fetchImpl !== "function") {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return !/\[native code\]/.test(Function.prototype.toString.call(fetchImpl));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function normalizeClientMode(value) {
|
|
18
|
-
const mode = String(value || "auto").trim().toLowerCase();
|
|
19
|
-
if (mode === "fetch" || mode === "impit") {
|
|
20
|
-
return mode;
|
|
21
|
-
}
|
|
22
|
-
return "auto";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveBrowserProfile(profile) {
|
|
26
|
-
const profileId = String(profile?.id || "").toLowerCase();
|
|
27
|
-
|
|
28
|
-
if (profileId.startsWith("firefox")) {
|
|
29
|
-
return "firefox144";
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return "chrome136";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function buildClientKey({ browser, proxyUrl, ignoreTlsErrors }) {
|
|
36
|
-
return JSON.stringify({
|
|
37
|
-
browser,
|
|
38
|
-
proxyUrl: proxyUrl || "",
|
|
39
|
-
ignoreTlsErrors: !!ignoreTlsErrors,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getImpitClient({ profile } = {}) {
|
|
44
|
-
const browser = resolveBrowserProfile(profile);
|
|
45
|
-
const proxyUrl = process.env.SEARCH_MCP_PROXY_URL || "";
|
|
46
|
-
const ignoreTlsErrors = ["1", "true", "yes", "on"].includes(
|
|
47
|
-
String(process.env.SEARCH_MCP_IGNORE_TLS_ERRORS || "").trim().toLowerCase()
|
|
48
|
-
);
|
|
49
|
-
const key = buildClientKey({ browser, proxyUrl, ignoreTlsErrors });
|
|
50
|
-
|
|
51
|
-
if (!impitClients.has(key)) {
|
|
52
|
-
impitClients.set(
|
|
53
|
-
key,
|
|
54
|
-
new Impit({
|
|
55
|
-
browser,
|
|
56
|
-
proxyUrl: proxyUrl || undefined,
|
|
57
|
-
ignoreTlsErrors,
|
|
58
|
-
followRedirects: true,
|
|
59
|
-
vanillaFallback: false,
|
|
60
|
-
timeout: Number.parseInt(process.env.SEARCH_MCP_UPSTREAM_TIMEOUT_MS || "15000", 10),
|
|
61
|
-
})
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return impitClients.get(key);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function normalizeRequestInit(init = {}) {
|
|
69
|
-
const normalized = { ...init };
|
|
70
|
-
|
|
71
|
-
if (normalized.headers instanceof Headers) {
|
|
72
|
-
normalized.headers = Object.fromEntries(normalized.headers.entries());
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
delete normalized.referrer;
|
|
76
|
-
return normalized;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export async function fetchWithOptionalCurlImpersonate(
|
|
80
|
-
url,
|
|
81
|
-
init = {},
|
|
82
|
-
{ profile } = {}
|
|
83
|
-
) {
|
|
84
|
-
const clientMode = normalizeClientMode(process.env.SEARCH_MCP_UPSTREAM_CLIENT);
|
|
85
|
-
const shouldUseImpit =
|
|
86
|
-
clientMode !== "fetch" &&
|
|
87
|
-
isNodeRuntime() &&
|
|
88
|
-
!isPatchedFetch(globalThis.fetch);
|
|
89
|
-
|
|
90
|
-
if (!shouldUseImpit) {
|
|
91
|
-
return fetch(url, init);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const client = getImpitClient({ profile });
|
|
96
|
-
return await client.fetch(url, normalizeRequestInit(init));
|
|
97
|
-
} catch (error) {
|
|
98
|
-
if (clientMode === "impit") {
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return fetch(url, init);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { env } from "../../envs.js";
|
|
2
|
-
import { sha256Hex } from "../core/crypto.js";
|
|
3
|
-
import { ApiError } from "../core/errors.js";
|
|
4
|
-
import { getStateKv } from "./stateKv.js";
|
|
5
|
-
import { logWarn } from "./logger.js";
|
|
6
|
-
|
|
7
|
-
const RATE_LIMIT_PREFIX = "rate:v2";
|
|
8
|
-
const rateLimitStore = new Map();
|
|
9
|
-
|
|
10
|
-
export function resetRateLimitState() {
|
|
11
|
-
rateLimitStore.clear();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function pruneExpiredEntries(now) {
|
|
15
|
-
if (rateLimitStore.size < 1000) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
for (const [key, entry] of rateLimitStore.entries()) {
|
|
20
|
-
if (entry.expiresAt <= now) {
|
|
21
|
-
rateLimitStore.delete(key);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getRateLimitClientKey(request, token) {
|
|
27
|
-
const clientIp =
|
|
28
|
-
request.headers.get("cf-connecting-ip") ||
|
|
29
|
-
request.headers.get("x-forwarded-for") ||
|
|
30
|
-
"anonymous";
|
|
31
|
-
|
|
32
|
-
return token ? `token:${token}` : `ip:${clientIp}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function getRateLimitBucketKey(request, token, bucket, bucketPrefix = RATE_LIMIT_PREFIX) {
|
|
36
|
-
const clientKey = getRateLimitClientKey(request, token);
|
|
37
|
-
const clientHash = await sha256Hex(clientKey);
|
|
38
|
-
return `${bucketPrefix}:${bucket}:${clientHash}`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function createRateLimitError({ maxRequests, windowSeconds, retryAfter }) {
|
|
42
|
-
return new ApiError({
|
|
43
|
-
status: 429,
|
|
44
|
-
code: "RATE_LIMITED",
|
|
45
|
-
category: "rate_limit",
|
|
46
|
-
message: "Rate limit exceeded",
|
|
47
|
-
details: {
|
|
48
|
-
limit: maxRequests,
|
|
49
|
-
window_seconds: windowSeconds,
|
|
50
|
-
retry_after: retryAfter,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function getRetryAfter(expiresAt, now) {
|
|
56
|
-
return Math.max(1, Math.ceil((expiresAt - now) / 1000));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function incrementMemoryBucket(key, expiresAt) {
|
|
60
|
-
const record = rateLimitStore.get(key) || { count: 0, expiresAt };
|
|
61
|
-
record.count += 1;
|
|
62
|
-
rateLimitStore.set(key, record);
|
|
63
|
-
return record.count;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function incrementKvBucket({ kv, key, expiresAt, windowSeconds }) {
|
|
67
|
-
const record = (await kv.get(key, "json")) || { count: 0, expiresAt };
|
|
68
|
-
const count = Number.parseInt(record.count || "0", 10) + 1;
|
|
69
|
-
|
|
70
|
-
await kv.put(
|
|
71
|
-
key,
|
|
72
|
-
JSON.stringify({
|
|
73
|
-
count,
|
|
74
|
-
expiresAt,
|
|
75
|
-
}),
|
|
76
|
-
{
|
|
77
|
-
expirationTtl: windowSeconds + 5,
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
return count;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export async function enforceRateLimit(request, token, options = {}) {
|
|
85
|
-
const maxRequests = options.maxRequests ?? Number.parseInt(env.RATE_LIMIT_MAX_REQUESTS || "0", 10);
|
|
86
|
-
const windowSeconds = options.windowSeconds ?? Number.parseInt(
|
|
87
|
-
env.RATE_LIMIT_WINDOW_SECONDS || "60",
|
|
88
|
-
10
|
|
89
|
-
);
|
|
90
|
-
const bucketPrefix = options.bucketPrefix ?? RATE_LIMIT_PREFIX;
|
|
91
|
-
|
|
92
|
-
if (maxRequests <= 0 || windowSeconds <= 0) {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const now = Date.now();
|
|
97
|
-
const windowMs = windowSeconds * 1000;
|
|
98
|
-
const bucket = Math.floor(now / windowMs);
|
|
99
|
-
const expiresAt = (bucket + 1) * windowMs;
|
|
100
|
-
const clientKey = await getRateLimitBucketKey(request, token, bucket, bucketPrefix);
|
|
101
|
-
const kv = getStateKv();
|
|
102
|
-
|
|
103
|
-
if (kv) {
|
|
104
|
-
try {
|
|
105
|
-
const count = await incrementKvBucket({
|
|
106
|
-
kv,
|
|
107
|
-
key: clientKey,
|
|
108
|
-
expiresAt,
|
|
109
|
-
windowSeconds,
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (count > maxRequests) {
|
|
113
|
-
throw createRateLimitError({
|
|
114
|
-
maxRequests,
|
|
115
|
-
windowSeconds,
|
|
116
|
-
retryAfter: getRetryAfter(expiresAt, now),
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return;
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (error instanceof ApiError) {
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
logWarn("rate_limit.kv_fallback", {}, error);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
pruneExpiredEntries(now);
|
|
131
|
-
|
|
132
|
-
const count = incrementMemoryBucket(clientKey, expiresAt);
|
|
133
|
-
|
|
134
|
-
if (count > maxRequests) {
|
|
135
|
-
throw createRateLimitError({
|
|
136
|
-
maxRequests,
|
|
137
|
-
windowSeconds,
|
|
138
|
-
retryAfter: getRetryAfter(expiresAt, now),
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./rateLimit.impl.js";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./requestContext.impl.js";
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { getStateKv } from "./stateKv.js";
|
|
2
|
-
|
|
3
|
-
const SESSION_PREFIX = "session:v1";
|
|
4
|
-
const SESSION_TTL_SECONDS = 86400; // 24 hours
|
|
5
|
-
const SESSION_COOKIE_NAME = "search_mcp_sid";
|
|
6
|
-
|
|
7
|
-
// In-memory fallback when KV is unavailable
|
|
8
|
-
const memorySessions = new Map();
|
|
9
|
-
let lastPruneTime = 0;
|
|
10
|
-
const PRUNE_INTERVAL_SECONDS = 3600; // Prune every hour
|
|
11
|
-
|
|
12
|
-
function nowSeconds() {
|
|
13
|
-
return Math.floor(Date.now() / 1000);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function pruneMemorySessions() {
|
|
17
|
-
const now = nowSeconds();
|
|
18
|
-
if (now - lastPruneTime < PRUNE_INTERVAL_SECONDS) return;
|
|
19
|
-
lastPruneTime = now;
|
|
20
|
-
|
|
21
|
-
if (memorySessions.size < 100) return;
|
|
22
|
-
|
|
23
|
-
for (const [key, entry] of memorySessions.entries()) {
|
|
24
|
-
if (entry.expiresAt <= now) {
|
|
25
|
-
memorySessions.delete(key);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function generateSessionId() {
|
|
31
|
-
return crypto.randomUUID();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function kvKey(sessionId) {
|
|
35
|
-
return `${SESSION_PREFIX}:${sessionId}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function resetSessionState() {
|
|
39
|
-
memorySessions.clear();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Extract session ID from request cookies.
|
|
44
|
-
*/
|
|
45
|
-
export function getSessionIdFromRequest(request) {
|
|
46
|
-
const cookieHeader = request.headers.get("cookie") || "";
|
|
47
|
-
const match = cookieHeader.match(
|
|
48
|
-
new RegExp(`(?:^|;)\\s*${SESSION_COOKIE_NAME}\\s*=\\s*([^;]+)`)
|
|
49
|
-
);
|
|
50
|
-
return match ? match[1].trim() : null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Build Set-Cookie header value for the session.
|
|
55
|
-
*/
|
|
56
|
-
export function buildSessionCookie(sessionId, { secure = true } = {}) {
|
|
57
|
-
const parts = [
|
|
58
|
-
`${SESSION_COOKIE_NAME}=${sessionId}`,
|
|
59
|
-
"Path=/",
|
|
60
|
-
`Max-Age=${SESSION_TTL_SECONDS}`,
|
|
61
|
-
"HttpOnly",
|
|
62
|
-
"SameSite=Lax",
|
|
63
|
-
];
|
|
64
|
-
if (secure) {
|
|
65
|
-
parts.push("Secure");
|
|
66
|
-
}
|
|
67
|
-
return parts.join("; ");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Load session data from KV or memory fallback.
|
|
72
|
-
*/
|
|
73
|
-
async function loadSession(sessionId) {
|
|
74
|
-
pruneMemorySessions();
|
|
75
|
-
|
|
76
|
-
const kv = getStateKv();
|
|
77
|
-
|
|
78
|
-
if (kv) {
|
|
79
|
-
try {
|
|
80
|
-
const data = await kv.get(kvKey(sessionId), "json");
|
|
81
|
-
return data;
|
|
82
|
-
} catch (_) {
|
|
83
|
-
// Fall through to memory
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const mem = memorySessions.get(sessionId);
|
|
88
|
-
if (!mem) return null;
|
|
89
|
-
if (mem.expiresAt <= nowSeconds()) {
|
|
90
|
-
memorySessions.delete(sessionId);
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
return mem.data;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Save session data to KV or memory fallback.
|
|
98
|
-
*/
|
|
99
|
-
async function saveSession(sessionId, data) {
|
|
100
|
-
const kv = getStateKv();
|
|
101
|
-
|
|
102
|
-
if (kv) {
|
|
103
|
-
try {
|
|
104
|
-
await kv.put(kvKey(sessionId), JSON.stringify(data), {
|
|
105
|
-
expirationTtl: SESSION_TTL_SECONDS,
|
|
106
|
-
});
|
|
107
|
-
return;
|
|
108
|
-
} catch (_) {
|
|
109
|
-
// Fall through to memory
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
memorySessions.set(sessionId, {
|
|
114
|
-
data,
|
|
115
|
-
expiresAt: nowSeconds() + SESSION_TTL_SECONDS,
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Delete session from KV or memory.
|
|
121
|
-
*/
|
|
122
|
-
async function deleteSession(sessionId) {
|
|
123
|
-
const kv = getStateKv();
|
|
124
|
-
|
|
125
|
-
if (kv) {
|
|
126
|
-
try {
|
|
127
|
-
await kv.delete(kvKey(sessionId));
|
|
128
|
-
return;
|
|
129
|
-
} catch (_) {
|
|
130
|
-
// Fall through
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
memorySessions.delete(sessionId);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Validate and refresh a session.
|
|
139
|
-
* Returns { valid, sessionId, setCookie } where setCookie is the updated
|
|
140
|
-
* Set-Cookie header value (to extend Max-Age) or null.
|
|
141
|
-
*/
|
|
142
|
-
export async function validateSession(request) {
|
|
143
|
-
const sessionId = getSessionIdFromRequest(request);
|
|
144
|
-
|
|
145
|
-
if (!sessionId) {
|
|
146
|
-
return { valid: false, sessionId: null, setCookie: null };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const session = await loadSession(sessionId);
|
|
150
|
-
|
|
151
|
-
if (!session) {
|
|
152
|
-
return { valid: false, sessionId: null, setCookie: null };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Sliding window: update lastUsed
|
|
156
|
-
const now = nowSeconds();
|
|
157
|
-
session.lastUsed = now;
|
|
158
|
-
await saveSession(sessionId, session);
|
|
159
|
-
|
|
160
|
-
// Determine if the request is HTTPS (for Secure cookie attribute)
|
|
161
|
-
const url = new URL(request.url);
|
|
162
|
-
const secure = url.protocol === "https:";
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
valid: true,
|
|
166
|
-
sessionId,
|
|
167
|
-
setCookie: buildSessionCookie(sessionId, { secure }),
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Create a new session and return { sessionId, setCookie }.
|
|
173
|
-
*/
|
|
174
|
-
export async function createSession(request) {
|
|
175
|
-
const sessionId = generateSessionId();
|
|
176
|
-
const now = nowSeconds();
|
|
177
|
-
|
|
178
|
-
await saveSession(sessionId, {
|
|
179
|
-
createdAt: now,
|
|
180
|
-
lastUsed: now,
|
|
181
|
-
requestCount: 0,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const url = new URL(request.url);
|
|
185
|
-
const secure = url.protocol === "https:";
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
sessionId,
|
|
189
|
-
setCookie: buildSessionCookie(sessionId, { secure }),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Revoke a session (logout / invalidate).
|
|
195
|
-
*/
|
|
196
|
-
export async function revokeSession(sessionId) {
|
|
197
|
-
await deleteSession(sessionId);
|
|
198
|
-
}
|
package/src/platform/session.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./session.impl.js";
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { env } from "../../envs.js";
|
|
2
|
-
|
|
3
|
-
export function getStateKv() {
|
|
4
|
-
if (env.SEARCH_STATE_KV && typeof env.SEARCH_STATE_KV.get === "function") {
|
|
5
|
-
return env.SEARCH_STATE_KV;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
return null;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function normalizeExpirationTtl(value, fallback) {
|
|
12
|
-
const parsed = Number.parseInt(value ?? String(fallback), 10);
|
|
13
|
-
if (Number.isNaN(parsed) || parsed <= 0) {
|
|
14
|
-
return fallback;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return parsed;
|
|
18
|
-
}
|
package/src/platform/stateKv.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./stateKv.impl.js";
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { logWarn } from "./logger.js";
|
|
2
|
-
|
|
3
|
-
export function runDeferredTask(runtimeContext, label, task) {
|
|
4
|
-
const runner = Promise.resolve()
|
|
5
|
-
.then(task)
|
|
6
|
-
.catch((error) => {
|
|
7
|
-
logWarn("task.background_failed", { task: label }, error, runtimeContext);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
const executionCtx = runtimeContext?.executionCtx;
|
|
11
|
-
if (executionCtx && typeof executionCtx.waitUntil === "function") {
|
|
12
|
-
executionCtx.waitUntil(runner);
|
|
13
|
-
return Promise.resolve();
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return runner;
|
|
17
|
-
}
|
package/src/platform/tasks.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./tasks.impl.js";
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function normalizePositiveInteger(value, fallback, { min = 1, max } = {}) {
|
|
2
|
-
const parsed = Number.parseInt(value, 10);
|
|
3
|
-
if (!Number.isFinite(parsed) || parsed < min) {
|
|
4
|
-
return fallback;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
if (max !== undefined && parsed > max) {
|
|
8
|
-
return max;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
return parsed;
|
|
12
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./requestParams.impl.js";
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { env } from "../../envs.js";
|
|
2
|
-
import { baiduAdapter } from "./engines/baidu.js";
|
|
3
|
-
import { bingAdapter } from "./engines/bing.js";
|
|
4
|
-
import { braveAdapter } from "./engines/brave.js";
|
|
5
|
-
import { duckDuckGoAdapter } from "./engines/duckduckgo.js";
|
|
6
|
-
import { mojeekAdapter } from "./engines/mojeek.js";
|
|
7
|
-
import { qwantAdapter } from "./engines/qwant.js";
|
|
8
|
-
import { startpageAdapter } from "./engines/startpage.js";
|
|
9
|
-
import { toutiaoAdapter } from "./engines/toutiao.js";
|
|
10
|
-
import { yahooAdapter } from "./engines/yahoo.js";
|
|
11
|
-
|
|
12
|
-
const ENGINE_REGISTRY = {
|
|
13
|
-
baidu: baiduAdapter,
|
|
14
|
-
bing: bingAdapter,
|
|
15
|
-
toutiao: toutiaoAdapter,
|
|
16
|
-
startpage: startpageAdapter,
|
|
17
|
-
mojeek: mojeekAdapter,
|
|
18
|
-
duckduckgo: duckDuckGoAdapter,
|
|
19
|
-
brave: braveAdapter,
|
|
20
|
-
qwant: qwantAdapter,
|
|
21
|
-
yahoo: yahooAdapter,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export function getEngineRegistry() {
|
|
25
|
-
return ENGINE_REGISTRY;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getSupportedVerticals(adapter) {
|
|
29
|
-
const verticals = adapter?.supports?.verticals;
|
|
30
|
-
return Array.isArray(verticals) && verticals.length > 0 ? verticals : ["web"];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function normalizeEngineList(engines) {
|
|
34
|
-
if (!engines) {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (Array.isArray(engines)) {
|
|
39
|
-
return engines;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return String(engines).split(",");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function normalizeRequestedEngines(engines) {
|
|
46
|
-
return normalizeEngineList(engines)
|
|
47
|
-
.map((engine) => String(engine).trim().toLowerCase())
|
|
48
|
-
.filter(Boolean);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Resolve the engines the caller explicitly requested.
|
|
53
|
-
* Language-specific defaults are resolved before this layer, so this function
|
|
54
|
-
* only filters and normalizes the concrete engine list it receives.
|
|
55
|
-
* `env.SUPPORTED_ENGINES` is still used to filter out engines the deployment
|
|
56
|
-
* does not recognize.
|
|
57
|
-
*/
|
|
58
|
-
export function resolveEngineSelection(engines, options = {}) {
|
|
59
|
-
const vertical = String(options.vertical || "web").trim().toLowerCase() || "web";
|
|
60
|
-
const requestedEngines = normalizeRequestedEngines(engines);
|
|
61
|
-
const supportedEngines = new Set(env.SUPPORTED_ENGINES);
|
|
62
|
-
const seen = new Set();
|
|
63
|
-
const enabledEngines = [];
|
|
64
|
-
const skippedEngines = [];
|
|
65
|
-
|
|
66
|
-
for (const engine of requestedEngines) {
|
|
67
|
-
if (seen.has(engine)) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
seen.add(engine);
|
|
72
|
-
|
|
73
|
-
const adapter = ENGINE_REGISTRY[engine];
|
|
74
|
-
if (!adapter || !supportedEngines.has(engine)) {
|
|
75
|
-
skippedEngines.push({
|
|
76
|
-
engine,
|
|
77
|
-
reason: "unsupported_engine",
|
|
78
|
-
});
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!getSupportedVerticals(adapter).includes(vertical)) {
|
|
83
|
-
skippedEngines.push({
|
|
84
|
-
engine,
|
|
85
|
-
reason: "unsupported_vertical",
|
|
86
|
-
});
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (adapter.isAvailable && !adapter.isAvailable()) {
|
|
91
|
-
skippedEngines.push({
|
|
92
|
-
engine,
|
|
93
|
-
reason: "unavailable_engine",
|
|
94
|
-
});
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
enabledEngines.push(engine);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
requestedEngines,
|
|
103
|
-
enabledEngines,
|
|
104
|
-
skippedEngines,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function resolveEngineOrder(engines, options = {}) {
|
|
109
|
-
return resolveEngineSelection(engines, options).enabledEngines;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function getSupportedEnginesForVertical(vertical = "web") {
|
|
113
|
-
return Object.keys(ENGINE_REGISTRY).filter((engineName) => {
|
|
114
|
-
const adapter = ENGINE_REGISTRY[engineName];
|
|
115
|
-
return getSupportedVerticals(adapter).includes(String(vertical).toLowerCase());
|
|
116
|
-
});
|
|
117
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./engineRegistry.impl.js";
|