@adityanair98/api-oracle 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/cli.d.ts +11 -0
  4. package/dist/cli.js +74 -0
  5. package/dist/dashboard/public/app.js +1004 -0
  6. package/dist/dashboard/public/index.html +142 -0
  7. package/dist/dashboard/public/public/app.js +1004 -0
  8. package/dist/dashboard/public/public/index.html +142 -0
  9. package/dist/dashboard/public/public/styles.css +1464 -0
  10. package/dist/dashboard/public/styles.css +1464 -0
  11. package/dist/dashboard/routes/api.d.ts +7 -0
  12. package/dist/dashboard/routes/api.js +245 -0
  13. package/dist/dashboard/server.d.ts +9 -0
  14. package/dist/dashboard/server.js +45 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.js +23 -0
  17. package/dist/knowledge/db.d.ts +22 -0
  18. package/dist/knowledge/db.js +182 -0
  19. package/dist/knowledge/schema.d.ts +275 -0
  20. package/dist/knowledge/schema.js +135 -0
  21. package/dist/knowledge/scorer.d.ts +63 -0
  22. package/dist/knowledge/scorer.js +314 -0
  23. package/dist/knowledge/search.d.ts +37 -0
  24. package/dist/knowledge/search.js +111 -0
  25. package/dist/knowledge/synonyms.d.ts +36 -0
  26. package/dist/knowledge/synonyms.js +523 -0
  27. package/dist/knowledge/tfidf.d.ts +42 -0
  28. package/dist/knowledge/tfidf.js +138 -0
  29. package/dist/server.d.ts +9 -0
  30. package/dist/server.js +40 -0
  31. package/dist/tools/check-freshness.d.ts +9 -0
  32. package/dist/tools/check-freshness.js +95 -0
  33. package/dist/tools/compare-apis.d.ts +8 -0
  34. package/dist/tools/compare-apis.js +149 -0
  35. package/dist/tools/find-api.d.ts +9 -0
  36. package/dist/tools/find-api.js +120 -0
  37. package/dist/tools/get-setup-guide.d.ts +8 -0
  38. package/dist/tools/get-setup-guide.js +127 -0
  39. package/dist/updater/linter.d.ts +31 -0
  40. package/dist/updater/linter.js +219 -0
  41. package/dist/updater/report.d.ts +29 -0
  42. package/dist/updater/report.js +96 -0
  43. package/dist/updater/staleness.d.ts +39 -0
  44. package/dist/updater/staleness.js +66 -0
  45. package/dist/updater/version-tracker.d.ts +28 -0
  46. package/dist/updater/version-tracker.js +50 -0
  47. package/dist/utils/config.d.ts +11 -0
  48. package/dist/utils/config.js +13 -0
  49. package/dist/utils/logger.d.ts +20 -0
  50. package/dist/utils/logger.js +32 -0
  51. package/package.json +56 -0
  52. package/src/entries/ai/anthropic.json +95 -0
  53. package/src/entries/ai/eleven-labs.json +90 -0
  54. package/src/entries/ai/openai.json +95 -0
  55. package/src/entries/ai/replicate.json +87 -0
  56. package/src/entries/ai/resemble-ai.json +88 -0
  57. package/src/entries/ai/stability-ai.json +89 -0
  58. package/src/entries/analytics/posthog.json +88 -0
  59. package/src/entries/analytics/sentry.json +84 -0
  60. package/src/entries/auth/auth0.json +90 -0
  61. package/src/entries/auth/clerk.json +95 -0
  62. package/src/entries/cms/contentful.json +92 -0
  63. package/src/entries/cms/sanity.json +92 -0
  64. package/src/entries/cms/strapi.json +93 -0
  65. package/src/entries/commerce/medusa.json +91 -0
  66. package/src/entries/commerce/shopify-api.json +91 -0
  67. package/src/entries/communication/sendbird.json +85 -0
  68. package/src/entries/communication/stream-chat.json +94 -0
  69. package/src/entries/database/firebase.json +88 -0
  70. package/src/entries/database/neon.json +94 -0
  71. package/src/entries/database/planetscale.json +95 -0
  72. package/src/entries/database/supabase.json +94 -0
  73. package/src/entries/database/upstash.json +94 -0
  74. package/src/entries/devops/fly-io.json +90 -0
  75. package/src/entries/devops/netlify.json +90 -0
  76. package/src/entries/devops/railway.json +90 -0
  77. package/src/entries/devops/vercel.json +90 -0
  78. package/src/entries/email/mailgun.json +91 -0
  79. package/src/entries/email/postmark.json +91 -0
  80. package/src/entries/email/resend.json +89 -0
  81. package/src/entries/email/sendgrid.json +90 -0
  82. package/src/entries/forms/formspark.json +85 -0
  83. package/src/entries/forms/typeform.json +98 -0
  84. package/src/entries/infrastructure/aws-s3.json +104 -0
  85. package/src/entries/infrastructure/cloudflare-r2.json +92 -0
  86. package/src/entries/infrastructure/cloudflare-workers.json +92 -0
  87. package/src/entries/infrastructure/digital-ocean-spaces.json +87 -0
  88. package/src/entries/integration/nango.json +90 -0
  89. package/src/entries/integration/zapier.json +92 -0
  90. package/src/entries/maps/google-maps.json +89 -0
  91. package/src/entries/maps/mapbox.json +87 -0
  92. package/src/entries/media/deepgram.json +84 -0
  93. package/src/entries/media/imgix.json +84 -0
  94. package/src/entries/media/mux.json +94 -0
  95. package/src/entries/messaging/ably.json +94 -0
  96. package/src/entries/messaging/pusher.json +94 -0
  97. package/src/entries/messaging/twilio.json +94 -0
  98. package/src/entries/messaging/vonage.json +89 -0
  99. package/src/entries/notifications/knock.json +84 -0
  100. package/src/entries/notifications/novu.json +84 -0
  101. package/src/entries/notifications/onesignal.json +84 -0
  102. package/src/entries/payments/lemonsqueezy.json +91 -0
  103. package/src/entries/payments/paddle.json +90 -0
  104. package/src/entries/payments/paypal.json +91 -0
  105. package/src/entries/payments/razorpay.json +85 -0
  106. package/src/entries/payments/square.json +91 -0
  107. package/src/entries/payments/stripe.json +96 -0
  108. package/src/entries/scheduling/cal-com.json +90 -0
  109. package/src/entries/scheduling/calendly.json +90 -0
  110. package/src/entries/search/algolia.json +96 -0
  111. package/src/entries/security/arcjet.json +89 -0
  112. package/src/entries/security/snyk.json +90 -0
  113. package/src/entries/storage/cloudinary.json +93 -0
  114. package/src/entries/storage/uploadthing.json +90 -0
  115. package/src/entries/testing/browserstack.json +86 -0
  116. package/src/entries/testing/checkly.json +89 -0
  117. package/src/entries/workflow/inngest.json +88 -0
  118. package/src/entries/workflow/temporal.json +90 -0
  119. package/src/entries/workflow/trigger-dev.json +89 -0
