openpets 1.0.4 → 1.0.6
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/data/api.json +3172 -0
- package/dist/src/core/ai-client-base/index.d.ts +47 -0
- package/dist/src/core/ai-client-base/index.d.ts.map +1 -0
- package/dist/src/core/ai-client-base/index.js +168 -0
- package/dist/src/core/ai-client-base/index.js.map +1 -0
- package/dist/src/core/browser.d.ts +10 -0
- package/dist/src/core/browser.d.ts.map +1 -0
- package/{browser.ts → dist/src/core/browser.js} +4 -4
- package/dist/src/core/browser.js.map +1 -0
- package/dist/src/core/build-pet.d.ts +2 -0
- package/dist/src/core/build-pet.d.ts.map +1 -0
- package/dist/src/core/build-pet.js +364 -0
- package/dist/src/core/build-pet.js.map +1 -0
- package/dist/src/core/cli.d.ts +3 -0
- package/dist/src/core/cli.d.ts.map +1 -0
- package/dist/src/core/cli.js +244 -0
- package/dist/src/core/cli.js.map +1 -0
- package/dist/src/core/config-manager.d.ts +13 -0
- package/dist/src/core/config-manager.d.ts.map +1 -0
- package/dist/src/core/config-manager.js +59 -0
- package/dist/src/core/config-manager.js.map +1 -0
- package/dist/src/core/deploy-pet.d.ts +2 -0
- package/dist/src/core/deploy-pet.d.ts.map +1 -0
- package/dist/src/core/deploy-pet.js +66 -0
- package/dist/src/core/deploy-pet.js.map +1 -0
- package/dist/src/core/index.d.ts +11 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +11 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/local-cache.d.ts +69 -0
- package/dist/src/core/local-cache.d.ts.map +1 -0
- package/dist/src/core/local-cache.js +212 -0
- package/dist/src/core/local-cache.js.map +1 -0
- package/dist/src/core/logger.d.ts.map +1 -0
- package/{logger.js → dist/src/core/logger.js} +8 -9
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/mcp-factory.d.ts +12 -0
- package/dist/src/core/mcp-factory.d.ts.map +1 -0
- package/dist/src/core/mcp-factory.js +143 -0
- package/dist/src/core/mcp-factory.js.map +1 -0
- package/dist/src/core/mcp-server.d.ts +3 -0
- package/dist/src/core/mcp-server.d.ts.map +1 -0
- package/dist/src/core/mcp-server.js +55 -0
- package/dist/src/core/mcp-server.js.map +1 -0
- package/dist/src/core/migrate-plugin.d.ts +15 -0
- package/dist/src/core/migrate-plugin.d.ts.map +1 -0
- package/dist/src/core/migrate-plugin.js +181 -0
- package/dist/src/core/migrate-plugin.js.map +1 -0
- package/dist/src/core/pets-registry.d.ts +47 -0
- package/dist/src/core/pets-registry.d.ts.map +1 -0
- package/dist/src/core/pets-registry.js +109 -0
- package/dist/src/core/pets-registry.js.map +1 -0
- package/dist/src/core/plugin-factory.d.ts +58 -0
- package/dist/src/core/plugin-factory.d.ts.map +1 -0
- package/dist/src/core/plugin-factory.js +212 -0
- package/dist/src/core/plugin-factory.js.map +1 -0
- package/dist/src/core/prompt-utils.d.ts +14 -0
- package/dist/src/core/prompt-utils.d.ts.map +1 -0
- package/dist/src/core/prompt-utils.js +106 -0
- package/dist/src/core/prompt-utils.js.map +1 -0
- package/dist/src/core/schema-helpers.d.ts +33 -0
- package/dist/src/core/schema-helpers.d.ts.map +1 -0
- package/dist/src/core/schema-helpers.js +46 -0
- package/dist/src/core/schema-helpers.js.map +1 -0
- package/dist/src/core/search-pets.d.ts +29 -0
- package/dist/src/core/search-pets.d.ts.map +1 -0
- package/dist/src/core/search-pets.js +196 -0
- package/dist/src/core/search-pets.js.map +1 -0
- package/dist/src/core/types.d.ts +63 -0
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/types.js +2 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/core/validate-pet.d.ts +40 -0
- package/dist/src/core/validate-pet.d.ts.map +1 -0
- package/dist/src/core/validate-pet.js +650 -0
- package/dist/src/core/validate-pet.js.map +1 -0
- package/package.json +7 -11
- package/ai-client-base/index.ts +0 -117
- package/build-pet.ts +0 -429
- package/cli.ts +0 -179
- package/config-manager.ts +0 -82
- package/deploy-pet.ts +0 -91
- package/index.ts +0 -10
- package/local-cache.ts +0 -280
- package/logger.ts +0 -143
- package/mcp-factory.ts +0 -180
- package/mcp-server.ts +0 -69
- package/migrate-plugin.ts +0 -220
- package/pets-registry.ts +0 -160
- package/plugin-factory.ts +0 -309
- package/prompt-utils.ts +0 -130
- package/schema-helpers.ts +0 -59
- package/search-pets.ts +0 -267
- package/types.ts +0 -68
- package/validate-pet.ts +0 -594
- /package/{logger.d.ts → dist/src/core/logger.d.ts} +0 -0
package/cli.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import { buildPet } from './build-pet.js'
|
|
4
|
-
import { deployPet } from './deploy-pet.js'
|
|
5
|
-
import { addFolderToHistory } from './config-manager.js'
|
|
6
|
-
import { spawn } from 'child_process'
|
|
7
|
-
import { resolve } from 'path'
|
|
8
|
-
import { readFileSync, existsSync } from 'fs'
|
|
9
|
-
|
|
10
|
-
const args = process.argv.slice(2)
|
|
11
|
-
const command = args[0]
|
|
12
|
-
|
|
13
|
-
const DEBUG = process.env.PETS_DEBUG === 'true'
|
|
14
|
-
|
|
15
|
-
function log(...msgs: any[]) {
|
|
16
|
-
if (DEBUG) {
|
|
17
|
-
console.log('[PETS-CLI]', ...msgs)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function displayInstalledPlugins() {
|
|
22
|
-
const opencodeJsonPath = resolve(process.cwd(), 'opencode.json')
|
|
23
|
-
|
|
24
|
-
log('Looking for opencode.json at:', opencodeJsonPath)
|
|
25
|
-
|
|
26
|
-
if (!existsSync(opencodeJsonPath)) {
|
|
27
|
-
log('opencode.json not found')
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
const content = readFileSync(opencodeJsonPath, 'utf-8')
|
|
33
|
-
log('opencode.json content length:', content.length)
|
|
34
|
-
|
|
35
|
-
const config = JSON.parse(content)
|
|
36
|
-
log('Parsed opencode.json config:', Object.keys(config))
|
|
37
|
-
|
|
38
|
-
if (config.plugin && Array.isArray(config.plugin) && config.plugin.length > 0) {
|
|
39
|
-
const plugins = config.plugin.filter((p: string) => typeof p === 'string' && p.trim())
|
|
40
|
-
log('Found plugins:', plugins)
|
|
41
|
-
|
|
42
|
-
if (plugins.length > 0) {
|
|
43
|
-
console.log('\x1b[36m%s\x1b[0m', '📦 Installed plugins:')
|
|
44
|
-
console.log('\x1b[2m%s\x1b[0m', ` ${plugins.join(', ')}`)
|
|
45
|
-
console.log('')
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
log('No plugins found in config')
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
log('Error reading opencode.json:', error)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function killPort(port: number) {
|
|
56
|
-
log(`Attempting to kill processes on port ${port}`)
|
|
57
|
-
try {
|
|
58
|
-
const { execSync } = require('child_process')
|
|
59
|
-
execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null || true`, { stdio: 'ignore' })
|
|
60
|
-
log(`Successfully killed processes on port ${port}`)
|
|
61
|
-
} catch (error) {
|
|
62
|
-
log(`No processes to kill on port ${port} or error occurred:`, error)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function launchManager() {
|
|
67
|
-
const startTime = Date.now()
|
|
68
|
-
const uiDir = resolve(__dirname, '../../apps/desktop')
|
|
69
|
-
const projectDir = process.cwd()
|
|
70
|
-
|
|
71
|
-
addFolderToHistory(projectDir)
|
|
72
|
-
|
|
73
|
-
console.log('Launching OpenPets Plugin Manager...')
|
|
74
|
-
console.log(`Project directory: ${projectDir}`)
|
|
75
|
-
console.log(`UI directory: ${uiDir}`)
|
|
76
|
-
|
|
77
|
-
log('Launch details:', {
|
|
78
|
-
__dirname,
|
|
79
|
-
uiDir,
|
|
80
|
-
projectDir,
|
|
81
|
-
uiDirExists: existsSync(uiDir),
|
|
82
|
-
timestamp: new Date().toISOString()
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
if (!existsSync(uiDir)) {
|
|
86
|
-
console.error(`❌ UI directory not found: ${uiDir}`)
|
|
87
|
-
log('Contents of parent directory:', require('fs').readdirSync(resolve(__dirname, '../../apps')))
|
|
88
|
-
process.exit(1)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
log('Checking for package.json in UI directory')
|
|
92
|
-
const packageJsonPath = resolve(uiDir, 'package.json')
|
|
93
|
-
if (!existsSync(packageJsonPath)) {
|
|
94
|
-
console.error(`❌ package.json not found in UI directory: ${packageJsonPath}`)
|
|
95
|
-
process.exit(1)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
killPort(1420)
|
|
99
|
-
|
|
100
|
-
log('Spawning npm process with tauri:dev')
|
|
101
|
-
const child = spawn('npm', ['run', 'tauri:dev'], {
|
|
102
|
-
cwd: uiDir,
|
|
103
|
-
stdio: 'inherit',
|
|
104
|
-
shell: true,
|
|
105
|
-
env: {
|
|
106
|
-
...process.env,
|
|
107
|
-
OPENPETS_PROJECT_DIR: projectDir,
|
|
108
|
-
PETS_DEBUG: DEBUG ? 'true' : 'false'
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
log('Child process spawned with PID:', child.pid)
|
|
113
|
-
|
|
114
|
-
child.on('error', (error) => {
|
|
115
|
-
console.error('Failed to launch manager:', error)
|
|
116
|
-
log('Error details:', {
|
|
117
|
-
message: error.message,
|
|
118
|
-
stack: error.stack,
|
|
119
|
-
elapsed: Date.now() - startTime
|
|
120
|
-
})
|
|
121
|
-
process.exit(1)
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
child.on('exit', (code) => {
|
|
125
|
-
const elapsed = Date.now() - startTime
|
|
126
|
-
log(`Manager process exited with code ${code} after ${elapsed}ms`)
|
|
127
|
-
if (code !== 0) {
|
|
128
|
-
console.error(`Manager exited with code ${code}`)
|
|
129
|
-
process.exit(code || 1)
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
log('=== PETS CLI STARTUP ===')
|
|
135
|
-
log('Command:', command)
|
|
136
|
-
log('Args:', args)
|
|
137
|
-
log('CWD:', process.cwd())
|
|
138
|
-
log('ENV.OPENPETS_PROJECT_DIR:', process.env.OPENPETS_PROJECT_DIR)
|
|
139
|
-
log('ENV.PETS_DEBUG:', process.env.PETS_DEBUG)
|
|
140
|
-
|
|
141
|
-
displayInstalledPlugins()
|
|
142
|
-
|
|
143
|
-
if (command === 'build') {
|
|
144
|
-
const petName = args[1]
|
|
145
|
-
log('Building pet:', petName)
|
|
146
|
-
buildPet(petName).catch(error => {
|
|
147
|
-
console.error('Error building pet:', error)
|
|
148
|
-
log('Build error details:', error)
|
|
149
|
-
process.exit(1)
|
|
150
|
-
})
|
|
151
|
-
} else if (command === 'deploy') {
|
|
152
|
-
const petName = args[1]
|
|
153
|
-
log('Deploying pet:', petName)
|
|
154
|
-
deployPet(petName).catch(error => {
|
|
155
|
-
console.error('Error deploying pet:', error)
|
|
156
|
-
log('Deploy error details:', error)
|
|
157
|
-
process.exit(1)
|
|
158
|
-
})
|
|
159
|
-
} else if (!command) {
|
|
160
|
-
log('No command provided, launching manager UI')
|
|
161
|
-
launchManager()
|
|
162
|
-
} else {
|
|
163
|
-
log('Unknown command:', command)
|
|
164
|
-
console.error('Usage: pets [command] [options]')
|
|
165
|
-
console.error('')
|
|
166
|
-
console.error('Commands:')
|
|
167
|
-
console.error(' (none) Launch the plugin manager UI (default)')
|
|
168
|
-
console.error(' build <pet-name> Build and validate a pet package')
|
|
169
|
-
console.error(' deploy <pet-name> Build and deploy a pet package with metadata')
|
|
170
|
-
console.error('')
|
|
171
|
-
console.error('Examples:')
|
|
172
|
-
console.error(' pets # Launch the desktop plugin manager')
|
|
173
|
-
console.error(' pets build postgres')
|
|
174
|
-
console.error(' pets deploy maps')
|
|
175
|
-
console.error('')
|
|
176
|
-
console.error('Environment:')
|
|
177
|
-
console.error(' PETS_DEBUG=true Enable detailed debug logging')
|
|
178
|
-
process.exit(1)
|
|
179
|
-
}
|
package/config-manager.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
2
|
-
import { join } from 'path'
|
|
3
|
-
import { homedir } from 'os'
|
|
4
|
-
|
|
5
|
-
export interface PetsConfig {
|
|
6
|
-
folderHistory: Array<{
|
|
7
|
-
path: string
|
|
8
|
-
timestamp: number
|
|
9
|
-
name?: string
|
|
10
|
-
}>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const CONFIG_DIR = join(homedir(), '.pets')
|
|
14
|
-
const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
|
|
15
|
-
const MAX_HISTORY_ITEMS = 20
|
|
16
|
-
|
|
17
|
-
function ensureConfigDir(): void {
|
|
18
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
19
|
-
mkdirSync(CONFIG_DIR, { recursive: true })
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function readConfig(): PetsConfig {
|
|
24
|
-
ensureConfigDir()
|
|
25
|
-
|
|
26
|
-
if (!existsSync(CONFIG_FILE)) {
|
|
27
|
-
return { folderHistory: [] }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const content = readFileSync(CONFIG_FILE, 'utf-8')
|
|
32
|
-
return JSON.parse(content)
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.error('Error reading config file:', error)
|
|
35
|
-
return { folderHistory: [] }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function writeConfig(config: PetsConfig): void {
|
|
40
|
-
ensureConfigDir()
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8')
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.error('Error writing config file:', error)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function addFolderToHistory(folderPath: string): void {
|
|
50
|
-
const config = readConfig()
|
|
51
|
-
|
|
52
|
-
const existingIndex = config.folderHistory.findIndex(
|
|
53
|
-
entry => entry.path === folderPath
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
if (existingIndex !== -1) {
|
|
57
|
-
config.folderHistory.splice(existingIndex, 1)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const folderName = folderPath.split('/').pop() || folderPath
|
|
61
|
-
|
|
62
|
-
config.folderHistory.unshift({
|
|
63
|
-
path: folderPath,
|
|
64
|
-
timestamp: Date.now(),
|
|
65
|
-
name: folderName
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
if (config.folderHistory.length > MAX_HISTORY_ITEMS) {
|
|
69
|
-
config.folderHistory = config.folderHistory.slice(0, MAX_HISTORY_ITEMS)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
writeConfig(config)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function getFolderHistory(): PetsConfig['folderHistory'] {
|
|
76
|
-
const config = readConfig()
|
|
77
|
-
return config.folderHistory
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function clearFolderHistory(): void {
|
|
81
|
-
writeConfig({ folderHistory: [] })
|
|
82
|
-
}
|
package/deploy-pet.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { join, dirname } from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
import { existsSync, readdirSync, readFileSync, writeFileSync, statSync, mkdirSync } from 'fs'
|
|
4
|
-
import { buildPet } from './build-pet.js'
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
7
|
-
const __dirname = dirname(__filename)
|
|
8
|
-
const projectRoot = dirname(dirname(__dirname))
|
|
9
|
-
|
|
10
|
-
interface PetData {
|
|
11
|
-
name: string
|
|
12
|
-
version: string
|
|
13
|
-
description: string
|
|
14
|
-
source_code_url: string
|
|
15
|
-
[key: string]: any
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function deployPet(petName?: string): Promise<void> {
|
|
19
|
-
if (!petName) {
|
|
20
|
-
const cwd = process.cwd()
|
|
21
|
-
const petsDir = join(projectRoot, 'pets')
|
|
22
|
-
|
|
23
|
-
if (cwd.includes('/pets/')) {
|
|
24
|
-
petName = cwd.split('/pets/')[1].split('/')[0]
|
|
25
|
-
console.log(`📦 Auto-detected pet: ${petName}`)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!petName) {
|
|
29
|
-
console.error('Usage: pets deploy <pet-name>')
|
|
30
|
-
console.error('Example: pets deploy postgres')
|
|
31
|
-
console.error('')
|
|
32
|
-
console.error('Available pets:')
|
|
33
|
-
if (existsSync(petsDir)) {
|
|
34
|
-
const pets = readdirSync(petsDir).filter(dir => {
|
|
35
|
-
const petPath = join(petsDir, dir)
|
|
36
|
-
return statSync(petPath).isDirectory() && dir !== '_TEMPLATE_'
|
|
37
|
-
})
|
|
38
|
-
pets.forEach(pet => console.error(` - ${pet}`))
|
|
39
|
-
}
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(`🚀 Deploying ${petName}...`)
|
|
45
|
-
|
|
46
|
-
await buildPet(petName)
|
|
47
|
-
|
|
48
|
-
const petDir = join(projectRoot, 'pets', petName)
|
|
49
|
-
const packageJsonPath = join(petDir, 'package.json')
|
|
50
|
-
|
|
51
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'))
|
|
52
|
-
|
|
53
|
-
const sourceCodeUrl = `https://github.com/raggle-ai/pets/tree/main/pets/${petName}`
|
|
54
|
-
|
|
55
|
-
const fieldsToExclude = ['$schema', 'version', 'main', 'types', 'scripts', 'license', 'repository', 'dependencies', 'author', 'utils', 'devDependencies']
|
|
56
|
-
|
|
57
|
-
const filteredData = Object.keys(packageJson).reduce((acc, key) => {
|
|
58
|
-
if (!fieldsToExclude.includes(key)) {
|
|
59
|
-
acc[key] = packageJson[key]
|
|
60
|
-
}
|
|
61
|
-
return acc
|
|
62
|
-
}, {} as Record<string, any>)
|
|
63
|
-
|
|
64
|
-
const petData = {
|
|
65
|
-
...filteredData,
|
|
66
|
-
name: packageJson.name,
|
|
67
|
-
version: packageJson.version,
|
|
68
|
-
description: packageJson.description,
|
|
69
|
-
source_code_url: sourceCodeUrl,
|
|
70
|
-
} as PetData
|
|
71
|
-
|
|
72
|
-
const dataDir = join(projectRoot, 'data')
|
|
73
|
-
if (!existsSync(dataDir)) {
|
|
74
|
-
mkdirSync(dataDir, { recursive: true })
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const petsJsonPath = join(dataDir, 'pets.json')
|
|
78
|
-
let petsData: Record<string, PetData> = {}
|
|
79
|
-
|
|
80
|
-
if (existsSync(petsJsonPath)) {
|
|
81
|
-
petsData = JSON.parse(readFileSync(petsJsonPath, 'utf8'))
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
petsData[packageJson.name] = petData
|
|
85
|
-
|
|
86
|
-
writeFileSync(petsJsonPath, JSON.stringify(petsData, null, 2), 'utf8')
|
|
87
|
-
|
|
88
|
-
console.log(`✅ ${petName} deployed successfully!`)
|
|
89
|
-
console.log(` Source URL: ${sourceCodeUrl}`)
|
|
90
|
-
console.log(` Data saved to: ${petsJsonPath}`)
|
|
91
|
-
}
|
package/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export * from './plugin-factory'
|
|
2
|
-
export * from './schema-helpers'
|
|
3
|
-
export * from './validate-pet'
|
|
4
|
-
export * from './pets-registry'
|
|
5
|
-
export * from './build-pet'
|
|
6
|
-
export * from './types'
|
|
7
|
-
export * from './ai-client-base'
|
|
8
|
-
export * from './search-pets'
|
|
9
|
-
export * from './logger'
|
|
10
|
-
export * from './local-cache'
|
package/local-cache.ts
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
export interface CacheEntry<T> {
|
|
2
|
-
data: T
|
|
3
|
-
timestamp: number
|
|
4
|
-
hits: number
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface CacheStats {
|
|
8
|
-
hits: number
|
|
9
|
-
misses: number
|
|
10
|
-
size: number
|
|
11
|
-
oldestEntry?: number
|
|
12
|
-
newestEntry?: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface CacheConfig {
|
|
16
|
-
ttl?: number
|
|
17
|
-
maxSize?: number
|
|
18
|
-
cleanupInterval?: number
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class LocalCache<T = any> {
|
|
22
|
-
private cache: Map<string, CacheEntry<T>> = new Map()
|
|
23
|
-
private stats = { hits: 0, misses: 0 }
|
|
24
|
-
private ttl: number
|
|
25
|
-
private maxSize: number
|
|
26
|
-
private cleanupInterval: number | null
|
|
27
|
-
private cleanupTimer: NodeJS.Timeout | null = null
|
|
28
|
-
|
|
29
|
-
constructor(config: CacheConfig = {}) {
|
|
30
|
-
this.ttl = config.ttl || 3600000
|
|
31
|
-
this.maxSize = config.maxSize || 1000
|
|
32
|
-
this.cleanupInterval = config.cleanupInterval || null
|
|
33
|
-
|
|
34
|
-
if (this.cleanupInterval) {
|
|
35
|
-
this.startCleanup()
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private startCleanup(): void {
|
|
40
|
-
if (this.cleanupInterval) {
|
|
41
|
-
this.cleanupTimer = setInterval(() => {
|
|
42
|
-
this.cleanup()
|
|
43
|
-
}, this.cleanupInterval)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
private stopCleanup(): void {
|
|
48
|
-
if (this.cleanupTimer) {
|
|
49
|
-
clearInterval(this.cleanupTimer)
|
|
50
|
-
this.cleanupTimer = null
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get(key: string): T | null {
|
|
55
|
-
const entry = this.cache.get(key)
|
|
56
|
-
|
|
57
|
-
if (!entry) {
|
|
58
|
-
this.stats.misses++
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const now = Date.now()
|
|
63
|
-
if (now - entry.timestamp > this.ttl) {
|
|
64
|
-
this.cache.delete(key)
|
|
65
|
-
this.stats.misses++
|
|
66
|
-
return null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
entry.hits++
|
|
70
|
-
this.stats.hits++
|
|
71
|
-
return entry.data
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
set(key: string, data: T): void {
|
|
75
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
76
|
-
this.evictOldest()
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.cache.set(key, {
|
|
80
|
-
data,
|
|
81
|
-
timestamp: Date.now(),
|
|
82
|
-
hits: 0
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
has(key: string): boolean {
|
|
87
|
-
const entry = this.cache.get(key)
|
|
88
|
-
if (!entry) return false
|
|
89
|
-
|
|
90
|
-
const now = Date.now()
|
|
91
|
-
if (now - entry.timestamp > this.ttl) {
|
|
92
|
-
this.cache.delete(key)
|
|
93
|
-
return false
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return true
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
delete(key: string): boolean {
|
|
100
|
-
return this.cache.delete(key)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
clear(): void {
|
|
104
|
-
this.cache.clear()
|
|
105
|
-
this.stats = { hits: 0, misses: 0 }
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
cleanup(): number {
|
|
109
|
-
const now = Date.now()
|
|
110
|
-
let removed = 0
|
|
111
|
-
|
|
112
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
113
|
-
if (now - entry.timestamp > this.ttl) {
|
|
114
|
-
this.cache.delete(key)
|
|
115
|
-
removed++
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return removed
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private evictOldest(): void {
|
|
123
|
-
let oldestKey: string | null = null
|
|
124
|
-
let oldestTime = Infinity
|
|
125
|
-
|
|
126
|
-
for (const [key, entry] of this.cache.entries()) {
|
|
127
|
-
if (entry.timestamp < oldestTime) {
|
|
128
|
-
oldestTime = entry.timestamp
|
|
129
|
-
oldestKey = key
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (oldestKey) {
|
|
134
|
-
this.cache.delete(oldestKey)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
getStats(): CacheStats {
|
|
139
|
-
const entries = Array.from(this.cache.values())
|
|
140
|
-
const timestamps = entries.map(e => e.timestamp)
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
hits: this.stats.hits,
|
|
144
|
-
misses: this.stats.misses,
|
|
145
|
-
size: this.cache.size,
|
|
146
|
-
oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined,
|
|
147
|
-
newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
keys(): string[] {
|
|
152
|
-
return Array.from(this.cache.keys())
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
values(): T[] {
|
|
156
|
-
return Array.from(this.cache.values()).map(entry => entry.data)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
entries(): Array<[string, T]> {
|
|
160
|
-
return Array.from(this.cache.entries()).map(([key, entry]) => [key, entry.data])
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
getHitRate(): number {
|
|
164
|
-
const total = this.stats.hits + this.stats.misses
|
|
165
|
-
return total === 0 ? 0 : this.stats.hits / total
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
destroy(): void {
|
|
169
|
-
this.stopCleanup()
|
|
170
|
-
this.clear()
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export class SingleValueCache<T = any> {
|
|
175
|
-
private data: T | null = null
|
|
176
|
-
private timestamp: number = 0
|
|
177
|
-
private ttl: number
|
|
178
|
-
private hits: number = 0
|
|
179
|
-
private misses: number = 0
|
|
180
|
-
|
|
181
|
-
constructor(config: { ttl?: number } = {}) {
|
|
182
|
-
this.ttl = config.ttl || 3600000
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
get(): T | null {
|
|
186
|
-
if (!this.data) {
|
|
187
|
-
this.misses++
|
|
188
|
-
return null
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const now = Date.now()
|
|
192
|
-
if (now - this.timestamp > this.ttl) {
|
|
193
|
-
this.data = null
|
|
194
|
-
this.misses++
|
|
195
|
-
return null
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
this.hits++
|
|
199
|
-
return this.data
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
set(data: T): void {
|
|
203
|
-
this.data = data
|
|
204
|
-
this.timestamp = Date.now()
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
has(): boolean {
|
|
208
|
-
if (!this.data) return false
|
|
209
|
-
|
|
210
|
-
const now = Date.now()
|
|
211
|
-
if (now - this.timestamp > this.ttl) {
|
|
212
|
-
this.data = null
|
|
213
|
-
return false
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return true
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
clear(): void {
|
|
220
|
-
this.data = null
|
|
221
|
-
this.timestamp = 0
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
getAge(): number {
|
|
225
|
-
if (!this.data) return -1
|
|
226
|
-
return Date.now() - this.timestamp
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
getStats(): { hits: number; misses: number; hasData: boolean; age: number } {
|
|
230
|
-
return {
|
|
231
|
-
hits: this.hits,
|
|
232
|
-
misses: this.misses,
|
|
233
|
-
hasData: this.has(),
|
|
234
|
-
age: this.getAge()
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
getHitRate(): number {
|
|
239
|
-
const total = this.hits + this.misses
|
|
240
|
-
return total === 0 ? 0 : this.hits / total
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export async function withCache<T>(
|
|
245
|
-
cache: LocalCache<T> | SingleValueCache<T>,
|
|
246
|
-
key: string | null,
|
|
247
|
-
fetcher: () => Promise<T>
|
|
248
|
-
): Promise<T> {
|
|
249
|
-
if (cache instanceof SingleValueCache) {
|
|
250
|
-
const cached = cache.get()
|
|
251
|
-
if (cached !== null) {
|
|
252
|
-
return cached
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const data = await fetcher()
|
|
256
|
-
cache.set(data)
|
|
257
|
-
return data
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (key === null) {
|
|
261
|
-
throw new Error('Key is required for LocalCache')
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const cached = cache.get(key)
|
|
265
|
-
if (cached !== null) {
|
|
266
|
-
return cached
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const data = await fetcher()
|
|
270
|
-
cache.set(key, data)
|
|
271
|
-
return data
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export function createCache<T = any>(config: CacheConfig = {}): LocalCache<T> {
|
|
275
|
-
return new LocalCache<T>(config)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export function createSingleValueCache<T = any>(config: { ttl?: number } = {}): SingleValueCache<T> {
|
|
279
|
-
return new SingleValueCache<T>(config)
|
|
280
|
-
}
|