gorig-cli 1.0.24 → 1.0.25

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.
Files changed (73) hide show
  1. package/README.md +112 -22
  2. package/commands/create.js +267 -112
  3. package/commands/doc.js +21 -8
  4. package/commands/init.js +316 -255
  5. package/commands/skill.js +55 -17
  6. package/package.json +1 -1
  7. package/templates/config.go.ejs +6 -5
  8. package/templates/config.yaml.ejs +3 -38
  9. package/templates/controller.go.ejs +11 -42
  10. package/templates/crud.controller.go.ejs +71 -0
  11. package/templates/crud.doc.md.ejs +60 -0
  12. package/templates/crud.dto.go.ejs +103 -0
  13. package/templates/crud.integration.init_test.go.ejs +17 -0
  14. package/templates/crud.integration_test.go.ejs +66 -0
  15. package/templates/crud.model.go.ejs +25 -0
  16. package/templates/crud.module.README.md.ejs +70 -0
  17. package/templates/crud.mongo.config.yaml.ejs +19 -0
  18. package/templates/crud.mysql.config.yaml.ejs +14 -0
  19. package/templates/crud.router.go.ejs +18 -0
  20. package/templates/crud.service.go.ejs +179 -0
  21. package/templates/crud.test.go.ejs +52 -0
  22. package/templates/dto.go.ejs +16 -0
  23. package/templates/gitignore.ejs +18 -49
  24. package/templates/hello.controller.go.ejs +19 -0
  25. package/templates/hello.router.go.ejs +12 -0
  26. package/templates/hello.service.go.ejs +31 -0
  27. package/templates/hello.test.go.ejs +34 -0
  28. package/templates/init.go.ejs +1 -27
  29. package/templates/main.go.ejs +2 -5
  30. package/templates/model.go.ejs +3 -4
  31. package/templates/project.README.md.ejs +35 -0
  32. package/templates/router.go.ejs +4 -8
  33. package/templates/service.go.ejs +25 -77
  34. package/templates/skills/gorig-backend/SKILL.md +89 -0
  35. package/templates/skills/gorig-backend/agents/openai.yaml +4 -0
  36. package/templates/skills/gorig-backend/assets/api-doc-template.md +59 -0
  37. package/templates/skills/gorig-backend/assets/gorig.gitignore +65 -0
  38. package/templates/skills/gorig-backend/assets/module-readme-template.md +58 -0
  39. package/templates/skills/gorig-backend/references/advanced-data-access.md +275 -0
  40. package/templates/skills/gorig-backend/references/auth-security.md +194 -0
  41. package/templates/skills/gorig-backend/references/business-scenarios.md +155 -0
  42. package/templates/skills/gorig-backend/references/cache.md +301 -0
  43. package/templates/skills/gorig-backend/references/capability-matrix.md +37 -0
  44. package/templates/skills/gorig-backend/references/configuration.md +48 -0
  45. package/templates/skills/gorig-backend/references/framework-api.md +190 -0
  46. package/templates/skills/gorig-backend/references/messaging.md +143 -0
  47. package/templates/skills/gorig-backend/references/onboarding-files.md +46 -0
  48. package/templates/skills/gorig-backend/references/outbound-http.md +162 -0
  49. package/templates/skills/gorig-backend/references/persistent-crud.md +332 -0
  50. package/templates/skills/gorig-backend/references/project-bootstrap.md +128 -0
  51. package/templates/skills/gorig-backend/references/scheduled-tasks.md +231 -0
  52. package/templates/skills/gorig-backend/references/service-lifecycle.md +51 -0
  53. package/templates/skills/gorig-backend/references/source-map.md +43 -0
  54. package/templates/skills/gorig-backend/references/source-policy.md +58 -0
  55. package/templates/skills/gorig-backend/references/sse.md +121 -0
  56. package/templates/skills/gorig-backend/references/testing.md +171 -0
  57. package/templates/skills/gorig-backend/scripts/check-source-links.sh +48 -0
  58. package/templates/skills/gorig-backend/scripts/detect-gorig-context.sh +67 -0
  59. package/templates/skills/gorig-backend/scripts/verify-basic-project.sh +108 -0
  60. package/npm_publish copy.sh +0 -65
  61. package/templates/internal.go.ejs +0 -49
  62. package/templates/req.go.ejs +0 -8
  63. package/templates/resp.go.ejs +0 -8
  64. package/templates/service.pub.go.ejs +0 -22
  65. package/templates/skills/claude/gorig-backend/SKILL.md +0 -766
  66. package/templates/skills/claude/gorig-backend/assets/api-doc-template.md +0 -50
  67. package/templates/skills/claude/gorig-backend/assets/module-readme-template.md +0 -46
  68. package/templates/skills/claude/gorig-backend/references/onboarding-files.md +0 -44
  69. package/templates/skills/codex/gorig-backend/SKILL.md +0 -766
  70. package/templates/skills/codex/gorig-backend/agents/openai.yaml +0 -4
  71. package/templates/skills/codex/gorig-backend/assets/api-doc-template.md +0 -50
  72. package/templates/skills/codex/gorig-backend/assets/module-readme-template.md +0 -46
  73. package/templates/skills/codex/gorig-backend/references/onboarding-files.md +0 -44
