sistemato ts generator

This commit is contained in:
fabio 2026-05-04 16:10:49 +02:00
parent f35fcbc875
commit 3b5c39ffc0
12 changed files with 522 additions and 465 deletions

Binary file not shown.

View File

@ -60,20 +60,17 @@ const (
GuestPermission = 0b0000000000000001
)
type AllRoles struct {
Roles map[string]string `json:"roles"`
}
// Typescript: type
type AllRoles map[string]string
func GetRoles(c fiber.Ctx) error {
a := AllRoles{
Roles: make(map[string]string),
}
a.Roles["RoleSuperAdmin"] = "superadmin"
a.Roles["RoleAdmin"] = "admin"
a.Roles["RoleManager"] = "manager"
a.Roles["RoleContentCreator"] = "content_creator"
a.Roles["RoleUser"] = "user"
a.Roles["RoleGuest"] = "guest"
a := make(AllRoles)
a["RoleSuperAdmin"] = "superadmin"
a["RoleAdmin"] = "admin"
a["RoleManager"] = "manager"
a["RoleContentCreator"] = "content_creator"
a["RoleUser"] = "user"
a["RoleGuest"] = "guest"
return c.JSON(responses.Success(a))
}

View File

@ -11,6 +11,7 @@ import (
"io"
"log"
"os"
"slices"
"strings"
"golang.org/x/tools/go/packages"
@ -79,6 +80,285 @@ type TSSourceFile struct {
Len int
}
func (i *TSInfo) Populate(path string, excludedPackages ExcudedPackages) {
i.Packages = make(map[string]TSInfoPakage)
cfg := &packages.Config{
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedSyntax,
Dir: path,
}
pkgs, err := packages.Load(cfg, "./...")
if err != nil {
log.Fatal(err)
}
if packages.PrintErrors(pkgs) > 0 {
log.Fatal("package loading failed")
}
//to get line info for endpoints and typescript declarations
for _, loadedPkg := range pkgs {
pkg := loadedPkg.Name
// skip packages
skip := false
for _, excluded := range excludedPackages {
if pkg == excluded {
skip = true
break
}
}
if skip {
continue
}
// initialize package info if not exists
if _, ok := i.Packages[pkg]; !ok {
ip := TSInfoPakage{}
ip.MakeMaps()
i.Packages[pkg] = ip
}
// start parsing files
for _, n := range loadedPkg.CompiledGoFiles {
// search for declarative // Typescript: declarations and endpoints
i.parseTypescriptDeclarations(pkg, n)
}
// parse doc to get all types and consts
docPkg, err := doc.NewFromFiles(loadedPkg.Fset, loadedPkg.Syntax, loadedPkg.PkgPath)
if err != nil {
exitOnError(err)
}
for _, t := range docPkg.Types {
i.getType(pkg, t)
}
for _, c := range docPkg.Consts {
i.getConst(pkg, c)
}
}
}
// estrae le definizioni typescript // Typescript: ...
func (ts *TSInfo) parseTypescriptDeclarations(p string, n string) {
pkg := ts.Packages[p]
f, err := os.OpenFile(n, os.O_RDONLY, os.ModePerm)
if err != nil {
log.Fatalf("open file error: %v", err)
return
}
defer f.Close()
l := 1
dat := ""
lines := []TSSourceLine{}
rd := bufio.NewReader(f)
// scansione del file per trovare le dichiarazioni typescript
for {
line, err := rd.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("read file line error: %v", err)
return
}
lines = append(lines, TSSourceLine{Pos: l, End: l + len(line), Line: l, Source: line})
l++
dat += line
// è una definizione typescript?
if strings.Contains(line, "// Typescript:") {
// typescript type, ad esempio: // Typescript: TStype=MyType=string | number
if strings.Contains(line, "TStype=") {
p := strings.Index(line, "TStype=")
s := strings.Trim(line[p+len("TStype="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSType{
Name: strings.Trim(a[0], " "),
Typescript: true,
Type: "UserDefined",
TsType: strings.Trim(a[1], " "),
dependOn: false,
}
pkg.types[strings.Trim(a[0], " ")] = t
}
}
// dicchiarazione typescript, ad esempio: // Typescript: TSDeclaration=MyType=string | number
if strings.Contains(line, "TSDeclaration=") {
p := strings.Index(line, "TSDeclaration=")
s := strings.Trim(line[p+len("TSDeclaration="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSDec{
Name: strings.Trim(a[0], " "),
Value: strings.Trim(a[1], " "),
}
pkg.decs[strings.Trim(a[0], " ")] = t
}
}
// dichiarazione endpoint, ad esempio: // Typescript: TSEndpoint= path=/api/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
if strings.Contains(line, "TSEndpoint= ") {
e := ParseEndpoint(line, n, l)
if _, ok := pkg.endpoints[e.Name]; ok {
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, line))
}
pkg_info := pkg
pkg_info.endpoints[e.Name] = e
pkg_info.imports = e.Imports
pkg_info.isUsed = true
pkg = pkg_info
}
}
}
}
func (i *TSInfo) getType(p string, t *doc.Type) {
var isTypescript = strings.HasPrefix(t.Doc, "Typescript:")
if isTypescript {
command := strings.TrimPrefix(t.Doc, "Typescript:")
command = strings.TrimSpace(command)
command = strings.Trim(command, "\n")
}
for _, spec := range t.Decl.Specs {
if len(t.Consts) > 0 {
i.getConst(p, t.Consts[0])
}
switch spec.(type) {
case *ast.TypeSpec:
typeSpec := spec.(*ast.TypeSpec)
switch typeSpec.Type.(type) {
case *ast.StructType:
v := TSStruct{
Name: typeSpec.Name.Name,
Typescript: false,
Fields: []TSSField{},
}
v.getStruct(typeSpec)
if len(v.Fields) == 0 {
continue
}
i.Packages[p].structs[typeSpec.Name.Name] = v
for _, imp := range v.Imports {
a := strings.Split(imp, ".")
if len(a) == 2 {
if _, ok := i.Packages[p].imports[a[0]]; !ok {
i.Packages[p].imports[a[0]] = []string{}
}
if !slices.Contains(i.Packages[p].imports[a[0]], a[1]) {
i.Packages[p].imports[a[0]] = append(i.Packages[p].imports[a[0]], a[1])
}
}
}
case *ast.Ident:
tsInfo := getFieldTsInfo(typeSpec.Type)
t := TSType{
Name: typeSpec.Name.Name,
Typescript: true,
Type: getFieldInfo(typeSpec.Type),
TsType: tsInfo,
dependOn: toBeImported(typeSpec.Type),
}
//fmt.Printf("Type found: %s Type: %s TsType: %s AT: %s\n", t.Name, t.Type, t.TsType, t.SourceInfo)
i.Packages[p].types[typeSpec.Name.Name] = t
default:
// if isType && command != "interface" {
// exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
// }
tsInfo := getFieldTsInfo(typeSpec.Type)
t := TSType{
Name: typeSpec.Name.Name,
Typescript: true,
Type: getFieldInfo(typeSpec.Type),
TsType: tsInfo,
dependOn: toBeImported(typeSpec.Type),
}
//fmt.Printf("Type found: %s Type: %s TsType: %s AT: %s\n", t.Name, t.Type, t.TsType, t.SourceInfo)
i.Packages[p].types[typeSpec.Name.Name] = t
}
}
}
}
func (i *TSInfo) getConst(p string, c *doc.Value) {
var isTypescript = strings.HasPrefix(c.Doc, "Typescript:")
if isTypescript {
command := strings.TrimPrefix(c.Doc, "Typescript:")
command = strings.TrimSpace(command)
command = strings.Trim(command, "\n")
if strings.Contains(command, "enum=") {
enumName := strings.TrimPrefix(command, "enum=")
enum := TSEnum{
Name: enumName,
Info: []TSEnumInfo{},
}
d := c.Decl
iota := false
iotaValue := 0
for _, s := range d.Specs {
v := s.(*ast.ValueSpec) // safe because decl.Tok == token.CONST
if len(v.Values) > 0 {
be, ok := v.Values[0].(*ast.BinaryExpr)
if ok {
x := be.X.(*ast.BasicLit)
exitOnError(fmt.Errorf("enum binary expression not implemented %s %s %s", x.Value, be.Op.String(), be.Y))
}
ident, ok := v.Values[0].(*ast.Ident)
if ok {
if ident.Name == "iota" {
iota = true
iotaValue = v.Names[0].Obj.Data.(int)
enum.Info = append(enum.Info, TSEnumInfo{Key: v.Names[0].Name, Value: fmt.Sprintf("%d", iotaValue)})
}
}
list, ok := v.Values[0].(*ast.BasicLit)
if ok {
enum.Info = append(enum.Info, TSEnumInfo{Key: v.Names[0].Name, Value: list.Value})
}
} else {
for _, name := range v.Names {
if iota {
iotaValue++
enum.Info = append(enum.Info, TSEnumInfo{Key: name.Name, Value: fmt.Sprintf("%d", iotaValue)})
}
}
}
}
i.Packages[p].enums[enumName] = enum
t1 := TSType{
Name: enumName,
Typescript: true,
Type: "",
TsType: fmt.Sprintf("typeof Enum%s[keyof typeof Enum%s] ", enumName, enumName), //getFieldTsInfo(expr.Type),
dependOn: false,
}
i.Packages[p].types[enumName] = t1
}
if strings.Contains(command, "const") {
d := c.Decl
for _, s := range d.Specs {
v := s.(*ast.ValueSpec)
if len(v.Names) == 0 || len(v.Values) == 0 {
continue
}
c := TSConst{
Name: v.Names[0].Name,
Value: v.Values[0].(*ast.BasicLit).Value,
}
i.Packages[p].consts[c.Name] = c
}
}
if strings.Contains(command, "type") {
fmt.Printf("TypeScript type declaration found in const s, but type parsing is not implemented yet\n")
}
}
}
func (ts *TSInfo) findStruct(p string, n string) bool {
if _, ok := ts.Packages[p]; ok {
@ -127,269 +407,6 @@ func (ts TSInfo) find(p string, n string) bool {
// popola TsInfo con tutte le definizioni dei tipi
func (i *TSInfo) getConst(p string, c *doc.Value) {
var isTypescript = strings.HasPrefix(c.Doc, "Typescript:")
if isTypescript {
command := strings.TrimPrefix(c.Doc, "Typescript:")
command = strings.TrimSpace(command)
command = strings.Trim(command, "\n")
if strings.Contains(command, "type") {
fmt.Printf("TypeScript type declaration found in const s, but type parsing is not implemented yet\n")
}
if strings.Contains(command, "enum=") {
enumName := strings.TrimPrefix(command, "enum=")
enum := TSEnum{
Name: enumName,
Info: []TSEnumInfo{},
}
d := c.Decl
iota := false
iotaValue := 0
for _, s := range d.Specs {
v := s.(*ast.ValueSpec) // safe because decl.Tok == token.CONST
if len(v.Values) > 0 {
be, ok := v.Values[0].(*ast.BinaryExpr)
if ok {
x := be.X.(*ast.BasicLit)
exitOnError(fmt.Errorf("enum binary expression not implemented %s %s %s", x.Value, be.Op.String(), be.Y))
}
ident, ok := v.Values[0].(*ast.Ident)
if ok {
if ident.Name == "iota" {
iota = true
iotaValue = v.Names[0].Obj.Data.(int)
enum.Info = append(enum.Info, TSEnumInfo{Key: v.Names[0].Name, Value: fmt.Sprintf("%d", iotaValue)})
}
}
list, ok := v.Values[0].(*ast.BasicLit)
if ok {
enum.Info = append(enum.Info, TSEnumInfo{Key: v.Names[0].Name, Value: list.Value})
}
} else {
for _, name := range v.Names {
if iota {
iotaValue++
enum.Info = append(enum.Info, TSEnumInfo{Key: name.Name, Value: fmt.Sprintf("%d", iotaValue)})
}
}
}
}
i.Packages[p].enums[enumName] = enum
t1 := TSType{
Name: enumName,
Typescript: false,
Type: "",
TsType: fmt.Sprintf("typeof Enum%s[keyof typeof Enum%s] ", enumName, enumName), //getFieldTsInfo(expr.Type),
dependOn: false,
}
i.Packages[p].types[enumName] = t1
}
if strings.Contains(command, "const") {
d := c.Decl
for _, s := range d.Specs {
v := s.(*ast.ValueSpec)
if len(v.Names) == 0 || len(v.Values) == 0 {
continue
}
c := TSConst{
Name: v.Names[0].Name,
Value: v.Values[0].(*ast.BasicLit).Value,
}
i.Packages[p].consts[c.Name] = c
}
}
if strings.Contains(command, "type") {
fmt.Printf("TypeScript type declaration found in const s, but type parsing is not implemented yet\n")
}
}
}
func (i *TSInfo) getType(p string, t *doc.Type) {
var isTypescript = strings.HasPrefix(t.Doc, "Typescript:")
if isTypescript {
command := strings.TrimPrefix(t.Doc, "Typescript:")
command = strings.TrimSpace(command)
command = strings.Trim(command, "\n")
if strings.Contains(command, "interface") {
fmt.Printf("TypeScript interface declaration found in type s, but interface parsing is not implemented yet\n")
}
if strings.Contains(command, "type") {
fmt.Printf("TypeScript type declaration found in type s, but type parsing is not implemented yet\n")
}
if strings.Contains(command, "enum=") {
fmt.Printf("TypeScript enum declaration found in type s, but enum parsing is not implemented yet\n")
}
}
for _, spec := range t.Decl.Specs {
/* if len(t.Consts) > 0 {
i.getConst(p, t.Consts[0])
continue
} */
switch spec.(type) {
case *ast.TypeSpec:
typeSpec := spec.(*ast.TypeSpec)
switch typeSpec.Type.(type) {
case *ast.StructType:
// if isType && command != "interface" {
// exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
// }
v := TSStruct{
Name: typeSpec.Name.Name,
Typescript: false,
Fields: []TSSField{},
SourceInfo: "",
}
v.getStruct(typeSpec)
i.Packages[p].structs[typeSpec.Name.Name] = v
default:
// if isType && command != "interface" {
// exitOnError(fmt.Errorf("mismatch delaration for interface %s AT: %s", t.Doc, getSourceInfo(int(typeSpec.Name.NamePos), src)))
// }
tsInfo := getFieldTsInfo(typeSpec.Type)
t := TSType{
Name: typeSpec.Name.Name,
Typescript: false,
Type: getFieldInfo(typeSpec.Type),
TsType: tsInfo,
dependOn: toBeImported(typeSpec.Type),
}
//fmt.Printf("Type found: %s Type: %s TsType: %s AT: %s\n", t.Name, t.Type, t.TsType, t.SourceInfo)
i.Packages[p].types[typeSpec.Name.Name] = t
}
}
}
}
func parseTypescriptDeclarations(n string, pkg TSInfoPakage) {
f, err := os.OpenFile(n, os.O_RDONLY, os.ModePerm)
if err != nil {
log.Fatalf("open file error: %v", err)
return
}
defer f.Close()
l := 1
dat := ""
lines := []TSSourceLine{}
rd := bufio.NewReader(f)
for {
line, err := rd.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
log.Fatalf("read file line error: %v", err)
return
}
lines = append(lines, TSSourceLine{Pos: l, End: l + len(line), Line: l, Source: line})
l++
dat += line
if strings.Contains(line, "// Typescript:") {
if strings.Contains(line, "TStype=") {
p := strings.Index(line, "TStype=")
s := strings.Trim(line[p+len("TStype="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSType{
Name: strings.Trim(a[0], " "),
Typescript: true,
Type: "UserDefined",
TsType: strings.Trim(a[1], " "),
dependOn: false,
}
pkg.types[strings.Trim(a[0], " ")] = t
}
}
if strings.Contains(line, "TSDeclaration=") {
p := strings.Index(line, "TSDeclaration=")
s := strings.Trim(line[p+len("TSDeclaration="):], " ")
a := strings.Split(s, "=")
if len(a) == 2 {
t := TSDec{
Name: strings.Trim(a[0], " "),
Value: strings.Trim(a[1], " "),
}
pkg.decs[strings.Trim(a[0], " ")] = t
}
}
if strings.Contains(line, "TSEndpoint= ") {
e := ParseEndpoint(line, n, l)
if _, ok := pkg.endpoints[e.Name]; ok {
exitOnError(fmt.Errorf("enpoint name %s allready in use: %s", e.Name, line))
}
pkg_info := pkg
pkg_info.endpoints[e.Name] = e
pkg_info.imports = e.Imports
pkg_info.isUsed = true
pkg = pkg_info
}
}
}
}
func (i *TSInfo) Populate(path string) {
i.Packages = make(map[string]TSInfoPakage)
cfg := &packages.Config{
Mode: packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedSyntax,
Dir: path,
}
pkgs, err := packages.Load(cfg, "./...")
if err != nil {
log.Fatal(err)
}
if packages.PrintErrors(pkgs) > 0 {
log.Fatal("package loading failed")
}
//to get line info for endpoints and typescript declarations
for _, loadedPkg := range pkgs {
pkg := loadedPkg.Name
// skip packages
if pkg == "typescript" || pkg == "tsrpc" {
continue
}
// initialize package info if not exists
if _, ok := i.Packages[pkg]; !ok {
i.Packages[pkg] = TSInfoPakage{structs: make(map[string]TSStruct), types: make(map[string]TSType), enums: make(map[string]TSEnum), consts: make(map[string]TSConst), decs: make(map[string]TSDec), endpoints: make(map[string]TSEndpoint)}
}
// start parsing files
for _, n := range loadedPkg.CompiledGoFiles {
// search for declarative // Typescript: declarations and endpoints
parseTypescriptDeclarations(n, i.Packages[pkg])
}
// parse doc to get all types and consts
docPkg, err := doc.NewFromFiles(loadedPkg.Fset, loadedPkg.Syntax, loadedPkg.PkgPath)
if err != nil {
exitOnError(err)
}
for _, t := range docPkg.Types {
i.getType(pkg, t)
}
for _, c := range docPkg.Consts {
i.getConst(pkg, c)
}
}
}
func (ts *TSInfo) findAvailableStruct(pkg string, n string, deep int) (int, bool) {
a := strings.Split(n, ".")
if n == "" || IsNativeType(n) {
@ -446,11 +463,11 @@ func (i *TSInfo) TestEndpoints() {
for _, v1 := range i.Packages[p].endpoints {
_, f := i.findAvailableStruct(p, v1.Request, 0)
if !f {
fmt.Printf("??Endpoint: request %s not found\n", v1.Request)
fmt.Printf("\n??Endpoint: request %s not found\n", v1.Request)
}
_, f = i.findAvailableStruct(p, v1.Response, 0)
if !f {
fmt.Printf("??Endpoint: response %s not found\n", v1.Response)
fmt.Printf("\n??Endpoint: response %s not found\n", v1.Response)
}
}

View File

@ -6,6 +6,8 @@ package tsrpc
import (
"fmt"
"go/ast"
"slices"
"strings"
)
type TSSField struct {
@ -15,14 +17,13 @@ type TSSField struct {
Json TSTagJson
Ts TSTagTs
DependOn bool
SourceInfo string
}
type TSStruct struct {
Name string
Typescript bool
Fields []TSSField
SourceInfo string
Imports []string
}
func IsNativeType(t string) bool {
@ -137,7 +138,15 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec) {
Type: getFieldInfo(field.Type),
TsType: tsType,
DependOn: toBeImported(field.Type),
SourceInfo: "",
}
if len(strings.Split(f.Type, ".")) == 2 && tagTs.Type == "" {
a := strings.Split(f.Type, ".")
a[1] = strings.Trim(a[1], "[]*")
a[0] = strings.Trim(a[0], "[]*")
imp := fmt.Sprintf("%s.%s", a[0], a[1])
if !slices.Contains(s.Imports, imp) {
s.Imports = append(s.Imports, imp)
}
}
s.Fields = append(s.Fields, f)
} else {
@ -149,7 +158,6 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec) {
Type: getFieldInfo(field.Type),
TsType: tsType,
DependOn: toBeImported(field.Type),
SourceInfo: "",
}
s.Fields = append(s.Fields, f)
} else {
@ -161,7 +169,6 @@ func (s *TSStruct) getStruct(ts *ast.TypeSpec) {
Type: se.Name,
TsType: tsType,
DependOn: false,
SourceInfo: "",
}
s.Fields = append(s.Fields, f)
} else {

View File

@ -6,6 +6,7 @@ package tsrpc
import (
"errors"
"fmt"
"slices"
"strings"
)
@ -59,6 +60,9 @@ func (ts *TSSouces) ensurePackage(p string) {
if pkg.Endpoints == nil {
pkg.Endpoints = make(map[string]string)
}
if pkg.Imports == nil {
pkg.Imports = make(map[string][]string)
}
ts.Pakages[p] = pkg
}
@ -147,7 +151,7 @@ func structToTs(info TSInfo, p string, k string) (string, []string, error) {
result += "}\n"
}
if fields == 0 {
return result, dependencies, fmt.Errorf("struct %s not export fields AT: %s", k, info.Packages[p].structs[k].SourceInfo)
return result, dependencies, fmt.Errorf("struct %s not export fields", k)
}
return result, dependencies, nil
}
@ -193,7 +197,7 @@ func (ts *TSSouces) AddDependencies(info TSInfo, p string, s string, dependencie
if info.findStruct(pk, st) {
if emptySrtuct(info, pk, st) {
ts.Errors = append(ts.Errors, fmt.Sprintf("Empty struct %s.%s AT: %s", pk, st, info.Packages[p].structs[s].SourceInfo))
ts.Errors = append(ts.Errors, fmt.Sprintf("Empty struct %s.%s ", pk, st))
}
s, d, err := structToTs(info, pk, st)
if err != nil {
@ -209,14 +213,14 @@ func (ts *TSSouces) AddDependencies(info TSInfo, p string, s string, dependencie
ts.ensurePackage(pk)
ts.Pakages[pk].Types[st] = s
} else {
ts.Errors = append(ts.Errors, fmt.Sprintf("Dipendence not found %s.%s AT: %s", pk, st, info.Packages[p].structs[s].SourceInfo))
ts.Errors = append(ts.Errors, fmt.Sprintf("Dipendence not found %s.%s", pk, st))
}
}
}
}
func (ts *TSSouces) Populate(info TSInfo) {
func (ts *TSSouces) BuildTSSources(info TSInfo) {
ts.Pakages = make(map[string]TSModule)
ts.Errors = []string{}
for p := range info.Packages {
@ -227,7 +231,7 @@ func (ts *TSSouces) Populate(info TSInfo) {
for _, st := range info.Packages[p].structs {
if st.Typescript {
if len(st.Fields) == 0 {
ts.Errors = append(ts.Errors, fmt.Sprintf("Empty struct %s.%s AT: %s", p, st.Name, info.Packages[p].structs[st.Name].SourceInfo))
ts.Errors = append(ts.Errors, fmt.Sprintf("Empty struct %s.%s ", p, st.Name))
}
s, dependencies, err := structToTs(info, p, st.Name)
if err != nil {
@ -253,9 +257,9 @@ func (ts *TSSouces) Populate(info TSInfo) {
}
for _, t := range info.Packages[p].types {
if t.Typescript {
ts.Pakages[p].Types[t.Name] = fmt.Sprintf("export type %s = %s\n", t.Name, t.TsType)
}
}
for _, e := range info.Packages[p].endpoints {
@ -280,5 +284,16 @@ func (ts *TSSouces) Populate(info TSInfo) {
pkg.Endpoints[e.Name] = e.ToTs()
ts.Pakages[p] = pkg
}
for pk, t := range info.Packages[p].imports {
pkg := ts.Pakages[p]
if _, ok := pkg.Imports[pk]; !ok {
for _, v := range t {
if !slices.Contains(pkg.Imports[pk], v) {
pkg.Imports[pk] = append(pkg.Imports[pk], v)
}
}
}
}
}
}

View File

@ -16,6 +16,12 @@ var TSReport = ""
var tsFiles = TSFiles{}
type ExcudedPackages []string
func (ip *TSInfoPakage) MakeMaps() {
*ip = TSInfoPakage{structs: make(map[string]TSStruct), types: make(map[string]TSType), enums: make(map[string]TSEnum), consts: make(map[string]TSConst), decs: make(map[string]TSDec), endpoints: make(map[string]TSEndpoint), imports: make(map[string][]string), isUsed: false}
}
func GetTSSource() error {
path := ""
if value, exists := os.LookupEnv("TS_GENERATOR_PATH"); exists {
@ -26,9 +32,9 @@ func GetTSSource() error {
var tsInfoData = TSInfo{}
var tsSourcesData = TSSouces{}
tsInfoData.Populate(path)
tsInfoData.Populate(path, ExcudedPackages{"Typescript", "tsrpc"})
tsInfoData.TestEndpoints()
tsSourcesData.Populate(tsInfoData)
tsSourcesData.BuildTSSources(tsInfoData)
if len(tsSourcesData.Errors) != 0 {
err := ""
@ -119,6 +125,7 @@ func GetTSSource() error {
for f := range tsSourcesData.Pakages[p].Imports {
imports += "import type * as " + f + " from './" + f + ".ts'\n"
}
tmp += imports
tmp += source
tsFiles.Add(p+".ts", tmp)
@ -128,7 +135,6 @@ func GetTSSource() error {
err = tsFiles.Save()
if err != nil {
fmt.Printf("save ts files: %s\n", err)
}
return err
}

View File

@ -2,18 +2,6 @@ import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
import type * as users from "./users.ts";
// Typescript: TSEndpoint= path=/api/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=admin.ListUsersResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 11
export const listUsers = async (
data: ListUsersRequest,
): Promise<{ data: ListUsersResponse; error: Nullable<string> }> => {
return (await api.POST("/api/admin/users", data)) as {
data: ListUsersResponse;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/admin/users/block; name=blockUser; method=PUT; request=admin.BlockUserRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 14
@ -26,6 +14,23 @@ export const blockUser = async (
};
};
// Typescript: TSEndpoint= path=/api/admin/users; name=listUsers; method=POST; request=admin.ListUsersRequest; response=admin.ListUsersResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/admin/routes.go Line: 11
export const listUsers = async (
data: ListUsersRequest,
): Promise<{ data: ListUsersResponse; error: Nullable<string> }> => {
return (await api.POST("/api/admin/users", data)) as {
data: ListUsersResponse;
error: Nullable<string>;
};
};
export interface ListUsersRequest {
page: number;
pageSize: number;
}
export interface ListUsersResponse {
items: users.User[];
page: number;
@ -36,8 +41,3 @@ export interface BlockUserRequest {
uuid: string;
action: string;
}
export interface ListUsersRequest {
page: number;
pageSize: number;
}

View File

@ -4,7 +4,7 @@
//
// This file was generated by github.com/millevolte/ts-rpc
//
// Apr 27, 2026 22:35:43 UTC
// May 04, 2026 16:09:10 UTC
//
export interface ApiRestResponse {

View File

@ -1,4 +1,4 @@
// API Declarations
export type Record<K extends string | number | symbol, T> = { [P in K]: T };
export type Nullable<T> = T | null;
export type Record<K extends string | number | symbol, T> = { [P in K]: T };

View File

@ -1,5 +1,5 @@
import { api } from "./api";
import type { Nullable, Record } from "./apiTypes.ts";
import type { Record, Nullable } from "./apiTypes.ts";
// Typescript: TSEndpoint= path=/api/roles; name=getRoles; method=GET; response=auth.AllRoles
@ -13,9 +13,18 @@ export const getRoles = async (): Promise<{
error: Nullable<string>;
};
};
export interface AllRoles {
roles: Record<string, string>;
}
export type Permission = string;
export type AllRoles = Record<string, string>;
export type EnumPermission =
(typeof EnumEnumPermission)[keyof typeof EnumEnumPermission];
export const EnumEnumPermission = {
RoleSuperAdmin: "superadmin",
RoleAdmin: "admin",
RoleManager: "manager",
RoleContentCreator: "content_creator",
RoleUser: "user",
RoleGuest: "guest",
} as const;

View File

@ -1,19 +1,6 @@
import { api } from "./api";
import type { Nullable } from "./apiTypes.ts";
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 36
export const health = async (): Promise<{
data: string;
error: Nullable<string>;
}> => {
return (await api.GET("/health")) as {
data: string;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/metrics; name=metrics; method=GET; response=string
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 39
@ -40,6 +27,19 @@ export const mailDebug = async (): Promise<{
};
};
// Typescript: TSEndpoint= path=/health; name=health; method=GET; response=string
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/systemUtils/routes.go Line: 36
export const health = async (): Promise<{
data: string;
error: Nullable<string>;
}> => {
return (await api.GET("/health")) as {
data: string;
error: Nullable<string>;
};
};
export interface MailDebugItem {
name: string;
content: string;

View File

@ -4,62 +4,13 @@ import type * as responses from "./responses.ts";
import type * as tokens from "./tokens.ts";
import type * as auth from "./auth.ts";
// Typescript: TSEndpoint= path=/api/auth/me; name=me; method=GET; response=users.User
// Typescript: TSEndpoint= path=/api/users; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 40
export const me = async (): Promise<{
data: User;
error: Nullable<string>;
}> => {
return (await api.GET("/api/auth/me")) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 46
export const refresh = async (
data: RefreshRequest,
): Promise<{ data: tokens.TokenPair; error: Nullable<string> }> => {
return (await api.POST("/api/auth/refresh", data)) as {
data: tokens.TokenPair;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/password/reset; name=resetPassword; method=POST; request=users.ResetPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 55
export const resetPassword = async (
data: ResetPasswordRequest,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
return (await api.POST("/api/auth/password/reset", data)) as {
data: responses.SimpleResponse;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/users/:uuid; name=getUser; method=GET; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 28
export const getUser = async (
uuid: string,
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 31
export const createUser = async (
data: UserCreateRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.GET(`/api/users/${uuid}`)) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/users/update; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 34
export const updateUser = async (
data: UpdateUserRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.PUT("/api/users/update", data)) as {
return (await api.POST("/api/users", data)) as {
data: User;
error: Nullable<string>;
};
@ -77,6 +28,18 @@ export const updatePassword = async (
};
};
// Typescript: TSEndpoint= path=/api/users/:uuid; name=getUser; method=GET; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 28
export const getUser = async (
uuid: string,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.GET(`/api/users/${uuid}`)) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/users/:uuid; name=deleteUser; method=DELETE; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 37
@ -89,30 +52,6 @@ export const deleteUser = async (
};
};
// Typescript: TSEndpoint= path=/api/auth/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 52
export const forgotPassword = async (
data: ForgotPasswordRequest,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
return (await api.POST("/api/auth/password/forgot", data)) as {
data: responses.SimpleResponse;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/users; name=createUser; method=POST; request=users.UserCreateRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 31
export const createUser = async (
data: UserCreateRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.POST("/api/users", data)) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/login; name=login; method=POST; request=users.LoginRequest; response=tokens.TokenPair
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 43
@ -125,6 +64,30 @@ export const login = async (
};
};
// Typescript: TSEndpoint= path=/api/auth/password/reset; name=resetPassword; method=POST; request=users.ResetPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 55
export const resetPassword = async (
data: ResetPasswordRequest,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
return (await api.POST("/api/auth/password/reset", data)) as {
data: responses.SimpleResponse;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/users/update; name=updateUser; method=PUT; request=users.UpdateUserRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 34
export const updateUser = async (
data: UpdateUserRequest,
): Promise<{ data: User; error: Nullable<string> }> => {
return (await api.PUT("/api/users/update", data)) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/register; name=register; method=POST; request=users.UserCreateRequest; response=users.User
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 49
@ -149,12 +112,45 @@ export const validToken = async (
};
};
export interface ForgotPasswordRequest {
email: string;
}
// Typescript: TSEndpoint= path=/api/auth/me; name=me; method=GET; response=users.User
export interface LoginRequest {
username: string;
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 40
export const me = async (): Promise<{
data: User;
error: Nullable<string>;
}> => {
return (await api.GET("/api/auth/me")) as {
data: User;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/refresh; name=refresh; method=POST; request=users.RefreshRequest; response=tokens.TokenPair
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 46
export const refresh = async (
data: RefreshRequest,
): Promise<{ data: tokens.TokenPair; error: Nullable<string> }> => {
return (await api.POST("/api/auth/refresh", data)) as {
data: tokens.TokenPair;
error: Nullable<string>;
};
};
// Typescript: TSEndpoint= path=/api/auth/password/forgot; name=forgotPassword; method=POST; request=users.ForgotPasswordRequest; response=responses.SimpleResponse
// /Users/fabio/CODE/omnimed/go-quasar-partial-ssr/backend/internal/user/routes.go Line: 52
export const forgotPassword = async (
data: ForgotPasswordRequest,
): Promise<{ data: responses.SimpleResponse; error: Nullable<string> }> => {
return (await api.POST("/api/auth/password/forgot", data)) as {
data: responses.SimpleResponse;
error: Nullable<string>;
};
};
export interface ResetPasswordRequest {
token: string;
password: string;
}
@ -171,6 +167,10 @@ export interface UpdateUserRequest {
preferences: Nullable<UserPreferences>;
}
export interface UpdatePasswordRequest {
password: string;
}
export interface UserCreateRequest {
name: string;
email: string;
@ -183,30 +183,6 @@ export interface UserCreateRequest {
preferences: Nullable<UserPreferences>;
}
export interface UserDetails {
id: number;
userId: number;
title: string;
firstName: string;
lastName: string;
address: string;
city: string;
zipCode: string;
country: string;
phone: string;
createdAt: Nullable<Date>;
updatedAt: Nullable<Date>;
}
export interface RefreshRequest {
refresh_token: string;
}
export interface ResetPasswordRequest {
token: string;
password: string;
}
export interface User {
id: number;
email: string;
@ -223,8 +199,19 @@ export interface User {
updatedAt: Nullable<Date>;
}
export interface UpdatePasswordRequest {
password: string;
export interface UserDetails {
id: number;
userId: number;
title: string;
firstName: string;
lastName: string;
address: string;
city: string;
zipCode: string;
country: string;
phone: string;
createdAt: Nullable<Date>;
updatedAt: Nullable<Date>;
}
export interface UserPreferences {
@ -242,6 +229,25 @@ export interface UserPreferences {
updatedAt: Nullable<Date>;
}
export interface ForgotPasswordRequest {
email: string;
}
export interface LoginRequest {
username: string;
password: string;
}
export interface RefreshRequest {
refresh_token: string;
}
export type UserStatus = string;
export type UserTypes = string[];
export const EnumUserStatus = {
UserStatusPending: "pending",
UserStatusActive: "active",
UserStatusDisabled: "disabled",
} as const;