Skip to content

Commit 62aafec

Browse files
committed
Merge branch 'main' into feature/update-sentry-integration
# Conflicts: # src/routeTree.gen.ts
2 parents da3bd48 + b4aefe4 commit 62aafec

File tree

76 files changed

+5767
-7927
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+5767
-7927
lines changed

media/brand.sketch

-1.46 MB
Binary file not shown.

netlify/functions/sync-blog-posts-background.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

netlify/functions/sync-github-releases-background.ts

Lines changed: 0 additions & 53 deletions
This file was deleted.
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
import type { Config } from '@netlify/functions'
2+
import {
3+
searchIntentPackages,
4+
fetchPackument,
5+
isIntentCompatible,
6+
selectVersionsToSync,
7+
extractSkillsFromTarball,
8+
} from '~/utils/intent.server'
9+
import {
10+
upsertIntentPackage,
11+
getKnownVersions,
12+
enqueuePackageVersion,
13+
markPackageVerified,
14+
} from '~/utils/intent-db.server'
15+
16+
/**
17+
* Netlify Scheduled Function - Discover Intent-compatible npm packages
18+
*
19+
* Phase 1 of 2. Fast: no tarball downloads (except brief header peeks for GitHub path).
20+
*
21+
* Two discovery paths:
22+
* 1. NPM keyword search — instant, finds packages that published with tanstack-intent keyword
23+
* 2. GitHub code search — finds repos that depend on @tanstack/intent but may not have the keyword yet
24+
*
25+
* Both paths enqueue new versions for tarball processing (syncStatus = 'pending').
26+
* Actual skill extraction happens in sync-intent-process-background.
27+
*
28+
* Scheduled: Every 6 hours
29+
*/
30+
const handler = async (req: Request) => {
31+
const { next_run } = await req.json()
32+
const startTime = Date.now()
33+
34+
console.log('[intent-discover] Starting discovery (NPM + GitHub)...')
35+
36+
let versionsEnqueued = 0
37+
const errors: Array<string> = []
38+
39+
// ---------------------------------------------------------------------------
40+
// Path 1: NPM keyword search
41+
// ---------------------------------------------------------------------------
42+
try {
43+
console.log(
44+
'[intent-discover] Searching NPM for keywords:tanstack-intent...',
45+
)
46+
const searchResults = await searchIntentPackages()
47+
console.log(
48+
`[intent-discover] NPM found ${searchResults.objects.length} candidates`,
49+
)
50+
51+
for (const { package: pkg } of searchResults.objects) {
52+
try {
53+
await upsertIntentPackage({ name: pkg.name, verified: false })
54+
55+
const packument = await fetchPackument(pkg.name)
56+
const latestVersion = packument['dist-tags'].latest
57+
if (!latestVersion) continue
58+
59+
const latestMeta = packument.versions[latestVersion]
60+
if (!latestMeta || !isIntentCompatible(latestMeta)) continue
61+
62+
await markPackageVerified(pkg.name)
63+
64+
const knownVersions = await getKnownVersions(pkg.name)
65+
const toEnqueue = selectVersionsToSync(packument, knownVersions)
66+
for (const { version, tarball, publishedAt } of toEnqueue) {
67+
await enqueuePackageVersion({
68+
packageName: pkg.name,
69+
version,
70+
tarballUrl: tarball,
71+
publishedAt,
72+
})
73+
versionsEnqueued++
74+
}
75+
console.log(
76+
`[intent-discover] NPM: ${pkg.name} - enqueued ${toEnqueue.length}`,
77+
)
78+
} catch (e) {
79+
const msg = `npm/${pkg.name}: ${e instanceof Error ? e.message : String(e)}`
80+
console.error(`[intent-discover] ${msg}`)
81+
errors.push(msg)
82+
}
83+
}
84+
} catch (e) {
85+
console.error(
86+
'[intent-discover] NPM path failed:',
87+
e instanceof Error ? e.message : String(e),
88+
)
89+
}
90+
91+
// ---------------------------------------------------------------------------
92+
// Path 2: GitHub code search for @tanstack/intent dependents
93+
// ---------------------------------------------------------------------------
94+
const githubToken = process.env.GITHUB_AUTH_TOKEN
95+
if (githubToken) {
96+
try {
97+
console.log(
98+
'[intent-discover] Searching GitHub for @tanstack/intent dependents...',
99+
)
100+
const ghHeaders = {
101+
Authorization: `Bearer ${githubToken}`,
102+
Accept: 'application/vnd.github.v3+json',
103+
}
104+
105+
const searchRes = await fetch(
106+
'https://api.github.com/search/code?q=%22%40tanstack%2Fintent%22+filename%3Apackage.json&per_page=100',
107+
{ headers: ghHeaders },
108+
)
109+
if (!searchRes.ok) throw new Error(`GitHub search ${searchRes.status}`)
110+
111+
const searchData = (await searchRes.json()) as {
112+
items: Array<{ path: string; repository: { full_name: string } }>
113+
}
114+
115+
// Deduplicate repo+path pairs
116+
const seen = new Set<string>()
117+
const candidates = searchData.items.filter((item) => {
118+
const key = `${item.repository.full_name}|${item.path}`
119+
if (seen.has(key)) return false
120+
seen.add(key)
121+
return true
122+
})
123+
124+
console.log(
125+
`[intent-discover] GitHub found ${candidates.length} package.json files`,
126+
)
127+
128+
for (const { repo, path } of candidates.map((i) => ({
129+
repo: i.repository.full_name,
130+
path: i.path,
131+
}))) {
132+
try {
133+
const contentRes = await fetch(
134+
`https://api.github.com/repos/${repo}/contents/${path}`,
135+
{ headers: ghHeaders },
136+
)
137+
if (!contentRes.ok) continue
138+
139+
const contentData = (await contentRes.json()) as { content?: string }
140+
if (!contentData.content) continue
141+
142+
const pkgJson = JSON.parse(
143+
Buffer.from(contentData.content, 'base64').toString('utf-8'),
144+
) as { name?: string; private?: boolean }
145+
146+
const pkgName = pkgJson.name
147+
if (!pkgName || pkgJson.private) continue
148+
149+
// Check NPM
150+
const npmRes = await fetch(
151+
`https://registry.npmjs.org/${encodeURIComponent(pkgName)}/latest`,
152+
)
153+
if (!npmRes.ok) continue
154+
155+
const npmMeta = (await npmRes.json()) as {
156+
version?: string
157+
dist?: { tarball?: string }
158+
}
159+
if (!npmMeta.version || !npmMeta.dist?.tarball) continue
160+
161+
// Peek at tarball for skills
162+
const skills = await extractSkillsFromTarball(npmMeta.dist.tarball)
163+
if (skills.length === 0) continue
164+
165+
await upsertIntentPackage({ name: pkgName, verified: true })
166+
await markPackageVerified(pkgName)
167+
168+
const packument = await fetchPackument(pkgName)
169+
const knownVersions = await getKnownVersions(pkgName)
170+
const toEnqueue = selectVersionsToSync(packument, knownVersions)
171+
172+
for (const { version, tarball, publishedAt } of toEnqueue) {
173+
await enqueuePackageVersion({
174+
packageName: pkgName,
175+
version,
176+
tarballUrl: tarball,
177+
publishedAt,
178+
})
179+
versionsEnqueued++
180+
}
181+
if (toEnqueue.length > 0) {
182+
console.log(
183+
`[intent-discover] GitHub: ${pkgName} - enqueued ${toEnqueue.length}`,
184+
)
185+
}
186+
} catch (e) {
187+
const msg = `github/${repo}: ${e instanceof Error ? e.message : String(e)}`
188+
console.error(`[intent-discover] ${msg}`)
189+
errors.push(msg)
190+
}
191+
}
192+
} catch (e) {
193+
console.error(
194+
'[intent-discover] GitHub path failed:',
195+
e instanceof Error ? e.message : String(e),
196+
)
197+
}
198+
} else {
199+
console.warn(
200+
'[intent-discover] GITHUB_AUTH_TOKEN not set, skipping GitHub path',
201+
)
202+
}
203+
204+
const duration = Date.now() - startTime
205+
console.log(
206+
`[intent-discover] Done in ${duration}ms - enqueued: ${versionsEnqueued}, errors: ${errors.length}`,
207+
)
208+
if (errors.length > 0)
209+
console.warn(`[intent-discover] Errors:\n ${errors.join('\n ')}`)
210+
console.log('[intent-discover] Next invocation at:', next_run)
211+
}
212+
213+
export default handler
214+
215+
export const config: Config = {
216+
schedule: '0 */6 * * *', // Every 6 hours
217+
}

0 commit comments

Comments
 (0)