@@ -0,0 +1,523 @@
1
+ /**
2
+ * Synonym expansion, phrase detection, and category detection for query processing.
3
+ *
4
+ * Exports:
5
+ * SYNONYM_GROUPS — raw synonym groups for testing
6
+ * expandQuery — expands a query with synonyms
7
+ * detectPhrases — extracts modifier intent from a query
8
+ * detectCategory — maps a query to a known category with confidence
9
+ */
10
+ // ─── Synonym Groups ───────────────────────────────────────────────────────────
11
+ // Each group is a list of terms that share meaning in the developer context.
12
+ // If any term from a group appears in the query, all terms are added.
13
+ export const SYNONYM_GROUPS = [
14
+ // Auth / Identity
15
+ [
16
+ "auth", "authentication", "authenticate", "login", "signin", "signup",
17
+ "register", "sso", "oauth", "saml", "identity", "credential", "session",
18
+ "jwt", "token", "passwordless", "social", "provider",
19
+ "log in", "sign in", "sign up", "sign on", "log on",
20
+ ],
21
+ // Payments / Billing
22
+ [
23
+ "payment", "pay", "billing", "checkout", "charge", "subscription",
24
+ "invoice", "revenue", "commerce", "merchant", "card", "stripe",
25
+ "transaction", "pricing", "monetize", "monetization",
26
+ ],
27
+ // Email
28
+ [
29
+ "email", "mail", "smtp", "newsletter", "inbox", "transactional",
30
+ "mailer", "sendgrid", "resend", "postmark", "bounce", "delivery",
31
+ ],
32
+ // Database / Backend storage
33
+ [
34
+ "database", "db", "sql", "nosql", "data", "record", "table",
35
+ "query", "orm", "schema", "postgres", "mysql", "backend", "persist",
36
+ "persistence", "datastore",
37
+ ],
38
+ // Real-time / WebSockets
39
+ [
40
+ "realtime", "real-time", "real time", "websocket", "socket",
41
+ "live", "streaming", "push", "broadcast", "pub-sub", "pubsub",
42
+ "presence", "channel",
43
+ ],
44
+ // SMS / Voice / Messaging
45
+ [
46
+ "sms", "text", "message", "messaging", "twilio", "vonage",
47
+ "phone", "call", "voice", "telephony", "otp", "verify", "verification",
48
+ ],
49
+ // Chat / Conversation
50
+ [
51
+ "chat", "conversation", "messaging", "inbox", "channel",
52
+ "thread", "room", "dm", "direct message",
53
+ ],
54
+ // File / Image / Media Storage
55
+ [
56
+ "upload", "file", "image", "photo", "video", "media", "storage",
57
+ "cdn", "asset", "bucket", "s3", "object", "blob", "picture",
58
+ "avatar", "attachment",
59
+ ],
60
+ // Search / Discovery
61
+ [
62
+ "search", "query", "index", "algolia", "full-text", "fulltext",
63
+ "autocomplete", "facet", "filter", "typeahead", "elasticsearch",
64
+ "discovery",
65
+ ],
66
+ // AI / ML
67
+ [
68
+ "ai", "ml", "llm", "gpt", "claude", "openai", "anthropic",
69
+ "model", "completion", "embedding", "chatbot", "generate", "inference",
70
+ "generative", "machine learning", "artificial intelligence",
71
+ "neural", "natural language", "nlp",
72
+ ],
73
+ // Error Tracking / Monitoring / Observability
74
+ [
75
+ "error", "crash", "crashes", "crashed", "crashing", "exception", "bug",
76
+ "monitoring", "observability", "error tracking", "crash report",
77
+ "sentry", "alerting", "incident", "apm", "logging", "trace",
78
+ "diagnose", "debug", "production issue", "alert",
79
+ ],
80
+ // Analytics / Product Analytics
81
+ [
82
+ "analytics", "tracking", "event", "funnel", "session", "user behavior",
83
+ "product analytics", "posthog", "mixpanel", "amplitude", "heatmap",
84
+ "conversion", "retention", "cohort", "metric",
85
+ ],
86
+ // Push Notifications
87
+ [
88
+ "notification", "push", "alert", "bell", "notify", "in-app",
89
+ "onesignal", "knock", "novu", "apns", "fcm", "mobile notification",
90
+ ],
91
+ // Maps / Location / Geocoding
92
+ [
93
+ "map", "maps", "geo", "geolocation", "location", "address",
94
+ "geocod", "navigate", "navigation", "direction", "coordinate",
95
+ "latitude", "longitude", "place", "places",
96
+ ],
97
+ // Video / Audio / Media Processing
98
+ [
99
+ "video", "stream", "streaming", "transcode", "encode", "playback",
100
+ "mux", "player", "hls", "dash", "vod", "audio", "speech",
101
+ "transcribe", "transcription", "voice synthesis",
102
+ ],
103
+ // Image Processing / CDN
104
+ [
105
+ "image", "resize", "crop", "optimize", "transform", "cdn", "imgix",
106
+ "thumbnail", "watermark", "compress",
107
+ ],
108
+ // DevOps / Deployment / Hosting
109
+ [
110
+ "deploy", "deployment", "host", "hosting", "server", "cloud",
111
+ "devops", "ci", "cd", "pipeline", "container", "docker", "kubernetes",
112
+ "vercel", "netlify", "railway", "heroku", "vps", "platform",
113
+ "ship", "launch", "publish", "paas", "infrastructure",
114
+ ],
115
+ // Edge / Serverless Compute
116
+ [
117
+ "edge", "serverless", "function", "lambda", "worker", "cloudflare",
118
+ "edge compute", "edge function", "faas", "compute",
119
+ ],
120
+ // Object Storage / S3
121
+ [
122
+ "s3", "bucket", "object storage", "blob storage", "r2",
123
+ "file storage", "static files", "presigned", "presigned url",
124
+ ],
125
+ // Commerce / eCommerce
126
+ [
127
+ "ecommerce", "e-commerce", "shop", "store", "storefront", "product",
128
+ "cart", "order", "checkout", "inventory", "catalog", "shopify",
129
+ "merchant", "sell", "digital products",
130
+ ],
131
+ // CMS / Content Management
132
+ [
133
+ "cms", "content", "content management", "headless cms", "blog",
134
+ "article", "page", "rich text", "editor", "contentful", "sanity",
135
+ "strapi", "markdown", "wysiwyg", "editorial",
136
+ ],
137
+ // Forms / Surveys
138
+ [
139
+ "form", "survey", "questionnaire", "typeform", "lead capture",
140
+ "quiz", "feedback", "response", "submission", "field", "input",
141
+ "contact form", "onboarding form",
142
+ ],
143
+ // Scheduling / Calendar / Booking
144
+ [
145
+ "schedule", "scheduling", "calendar", "booking", "appointment",
146
+ "meeting", "calendly", "cal.com", "availability", "timeslot",
147
+ "slot", "book a call", "event scheduling",
148
+ ],
149
+ // Security / Vulnerability / Protection
150
+ [
151
+ "security", "vulnerability", "cve", "sast", "dast", "scan",
152
+ "firewall", "waf", "rate limit", "ddos", "bot", "abuse", "snyk",
153
+ "arcjet", "pen test", "pentest", "owasp", "secret scanning",
154
+ ],
155
+ // Background Jobs / Queues / Workflows
156
+ [
157
+ "background job", "job queue", "queue", "worker", "cron", "scheduled job",
158
+ "task", "async", "workflow", "trigger", "inngest", "temporal",
159
+ "job processing", "event-driven", "fan-out", "batch",
160
+ ],
161
+ // Integration / Automation / OAuth connectors
162
+ [
163
+ "integration", "connect", "connector", "automation", "zapier",
164
+ "oauth", "third-party", "saas integration", "webhook",
165
+ "no-code", "low-code", "nango", "token refresh",
166
+ ],
167
+ // Testing / QA / Monitoring
168
+ [
169
+ "test", "testing", "qa", "quality assurance", "browser test",
170
+ "e2e", "end-to-end", "selenium", "playwright", "puppeteer",
171
+ "synthetic", "uptime monitor", "checkly", "browserstack",
172
+ "cross-browser", "regression",
173
+ ],
174
+ ];
175
+ // Build a lookup: term → all terms in its group (for fast expansion)
176
+ const TERM_TO_GROUP = new Map();
177
+ for (const group of SYNONYM_GROUPS) {
178
+ for (const term of group) {
179
+ TERM_TO_GROUP.set(term.toLowerCase(), group);
180
+ }
181
+ }
182
+ // ─── Query Expansion ──────────────────────────────────────────────────────────
183
+ /**
184
+ * Build a regex to match a synonym term in a query.
185
+ * - Multi-word phrases ("log in"): exact phrase with word boundaries
186
+ * - Single words: left word boundary only (so "crash" matches "crashes")
187
+ */
188
+ function buildTermRegex(term) {
189
+ const escaped = term.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
190
+ if (term.includes(" ")) {
191
+ // Multi-word: exact phrase match (word boundary on left, space/end on right)
192
+ return new RegExp(`(?<![a-z])${escaped}(?![a-z])`, "i");
193
+ }
194
+ else {
195
+ // Single word: match at word start; allow suffixes (crash → crashes)
196
+ return new RegExp(`\\b${escaped}`, "i");
197
+ }
198
+ }
199
+ // Sort terms longest-first so multi-word phrases are checked before their parts
200
+ const SORTED_TERM_ENTRIES = [...TERM_TO_GROUP.entries()].sort(([a], [b]) => b.length - a.length);
201
+ /**
202
+ * Expand a query by adding synonyms for any terms it contains.
203
+ * Example: "log in with Google" → "log in with Google auth authentication
204
+ * login signin signup register sso oauth identity..."
205
+ */
206
+ export function expandQuery(query) {
207
+ if (!query)
208
+ return query;
209
+ const lowerQuery = query.toLowerCase();
210
+ const addedTerms = new Set();
211
+ // Track which positions have been matched to avoid double-matching
212
+ const matchedGroups = new Set();
213
+ for (const [term, group] of SORTED_TERM_ENTRIES) {
214
+ if (matchedGroups.has(group))
215
+ continue; // Already expanding this group
216
+ const regex = buildTermRegex(term);
217
+ if (regex.test(lowerQuery)) {
218
+ matchedGroups.add(group);
219
+ for (const synonym of group) {
220
+ const synLower = synonym.toLowerCase();
221
+ // Only add if not already present (substring check)
222
+ if (!lowerQuery.includes(synLower)) {
223
+ addedTerms.add(synonym);
224
+ }
225
+ }
226
+ }
227
+ }
228
+ if (addedTerms.size === 0)
229
+ return query;
230
+ return `${query} ${[...addedTerms].join(" ")}`;
231
+ }
232
+ const FREE_PATTERNS = [
233
+ /\bfree\b/i, /\bcheap/i, /\bno cost\b/i, /\bbudget\b/i, /\baffordable\b/i,
234
+ ];
235
+ const OPEN_SOURCE_PATTERNS = [
236
+ /\bopen.?source\b/i, /\bself.?host/i, /\bself host/i, /\bown server\b/i,
237
+ ];
238
+ const RELIABLE_PATTERNS = [
239
+ /\breliable\b/i, /\benterprise\b/i, /\bproduction.grade\b/i,
240
+ /\bmost reliable\b/i, /\bhigh.?availab/i, /\buptime\b/i,
241
+ ];
242
+ const SERVERLESS_PATTERNS = [
243
+ /\bserverless\b/i, /\bedge\b/i, /\bvercel\b/i, /\bnextjs?\b/i,
244
+ /\bnext\.js\b/i, /\bcloudflare\b/i,
245
+ ];
246
+ // Known API names to slugs for reference extraction
247
+ const API_NAME_TO_SLUG = {
248
+ // Original entries
249
+ firebase: "firebase", supabase: "supabase", stripe: "stripe",
250
+ sendgrid: "sendgrid", twilio: "twilio", algolia: "algolia",
251
+ auth0: "auth0", clerk: "clerk", cloudinary: "cloudinary",
252
+ openai: "openai", anthropic: "anthropic", sentry: "sentry",
253
+ pusher: "pusher", mapbox: "mapbox", "google maps": "google-maps",
254
+ // Session 4 additions
255
+ vercel: "vercel", netlify: "netlify", railway: "railway",
256
+ "fly.io": "fly-io", "aws s3": "aws-s3", "cloudflare r2": "cloudflare-r2",
257
+ "cloudflare workers": "cloudflare-workers", paddle: "paddle",
258
+ lemonsqueezy: "lemonsqueezy", paypal: "paypal", shopify: "shopify-api",
259
+ medusa: "medusa", contentful: "contentful", sanity: "sanity",
260
+ strapi: "strapi", typeform: "typeform", calendly: "calendly",
261
+ "cal.com": "cal-com", browserstack: "browserstack", checkly: "checkly",
262
+ snyk: "snyk", arcjet: "arcjet", "stability ai": "stability-ai",
263
+ elevenlabs: "eleven-labs", "eleven labs": "eleven-labs",
264
+ mailgun: "mailgun", "trigger.dev": "trigger-dev", inngest: "inngest",
265
+ temporal: "temporal", zapier: "zapier", nango: "nango",
266
+ };
267
+ /** Extract modifier intent from a query */
268
+ export function detectPhrases(query) {
269
+ const preferFree = FREE_PATTERNS.some((p) => p.test(query));
270
+ const preferOpenSource = OPEN_SOURCE_PATTERNS.some((p) => p.test(query));
271
+ const preferReliable = RELIABLE_PATTERNS.some((p) => p.test(query));
272
+ const preferServerless = SERVERLESS_PATTERNS.some((p) => p.test(query));
273
+ // Detect "like X but..." reference pattern
274
+ let referenceSlug = null;
275
+ const likePattern = /like\s+([a-z][a-z0-9\s-]*?)(?:\s+but|\s+except|\s+without|,|\.|$)/i;
276
+ const match = likePattern.exec(query);
277
+ if (match) {
278
+ const refName = match[1].trim().toLowerCase();
279
+ referenceSlug = API_NAME_TO_SLUG[refName] ?? null;
280
+ }
281
+ return { preferFree, preferOpenSource, preferReliable, preferServerless, referenceSlug };
282
+ }
283
+ const CATEGORY_DETECTORS = [
284
+ {
285
+ category: "email",
286
+ patterns: [
287
+ /\bemail\b/i, /\bmail\b/i, /\bsmtp\b/i, /\bnewsletter\b/i,
288
+ /\binbox\b/i, /\btransactional\b/i, /\bsend.*mail\b/i,
289
+ ],
290
+ weight: 0.4,
291
+ },
292
+ {
293
+ category: "payments",
294
+ patterns: [
295
+ /\bpay(ment)?s?\b/i, /\bbilling\b/i, /\bcheckout\b/i,
296
+ /\bcharge\b/i, /\bsubscription\b/i, /\binvoice\b/i,
297
+ /\bsaas.*billing\b/i, /\baccept.*payment\b/i, /\bcredit card\b/i,
298
+ ],
299
+ weight: 0.4,
300
+ },
301
+ {
302
+ category: "auth",
303
+ patterns: [
304
+ /\bauth(entication)?\b/i, /\blogin\b/i, /\bsign.?in\b/i,
305
+ /\bsign.?up\b/i, /\boauth\b/i, /\bsso\b/i, /\bidentity\b/i,
306
+ /\blog.*in\b/i, /\buser.*account\b/i, /\bcredential\b/i,
307
+ ],
308
+ weight: 0.4,
309
+ },
310
+ {
311
+ category: "database",
312
+ patterns: [
313
+ /\bdatabase\b/i, /\b(postgres|mysql|sqlite|mongodb)\b/i,
314
+ /\bdata store\b/i, /\bsql\b/i, /\bbackend\b/i,
315
+ /\bstore.*data\b/i, /\bpersist\b/i,
316
+ ],
317
+ weight: 0.35,
318
+ },
319
+ {
320
+ category: "storage",
321
+ patterns: [
322
+ /\bupload\b/i, /\bfile.*upload\b/i, /\bimage.*upload\b/i,
323
+ /\bstore.*file\b/i, /\bcdn\b/i, /\bmedia.*storage\b/i,
324
+ ],
325
+ weight: 0.4,
326
+ },
327
+ {
328
+ category: "ai",
329
+ patterns: [
330
+ /\bai\b/i, /\bllm\b/i, /\bgpt\b/i, /\bchatbot\b/i,
331
+ /\bml\b/i, /\bmachine learning\b/i, /\bartificial intelligence\b/i,
332
+ /\bgenerat(e|ive)\b/i, /\bcompletion\b/i, /\bembedding\b/i,
333
+ ],
334
+ weight: 0.4,
335
+ },
336
+ {
337
+ category: "analytics",
338
+ patterns: [
339
+ /\banalytics\b/i, /\bmonitoring\b/i, /\berror track/i,
340
+ /\bcrash(es|ing)?\b/i, /\bexception\b/i, /\buser.*behav/i,
341
+ /\bproduct.*analytics\b/i, /\btrack.*user\b/i, /\bhow user/i,
342
+ /\bobservabilit/i, /\bsentry\b/i, /\bposthog\b/i,
343
+ ],
344
+ weight: 0.35,
345
+ },
346
+ {
347
+ category: "messaging",
348
+ patterns: [
349
+ /\bsms\b/i, /\btext message\b/i, /\bwhatsapp\b/i, /\bvoice call\b/i,
350
+ /\bphone.*notif/i, /\btwilio\b/i,
351
+ ],
352
+ weight: 0.4,
353
+ },
354
+ {
355
+ category: "notifications",
356
+ patterns: [
357
+ /\bpush.*notif/i, /\bnotif(y|ication)/i, /\bin.?app.*notif/i,
358
+ /\balert.*user\b/i, /\bemail.*notif/i,
359
+ ],
360
+ weight: 0.35,
361
+ },
362
+ {
363
+ category: "communication",
364
+ patterns: [
365
+ /\bchat\b/i, /\bmessag(e|ing)\b/i, /\bconversation\b/i,
366
+ /\breal.?time.*chat\b/i, /\bin.?app.*chat\b/i,
367
+ ],
368
+ weight: 0.35,
369
+ },
370
+ {
371
+ category: "maps",
372
+ patterns: [
373
+ /\bmap(s)?\b/i, /\bgeocode\b/i, /\blocation\b/i,
374
+ /\bnavigation\b/i, /\baddress.*look/i, /\bnearby\b/i,
375
+ ],
376
+ weight: 0.4,
377
+ },
378
+ {
379
+ category: "media",
380
+ patterns: [
381
+ /\bvideo.*stream/i, /\bvideo.*host/i, /\baudio\b/i,
382
+ /\btranscribe\b/i, /\bspeech.?to.?text\b/i, /\bvideo.*encod/i,
383
+ /\bmux\b/i, /\bdeepgram\b/i,
384
+ ],
385
+ weight: 0.4,
386
+ },
387
+ {
388
+ category: "search",
389
+ patterns: [
390
+ /\bsearch\b/i, /\bfull.?text\b/i, /\bautocomplete\b/i,
391
+ /\bsearch engine\b/i, /\balgolia\b/i, /\bindex.*search\b/i,
392
+ ],
393
+ weight: 0.4,
394
+ },
395
+ {
396
+ category: "devops",
397
+ patterns: [
398
+ /\bdeploy(ment)?\b/i, /\bhost(ing)?\b/i, /\bvps\b/i,
399
+ /\bpaas\b/i, /\bcontainer\b/i, /\bdocker\b/i,
400
+ /\bci\/cd\b/i, /\bpipeline\b/i, /\bvercel\b/i, /\bnetlify\b/i,
401
+ /\brailway\b/i, /\bfly\.io\b/i, /\bheroku\b/i,
402
+ ],
403
+ weight: 0.4,
404
+ },
405
+ {
406
+ category: "infrastructure",
407
+ patterns: [
408
+ /\bobject storage\b/i, /\bfile storage\b/i, /\bs3\b/i,
409
+ /\bbucket\b/i, /\bedge compute\b/i, /\bcloudflare workers\b/i,
410
+ /\bcdn\b/i, /\bblob\b/i, /\bpresigned url\b/i,
411
+ ],
412
+ weight: 0.4,
413
+ },
414
+ {
415
+ category: "commerce",
416
+ patterns: [
417
+ /\becommerce\b/i, /\be-commerce\b/i, /\bheadless.*shop\b/i,
418
+ /\bstorefront\b/i, /\bproduct catalog\b/i, /\bcart\b/i,
419
+ /\bdigital.*product\b/i, /\bsell.*online\b/i, /\bshopify\b/i,
420
+ ],
421
+ weight: 0.4,
422
+ },
423
+ {
424
+ category: "cms",
425
+ patterns: [
426
+ /\bcms\b/i, /\bcontent management\b/i, /\bheadless cms\b/i,
427
+ /\bblog\b/i, /\beditorial\b/i, /\brich text\b/i,
428
+ /\bsanity\b/i, /\bcontentful\b/i, /\bstrapi\b/i,
429
+ ],
430
+ weight: 0.4,
431
+ },
432
+ {
433
+ category: "forms",
434
+ patterns: [
435
+ /\bform\b/i, /\bsurvey\b/i, /\bquestionnaire\b/i,
436
+ /\blead capture\b/i, /\bfeedback form\b/i, /\btypeform\b/i,
437
+ /\bform submission\b/i, /\bcontact form\b/i,
438
+ ],
439
+ weight: 0.4,
440
+ },
441
+ {
442
+ category: "scheduling",
443
+ patterns: [
444
+ /\bschedul(e|ing)\b/i, /\bbooking\b/i, /\bappointment\b/i,
445
+ /\bcalendar\b/i, /\bmeeting.*book\b/i, /\bcalendly\b/i,
446
+ /\bbook a call\b/i, /\btime.*slot\b/i, /\bavailability\b/i,
447
+ ],
448
+ weight: 0.4,
449
+ },
450
+ {
451
+ category: "security",
452
+ patterns: [
453
+ /\bsecurity\b/i, /\bvulnerabilit/i, /\bwaf\b/i, /\bfirewall\b/i,
454
+ /\brate.?limit\b/i, /\bddos\b/i, /\bbot protection\b/i,
455
+ /\bsast\b/i, /\bsnyk\b/i, /\barcjet\b/i, /\bscan.*dep/i,
456
+ ],
457
+ weight: 0.4,
458
+ },
459
+ {
460
+ category: "workflow",
461
+ patterns: [
462
+ /\bbackground job\b/i, /\bjob queue\b/i, /\bqueue\b/i,
463
+ /\bcron\b/i, /\bscheduled task\b/i, /\bworkflow engine\b/i,
464
+ /\btrigger\.dev\b/i, /\binngest\b/i, /\btemporal\b/i,
465
+ /\bfan.?out\b/i, /\bworker.*task\b/i, /\basync.*process\b/i,
466
+ ],
467
+ weight: 0.4,
468
+ },
469
+ {
470
+ category: "integration",
471
+ patterns: [
472
+ /\bintegration\b/i, /\bno.?code.*automat/i, /\bautomat.*no.?code/i,
473
+ /\bzapier\b/i, /\bnango\b/i, /\boauth.*connect\b/i,
474
+ /\bsaas.*connect\b/i, /\bconnect.*third.?party\b/i,
475
+ /\bwebhook.*forward\b/i, /\bconnect.*apps?\b/i,
476
+ ],
477
+ weight: 0.4,
478
+ },
479
+ {
480
+ category: "testing",
481
+ patterns: [
482
+ /\bcross.?browser.*test\b/i, /\be2e.*test\b/i, /\bend.?to.?end.*test\b/i,
483
+ /\bqa\b/i, /\bsynth(etic)?\b/i, /\buptime.*monitor\b/i,
484
+ /\bbrowserstack\b/i, /\bcheckly\b/i, /\bplaywright\b/i,
485
+ ],
486
+ weight: 0.4,
487
+ },
488
+ ];
489
+ /** Detect the most likely category for a query, with a confidence score */
490
+ export function detectCategory(query) {
491
+ const scores = new Map();
492
+ for (const detector of CATEGORY_DETECTORS) {
493
+ let categoryScore = 0;
494
+ for (const pattern of detector.patterns) {
495
+ if (pattern.test(query)) {
496
+ categoryScore += detector.weight;
497
+ }
498
+ }
499
+ if (categoryScore > 0) {
500
+ scores.set(detector.category, categoryScore);
501
+ }
502
+ }
503
+ if (scores.size === 0)
504
+ return { category: null, confidence: 0 };
505
+ // Find highest-scoring category
506
+ let bestCategory = "";
507
+ let bestScore = 0;
508
+ let totalScore = 0;
509
+ for (const [cat, score] of scores) {
510
+ totalScore += score;
511
+ if (score > bestScore) {
512
+ bestScore = score;
513
+ bestCategory = cat;
514
+ }
515
+ }
516
+ // Confidence: how dominant is the top category vs others
517
+ // If one category is clearly dominant, confidence is high
518
+ const dominance = bestScore / totalScore;
519
+ // Clamp raw score: a single strong signal (0.4+) → confidence 0.6+
520
+ const rawConfidence = Math.min(bestScore / 0.8, 1.0);
521
+ const confidence = rawConfidence * (0.5 + dominance * 0.5);
522
+ return { category: bestCategory, confidence: Math.min(confidence, 1.0) };
523
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * TF-IDF text search engine for API entries.
3
+ *
4
+ * Builds an in-memory index from entries and provides relevance-ranked search
5
+ * without requiring any external API calls or embedding models. Uses weighted
6
+ * multi-field document construction to boost precision:
7
+ * name (x4) > bestFor/subcategory (x3) > useCases (x2) > description (x1)
8
+ *
9
+ * TF-IDF formula:
10
+ * score(doc, query) = sum_t( TF(t, doc) * IDF(t) ) for each query term t
11
+ * IDF(t) = log((N + 1) / (df(t) + 1)) + 1 (Laplace smoothing)
12
+ *
13
+ * Exports: TfIdfEngine, getTfIdfEngine, resetTfIdfEngine
14
+ */
15
+ import type { ApiEntry } from "./schema.js";
16
+ export declare class TfIdfEngine {
17
+ private vectors;
18
+ private idf;
19
+ private entryBySlug;
20
+ private _built;
21
+ /**
22
+ * Build the TF-IDF index from a list of entries.
23
+ * Can be called again to rebuild with updated entries.
24
+ */
25
+ build(entries: ApiEntry[]): void;
26
+ /**
27
+ * Search the index for entries most relevant to the query.
28
+ * Returns up to topN entries sorted by TF-IDF score descending.
29
+ * Entries with zero score are excluded.
30
+ */
31
+ search(query: string, topN?: number): ApiEntry[];
32
+ get isBuilt(): boolean;
33
+ /** Number of indexed documents */
34
+ get size(): number;
35
+ }
36
+ /** Get the shared engine instance (lazily created) */
37
+ export declare function getTfIdfEngine(): TfIdfEngine;
38
+ /**
39
+ * Reset the shared engine (forces rebuild on next search).
40
+ * Must be called in test afterEach when the DB is replaced.
41
+ */
42
+ export declare function resetTfIdfEngine(): void;