From 2bae07d1f56aedcee75d8b02cd1e3eefe1e568d7 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sat, 28 Mar 2026 23:23:12 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20fn=20run=20=E2=80=94=20ejecuci=C3=B3n?= =?UTF-8?q?=20multi-lenguaje=20de=20funciones=20y=20pipelines=20desde=20CL?= =?UTF-8?q?I?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nuevo comando que despacha automáticamente según lenguaje: Go pipelines con go run, Go functions con go test/vet, Python con venv y -m para imports relativos, Bash directo, TypeScript con tsx del frontend. Resolución por nombre con desambiguación. Añadido GetFunctionsByName al store y tsx al frontend. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/fn/main.go | 3 + cmd/fn/run.go | 208 ++++++++++++++++++++++++++ frontend/package.json | 1 + frontend/pnpm-lock.yaml | 313 +++++++++++++++++++++++++++++++++++++++- registry/store.go | 10 ++ 5 files changed, 527 insertions(+), 8 deletions(-) create mode 100644 cmd/fn/run.go diff --git a/cmd/fn/main.go b/cmd/fn/main.go index cfa6a068..40507c40 100644 --- a/cmd/fn/main.go +++ b/cmd/fn/main.go @@ -33,6 +33,8 @@ func main() { cmdOps(os.Args[2:]) case "proposal": cmdProposal(os.Args[2:]) + case "run": + cmdRun(os.Args[2:]) case "help", "-h", "--help": printUsage() default: @@ -51,6 +53,7 @@ Usage: fn list [-k kind] [-d domain] [-l lang] fn show Muestra entrada completa fn add [-k kind] Abre $EDITOR con template + fn run [args...] Ejecuta funcion/pipeline (go/py/bash) fn ops Gestiona operations.db (fn ops help) fn proposal Gestiona proposals`) } diff --git a/cmd/fn/run.go b/cmd/fn/run.go new file mode 100644 index 00000000..6e6c4b4b --- /dev/null +++ b/cmd/fn/run.go @@ -0,0 +1,208 @@ +package main + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "fn-registry/registry" +) + +func cmdRun(args []string) { + if len(args) == 0 { + fmt.Fprintln(os.Stderr, "usage: fn run [args...]") + os.Exit(1) + } + + idOrName := args[0] + passArgs := args[1:] + + registryRoot := root() + dbPath := filepath.Join(registryRoot, dbName) + + db, err := registry.Open(dbPath) + if err != nil { + fmt.Fprintf(os.Stderr, "error: cannot open registry: %v\n", err) + os.Exit(1) + } + defer db.Close() + + fn, err := resolveFunction(db, idOrName) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + if fn.FilePath == "" { + fmt.Fprintf(os.Stderr, "error: %s has no file_path in registry\n", fn.ID) + os.Exit(1) + } + + absPath := filepath.Join(registryRoot, fn.FilePath) + if _, err := os.Stat(absPath); os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "error: file not found: %s\n", absPath) + os.Exit(1) + } + + cmd, err := buildCommand(fn, registryRoot, absPath, passArgs) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + fmt.Fprintf(os.Stderr, "[fn run] %s (%s/%s) %s\n", fn.ID, fn.Lang, fn.Kind, strings.Join(passArgs, " ")) + + if err := cmd.Run(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + os.Exit(exitErr.ExitCode()) + } + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } +} + +func resolveFunction(db *registry.DB, idOrName string) (*registry.Function, error) { + fn, err := db.GetFunction(idOrName) + if err == nil { + return fn, nil + } + + fns, err := db.GetFunctionsByName(idOrName) + if err != nil { + return nil, fmt.Errorf("lookup failed: %w", err) + } + if len(fns) == 0 { + return nil, fmt.Errorf("function %q not found (tried as ID and name)", idOrName) + } + if len(fns) == 1 { + return &fns[0], nil + } + + var b strings.Builder + fmt.Fprintf(&b, "ambiguous name %q — found %d matches:\n", idOrName, len(fns)) + for _, f := range fns { + fmt.Fprintf(&b, " %s (%s/%s)\n", f.ID, f.Lang, f.Kind) + } + fmt.Fprintf(&b, "use the full ID to disambiguate") + return nil, fmt.Errorf("%s", b.String()) +} + +func buildCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) { + switch fn.Lang { + case "go": + return buildGoCommand(fn, registryRoot, absPath, args) + case "py": + return buildPyCommand(registryRoot, absPath, args) + case "bash": + return buildBashCommand(absPath, args) + case "ts": + return buildTsCommand(registryRoot, absPath, args) + default: + return nil, fmt.Errorf("unsupported lang %q for execution", fn.Lang) + } +} + +func buildGoCommand(fn *registry.Function, registryRoot, absPath string, args []string) (*exec.Cmd, error) { + dir := filepath.Dir(absPath) + env := append(os.Environ(), "CGO_ENABLED=1") + + // If directory has main.go → go run . (pipelines and standalone executables) + mainGo := filepath.Join(dir, "main.go") + if _, err := os.Stat(mainGo); err == nil { + cmdArgs := append([]string{"run", "."}, args...) + cmd := exec.Command("go", cmdArgs...) + cmd.Dir = dir + cmd.Env = env + return cmd, nil + } + + // Library code: if it has tests → go test + if fn.Tested && fn.TestFilePath != "" { + testAbs := filepath.Join(registryRoot, fn.TestFilePath) + if _, err := os.Stat(testAbs); err == nil { + relPkg, _ := filepath.Rel(registryRoot, dir) + pkgPath := "./" + filepath.ToSlash(relPkg) + cmdArgs := append([]string{"test", "-v", "-count=1", "-tags", "fts5", pkgPath}, args...) + cmd := exec.Command("go", cmdArgs...) + cmd.Dir = registryRoot + cmd.Env = env + return cmd, nil + } + } + + // No tests: go vet (compilation check) + relPkg, _ := filepath.Rel(registryRoot, dir) + pkgPath := "./" + filepath.ToSlash(relPkg) + cmdArgs := []string{"vet", "-tags", "fts5", pkgPath} + cmd := exec.Command("go", cmdArgs...) + cmd.Dir = registryRoot + cmd.Env = env + fmt.Fprintf(os.Stderr, "[fn run] %s is library code without tests — running go vet\n", fn.ID) + return cmd, nil +} + +func buildPyCommand(registryRoot, absPath string, args []string) (*exec.Cmd, error) { + venvPython := filepath.Join(registryRoot, "python", ".venv", "bin", "python3") + pythonBin := "python3" + if _, err := os.Stat(venvPython); err == nil { + pythonBin = venvPython + } + + dir := filepath.Dir(absPath) + + // If the file is inside a package (has __init__.py), use python -m + // so relative imports work. PYTHONPATH points to python/functions/ or + // the equivalent parent that contains the domain packages. + initPy := filepath.Join(dir, "__init__.py") + if _, err := os.Stat(initPy); err == nil { + // The pythonPath is the well-known python/functions/ directory + // which contains domain packages (metabase/, etc.) + pythonPath := filepath.Join(registryRoot, "python", "functions") + if _, err := os.Stat(pythonPath); os.IsNotExist(err) { + // Fallback: walk up from dir to find the parent of the top package + pythonPath = filepath.Dir(dir) + } + + // Build module path: metabase/databases.py → metabase.databases + relToRoot, _ := filepath.Rel(pythonPath, absPath) + modPath := strings.TrimSuffix(relToRoot, ".py") + modPath = strings.ReplaceAll(filepath.ToSlash(modPath), "/", ".") + + cmdArgs := append([]string{"-m", modPath}, args...) + cmd := exec.Command(pythonBin, cmdArgs...) + cmd.Dir = pythonPath + cmd.Env = append(os.Environ(), "PYTHONPATH="+pythonPath) + return cmd, nil + } + + // Standalone script (no __init__.py) + cmdArgs := append([]string{absPath}, args...) + cmd := exec.Command(pythonBin, cmdArgs...) + cmd.Dir = dir + return cmd, nil +} + +func buildBashCommand(absPath string, args []string) (*exec.Cmd, error) { + cmdArgs := append([]string{absPath}, args...) + cmd := exec.Command("bash", cmdArgs...) + cmd.Dir = filepath.Dir(absPath) + return cmd, nil +} + +func buildTsCommand(registryRoot, absPath string, args []string) (*exec.Cmd, error) { + tsxBin := filepath.Join(registryRoot, "frontend", "node_modules", ".bin", "tsx") + if _, err := os.Stat(tsxBin); os.IsNotExist(err) { + return nil, fmt.Errorf("tsx not found — run: cd frontend && pnpm add -D tsx") + } + + cmdArgs := append([]string{absPath}, args...) + cmd := exec.Command(tsxBin, cmdArgs...) + cmd.Dir = filepath.Dir(absPath) + return cmd, nil +} diff --git a/frontend/package.json b/frontend/package.json index 0de59957..8c89cd00 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "tailwindcss": "^4.2.2", + "tsx": "^4.21.0", "typescript": "^6.0.2", "vite": "^8.0.3" } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index bdb9c5eb..133ea1d6 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -41,7 +41,7 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.2.2 - version: 4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1)) + version: 4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -50,16 +50,19 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1)) + version: 6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0)) tailwindcss: specifier: ^4.2.2 version: 4.2.2 + tsx: + specifier: ^4.21.0 + version: 4.21.0 typescript: specifier: ^6.0.2 version: 6.0.2 vite: specifier: ^8.0.3 - version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1) + version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0) packages: @@ -236,6 +239,162 @@ packages: '@emnapi/wasi-threads@1.2.0': resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@esbuild/aix-ppc64@0.27.4': + resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.4': + resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.4': + resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.4': + resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.4': + resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.4': + resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.4': + resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.4': + resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.4': + resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.4': + resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.4': + resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.4': + resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.4': + resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.4': + resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.4': + resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.4': + resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.4': + resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.4': + resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.4': + resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.4': + resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.4': + resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.4': + resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.4': + resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.4': + resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.4': + resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.4': + resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -872,6 +1031,11 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + esbuild@0.27.4: + resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1011,6 +1175,9 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -1539,6 +1706,9 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -1729,6 +1899,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.21.0: + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} + hasBin: true + tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} @@ -2131,6 +2306,84 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.27.4': + optional: true + + '@esbuild/android-arm64@0.27.4': + optional: true + + '@esbuild/android-arm@0.27.4': + optional: true + + '@esbuild/android-x64@0.27.4': + optional: true + + '@esbuild/darwin-arm64@0.27.4': + optional: true + + '@esbuild/darwin-x64@0.27.4': + optional: true + + '@esbuild/freebsd-arm64@0.27.4': + optional: true + + '@esbuild/freebsd-x64@0.27.4': + optional: true + + '@esbuild/linux-arm64@0.27.4': + optional: true + + '@esbuild/linux-arm@0.27.4': + optional: true + + '@esbuild/linux-ia32@0.27.4': + optional: true + + '@esbuild/linux-loong64@0.27.4': + optional: true + + '@esbuild/linux-mips64el@0.27.4': + optional: true + + '@esbuild/linux-ppc64@0.27.4': + optional: true + + '@esbuild/linux-riscv64@0.27.4': + optional: true + + '@esbuild/linux-s390x@0.27.4': + optional: true + + '@esbuild/linux-x64@0.27.4': + optional: true + + '@esbuild/netbsd-arm64@0.27.4': + optional: true + + '@esbuild/netbsd-x64@0.27.4': + optional: true + + '@esbuild/openbsd-arm64@0.27.4': + optional: true + + '@esbuild/openbsd-x64@0.27.4': + optional: true + + '@esbuild/openharmony-arm64@0.27.4': + optional: true + + '@esbuild/sunos-x64@0.27.4': + optional: true + + '@esbuild/win32-arm64@0.27.4': + optional: true + + '@esbuild/win32-ia32@0.27.4': + optional: true + + '@esbuild/win32-x64@0.27.4': + optional: true + '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -2383,12 +2636,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1))': + '@tailwindcss/vite@4.2.2(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1) + vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0) '@ts-morph/common@0.27.0': dependencies: @@ -2413,10 +2666,10 @@ snapshots: '@types/validate-npm-package-name@4.0.2': {} - '@vitejs/plugin-react@6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1))': + '@vitejs/plugin-react@6.0.1(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1) + vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0) accepts@2.0.0: dependencies: @@ -2643,6 +2896,35 @@ snapshots: dependencies: es-errors: 1.3.0 + esbuild@0.27.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.4 + '@esbuild/android-arm': 0.27.4 + '@esbuild/android-arm64': 0.27.4 + '@esbuild/android-x64': 0.27.4 + '@esbuild/darwin-arm64': 0.27.4 + '@esbuild/darwin-x64': 0.27.4 + '@esbuild/freebsd-arm64': 0.27.4 + '@esbuild/freebsd-x64': 0.27.4 + '@esbuild/linux-arm': 0.27.4 + '@esbuild/linux-arm64': 0.27.4 + '@esbuild/linux-ia32': 0.27.4 + '@esbuild/linux-loong64': 0.27.4 + '@esbuild/linux-mips64el': 0.27.4 + '@esbuild/linux-ppc64': 0.27.4 + '@esbuild/linux-riscv64': 0.27.4 + '@esbuild/linux-s390x': 0.27.4 + '@esbuild/linux-x64': 0.27.4 + '@esbuild/netbsd-arm64': 0.27.4 + '@esbuild/netbsd-x64': 0.27.4 + '@esbuild/openbsd-arm64': 0.27.4 + '@esbuild/openbsd-x64': 0.27.4 + '@esbuild/openharmony-arm64': 0.27.4 + '@esbuild/sunos-x64': 0.27.4 + '@esbuild/win32-arm64': 0.27.4 + '@esbuild/win32-ia32': 0.27.4 + '@esbuild/win32-x64': 0.27.4 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -2820,6 +3102,10 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -3248,6 +3534,8 @@ snapshots: resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -3501,6 +3789,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.21.0: + dependencies: + esbuild: 0.27.4 + get-tsconfig: 4.13.7 + optionalDependencies: + fsevents: 2.3.3 + tw-animate-css@1.4.0: {} type-fest@5.5.0: @@ -3539,7 +3834,7 @@ snapshots: vary@1.1.2: {} - vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(jiti@2.6.1): + vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(esbuild@0.27.4)(jiti@2.6.1)(tsx@4.21.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -3547,8 +3842,10 @@ snapshots: rolldown: 1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) tinyglobby: 0.2.15 optionalDependencies: + esbuild: 0.27.4 fsevents: 2.3.3 jiti: 2.6.1 + tsx: 4.21.0 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' diff --git a/registry/store.go b/registry/store.go index dd063fc2..8d24a8ee 100644 --- a/registry/store.go +++ b/registry/store.go @@ -239,6 +239,16 @@ func (db *DB) GetType(id string) (*Type, error) { return &ts[0], nil } +// GetFunctionsByName returns all functions matching a given name (across langs/domains). +func (db *DB) GetFunctionsByName(name string) ([]Function, error) { + rows, err := db.conn.Query("SELECT * FROM functions WHERE name = ? ORDER BY lang, domain", name) + if err != nil { + return nil, err + } + defer rows.Close() + return scanFunctions(rows) +} + // DeleteFunction removes a function by ID. func (db *DB) DeleteFunction(id string) error { _, err := db.conn.Exec("DELETE FROM functions WHERE id = ?", id)