@@ -57,21 +57,31 @@ Advanced loading with functional options for custom validators, middleware, and
5757
5858** Options:**
5959- ` WithSource(source Source) ` — Add a source
60- - ` WithValidator(name string, fn CustomValidatorFunc) ` — Register custom validator
61- - ` WithMiddleware(fn MiddlewareFunc) ` — Add transformation middleware
62- - ` WithInterpolationMax(max int) ` — Set interpolation recursion limit
60+ - ` WithValidator(name string, fn CustomValidatorFunc) ` — Register a custom per-field validator
61+ - ` WithModelValidator[T](fn func(*T) error) ` — Register a cross-field validator
62+ - ` WithMiddleware(fn MiddlewareFunc) ` — Add a value transformation middleware
63+ - ` WithInterpolationMaxDepth(n int) ` — Set interpolation recursion limit
64+ - ` WithAuditLogger(fn AuditLogger) ` — Receive a log of every field's resolved value and source
65+ - ` WithLoadHook(fn LoadHookFunc) ` — Receive success/duration/errCount after every load
6366
6467** Example:**
6568``` go
6669cfg , err := confkit.LoadWithOptions [Config](
6770 confkit.WithSource (confkit.FromYAML (" config.yaml" )),
6871 confkit.WithSource (confkit.FromEnv ()),
69- confkit.WithValidator (" custom" , func (val string ) error {
70- if val == " invalid" {
71- return fmt.Errorf (" cannot be 'invalid'" )
72+ // Cross-field validation
73+ confkit.WithModelValidator (func (cfg *Config) error {
74+ if cfg.TLSEnabled && cfg.CertPath == " " {
75+ return fmt.Errorf (" cert_path required when tls_enabled is true" )
7276 }
7377 return nil
7478 }),
79+ // Audit trail
80+ confkit.WithAuditLogger (func (entries []confkit.AuditEntry ) {
81+ for _ , e := range entries {
82+ log.Printf (" field=%s source=%s value=%s " , e.Field , e.Source , e.Value )
83+ }
84+ }),
7585)
7686```
7787
@@ -217,6 +227,26 @@ cfg, _ := confkit.Load[Config](confkit.FromTOML("config.toml"))
217227
218228---
219229
230+ ### FromYAMLFiles / FromJSONFiles / FromTOMLFiles
231+
232+ ```go
233+ func FromYAMLFiles(paths ...string) Source
234+ func FromJSONFiles(paths ...string) Source
235+ func FromTOMLFiles(paths ...string) Source
236+ ```
237+
238+ Merges multiple files of the same format into a single source. Later files override earlier ones; nested maps are deep-merged.
239+
240+ **Example:**
241+ ```go
242+ cfg, _ := confkit.Load[Config](
243+ confkit.FromYAMLFiles("base.yaml", "production.yaml", "local.yaml"),
244+ confkit.FromEnv(),
245+ )
246+ ```
247+
248+ ---
249+
220250### FromFlags
221251
222252```go
@@ -233,6 +263,42 @@ cfg, _ := confkit.Load[Config](confkit.FromFlags())
233263
234264---
235265
266+ ## Observability Submodules
267+
268+ ### confkit/prometheus
269+
270+ ```go
271+ import "github.com/MimoJanra/confkit/prometheus"
272+
273+ m := prometheus.NewMetrics(prometheus.DefaultRegisterer)
274+
275+ cfg, err := confkit.LoadWithOptions[Config](
276+ confkit.WithSource(confkit.FromEnv()),
277+ m.Hook(), // records loads_total, load_duration_seconds, errors_total
278+ )
279+ ```
280+
281+ **Metrics registered:**
282+ - `confkit_loads_total{status=" success|error" }` — counter
283+ - ` confkit_load_duration_seconds` — histogram
284+ - ` confkit_errors_total{kind=" validation" }` — counter
285+
286+ ---
287+
288+ ### confkit/otel
289+
290+ ` ` ` go
291+ import " github.com/MimoJanra/confkit/otel"
292+
293+ cfg , err := otel.Load [Config](ctx, tracer,
294+ confkit.FromEnv (),
295+ confkit.FromYAML (" config.yaml" ),
296+ )
297+ // Creates span "confkit.Load" with attributes: confkit.sources, confkit.success
298+ ```
299+
300+ ---
301+
236302## Cloud & Enterprise Sources
237303
238304### FromK8sConfigMap
@@ -310,17 +376,43 @@ Configuration is driven by struct tags. Common tags:
310376
311377### Validation Rules
312378
313- - `required` — Field must be present
314- - `min=N` — Minimum value (int, float, string length)
315- - `max=N` — Maximum value (int, float, string length)
316- - `oneof=val1,val2,...` — Must be one of the listed values
379+ **Numeric / range:**
380+ - `required` — field must be present and non-zero
381+ - `min=N` — minimum value (int/float) or minimum length (string)
382+ - `max=N` — maximum value (int/float) or maximum length (string)
383+ - `oneof=val1,val2,...` — must be one of the listed values
384+
385+ **Format (string fields):**
386+ - `email` — valid email address
387+ - `url` — valid URL (any scheme)
388+ - `http_url` — valid HTTP or HTTPS URL
389+ - `ip` — valid IPv4 or IPv6 address
390+ - `ipv4` — valid IPv4 address
391+ - `ipv6` — valid IPv6 address
392+ - `uuid` — valid UUID (v1–v5)
393+ - `hostname` — valid hostname per RFC 1123
394+ - `port` — valid port number 1–65535 (works on `int` and `string`)
395+ - `regex=pattern` — must match the regular expression
396+ - `len=N` — must be exactly N characters (Unicode-aware)
397+ - `contains=str` — must contain the substring
398+ - `startswith=str` — must start with the prefix
399+ - `endswith=str` — must end with the suffix
400+ - `alpha` — letters only
401+ - `alphanum` — letters and digits only
402+ - `numeric` — digits only
403+ - `lowercase` — must be all lowercase
404+ - `uppercase` — must be all uppercase
405+ - `notempty` — must not be blank (non-whitespace)
317406
318407**Example:**
319408```go
320409type Config struct {
321- Port int ` env:"PORT" validate:"required,min=1,max=65535"`
322- Status string ` validate:"oneof=active,inactive,pending"`
323- DatabaseURL string ` validate:"required" secret:"true"`
410+ Port int ` env:"PORT" validate:"required,port"`
411+ AdminEmail string ` env:"EMAIL" validate:"required,email"`
412+ APIKey string ` env:"API_KEY" validate:"required,len=32" secret:"true"`
413+ Environment string ` env:"ENV" validate:"oneof=dev,staging,prod"`
414+ ServiceURL string ` env:"SVC_URL" validate:"http_url"`
415+ ServiceID string ` env:"SVC_ID" validate:"uuid"`
324416}
325417```
326418
0 commit comments