@tanstack/cli 0.0.8 → 0.48.2
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/bin.js +7 -0
- package/dist/cli.js +481 -0
- package/dist/command-line.js +174 -0
- package/dist/dev-watch.js +290 -0
- package/dist/file-syncer.js +148 -0
- package/dist/index.js +1 -0
- package/dist/mcp/api.js +31 -0
- package/dist/mcp/tools.js +250 -0
- package/dist/mcp/types.js +37 -0
- package/dist/mcp.js +121 -0
- package/dist/options.js +162 -0
- package/dist/types/bin.d.ts +2 -0
- package/dist/types/cli.d.ts +16 -0
- package/dist/types/command-line.d.ts +10 -0
- package/dist/types/dev-watch.d.ts +27 -0
- package/dist/types/file-syncer.d.ts +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/mcp/api.d.ts +4 -0
- package/dist/types/mcp/tools.d.ts +2 -0
- package/dist/types/mcp/types.d.ts +217 -0
- package/dist/types/mcp.d.ts +6 -0
- package/dist/types/options.d.ts +8 -0
- package/dist/types/types.d.ts +25 -0
- package/dist/types/ui-environment.d.ts +2 -0
- package/dist/types/ui-prompts.d.ts +12 -0
- package/dist/types/utils.d.ts +8 -0
- package/dist/types.js +1 -0
- package/dist/ui-environment.js +52 -0
- package/dist/ui-prompts.js +244 -0
- package/dist/utils.js +30 -0
- package/package.json +46 -46
- package/src/bin.ts +6 -93
- package/src/cli.ts +692 -0
- package/src/command-line.ts +236 -0
- package/src/dev-watch.ts +430 -0
- package/src/file-syncer.ts +205 -0
- package/src/index.ts +1 -85
- package/src/mcp.ts +190 -0
- package/src/options.ts +260 -0
- package/src/types.ts +27 -0
- package/src/ui-environment.ts +74 -0
- package/src/ui-prompts.ts +322 -0
- package/src/utils.ts +38 -0
- package/tests/command-line.test.ts +304 -0
- package/tests/index.test.ts +9 -0
- package/tests/mcp.test.ts +225 -0
- package/tests/options.test.ts +304 -0
- package/tests/setupVitest.ts +6 -0
- package/tests/ui-environment.test.ts +97 -0
- package/tests/ui-prompts.test.ts +238 -0
- package/tsconfig.json +17 -0
- package/vitest.config.js +7 -0
- package/dist/bin.cjs +0 -769
- package/dist/bin.d.cts +0 -1
- package/dist/bin.d.mts +0 -1
- package/dist/bin.mjs +0 -768
- package/dist/fetch-CbFFGJEw.cjs +0 -3
- package/dist/fetch-DG5dLrsb.cjs +0 -522
- package/dist/fetch-DhlVXS6S.mjs +0 -390
- package/dist/fetch-I_OVg8JX.mjs +0 -3
- package/dist/index.cjs +0 -37
- package/dist/index.d.cts +0 -1172
- package/dist/index.d.mts +0 -1172
- package/dist/index.mjs +0 -4
- package/dist/template-Szi7-AZJ.mjs +0 -2202
- package/dist/template-lWrIZhCQ.cjs +0 -2314
- package/src/api/fetch.test.ts +0 -114
- package/src/api/fetch.ts +0 -278
- package/src/cache/index.ts +0 -89
- package/src/commands/create.ts +0 -470
- package/src/commands/mcp.test.ts +0 -152
- package/src/commands/mcp.ts +0 -211
- package/src/engine/compile-with-addons.test.ts +0 -302
- package/src/engine/compile.test.ts +0 -404
- package/src/engine/compile.ts +0 -569
- package/src/engine/config-file.test.ts +0 -118
- package/src/engine/config-file.ts +0 -61
- package/src/engine/custom-addons/integration.ts +0 -323
- package/src/engine/custom-addons/shared.test.ts +0 -98
- package/src/engine/custom-addons/shared.ts +0 -281
- package/src/engine/custom-addons/template.test.ts +0 -288
- package/src/engine/custom-addons/template.ts +0 -124
- package/src/engine/template.test.ts +0 -256
- package/src/engine/template.ts +0 -269
- package/src/engine/types.ts +0 -336
- package/src/parse-gitignore.d.ts +0 -5
- package/src/templates/base.ts +0 -883
package/src/api/fetch.test.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import { describe, expect, it } from 'vitest'
|
|
3
|
-
import {
|
|
4
|
-
fetchIntegration,
|
|
5
|
-
fetchIntegrationFiles,
|
|
6
|
-
fetchIntegrationInfo,
|
|
7
|
-
fetchIntegrations,
|
|
8
|
-
fetchManifest,
|
|
9
|
-
} from './fetch.js'
|
|
10
|
-
|
|
11
|
-
const INTEGRATIONS_PATH = resolve(__dirname, '../../../../integrations')
|
|
12
|
-
|
|
13
|
-
describe('fetch API', () => {
|
|
14
|
-
describe('fetchManifest', () => {
|
|
15
|
-
it('should fetch manifest from local path', async () => {
|
|
16
|
-
const manifest = await fetchManifest(INTEGRATIONS_PATH)
|
|
17
|
-
|
|
18
|
-
expect(manifest).toBeDefined()
|
|
19
|
-
expect(manifest.integrations).toBeInstanceOf(Array)
|
|
20
|
-
expect(manifest.integrations.length).toBeGreaterThan(0)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should have integrations with required fields', async () => {
|
|
24
|
-
const manifest = await fetchManifest(INTEGRATIONS_PATH)
|
|
25
|
-
|
|
26
|
-
for (const integration of manifest.integrations) {
|
|
27
|
-
expect(integration.id).toBeDefined()
|
|
28
|
-
expect(integration.name).toBeDefined()
|
|
29
|
-
expect(integration.modes).toBeInstanceOf(Array)
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should throw for non-existent path', async () => {
|
|
34
|
-
await expect(fetchManifest('/non/existent/path')).rejects.toThrow()
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('fetchIntegrationInfo', () => {
|
|
39
|
-
it('should fetch integration info from local path', async () => {
|
|
40
|
-
const info = await fetchIntegrationInfo('tanstack-query', INTEGRATIONS_PATH)
|
|
41
|
-
|
|
42
|
-
expect(info.name).toBe('TanStack Query')
|
|
43
|
-
expect(info.type).toBe('integration')
|
|
44
|
-
expect(info.modes).toContain('file-router')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('should throw for non-existent integration', async () => {
|
|
48
|
-
await expect(
|
|
49
|
-
fetchIntegrationInfo('non-existent', INTEGRATIONS_PATH),
|
|
50
|
-
).rejects.toThrow()
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('fetchIntegrationFiles', () => {
|
|
55
|
-
it('should fetch integration files from local path', async () => {
|
|
56
|
-
const files = await fetchIntegrationFiles('tanstack-query', INTEGRATIONS_PATH)
|
|
57
|
-
|
|
58
|
-
expect(Object.keys(files).length).toBeGreaterThan(0)
|
|
59
|
-
// Files are in assets/src/integrations/query/
|
|
60
|
-
expect(files).toHaveProperty('src/integrations/query/provider.tsx')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('should return empty object for integration without assets', async () => {
|
|
64
|
-
// This tests the case where assets dir doesn't exist
|
|
65
|
-
const files = await fetchIntegrationFiles('non-existent', INTEGRATIONS_PATH)
|
|
66
|
-
expect(files).toEqual({})
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
describe('fetchIntegration', () => {
|
|
71
|
-
it('should fetch complete integration', async () => {
|
|
72
|
-
const integration = await fetchIntegration('tanstack-query', INTEGRATIONS_PATH)
|
|
73
|
-
|
|
74
|
-
expect(integration.id).toBe('tanstack-query')
|
|
75
|
-
expect(integration.name).toBe('TanStack Query')
|
|
76
|
-
expect(integration.files).toBeDefined()
|
|
77
|
-
expect(Object.keys(integration.files).length).toBeGreaterThan(0)
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should include hooks if defined', async () => {
|
|
81
|
-
const integration = await fetchIntegration('tanstack-query', INTEGRATIONS_PATH)
|
|
82
|
-
|
|
83
|
-
expect(integration.hooks).toBeDefined()
|
|
84
|
-
expect(integration.hooks!.length).toBeGreaterThan(0)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('should merge package.json into packageAdditions', async () => {
|
|
88
|
-
const integration = await fetchIntegration('tanstack-query', INTEGRATIONS_PATH)
|
|
89
|
-
|
|
90
|
-
expect(integration.packageAdditions).toBeDefined()
|
|
91
|
-
expect(integration.packageAdditions?.dependencies).toHaveProperty(
|
|
92
|
-
'@tanstack/react-query',
|
|
93
|
-
)
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe('fetchIntegrations', () => {
|
|
98
|
-
it('should fetch multiple integrations in parallel', async () => {
|
|
99
|
-
const integrations = await fetchIntegrations(
|
|
100
|
-
['tanstack-query', 'tanstack-form'],
|
|
101
|
-
INTEGRATIONS_PATH,
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
expect(integrations).toHaveLength(2)
|
|
105
|
-
expect(integrations[0]?.id).toBe('tanstack-query')
|
|
106
|
-
expect(integrations[1]?.id).toBe('tanstack-form')
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('should return empty array for empty input', async () => {
|
|
110
|
-
const integrations = await fetchIntegrations([], INTEGRATIONS_PATH)
|
|
111
|
-
expect(integrations).toEqual([])
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
})
|
package/src/api/fetch.ts
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'
|
|
2
|
-
import { extname, join } from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
IntegrationCompiledSchema,
|
|
5
|
-
IntegrationInfoSchema,
|
|
6
|
-
ManifestSchema
|
|
7
|
-
} from '../engine/types.js'
|
|
8
|
-
import { fetchWithCache } from '../cache/index.js'
|
|
9
|
-
import type { IntegrationCompiled, IntegrationInfo, Manifest } from '../engine/types.js'
|
|
10
|
-
|
|
11
|
-
const GITHUB_RAW_BASE =
|
|
12
|
-
'https://raw.githubusercontent.com/TanStack/cli/main/integrations'
|
|
13
|
-
|
|
14
|
-
// 1 hour cache TTL for remote fetches
|
|
15
|
-
const CACHE_TTL_MS = 60 * 60 * 1000
|
|
16
|
-
|
|
17
|
-
// Binary file extensions that should be read as base64
|
|
18
|
-
const BINARY_EXTENSIONS = new Set([
|
|
19
|
-
'.png', '.jpg', '.jpeg', '.gif', '.webp', '.ico', '.svg',
|
|
20
|
-
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
|
21
|
-
'.pdf', '.zip', '.tar', '.gz',
|
|
22
|
-
'.mp3', '.mp4', '.wav', '.ogg', '.webm',
|
|
23
|
-
])
|
|
24
|
-
|
|
25
|
-
// Prefix for base64-encoded binary files
|
|
26
|
-
export const BINARY_PREFIX = 'base64:'
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Check if a file should be treated as binary based on extension
|
|
30
|
-
*/
|
|
31
|
-
function isBinaryFile(filePath: string): boolean {
|
|
32
|
-
return BINARY_EXTENSIONS.has(extname(filePath).toLowerCase())
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Check if a path is a local directory
|
|
37
|
-
*/
|
|
38
|
-
function isLocalPath(path: string): boolean {
|
|
39
|
-
return path.startsWith('/') || path.startsWith('./') || path.startsWith('..')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Fetch the integration manifest from GitHub or local path (with caching for remote)
|
|
44
|
-
*/
|
|
45
|
-
export async function fetchManifest(
|
|
46
|
-
baseUrl: string = GITHUB_RAW_BASE,
|
|
47
|
-
): Promise<Manifest> {
|
|
48
|
-
if (isLocalPath(baseUrl)) {
|
|
49
|
-
const manifestPath = join(baseUrl, 'manifest.json')
|
|
50
|
-
if (!existsSync(manifestPath)) {
|
|
51
|
-
throw new Error(`Manifest not found at ${manifestPath}`)
|
|
52
|
-
}
|
|
53
|
-
const data = JSON.parse(readFileSync(manifestPath, 'utf-8'))
|
|
54
|
-
return ManifestSchema.parse(data)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const cacheKey = `manifest_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
|
|
58
|
-
|
|
59
|
-
return fetchWithCache(
|
|
60
|
-
cacheKey,
|
|
61
|
-
async () => {
|
|
62
|
-
const url = `${baseUrl}/manifest.json`
|
|
63
|
-
const response = await fetch(url)
|
|
64
|
-
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
throw new Error(`Failed to fetch manifest: ${response.statusText}`)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const data = await response.json()
|
|
70
|
-
return ManifestSchema.parse(data)
|
|
71
|
-
},
|
|
72
|
-
CACHE_TTL_MS,
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Fetch integration info.json from GitHub or local path (with caching for remote)
|
|
78
|
-
*/
|
|
79
|
-
export async function fetchIntegrationInfo(
|
|
80
|
-
integrationId: string,
|
|
81
|
-
baseUrl: string = GITHUB_RAW_BASE,
|
|
82
|
-
): Promise<IntegrationInfo> {
|
|
83
|
-
if (isLocalPath(baseUrl)) {
|
|
84
|
-
const infoPath = join(baseUrl, integrationId, 'info.json')
|
|
85
|
-
if (!existsSync(infoPath)) {
|
|
86
|
-
throw new Error(`Integration info not found at ${infoPath}`)
|
|
87
|
-
}
|
|
88
|
-
const data = JSON.parse(readFileSync(infoPath, 'utf-8'))
|
|
89
|
-
return IntegrationInfoSchema.parse(data)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const cacheKey = `integration_info_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
|
|
93
|
-
|
|
94
|
-
return fetchWithCache(
|
|
95
|
-
cacheKey,
|
|
96
|
-
async () => {
|
|
97
|
-
const url = `${baseUrl}/${integrationId}/info.json`
|
|
98
|
-
const response = await fetch(url)
|
|
99
|
-
|
|
100
|
-
if (!response.ok) {
|
|
101
|
-
throw new Error(`Failed to fetch integration ${integrationId}: ${response.statusText}`)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const data = await response.json()
|
|
105
|
-
return IntegrationInfoSchema.parse(data)
|
|
106
|
-
},
|
|
107
|
-
CACHE_TTL_MS,
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Recursively read all files from a directory
|
|
113
|
-
* Binary files are read as base64 with a prefix marker
|
|
114
|
-
*/
|
|
115
|
-
function readDirRecursive(
|
|
116
|
-
dir: string,
|
|
117
|
-
basePath: string = '',
|
|
118
|
-
): Record<string, string> {
|
|
119
|
-
const files: Record<string, string> = {}
|
|
120
|
-
|
|
121
|
-
if (!existsSync(dir)) return files
|
|
122
|
-
|
|
123
|
-
for (const entry of readdirSync(dir)) {
|
|
124
|
-
const fullPath = join(dir, entry)
|
|
125
|
-
const relativePath = basePath ? `${basePath}/${entry}` : entry
|
|
126
|
-
const stat = statSync(fullPath)
|
|
127
|
-
|
|
128
|
-
if (stat.isDirectory()) {
|
|
129
|
-
Object.assign(files, readDirRecursive(fullPath, relativePath))
|
|
130
|
-
} else if (isBinaryFile(relativePath)) {
|
|
131
|
-
// Read binary files as base64 with prefix
|
|
132
|
-
const buffer = readFileSync(fullPath)
|
|
133
|
-
files[relativePath] = BINARY_PREFIX + buffer.toString('base64')
|
|
134
|
-
} else {
|
|
135
|
-
files[relativePath] = readFileSync(fullPath, 'utf-8')
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return files
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Fetch all files for an integration from GitHub or local path (with caching for remote)
|
|
144
|
-
*/
|
|
145
|
-
export async function fetchIntegrationFiles(
|
|
146
|
-
integrationId: string,
|
|
147
|
-
baseUrl: string = GITHUB_RAW_BASE,
|
|
148
|
-
): Promise<Record<string, string>> {
|
|
149
|
-
if (isLocalPath(baseUrl)) {
|
|
150
|
-
const assetsPath = join(baseUrl, integrationId, 'assets')
|
|
151
|
-
return readDirRecursive(assetsPath)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const cacheKey = `integration_files_${integrationId}_${baseUrl.replace(/[^a-zA-Z0-9]/g, '_')}`
|
|
155
|
-
|
|
156
|
-
return fetchWithCache(
|
|
157
|
-
cacheKey,
|
|
158
|
-
async () => {
|
|
159
|
-
// First fetch the file list (we'll need a files.json or similar)
|
|
160
|
-
const filesUrl = `${baseUrl}/${integrationId}/files.json`
|
|
161
|
-
const response = await fetch(filesUrl)
|
|
162
|
-
|
|
163
|
-
if (!response.ok) {
|
|
164
|
-
// No files.json, return empty
|
|
165
|
-
return {}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const fileList: Array<string> = await response.json()
|
|
169
|
-
const files: Record<string, string> = {}
|
|
170
|
-
|
|
171
|
-
// Fetch each file
|
|
172
|
-
await Promise.all(
|
|
173
|
-
fileList.map(async (filePath) => {
|
|
174
|
-
const fileUrl = `${baseUrl}/${integrationId}/assets/${filePath}`
|
|
175
|
-
const fileResponse = await fetch(fileUrl)
|
|
176
|
-
|
|
177
|
-
if (fileResponse.ok) {
|
|
178
|
-
if (isBinaryFile(filePath)) {
|
|
179
|
-
// Fetch binary files as arrayBuffer and convert to base64
|
|
180
|
-
const buffer = await fileResponse.arrayBuffer()
|
|
181
|
-
files[filePath] = BINARY_PREFIX + Buffer.from(buffer).toString('base64')
|
|
182
|
-
} else {
|
|
183
|
-
files[filePath] = await fileResponse.text()
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}),
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
return files
|
|
190
|
-
},
|
|
191
|
-
CACHE_TTL_MS,
|
|
192
|
-
)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Fetch integration package.json if it exists
|
|
197
|
-
*/
|
|
198
|
-
async function fetchIntegrationPackageJson(
|
|
199
|
-
integrationId: string,
|
|
200
|
-
baseUrl: string,
|
|
201
|
-
): Promise<{
|
|
202
|
-
dependencies?: Record<string, string>
|
|
203
|
-
devDependencies?: Record<string, string>
|
|
204
|
-
scripts?: Record<string, string>
|
|
205
|
-
} | null> {
|
|
206
|
-
if (isLocalPath(baseUrl)) {
|
|
207
|
-
const pkgPath = join(baseUrl, integrationId, 'package.json')
|
|
208
|
-
if (existsSync(pkgPath)) {
|
|
209
|
-
return JSON.parse(readFileSync(pkgPath, 'utf-8'))
|
|
210
|
-
}
|
|
211
|
-
return null
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const url = `${baseUrl}/${integrationId}/package.json`
|
|
215
|
-
const response = await fetch(url)
|
|
216
|
-
|
|
217
|
-
if (!response.ok) {
|
|
218
|
-
return null
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return response.json()
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Fetch a complete compiled integration from GitHub
|
|
226
|
-
*/
|
|
227
|
-
export async function fetchIntegration(
|
|
228
|
-
integrationId: string,
|
|
229
|
-
baseUrl: string = GITHUB_RAW_BASE,
|
|
230
|
-
): Promise<IntegrationCompiled> {
|
|
231
|
-
const [info, files, pkgJson] = await Promise.all([
|
|
232
|
-
fetchIntegrationInfo(integrationId, baseUrl),
|
|
233
|
-
fetchIntegrationFiles(integrationId, baseUrl),
|
|
234
|
-
fetchIntegrationPackageJson(integrationId, baseUrl),
|
|
235
|
-
])
|
|
236
|
-
|
|
237
|
-
// Merge package.json into packageAdditions if present
|
|
238
|
-
const packageAdditions = info.packageAdditions ?? {}
|
|
239
|
-
if (pkgJson) {
|
|
240
|
-
if (pkgJson.dependencies) {
|
|
241
|
-
packageAdditions.dependencies = {
|
|
242
|
-
...packageAdditions.dependencies,
|
|
243
|
-
...pkgJson.dependencies,
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (pkgJson.devDependencies) {
|
|
247
|
-
packageAdditions.devDependencies = {
|
|
248
|
-
...packageAdditions.devDependencies,
|
|
249
|
-
...pkgJson.devDependencies,
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
if (pkgJson.scripts) {
|
|
253
|
-
packageAdditions.scripts = {
|
|
254
|
-
...packageAdditions.scripts,
|
|
255
|
-
...pkgJson.scripts,
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return IntegrationCompiledSchema.parse({
|
|
261
|
-
...info,
|
|
262
|
-
id: integrationId,
|
|
263
|
-
files,
|
|
264
|
-
packageAdditions:
|
|
265
|
-
Object.keys(packageAdditions).length > 0 ? packageAdditions : undefined,
|
|
266
|
-
deletedFiles: [],
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Fetch multiple integrations in parallel
|
|
272
|
-
*/
|
|
273
|
-
export async function fetchIntegrations(
|
|
274
|
-
integrationIds: Array<string>,
|
|
275
|
-
baseUrl: string = GITHUB_RAW_BASE,
|
|
276
|
-
): Promise<Array<IntegrationCompiled>> {
|
|
277
|
-
return Promise.all(integrationIds.map((id) => fetchIntegration(id, baseUrl)))
|
|
278
|
-
}
|
package/src/cache/index.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { homedir } from 'node:os'
|
|
4
|
-
|
|
5
|
-
const CACHE_DIR = join(homedir(), '.tanstack', 'cache')
|
|
6
|
-
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000 // 24 hours
|
|
7
|
-
|
|
8
|
-
interface CacheEntry<T> {
|
|
9
|
-
data: T
|
|
10
|
-
timestamp: number
|
|
11
|
-
ttl: number
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function ensureCacheDir() {
|
|
15
|
-
if (!existsSync(CACHE_DIR)) {
|
|
16
|
-
mkdirSync(CACHE_DIR, { recursive: true })
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getCachePath(key: string): string {
|
|
21
|
-
// Sanitize key to be filesystem-safe
|
|
22
|
-
const safeKey = key.replace(/[^a-zA-Z0-9-_]/g, '_')
|
|
23
|
-
return join(CACHE_DIR, `${safeKey}.json`)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getCached<T>(key: string): T | null {
|
|
27
|
-
const cachePath = getCachePath(key)
|
|
28
|
-
|
|
29
|
-
if (!existsSync(cachePath)) {
|
|
30
|
-
return null
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const raw = readFileSync(cachePath, 'utf-8')
|
|
35
|
-
const entry: CacheEntry<T> = JSON.parse(raw)
|
|
36
|
-
|
|
37
|
-
const age = Date.now() - entry.timestamp
|
|
38
|
-
if (age > entry.ttl) {
|
|
39
|
-
return null // Expired
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return entry.data
|
|
43
|
-
} catch {
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function setCache<T>(key: string, data: T, ttlMs: number = DEFAULT_TTL_MS): void {
|
|
49
|
-
ensureCacheDir()
|
|
50
|
-
const cachePath = getCachePath(key)
|
|
51
|
-
|
|
52
|
-
const entry: CacheEntry<T> = {
|
|
53
|
-
data,
|
|
54
|
-
timestamp: Date.now(),
|
|
55
|
-
ttl: ttlMs,
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
writeFileSync(cachePath, JSON.stringify(entry, null, 2), 'utf-8')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function fetchWithCache<T>(
|
|
62
|
-
key: string,
|
|
63
|
-
fetcher: () => Promise<T>,
|
|
64
|
-
ttlMs: number = DEFAULT_TTL_MS,
|
|
65
|
-
): Promise<T> {
|
|
66
|
-
// Try cache first
|
|
67
|
-
const cached = getCached<T>(key)
|
|
68
|
-
if (cached !== null) {
|
|
69
|
-
return cached
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Fetch fresh data
|
|
73
|
-
const data = await fetcher()
|
|
74
|
-
|
|
75
|
-
// Cache it
|
|
76
|
-
setCache(key, data, ttlMs)
|
|
77
|
-
|
|
78
|
-
return data
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function clearCache(): void {
|
|
82
|
-
if (existsSync(CACHE_DIR)) {
|
|
83
|
-
rmSync(CACHE_DIR, { recursive: true, force: true })
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function getCacheDir(): string {
|
|
88
|
-
return CACHE_DIR
|
|
89
|
-
}
|