@nakedev/go-scaffold 0.1.2 → 0.1.3
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/README.md +75 -0
- package/dist/commands/auth.js +129 -0
- package/dist/commands/create.js +3 -2
- package/dist/commands/generate.js +59 -1
- package/dist/commands/method.js +3 -0
- package/dist/commands/migration.js +34 -0
- package/dist/commands/rbac.js +103 -0
- package/dist/commands/remove.js +19 -2
- package/dist/commands/worker.js +75 -0
- package/dist/index.js +66 -3
- package/dist/prompts/create-wizard.js +6 -1
- package/dist/prompts/generate-wizard.js +8 -0
- package/dist/templates/auth-manifest.js +19 -0
- package/dist/templates/create-manifest.js +25 -0
- package/dist/templates/rbac-manifest.js +17 -0
- package/dist/templates/worker-manifest.js +12 -0
- package/dist/utils/auth-patcher.js +96 -0
- package/dist/utils/gocheck.js +65 -0
- package/dist/utils/main-patcher.js +8 -1
- package/dist/utils/migrations.js +30 -8
- package/dist/utils/openapi-patcher.js +16 -0
- package/dist/utils/platform-patcher.js +59 -0
- package/dist/utils/rbac-patcher.js +277 -0
- package/dist/utils/version.js +24 -0
- package/package.json +2 -2
- package/templates/add/auth/cmd/seed/main.go.hbs +76 -0
- package/templates/add/auth/docs/forgot-password.yaml.hbs +19 -0
- package/templates/add/auth/docs/google-callback.yaml.hbs +22 -0
- package/templates/add/auth/docs/google-login.yaml.hbs +7 -0
- package/templates/add/auth/docs/login.yaml.hbs +19 -0
- package/templates/add/auth/docs/logout.yaml.hbs +8 -0
- package/templates/add/auth/docs/refresh.yaml.hbs +15 -0
- package/templates/add/auth/docs/register.yaml.hbs +19 -0
- package/templates/add/auth/docs/reset-password.yaml.hbs +16 -0
- package/templates/add/auth/docs/schemas.yaml.hbs +58 -0
- package/templates/add/auth/docs/users-me-logout-all.yaml.hbs +9 -0
- package/templates/add/auth/docs/users-me-resend-verification.yaml.hbs +10 -0
- package/templates/add/auth/docs/users-me.yaml.hbs +12 -0
- package/templates/add/auth/docs/verify-email.yaml.hbs +16 -0
- package/templates/add/auth/internal/app/user/dto.go.hbs +77 -0
- package/templates/add/auth/internal/app/user/errors.go.hbs +36 -0
- package/templates/add/auth/internal/app/user/handler.go.hbs +235 -0
- package/templates/add/auth/internal/app/user/jwt.go.hbs +84 -0
- package/templates/add/auth/internal/app/user/model/identity.go.hbs +28 -0
- package/templates/add/auth/internal/app/user/model/user.go.hbs +22 -0
- package/templates/add/auth/internal/app/user/repository.go.hbs +84 -0
- package/templates/add/auth/internal/app/user/service.go.hbs +447 -0
- package/templates/add/auth/internal/app/user/service_test.go.hbs +237 -0
- package/templates/add/auth/internal/app/user/tokenstore.go.hbs +159 -0
- package/templates/add/auth/internal/shared/middleware/auth.go.hbs +64 -0
- package/templates/add/auth/internal/shared/middleware/ratelimit.go.hbs +44 -0
- package/templates/add/auth/migrations/create_identities.down.sql.hbs +1 -0
- package/templates/add/auth/migrations/create_identities.up.sql.hbs +11 -0
- package/templates/add/auth/migrations/create_users.down.sql.hbs +1 -0
- package/templates/add/auth/migrations/create_users.up.sql.hbs +9 -0
- package/templates/add/rbac/docs/permissions.yaml.hbs +24 -0
- package/templates/add/rbac/docs/role-permissions.yaml.hbs +31 -0
- package/templates/add/rbac/docs/role.yaml.hbs +17 -0
- package/templates/add/rbac/docs/roles.yaml.hbs +43 -0
- package/templates/add/rbac/docs/schemas.yaml.hbs +37 -0
- package/templates/add/rbac/docs/user-set-role.yaml.hbs +23 -0
- package/templates/add/rbac/docs/user.yaml.hbs +16 -0
- package/templates/add/rbac/docs/users.yaml.hbs +23 -0
- package/templates/add/rbac/internal/app/role/dto.go.hbs +40 -0
- package/templates/add/rbac/internal/app/role/errors.go.hbs +39 -0
- package/templates/add/rbac/internal/app/role/handler.go.hbs +101 -0
- package/templates/add/rbac/internal/app/role/model/permission.go.hbs +9 -0
- package/templates/add/rbac/internal/app/role/model/role.go.hbs +18 -0
- package/templates/add/rbac/internal/app/role/model/role_permission.go.hbs +8 -0
- package/templates/add/rbac/internal/app/role/repository.go.hbs +96 -0
- package/templates/add/rbac/internal/app/role/service.go.hbs +210 -0
- package/templates/add/rbac/internal/app/role/service_test.go.hbs +119 -0
- package/templates/add/rbac/internal/shared/middleware/authz.go.hbs +88 -0
- package/templates/add/rbac/internal/shared/middleware/authz_test.go.hbs +88 -0
- package/templates/add/rbac/migrations/add_roles.down.sql.hbs +15 -0
- package/templates/add/rbac/migrations/add_roles.up.sql.hbs +35 -0
- package/templates/add/worker/cmd/worker/main.go.hbs +77 -0
- package/templates/add/worker/internal/platform/cache/redis.go.hbs +18 -0
- package/templates/add/worker/internal/platform/mail/mail.go.hbs +48 -0
- package/templates/add/worker/internal/platform/mail/task.go.hbs +52 -0
- package/templates/add/worker/internal/platform/queue/client.go.hbs +31 -0
- package/templates/add/worker/internal/platform/queue/server.go.hbs +68 -0
- package/templates/create/base/.env.example.hbs +20 -0
- package/templates/create/base/.github/workflows/ci.yml.hbs +4 -2
- package/templates/create/base/.gitignore.hbs +2 -0
- package/templates/create/base/Makefile.hbs +30 -5
- package/templates/create/base/README.md.hbs +36 -8
- package/templates/create/base/cmd/api/main.go.hbs +26 -1
- package/templates/create/base/internal/platform/database/database.go.hbs +53 -0
- package/templates/create/base/internal/shared/config/config.go.hbs +43 -1
- package/templates/create/base/internal/shared/middleware/cors.go.hbs +29 -0
- package/templates/create/base/internal/shared/middleware/error.go.hbs +13 -1
- package/templates/create/base/migrations/embed.go.hbs +15 -0
- package/templates/create/features/docs/architecture.md.hbs +22 -0
- package/templates/create/features/docs/common/responses.yaml.hbs +15 -0
- package/templates/create/features/docs/observability/metrics.yaml.hbs +12 -0
- package/templates/create/features/docs/openapi.yaml.hbs +13 -0
- package/templates/create/features/docs/techstack.md.hbs +3 -0
- package/templates/create/features/observability/middleware/metrics.go.hbs +41 -0
- package/templates/create/features/observability/middleware/tracing.go.hbs +46 -0
- package/templates/create/features/observability/platform/telemetry/tracing.go.hbs +130 -0
- package/templates/generate/module/handler.go.hbs +20 -3
- package/templates/generate/module/handler_test.go.hbs +49 -6
- package/templates/generate/module/minimal/handler.go.hbs +21 -3
- package/templates/generate/module/minimal/handler_test.go.hbs +53 -6
- package/templates/generate/module/permission.down.sql.hbs +5 -0
- package/templates/generate/module/permission.up.sql.hbs +4 -0
- package/dist/utils/module-paths.js +0 -33
|
@@ -2,24 +2,37 @@ package {{pkg}}
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"bytes"
|
|
5
|
+
{{#if permission}}
|
|
6
|
+
"context"
|
|
7
|
+
{{/if}}
|
|
5
8
|
"encoding/json"
|
|
6
9
|
"net/http"
|
|
7
10
|
"net/http/httptest"
|
|
8
11
|
"os"
|
|
9
12
|
"sync"
|
|
10
13
|
"testing"
|
|
14
|
+
{{#if auth}}
|
|
15
|
+
"time"
|
|
16
|
+
{{/if}}
|
|
11
17
|
|
|
12
18
|
"{{goModule}}/internal/app/{{modulePath}}/model"
|
|
13
19
|
"{{goModule}}/internal/shared/middleware"
|
|
14
20
|
|
|
15
21
|
"github.com/gin-gonic/gin"
|
|
22
|
+
{{#if auth}}
|
|
23
|
+
"github.com/golang-jwt/jwt/v5"
|
|
24
|
+
{{/if}}
|
|
16
25
|
"github.com/google/uuid"
|
|
17
26
|
"gorm.io/driver/postgres"
|
|
18
27
|
"gorm.io/gorm"
|
|
19
28
|
)
|
|
20
29
|
|
|
21
|
-
// integration test backed by real Postgres (same engine as prod, no sqlite) — skips if the DB isn't reachable
|
|
22
|
-
//
|
|
30
|
+
// integration test backed by real Postgres (same engine as prod, no sqlite) — skips if the DB isn't reachable.
|
|
31
|
+
// Runs against its own {{dbName}}_test database, never the one DB_DSN points at: the
|
|
32
|
+
// harness does DropTable+AutoMigrate on every run, which would otherwise wipe the
|
|
33
|
+
// schema `make migrate-up` built in your dev DB (FK constraints, seed data and all).
|
|
34
|
+
// Create it once with `make db-create DB_NAME={{dbName}}_test`, or point TEST_DB_DSN
|
|
35
|
+
// somewhere else.
|
|
23
36
|
var (
|
|
24
37
|
testDBOnce sync.Once
|
|
25
38
|
testDB *gorm.DB
|
|
@@ -31,7 +44,7 @@ func dbForTest(t *testing.T) *gorm.DB {
|
|
|
31
44
|
testDBOnce.Do(func() {
|
|
32
45
|
dsn := os.Getenv("TEST_DB_DSN")
|
|
33
46
|
if dsn == "" {
|
|
34
|
-
dsn = "postgres://postgres:postgres@localhost:5432/{{dbName}}?sslmode=disable"
|
|
47
|
+
dsn = "postgres://postgres:postgres@localhost:5432/{{dbName}}_test?sslmode=disable"
|
|
35
48
|
}
|
|
36
49
|
if testDB, testDBErr = gorm.Open(postgres.Open(dsn), &gorm.Config{TranslateError: true}); testDBErr == nil {
|
|
37
50
|
// drop first — AutoMigrate can't change an existing column's type, always start from a fresh schema
|
|
@@ -40,7 +53,7 @@ func dbForTest(t *testing.T) *gorm.DB {
|
|
|
40
53
|
}
|
|
41
54
|
})
|
|
42
55
|
if testDBErr != nil {
|
|
43
|
-
t.Skipf("postgres not ready (
|
|
56
|
+
t.Skipf("postgres not ready (make db-create DB_NAME={{dbName}}_test, or set TEST_DB_DSN): %v", testDBErr)
|
|
44
57
|
}
|
|
45
58
|
return testDB
|
|
46
59
|
}
|
|
@@ -52,14 +65,44 @@ func setup(t *testing.T) *gin.Engine {
|
|
|
52
65
|
tx := dbForTest(t).Begin()
|
|
53
66
|
t.Cleanup(func() { tx.Rollback() })
|
|
54
67
|
r := gin.New()
|
|
55
|
-
r.Use(middleware.RequestID(), middleware.Error())
|
|
56
|
-
|
|
68
|
+
r.Use(middleware.RequestID(), middleware.Error(true))
|
|
69
|
+
{{#if permission}}
|
|
70
|
+
// stub authz: always grants "{{permission}}" — this test is about the
|
|
71
|
+
// CRUD handler, not re-proving RBAC (that's covered by the role
|
|
72
|
+
// package's own tests), so the resolver doesn't need a real DB lookup.
|
|
73
|
+
authz := middleware.NewAuthz(func(_ context.Context, _ string) (map[string]struct{}, error) {
|
|
74
|
+
return map[string]struct{}{"{{permission}}": {}}, nil
|
|
75
|
+
}, time.Minute)
|
|
76
|
+
{{/if}}
|
|
77
|
+
NewHandler(NewService(NewRepository(tx)){{#if auth}}, testJWTSecret{{/if}}{{#if permission}}, authz{{/if}}).Register(r)
|
|
57
78
|
return r
|
|
58
79
|
}
|
|
59
80
|
|
|
81
|
+
{{#if auth}}
|
|
82
|
+
const testJWTSecret = "test-secret"
|
|
83
|
+
|
|
84
|
+
// authHeader mints a valid access token signed with testJWTSecret — the
|
|
85
|
+
// route's own RequireAuth(testJWTSecret) (wired in via NewHandler above)
|
|
86
|
+
// verifies against the same secret, so this round-trips for real.
|
|
87
|
+
func authHeader() string {
|
|
88
|
+
claims := jwt.MapClaims{
|
|
89
|
+
"typ": "access",
|
|
90
|
+
"sub": uuid.NewString(),
|
|
91
|
+
"role": "staff",
|
|
92
|
+
"exp": time.Now().Add(time.Hour).Unix(),
|
|
93
|
+
"iat": time.Now().Unix(),
|
|
94
|
+
}
|
|
95
|
+
tok, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(testJWTSecret))
|
|
96
|
+
return "Bearer " + tok
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
{{/if}}
|
|
60
100
|
func do(r *gin.Engine, method, path, body string) *httptest.ResponseRecorder {
|
|
61
101
|
req := httptest.NewRequest(method, path, bytes.NewBufferString(body))
|
|
62
102
|
req.Header.Set("Content-Type", "application/json")
|
|
103
|
+
{{#if auth}}
|
|
104
|
+
req.Header.Set("Authorization", authHeader())
|
|
105
|
+
{{/if}}
|
|
63
106
|
w := httptest.NewRecorder()
|
|
64
107
|
r.ServeHTTP(w, req)
|
|
65
108
|
return w
|
|
@@ -1,22 +1,40 @@
|
|
|
1
1
|
package {{pkg}}
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
{{#if auth}}
|
|
5
|
+
"{{goModule}}/internal/shared/middleware"
|
|
6
|
+
|
|
7
|
+
{{/if}}
|
|
4
8
|
"github.com/gin-gonic/gin"
|
|
5
9
|
)
|
|
6
10
|
|
|
7
11
|
// Handler = delivery for {{pkg}} (parses HTTP, calls the service, attaches errors for the middleware to render)
|
|
8
12
|
type Handler struct {
|
|
9
13
|
svc *Service
|
|
14
|
+
{{#if auth}}
|
|
15
|
+
jwtSecret string
|
|
16
|
+
{{/if}}
|
|
17
|
+
{{#if permission}}
|
|
18
|
+
authz *middleware.Authz
|
|
19
|
+
{{/if}}
|
|
10
20
|
}
|
|
11
21
|
|
|
12
|
-
func NewHandler(svc *Service) *Handler {
|
|
13
|
-
return &Handler{
|
|
22
|
+
func NewHandler(svc *Service{{#if auth}}, jwtSecret string{{/if}}{{#if permission}}, authz *middleware.Authz{{/if}}) *Handler {
|
|
23
|
+
return &Handler{
|
|
24
|
+
svc: svc,
|
|
25
|
+
{{#if auth}}
|
|
26
|
+
jwtSecret: jwtSecret,
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{#if permission}}
|
|
29
|
+
authz: authz,
|
|
30
|
+
{{/if}}
|
|
31
|
+
}
|
|
14
32
|
}
|
|
15
33
|
|
|
16
34
|
// Register wires {{pkg}}'s routes onto the router group (takes an IRouter so it can be nested under /v1)
|
|
17
35
|
// minimal module: no routes yet — add them with `go-scaffold generate method {{pkg}} <name> --type ...`
|
|
18
36
|
func (h *Handler) Register(rg gin.IRouter) {
|
|
19
|
-
g := rg.Group("/{{plural}}")
|
|
37
|
+
g := rg.Group("/{{plural}}"{{#if auth}}, middleware.RequireAuth(h.jwtSecret){{/if}}{{#if permission}}, h.authz.Require("{{permission}}"){{/if}})
|
|
20
38
|
_ = g
|
|
21
39
|
// go-scaffold:handler-routes
|
|
22
40
|
}
|
|
@@ -2,10 +2,16 @@ package {{pkg}}
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"bytes"
|
|
5
|
+
{{#if permission}}
|
|
6
|
+
"context"
|
|
7
|
+
{{/if}}
|
|
5
8
|
"net/http/httptest"
|
|
6
9
|
"os"
|
|
7
10
|
"sync"
|
|
8
11
|
"testing"
|
|
12
|
+
{{#if auth}}
|
|
13
|
+
"time"
|
|
14
|
+
{{/if}}
|
|
9
15
|
|
|
10
16
|
"{{goModule}}/internal/app/{{modulePath}}/model"
|
|
11
17
|
"{{goModule}}/internal/shared/middleware"
|
|
@@ -13,13 +19,21 @@ import (
|
|
|
13
19
|
"gorm.io/gorm"
|
|
14
20
|
|
|
15
21
|
"github.com/gin-gonic/gin"
|
|
22
|
+
{{#if auth}}
|
|
23
|
+
"github.com/golang-jwt/jwt/v5"
|
|
24
|
+
"github.com/google/uuid"
|
|
25
|
+
{{/if}}
|
|
16
26
|
"gorm.io/driver/postgres"
|
|
17
27
|
)
|
|
18
28
|
|
|
19
29
|
// integration test harness, backed by real Postgres (same engine as prod, no sqlite) — skips if
|
|
20
30
|
// the DB isn't reachable. minimal module: no routes yet, so no tests reference this yet — kept
|
|
21
|
-
// ready for once `generate method` adds endpoints
|
|
22
|
-
//
|
|
31
|
+
// ready for once `generate method` adds endpoints.
|
|
32
|
+
// Runs against its own {{dbName}}_test database, never the one DB_DSN points at: the
|
|
33
|
+
// harness does DropTable+AutoMigrate on every run, which would otherwise wipe the
|
|
34
|
+
// schema `make migrate-up` built in your dev DB (FK constraints, seed data and all).
|
|
35
|
+
// Create it once with `make db-create DB_NAME={{dbName}}_test`, or point TEST_DB_DSN
|
|
36
|
+
// somewhere else.
|
|
23
37
|
//nolint:unused
|
|
24
38
|
var (
|
|
25
39
|
testDBOnce sync.Once
|
|
@@ -33,7 +47,7 @@ func dbForTest(t *testing.T) *gorm.DB {
|
|
|
33
47
|
testDBOnce.Do(func() {
|
|
34
48
|
dsn := os.Getenv("TEST_DB_DSN")
|
|
35
49
|
if dsn == "" {
|
|
36
|
-
dsn = "postgres://postgres:postgres@localhost:5432/{{dbName}}?sslmode=disable"
|
|
50
|
+
dsn = "postgres://postgres:postgres@localhost:5432/{{dbName}}_test?sslmode=disable"
|
|
37
51
|
}
|
|
38
52
|
if testDB, testDBErr = gorm.Open(postgres.Open(dsn), &gorm.Config{TranslateError: true}); testDBErr == nil {
|
|
39
53
|
_ = testDB.Migrator().DropTable(&model.{{pascalName}}{})
|
|
@@ -41,7 +55,7 @@ func dbForTest(t *testing.T) *gorm.DB {
|
|
|
41
55
|
}
|
|
42
56
|
})
|
|
43
57
|
if testDBErr != nil {
|
|
44
|
-
t.Skipf("postgres not ready (
|
|
58
|
+
t.Skipf("postgres not ready (make db-create DB_NAME={{dbName}}_test, or set TEST_DB_DSN): %v", testDBErr)
|
|
45
59
|
}
|
|
46
60
|
return testDB
|
|
47
61
|
}
|
|
@@ -55,15 +69,48 @@ func setup(t *testing.T) *gin.Engine {
|
|
|
55
69
|
tx := dbForTest(t).Begin()
|
|
56
70
|
t.Cleanup(func() { tx.Rollback() })
|
|
57
71
|
r := gin.New()
|
|
58
|
-
r.Use(middleware.RequestID(), middleware.Error())
|
|
59
|
-
|
|
72
|
+
r.Use(middleware.RequestID(), middleware.Error(true))
|
|
73
|
+
{{#if permission}}
|
|
74
|
+
// stub authz: always grants "{{permission}}" — this test is about the
|
|
75
|
+
// CRUD handler, not re-proving RBAC (that's covered by the role
|
|
76
|
+
// package's own tests), so the resolver doesn't need a real DB lookup.
|
|
77
|
+
authz := middleware.NewAuthz(func(_ context.Context, _ string) (map[string]struct{}, error) {
|
|
78
|
+
return map[string]struct{}{"{{permission}}": {}}, nil
|
|
79
|
+
}, time.Minute)
|
|
80
|
+
{{/if}}
|
|
81
|
+
NewHandler(NewService(NewRepository(tx)){{#if auth}}, testJWTSecret{{/if}}{{#if permission}}, authz{{/if}}).Register(r)
|
|
60
82
|
return r
|
|
61
83
|
}
|
|
62
84
|
|
|
85
|
+
{{#if auth}}
|
|
86
|
+
//nolint:unused
|
|
87
|
+
const testJWTSecret = "test-secret"
|
|
88
|
+
|
|
89
|
+
// authHeader mints a valid access token signed with testJWTSecret — the
|
|
90
|
+
// route's own RequireAuth(testJWTSecret) (wired in via NewHandler above)
|
|
91
|
+
// verifies against the same secret, so this round-trips for real.
|
|
92
|
+
//
|
|
93
|
+
//nolint:unused
|
|
94
|
+
func authHeader() string {
|
|
95
|
+
claims := jwt.MapClaims{
|
|
96
|
+
"typ": "access",
|
|
97
|
+
"sub": uuid.NewString(),
|
|
98
|
+
"role": "staff",
|
|
99
|
+
"exp": time.Now().Add(time.Hour).Unix(),
|
|
100
|
+
"iat": time.Now().Unix(),
|
|
101
|
+
}
|
|
102
|
+
tok, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(testJWTSecret))
|
|
103
|
+
return "Bearer " + tok
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
{{/if}}
|
|
63
107
|
//nolint:unused
|
|
64
108
|
func do(r *gin.Engine, method, path, body string) *httptest.ResponseRecorder {
|
|
65
109
|
req := httptest.NewRequest(method, path, bytes.NewBufferString(body))
|
|
66
110
|
req.Header.Set("Content-Type", "application/json")
|
|
111
|
+
{{#if auth}}
|
|
112
|
+
req.Header.Set("Authorization", authHeader())
|
|
113
|
+
{{/if}}
|
|
67
114
|
w := httptest.NewRecorder()
|
|
68
115
|
r.ServeHTTP(w, req)
|
|
69
116
|
return w
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- intentionally not deleting the permission row: another module generated
|
|
2
|
+
-- later may have reused this same code (the up migration's ON CONFLICT
|
|
3
|
+
-- assumes exactly that), and this file can't know whether that happened.
|
|
4
|
+
-- An unused permission row with no route checking it is harmless — delete
|
|
5
|
+
-- it by hand if you're sure nothing else references '{{permission}}'.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- ON CONFLICT DO NOTHING: safe to re-generate a module reusing a permission
|
|
2
|
+
-- code another module already inserted (e.g. a shared "orders:manage").
|
|
3
|
+
INSERT INTO permissions (code, description) VALUES ('{{permission}}', 'Manage {{plural}}')
|
|
4
|
+
ON CONFLICT (code) DO NOTHING;
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.listVersionFolders = listVersionFolders;
|
|
7
|
-
exports.versionsContainingModule = versionsContainingModule;
|
|
8
|
-
exports.nextVersionName = nextVersionName;
|
|
9
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
// folder-based domain versioning lays modules out as internal/app/v<n>/<pkg>.
|
|
12
|
-
// listVersionFolders returns the existing v<n> dirs, sorted ascending.
|
|
13
|
-
function listVersionFolders(projectDir) {
|
|
14
|
-
const appDir = path_1.default.join(projectDir, "internal", "app");
|
|
15
|
-
if (!fs_extra_1.default.existsSync(appDir))
|
|
16
|
-
return [];
|
|
17
|
-
return fs_extra_1.default
|
|
18
|
-
.readdirSync(appDir, { withFileTypes: true })
|
|
19
|
-
.filter((e) => e.isDirectory() && /^v\d+$/.test(e.name))
|
|
20
|
-
.map((e) => e.name)
|
|
21
|
-
.sort((a, b) => parseInt(a.slice(1), 10) - parseInt(b.slice(1), 10));
|
|
22
|
-
}
|
|
23
|
-
// version folders that actually contain the given module (identified by its
|
|
24
|
-
// handler.go) — a module can legitimately live in more than one version at
|
|
25
|
-
// once (that's the point of versioning), so callers that need exactly one
|
|
26
|
-
// (generate method, remove module) must pick among the matches.
|
|
27
|
-
function versionsContainingModule(projectDir, pkg) {
|
|
28
|
-
return listVersionFolders(projectDir).filter((v) => fs_extra_1.default.existsSync(path_1.default.join(projectDir, "internal", "app", v, pkg, "handler.go")));
|
|
29
|
-
}
|
|
30
|
-
function nextVersionName(versions) {
|
|
31
|
-
const max = versions.reduce((m, v) => Math.max(m, parseInt(v.slice(1), 10) || 0), 0);
|
|
32
|
-
return `v${max + 1}`;
|
|
33
|
-
}
|