@@ -0,0 +1,37 @@
1
+ # Capability Maturity Matrix
2
+
3
+ This baseline reflects the locally inspected Gorig `master` from 2026-05-13. Re-evaluate it for the target project's resolved version.
4
+
5
+ | Capability | Baseline | Verification source | Important boundary |
6
+ |---|---|---|---|
7
+ | `bootstrap.StartUp` and `serv` lifecycle | verified | `bootstrap/startup.go`, `serv/serv.go` | Service startup order uses map iteration; do not depend on ordering. |
8
+ | HTTP server, routing, recovery, logging, CORS | verified | `httpx/serv.go`, middleware, HTTP tests, `references/auth-security.md` | `RegisterRouter` currently accepts `func(*gin.RouterGroup)`; CORS echoes request origin and must match the business security model; the built-in `/ping` payload has a timestamp formatting defect in the inspected baseline. |
9
+ | `apix` binding, typed parameters, response handling | verified | `apix/params.go`, `apix/handle.go` | Typed getters and `HandleData` signatures are version-sensitive. |
10
+ | Persistent CRUD module workflow | verified | `references/persistent-crud.md`, generated-project tests, live MySQL verification | Supports only MySQL and MongoDB in the verified workflow; HTTP is an optional adapter, not required for service-level CRUD. MongoDB effect verification still requires reachable development infrastructure. |
11
+ | MySQL and MongoDB adapters | verified | `domainx` adapters and `test/dx_test.go` | Requires external services and matching configuration. |
12
+ | Baseline `domainx/dx` CRUD query facade | verified | `domainx/dx/dx.go`, generated CRUD tests, real consumers | Some operations require an ID or non-empty matches as a safety guard. |
13
+ | Advanced `domainx/dx` access | verified | `references/advanced-data-access.md`, `domainx/dx/dx.go`, compile fixture | External database behavior for aggregates, geo queries, indexes, escape hatches, and transactions must be integration-tested per backend. |
14
+ | Memory, JSON, and SQLite cache | verified | `references/cache.md`, local cache behavior fixture, `cache/` source | Local backends do not prove Redis or cross-process behavior. |
15
+ | Redis cache | source-verified | `cache/cache.redis.go`, `test/cache_test.go`, `references/cache.md` | Reads `redis.addr`, `redis.password`, and `redis.db`; runtime behavior requires configured Redis infrastructure before it is claimed in a delivery. |
16
+ | Multi-level cache and singleflight | verified | `cache/cache.go`, local behavior fixture | Loader and invalidation behavior must be tested per value type. |
17
+ | `cronx.AddCronTask` | verified | `references/scheduled-tasks.md`, `cronx/cron.go`, local fixture | Uses second-level cron expressions and optional timeout. |
18
+ | `cronx.AddDelayTask`, `AddOnceTask` | verified | `references/scheduled-tasks.md`, `cronx/cron.go`, local fixture | Delay/once jobs run once and start cron internally. |
19
+ | `cronx.AddEveryTask` | verified | `cronx/cron.go`, local fixture, Gorig `92c28b5` | Fixed interval helper delegates to `AddCronTask`; require commit `92c28b5` or equivalent in the target version. |
20
+ | `cronx` persistent delay/once tasks | source-verified | `cronx/persist.go`, `test/cron_persist_test.go`, local fixture | Requires Gorig `0af68e8` or later plus configured Redis. Local validation covers handler/payload errors; runtime persistence requires Redis integration. |
21
+ | Local `messagex` | verified | `references/messaging.md`, `mid/messagex/`, local fixture | Process-local only. Verify lifecycle and unsubscribe behavior per use case. |
22
+ | Redis `messagex` | source-verified | `references/messaging.md`, `mid/messagex/`, `test/message_test.go` | Requires configured Redis integration before runtime behavior is claimed. |
23
+ | Sequential messages, retry, local DLQ delivery | verified | `broker.simple.go`, local fixture | DLQ applies to sequential subscribers. Local broker cannot replay because it has no store. |
24
+ | Redis DLQ replay | source-verified | `broker.simple.go`, Redis tests | Requires configured Redis integration before runtime replay behavior is claimed. |
25
+ | RabbitMQ broker enum/path | unsupported | `mid/messagex/serv.go` | No broker implementation is constructed. |
26
+ | SSE middleware and send helpers | verified | `httpx/ssex/`, `test/ssex_test.go` | Endpoint must be GET and must handle disconnects. |
27
+ | Memory-backed JWT token manager and `SignDef` | verified | `mid/tokenx/`, `httpx/mid.sign.go`, local auth fixture, `references/auth-security.md` | Single-process default with local `./tokens.json` persistence; not cross-instance session storage. Test expiry, refresh, revoke, logout, and forbidden paths per app. |
28
+ | Redis token manager and `SignRedis` | verified | `mid/tokenx/serv.go`, `mid/tokenx/redis.manager.go`, `test/tokenx_redis_test.go` | Implemented in local `master` commit `4dcf601` and expected in releases that include the Redis manager, such as planned `v0.0.53+`. Use it with Redis configuration. The real Redis integration test covers generate/reuse/clean/refresh/destroy/effective behavior and skips only when the environment lacks Redis. |
29
+ | Role and attribute filtering | verified | `httpx.SignUserDef`, local auth fixture | Filters JWT `UserInfo` only; use service-layer authorization for database-owner or live-state rules. |
30
+ | Debounce/rate protection | verified | `httpx/mid.debounce.go`, local auth/security fixture | Process-global state; whitelist API is spelled `DebouceAw`; repeated requests key by user id when token is valid, otherwise client IP. |
31
+ | Request signing | unsupported | source search, `references/auth-security.md` | No verified generic request-signature middleware in the inspected baseline. Implement custom signing only after explicit security design confirmation. |
32
+ | Outbound HTTP helpers | verified | `httpx/http.go`, local outbound fixture, `references/outbound-http.md` | Timeout support is helper-specific and `SetTimeOutTmp` is process-global; bad JSON/XML/status behavior differs by helper. |
33
+ | Structured logging and trace context | verified | `utils/logger/`, logger tests | Propagate the request context instead of replacing it mid-request. |
34
+ | Gorig OM integration | experimental | `gorig-om` | Separate repository and configuration; validate against its pinned Gorig version. |
35
+ | Gorig Hub and Node | experimental | `gorig-hub`, `gorig-node` | Ecosystem components, not automatic core-framework behavior. |
36
+
37
+ Update this matrix whenever source inspection or effect verification changes a status.
@@ -0,0 +1,48 @@
1
+ # Environment Configuration
2
+
3
+ Gorig configuration uses Viper and loads a YAML file from `./_bin/` or `./`. The configuration name is selected before file loading.
4
+
5
+ ## Select an Environment
6
+
7
+ Use the `GORIG_SYS_MODE` environment variable:
8
+
9
+ ```sh
10
+ GORIG_SYS_MODE=local go run ./_cmd
11
+ GORIG_SYS_MODE=dev go run ./_cmd
12
+ GORIG_SYS_MODE=prod ./demo-api
13
+ ```
14
+
15
+ The basic project generates:
16
+
17
+ - `_bin/local.yaml`
18
+ - `_bin/dev.yaml`
19
+ - `_bin/prod.yaml`
20
+
21
+ Each file declares its own `sys.mode` and HTTP address. The generated `/hello` response includes the loaded mode so the selection can be effect-tested.
22
+
23
+ ## Override Values
24
+
25
+ Gorig uses the `GORIG` prefix and replaces dots with underscores. Examples:
26
+
27
+ ```sh
28
+ GORIG_SYS_MODE=prod
29
+ GORIG_API_REST_ADDR=:8080
30
+ ```
31
+
32
+ Use environment variables or external secret management for passwords, private keys, tokens, and production endpoints. Do not put real secrets in generated or skill examples.
33
+
34
+ ## Working Directory Requirement
35
+
36
+ Configuration paths are relative to the process working directory. Start the application from the project root unless the deployment explicitly arranges the configuration path.
37
+
38
+ Go tests run with each package directory as the working directory. Shared external-style tests therefore keep a minimal configuration under `test/_bin/local.yaml`.
39
+
40
+ ## Validate Selection
41
+
42
+ Do not validate configuration only by checking files. Start at least two environments and assert:
43
+
44
+ - The process listens on the expected port.
45
+ - `/hello` returns the expected `mode`.
46
+ - Shutdown completes cleanly.
47
+
48
+ If an environment variable overrides the port, report that override in the verification evidence.
@@ -0,0 +1,190 @@
1
+ # Verified Framework API Baseline
2
+
3
+ Use these snippets only after checking the target project's resolved Gorig version. They reflect the locally inspected `master` baseline and intentionally avoid incomplete scenarios that belong to later roadmap phases.
4
+
5
+ ## Application Entry
6
+
7
+ `bootstrap.StartUp()` registers the built-in HTTP service. Do not register the same `httpx.Startup` lifecycle a second time unless the resolved framework version or a distinct server requires it.
8
+
9
+ ```go
10
+ package main
11
+
12
+ import "github.com/jom-io/gorig/bootstrap"
13
+
14
+ import _ "example/domain"
15
+
16
+ func main() {
17
+ bootstrap.StartUp()
18
+ }
19
+ ```
20
+
21
+ Import component packages for their documented `init()` registration only when the application uses them.
22
+
23
+ ## Route and Controller
24
+
25
+ The inspected `RegisterRouter` callback receives `*gin.RouterGroup`.
26
+
27
+ ```go
28
+ func init() {
29
+ httpx.RegisterRouter(func(root *gin.RouterGroup) {
30
+ group := root.Group("/hello")
31
+ group.GET("", getHello)
32
+ })
33
+ }
34
+
35
+ func getHello(ctx *gin.Context) {
36
+ defer apix.HandlePanic(ctx)
37
+ name, err := apix.GetParamType[string](ctx, "name", apix.NotForce, "world")
38
+ if err != nil {
39
+ return
40
+ }
41
+ result := map[string]string{"message": "hello " + name}
42
+ apix.HandleData(ctx, consts.CurdSelectFailCode, result, nil)
43
+ }
44
+ ```
45
+
46
+ Do not use obsolete examples that call typed parameter getters without `Force`/`NotForce`, discard their returned error, or call `HandleData` without a business error code.
47
+
48
+ ## Custom Lifecycle Service
49
+
50
+ ```go
51
+ err := serv.RegisterService(serv.Service{
52
+ Code: "WORKER",
53
+ Startup: workerStartup,
54
+ Shutdown: workerShutdown,
55
+ })
56
+ if err != nil {
57
+ sys.Exit(err)
58
+ }
59
+ ```
60
+
61
+ Do not rely on relative startup order among registered services unless the target version provides ordering guarantees.
62
+
63
+ ## Model and dx
64
+
65
+ For advanced filters, aggregates, projections, batch scans, indexes, direct-driver escape hatches, and transaction boundaries, read `advanced-data-access.md`.
66
+
67
+ ```go
68
+ type Order struct {
69
+ UserID int64 `bson:"user_id" json:"user_id"`
70
+ Status string `bson:"status" json:"status"`
71
+ }
72
+
73
+ func (*Order) DConfig() (domainx.ConType, string, string) {
74
+ return domainx.Mongo, "main", "order"
75
+ }
76
+
77
+ func init() {
78
+ domainx.AutoMigrate(
79
+ func() domainx.ConTable {
80
+ return dx.On[Order](context.Background()).Complex()
81
+ },
82
+ domainx.CtIdx(domainx.Idx, "user_id", "status"),
83
+ )
84
+ }
85
+ ```
86
+
87
+ Common operations in the inspected version:
88
+
89
+ ```go
90
+ id, err := dx.On(ctx, &order).Save()
91
+ one, err := dx.On[Order](ctx).WithID(id).Get()
92
+ list, err := dx.On[Order](ctx).Eq("status", status).Sort("id").Find()
93
+ err = dx.On[Order](ctx).WithID(id).Update("status", "done")
94
+ err = dx.On[Order](ctx).WithID(id).Delete()
95
+ ```
96
+
97
+ Update and delete operations intentionally reject an empty ID/filter. Preserve those guards.
98
+
99
+ The optional boolean on `Eq`, `Like`, and related methods means "ignore the framework's empty-value check." `true` therefore forces zero/empty values into the query; it does not skip the filter. Omit the boolean for ordinary optional filters.
100
+
101
+ ## Cache
102
+
103
+ For cache-aside, invalidation, counters, queues, multi-level cache, singleflight, and source-of-truth guidance, read `cache.md`.
104
+
105
+ ```go
106
+ memory := cache.New[Order](cache.Memory, time.Minute, time.Minute)
107
+ jsonFile := cache.New[Order](cache.JSON, "orders")
108
+ sqlite := cache.New[Order](cache.Sqlite, "orders")
109
+ redis := cache.New[Order](cache.Redis)
110
+ ```
111
+
112
+ Test local backends independently. Require explicit Redis configuration before claiming Redis behavior is verified.
113
+
114
+ ## Scheduled Task
115
+
116
+ For task lifecycle, timeout, panic recovery, deduplication, delay/once jobs, and verification rules, read `scheduled-tasks.md`.
117
+
118
+ ```go
119
+ cronx.AddCronTask("0 */5 * * * *", func(ctx context.Context) {
120
+ logger.Info(ctx, "scheduled sync")
121
+ }, 30*time.Second)
122
+
123
+ cronx.AddEveryTask(5*time.Minute, func(ctx context.Context) {
124
+ logger.Info(ctx, "interval sync")
125
+ }, 30*time.Second)
126
+
127
+ type DelayPayload struct {
128
+ OrderID int64 `json:"order_id"`
129
+ }
130
+
131
+ func (DelayPayload) PersistPayload() {}
132
+
133
+ func handleDelay(ctx context.Context, payload DelayPayload) error {
134
+ logger.Info(ctx, "persistent delay task", zap.Int64("order_id", payload.OrderID))
135
+ return nil
136
+ }
137
+
138
+ _ = cronx.RegisterPersistTask(handleDelay)
139
+ _, _ = cronx.AddPersistDelayTask(time.Minute, handleDelay, DelayPayload{OrderID: 1}, 30*time.Second)
140
+ ```
141
+
142
+ Use `AddEveryTask` only when the target Gorig version includes commit `92c28b5` or an equivalent fix. Older versions can recursively lock during registration; use `AddCronTask("@every <interval>", ...)` directly on those versions.
143
+
144
+ Use persistent delay/once tasks only when the target Gorig version includes commit `0af68e8` or an equivalent implementation and Redis is configured. Persistent handlers must be named functions, payloads must implement `PersistPayload`, and payloads must be JSON serializable.
145
+
146
+ ## Messaging
147
+
148
+ For broker selection, sequential subscribers, retries, DLQ behavior, Redis integration, unsubscribe cleanup, and message-to-SSE composition, read `messaging.md`.
149
+
150
+ ```go
151
+ broker := messagex.Ins(messagex.Local)
152
+ subID, err := broker.RegisterTopic("order.created", func(msg *messagex.Message) *errors.Error {
153
+ return nil
154
+ })
155
+ if err == nil {
156
+ defer broker.UnRegisterTopic("order.created", subID)
157
+ }
158
+ broker.PublishNewMsg(ctx, "order.created", map[string]any{"order_id": id})
159
+ ```
160
+
161
+ Use `messagex.Redis` only with configured Redis integration. RabbitMQ is unsupported in the inspected baseline.
162
+
163
+ ## SSE
164
+
165
+ For streaming route structure, event/error payloads, long-lived streams, disconnect handling, and message-to-SSE composition, read `sse.md`.
166
+
167
+ ```go
168
+ httpx.RegisterRouter(func(root *gin.RouterGroup) {
169
+ root.GET("/events", ssex.Mid(), func(ctx *gin.Context) {
170
+ _ = ssex.SendOK(ctx, "update", map[string]any{"ready": true})
171
+ })
172
+ })
173
+ ```
174
+
175
+ Handle send errors and client disconnects in long-lived streams.
176
+
177
+ ## Authentication Boundary
178
+
179
+ `httpx.SignDef()` uses the memory-backed token manager. Verify generation, expiry, refresh, revocation, and persistence for the application before production use.
180
+
181
+ Do not use `httpx.SignRedis()` against a version whose `tokenx.Redis` manager is unimplemented.
182
+
183
+ ## Logging
184
+
185
+ ```go
186
+ logger.Info(ctx, "order created", zap.Int64("order_id", id))
187
+ logger.Error(ctx, "save order failed", zap.Error(err))
188
+ ```
189
+
190
+ Pass request context through every layer. Use `logger.NewCtx()` only when there is no request context, such as an independent background job.
@@ -0,0 +1,143 @@
1
+ # Messaging
2
+
3
+ Use this reference when a task needs in-process pub/sub, Redis-backed messaging, sequential subscribers, retries, dead letter queues, replay, unsubscribe behavior, or message-to-SSE composition.
4
+
5
+ Always inspect the target project's resolved Gorig source first. The examples below reflect the locally inspected Gorig `master` commit `35bbefb`.
6
+
7
+ ## Broker Choice
8
+
9
+ Use `github.com/jom-io/gorig/mid/messagex`.
10
+
11
+ ```go
12
+ localBroker := messagex.Ins(messagex.Local)
13
+ redisBroker := messagex.Ins(messagex.Redis)
14
+ ```
15
+
16
+ Supported in the inspected baseline:
17
+
18
+ | Broker | Status | Notes |
19
+ |---|---|---|
20
+ | `messagex.Local` | verified | Process-local pub/sub. Good for in-process events and tests. |
21
+ | `messagex.Redis` | source-verified | Uses Redis cache/list storage. Requires configured Redis before runtime behavior is claimed. |
22
+ | `messagex.RabbitMQ` | unsupported | Enum exists, but no broker implementation is constructed. |
23
+
24
+ Do not silently switch to Redis. Redis messaging requires `redis.addr`, `redis.password`, and `redis.db` or the corresponding `GORIG_REDIS_*` environment variables.
25
+
26
+ ## Publish and Subscribe
27
+
28
+ Concurrent subscribers receive messages independently. Handler calls run asynchronously for non-sequential subscriptions.
29
+
30
+ ```go
31
+ broker := messagex.Ins(messagex.Local)
32
+
33
+ subID, err := broker.RegisterTopic("order.created", func(msg *messagex.Message) *errors.Error {
34
+ orderID := msg.GetValueInt64("order_id")
35
+ logger.Info(msg.Ctx, "order created event received", zap.Int64("order_id", orderID))
36
+ return nil
37
+ })
38
+ if err != nil {
39
+ return err
40
+ }
41
+ defer broker.UnRegisterTopic("order.created", subID)
42
+
43
+ broker.PublishNewMsg(ctx, "order.created", map[string]any{
44
+ "order_id": id,
45
+ })
46
+ ```
47
+
48
+ `PublishNewMsg` converts struct and map content to `map[string]interface{}` and lowercases content keys. Use the typed getters when consuming:
49
+
50
+ ```go
51
+ msg.GetValue("name")
52
+ msg.GetValueStr("name")
53
+ msg.GetValueInt64("order_id")
54
+ msg.GetValueFloat64("amount")
55
+ ```
56
+
57
+ ## Sequential Consumption
58
+
59
+ Use `RegisterTopicSeq` when a subscriber must process messages one at a time in publish order.
60
+
61
+ ```go
62
+ subID, err := broker.RegisterTopicSeq("order.created", func(msg *messagex.Message) *errors.Error {
63
+ return rebuildProjection(msg.Ctx, msg.GetValueInt64("order_id"))
64
+ }, messagex.WithMaxRetry(3), messagex.WithRetryIntervals(500*time.Millisecond))
65
+ ```
66
+
67
+ Sequential behavior is per subscriber. Multiple subscribers still receive their own copy of each message.
68
+
69
+ ## Retry and DLQ
70
+
71
+ Sequential subscribers can retry failed messages. When retries are exhausted, the message is sent to the configured DLQ topic.
72
+
73
+ ```go
74
+ subID, err := broker.RegisterTopicSeq(
75
+ "order.project",
76
+ handler,
77
+ messagex.WithMaxRetry(2),
78
+ messagex.WithRetryIntervals(100*time.Millisecond, time.Second),
79
+ messagex.WithDLQTopic("order.project.dead"),
80
+ )
81
+ ```
82
+
83
+ Local broker behavior:
84
+
85
+ - Retry is verified.
86
+ - Failed messages can be published to a DLQ topic.
87
+ - `ReplayDLQ` is not available because the local broker has no store; it returns `store not initialized for dlq replay`.
88
+
89
+ Redis broker behavior:
90
+
91
+ - Retry, delayed requeue, and replay use Redis storage.
92
+ - `ReplayDLQ(topic, limit)` requeues messages from `topic + ".dlq"` unless a custom DLQ topic was configured.
93
+ - Verify with disposable Redis before claiming replay behavior.
94
+
95
+ ## Unsubscribe and Cleanup
96
+
97
+ Always keep the returned subscription ID and unregister when the subscriber is scoped to a test, lifecycle, or temporary workflow:
98
+
99
+ ```go
100
+ subID, err := broker.RegisterTopic(topic, handler)
101
+ if err != nil {
102
+ return err
103
+ }
104
+ defer broker.UnRegisterTopic(topic, subID)
105
+ ```
106
+
107
+ Unsubscribe closes the subscriber channel and removes topic state when no subscribers remain. Do not publish to a topic from a goroutine that outlives its intended subscriber lifecycle unless that behavior is part of the design.
108
+
109
+ ## Message to SSE Composition
110
+
111
+ For simple same-process streams, register a local topic subscriber inside the SSE handler and unregister it when the request context is done:
112
+
113
+ ```go
114
+ func streamOrders(ctx *gin.Context) {
115
+ subID, err := messagex.Ins(messagex.Local).RegisterTopic("order.updated", func(msg *messagex.Message) *errors.Error {
116
+ if sendErr := ssex.SendOK(ctx, "order.updated", msg.Content); sendErr != nil {
117
+ return errors.Sys(sendErr.Error())
118
+ }
119
+ return nil
120
+ })
121
+ if err != nil {
122
+ _ = ssex.SendError(ctx, "order.updated", err.Error())
123
+ return
124
+ }
125
+ defer messagex.Ins(messagex.Local).UnRegisterTopic("order.updated", subID)
126
+
127
+ <-ctx.Request.Context().Done()
128
+ }
129
+ ```
130
+
131
+ This pattern holds a request open. Use it only for SSE routes and handle send errors as disconnect signals. For cross-process events, use Redis messaging after Redis integration is verified.
132
+
133
+ ## Verification Checklist
134
+
135
+ - Compile against the target project's resolved Gorig version.
136
+ - Verify local publish/consume for success payloads.
137
+ - Verify multiple subscribers each receive each message.
138
+ - Verify sequential subscribers preserve publish order.
139
+ - Verify retry count and retry ordering.
140
+ - Verify DLQ delivery for local broker or DLQ replay for Redis broker.
141
+ - Verify unsubscribe prevents further delivery.
142
+ - Verify Redis publish/consume, ordering, retry, DLQ, replay, and cleanup only against disposable or explicitly supplied Redis configuration.
143
+ - Record RabbitMQ as unsupported unless the target version adds a real implementation.
@@ -0,0 +1,46 @@
1
+ # Repository Onboarding
2
+
3
+ Inspect only the files needed to build an accurate request path and lifecycle model.
4
+
5
+ ## Target Project First
6
+
7
+ Locate:
8
+
9
+ - `AGENTS.md` and repository instructions.
10
+ - `go.mod`, `go.sum`, and `replace` directives.
11
+ - Entry points such as `_cmd/main.go`, `_cmd/api/main.go`, or `simple/main.go`.
12
+ - Environment files under `_bin/` and configuration access under `global/`.
13
+ - Domain registration and blank imports.
14
+ - Existing Router -> Controller -> Service -> Model/Domainx modules.
15
+ - Existing tests, build scripts, deployment files, and documentation.
16
+
17
+ Trace one complete existing behavior before proposing a new pattern.
18
+
19
+ ## Framework Files by Concern
20
+
21
+ Read the resolved Gorig version, not an unrelated checkout.
22
+
23
+ - Startup and lifecycle: `bootstrap/startup.go`, `serv/serv.go`.
24
+ - HTTP server and routing: `httpx/serv.go`, `httpx/mid.*.go`.
25
+ - Parameters and responses: `apix/params.go`, `apix/handle.go`, `apix/response/response.go`.
26
+ - Data access: `domainx/`, `domainx/dx/dx.go`, relevant database adapter, and `test/dx_test.go`.
27
+ - Cache: `cache/` and `test/cache_test.go`.
28
+ - Scheduled tasks: `cronx/` and `test/cron_test.go`.
29
+ - Messaging: `mid/messagex/` and `test/message_test.go`.
30
+ - Authentication: `mid/tokenx/`, `httpx/mid.sign.go`, and relevant tests or real consumers.
31
+ - Security middleware: `httpx/mid.cors.go`, `httpx/mid.debounce.go`, `httpx/mid.logger.go`, and response helpers for auth/rate-limit errors.
32
+ - Outbound HTTP: `httpx/http.go` and any project-specific external API wrappers.
33
+ - SSE: `httpx/ssex/` and `test/ssex_test.go`.
34
+ - Configuration, errors, and logs: `utils/cofigure/`, `utils/errors/`, `utils/logger/`.
35
+
36
+ ## Reading Strategy
37
+
38
+ 1. Trace imports and `init()` registration from the entry point.
39
+ 2. Trace one HTTP request from route to response.
40
+ 3. Confirm parameter and response function signatures.
41
+ 4. Confirm context, error, and logging conventions.
42
+ 5. Confirm component startup and shutdown behavior.
43
+ 6. Confirm the behavior in tests or a real project.
44
+ 7. Reuse the target project's established architecture unless there is a clear reason to change it.
45
+
46
+ If the project layout is unfamiliar, discover it with `rg --files` and package imports before asking the user.
@@ -0,0 +1,162 @@
1
+ # Outbound HTTP
2
+
3
+ Use this reference when the backend must call another HTTP service, fetch remote data, post forms, send JSON/XML, forward headers, propagate context, enforce timeouts, handle bad responses, or fetch images.
4
+
5
+ Always inspect the target project's resolved `httpx/http.go` before coding. The inspected API is convenient but not a complete production client abstraction.
6
+
7
+ ## Business Workflow
8
+
9
+ Translate business language before choosing helpers:
10
+
11
+ - "Call the partner API" means define the request, timeout, retry/idempotency expectation, headers, response mapping, and business error shown to callers.
12
+ - "Forward the user's token" means use context-aware helpers only when forwarding `Authorization` is intended and safe.
13
+ - "Do not hang the request" means use a per-call timeout and test it against a slow local server.
14
+ - "Bad upstream response" means decide whether the caller sees a retryable error, fallback data, or a business failure.
15
+ - "Fetch an image" means verify content type, size limits, and allowed hosts if the URL comes from users.
16
+
17
+ For material outbound changes, confirm:
18
+
19
+ ```text
20
+ Business goal:
21
+ - <external effect or data needed>
22
+
23
+ Upstream contract:
24
+ - Method/path:
25
+ - Request body/query:
26
+ - Headers:
27
+ - Expected success response:
28
+ - Bad response behavior:
29
+ - Timeout:
30
+
31
+ Security:
32
+ - Which user headers are forwarded:
33
+ - Which secrets/config keys are required:
34
+ - URL allowlist or SSRF boundary:
35
+
36
+ Verification:
37
+ - Local httptest server cases:
38
+ - Timeout case:
39
+ - Bad JSON/XML/status case:
40
+ ```
41
+
42
+ ## Supported Helpers
43
+
44
+ The inspected source exposes:
45
+
46
+ ```go
47
+ body, err := httpx.Get(url, map[string]string{"q": "abc"})
48
+ body, err := httpx.GetHeader(url, params, map[string]string{"X-Trace-ID": traceID})
49
+ data, err := httpx.GetMap(url, params)
50
+ data, err := httpx.GetMapHeader(url, params, headers)
51
+
52
+ body, err := httpx.PostForm(url, form)
53
+ body, err := httpx.PostJSONResp(url, payload)
54
+ body, err := httpx.PostJSONRespHeader(url, payload, headers)
55
+ data, err := httpx.PostJSON(url, payload)
56
+ data, err := httpx.PostJSONHeader(url, payload, headers)
57
+
58
+ data, err := httpx.GetByCtx(ctx, url, map[string]interface{}{"id": id})
59
+ data, err := httpx.PostJSONByCtx(ctx, url, payload)
60
+
61
+ xmlBody, err := httpx.PostXML(url, map[string]string{"id": "1"})
62
+ parsed, err := httpx.ParseXML[Resp](xmlBody)
63
+
64
+ img, contentType, ext, err := httpx.FetchImage(url)
65
+ ```
66
+
67
+ Known boundaries in inspected source:
68
+
69
+ - `Get` and `PostForm` use package-level `http.Get`/`http.PostForm`, not the timeout-aware shared client.
70
+ - `GetHeader`, `PostJSONResp`, and `PostJSONRespHeader` use the shared client.
71
+ - `SetTimeOutTmp(duration)` mutates the package-level shared client temporarily; it is process-global and can affect concurrent calls.
72
+ - `PostJSONResp` returns an error for non-200/non-201 status and includes the response body string.
73
+ - `PostJSONRespHeader` does not reject non-2xx by itself in the inspected source.
74
+ - `ParseJSON` returns nil for empty or invalid JSON after logging; callers must validate required fields.
75
+ - `ParseXML` panics on invalid XML in the inspected source. Wrap or avoid it when upstream XML may be malformed.
76
+ - `FetchImage` infers type from URL suffix and does not enforce response status, response content type, size, or host allowlist.
77
+
78
+ ## Context and Header Propagation
79
+
80
+ For internal calls that intentionally forward the caller's token:
81
+
82
+ ```go
83
+ result, err := httpx.PostJSONByCtx(ctx, upstreamURL, payload)
84
+ ```
85
+
86
+ This forwards only `Authorization` in inspected source. If trace or tenant headers must propagate, build an explicit header map:
87
+
88
+ ```go
89
+ headers := map[string]string{
90
+ "X-Request-ID": apix.GetTraceID(ctx),
91
+ "X-Tenant-ID": tenantID,
92
+ }
93
+ result, err := httpx.PostJSONHeader(upstreamURL, payload, headers)
94
+ ```
95
+
96
+ Do not forward user tokens to third-party systems unless that is the explicit contract.
97
+
98
+ ## Timeout Pattern
99
+
100
+ For helpers that use the shared client:
101
+
102
+ ```go
103
+ httpx.SetTimeOutTmp(300 * time.Millisecond)
104
+ resp, err := httpx.GetHeader(url, nil, nil)
105
+ ```
106
+
107
+ Because the timeout is package-global, prefer a narrow call scope and avoid parallel tests that depend on different timeout values. For production-grade per-request cancellation, use a local `http.Client` and `http.NewRequestWithContext` after confirming this custom path with the user.
108
+
109
+ ## Error Mapping
110
+
111
+ Keep outbound failure handling in the service layer:
112
+
113
+ - network timeout or connection refused: return a retryable business error when appropriate;
114
+ - non-success status: include safe upstream status and correlation id, not raw secrets or full upstream body;
115
+ - invalid JSON/XML: return a parse error with enough context for support;
116
+ - missing required fields: treat as bad upstream contract, not success with zero values;
117
+ - image fetch failures: reject unsupported/oversized/untrusted URLs before download when user-supplied.
118
+
119
+ ## XML Boundary
120
+
121
+ `PostXML` builds XML from simple string values and is suitable only for flat request bodies. For nested XML or signed XML, use typed `encoding/xml` structs and explicit tests.
122
+
123
+ Wrap `ParseXML` if malformed XML is a normal upstream failure:
124
+
125
+ ```go
126
+ func safeParseXML[T any](body string) (out *T, err *errors.Error) {
127
+ defer func() {
128
+ if r := recover(); r != nil {
129
+ err = errors.Sys("invalid upstream XML")
130
+ }
131
+ }()
132
+ return httpx.ParseXML[T](body)
133
+ }
134
+ ```
135
+
136
+ ## Image Fetch Boundary
137
+
138
+ Before fetching user-provided image URLs, define:
139
+
140
+ - allowed schemes and hosts;
141
+ - max response bytes;
142
+ - accepted content types;
143
+ - behavior for redirects;
144
+ - storage location and malware scanning responsibility when relevant.
145
+
146
+ Use the built-in `FetchImage` only for trusted internal or controlled fixture URLs unless these checks are added.
147
+
148
+ ## Verification Checklist
149
+
150
+ Use a local `httptest.Server` or controlled local HTTP server. Cover:
151
+
152
+ - GET query parameters;
153
+ - form POST content type and body;
154
+ - JSON POST request body and response mapping;
155
+ - XML POST and parse success;
156
+ - custom headers received by upstream;
157
+ - `Authorization` forwarding through `GetByCtx` or `PostJSONByCtx` only when intended;
158
+ - timeout against a slow handler;
159
+ - connection or bad status error handling;
160
+ - malformed JSON/XML response behavior;
161
+ - image fetch returns bytes, content type, and extension for a controlled image URL;
162
+ - no real secrets in URLs, headers, fixtures, or logs.