Go stdlib extensions — admitted only when the standard library doesn't offer it.
The long-term ambition is the slot Guava fills in Java: the utility layer a project reaches for before hand-writing a helper. Go's ecosystem splits that slot today — samber/lo owns generic collection transforms, gods owns data structures, lancet goes kitchen-sink — and go-stdx competes on discipline, not breadth: stdlib-mirror naming, and only the small, common data operations every service would otherwise re-wrap.
The name is the admission rule: a hand-rolled max, a slices.Clone re-implementation, or a strconv wrapper does not belong here — use stdlib. What earns a slot is the three-to-five-line wrapper real projects keep re-writing because the stdlib deliberately omits it. Generating the underlying data (a UUID, a hash) is not our job — we lean on a mature library for that and wrap only the shape callers pass around.
Subpackages mirror stdlib naming so call sites read like the standard library they extend:
| package | what | why not stdlib / a lib |
|---|---|---|
slicesx |
Uniq, UniqBy — first-occurrence, order-preserving dedup |
the "seen map" loop everyone re-writes; stdlib's slices has no transform family |
stringsx |
Truncate / TruncateEllipsis — byte-budget cuts; FirstNonBlank; SplitAndTrim |
the log-truncation and human-written-list helpers every service re-writes (cmp.Or covers non-empty, not non-blank) |
osx |
EnvStr / EnvBool / EnvInt / EnvInt64 / EnvDuration — typed env lookups with defaults; WriteFileAtomic — temp+rename write |
config-from-env boilerplate in every service; os.WriteFile can leave readers a torn file |
ptrx |
To / Value / FormatOr — optional-field pointer helpers |
no stdlib answer to &literal (k8s.io/utils/ptr exists because of the gap) |
filepathx |
DirBytes — recursive regular-file byte count |
the quota/GC accounting walk everyone re-writes |
tarx |
PackDir / UnpackDir — directory ⇄ tar.gz with zip-slip defense |
archive/tar leaves both loops to the caller, and the extraction loop is famously easy to get wrong |
shellx |
Quote — POSIX single-quoting |
Go has no shlex; unquoted interpolation into a shell line is an injection |
netx |
IsDNSHostname — ASCII DNS hostname syntax without IP literals |
net parses IP addresses but does not export hostname validation |
randx |
Hex(n) — n random bytes as lowercase hex |
the "short random id" helper every daemon re-writes |
uuid |
New, NewWithPrefix, V4, V7, V7Hex — resource / random / time-ordered ids |
thin wrappers over google/uuid for the resource ID, string, and dashless-hex shapes services keep re-wrapping |
timeline |
operation/stage interfaces and detached, structured snapshots | joins stages from independent processes by business ID; snapshots retain intervals, hierarchy, optional actors and results |
Rules of the house:
- Don't reinvent the data, wrap the shape: no chasing zero dependencies — a mature foundational library (
google/uuid, …) is a fine dependency. What we add is the small wrapper around it, not a re-implementation of it. - stdlib-first: when Go's standard library grows an equivalent, the entry here is deprecated and removed.
- Positioning is the bar: anything genuinely generic that projects would otherwise hand-write belongs here — no waiting for N copies to accumulate first.
import (
"github.com/compforge/go-stdx/osx"
"github.com/compforge/go-stdx/randx"
"github.com/compforge/go-stdx/slicesx"
"github.com/compforge/go-stdx/uuid"
)
port := osx.EnvInt("APP_PORT", 8080)
id := "job-" + randx.Hex(6)
resourceID := uuid.NewWithPrefix("job")
ids := slicesx.Uniq(rawIDs)Used by case-code-review, hostel, and other Go projects under this account.
Collect stage facts from concurrent components and processes under one timeline ID.
The timeline/manager package supplies six business entry points:
Begin, End, Record, Read, and optional Start / Finish.
You can record stages without declaring an operation beginning or result.
import (
"github.com/compforge/go-stdx/timeline"
"github.com/compforge/go-stdx/timeline/manager"
"github.com/compforge/go-stdx/timeline/store/sqlstore"
)
// Configure once per process using the application's existing *sql.DB.
m, err := manager.New(sqlstore.New(db), manager.Config{
MaxTimelines: 1024,
})
if err != nil {
return err
}
manager.SetDefault(m)
// Neither Start nor Finish is required.
if _, err := manager.Begin(operationID, "acquire_carrier",
timeline.WithStageActor(timeline.Actor{Name: podName}),
); err != nil {
return err
}
workErr := acquireCarrier(ctx)
if err := manager.End(operationID, "acquire_carrier", workErr); err != nil {
return err
}
snapshot, recordingErr := manager.Read(ctx, operationID)
// json.Marshal(snapshot) serializes the detached view.Record(id, Stage) imports a completed interval with its source timestamps and actor.
Start(id, operation, ...Attribute) optionally records the operation beginning;
Finish(id, result) optionally records its outcome, even without Start. Undeclared
outcomes remain unknown; Finish accepts late stages. For parallel stages sharing
a name, retain their returned handles to end them precisely.
Manager serves reads and writes from memory, loading Store on a cache miss and creating missing timelines for writes. It saves changes in the background. At capacity, LRU eviction releases cached copies; persisted stages can be restored and continued through their existing handles. Eviction does not finish a timeline.
This is a best-effort cache: warm reads may lag other processes, and unsaved facts
can be lost if final saving fails. Read returns the current view without waiting
for persistence. Use m.FlushID(ctx, id) for an explicit persistence checkpoint;
use Store directly when each operation must observe or update durable state.
Snapshot methods include Summary(), RunningStages() and LatestFailedStage().
At shutdown, stop producers, call m.Shutdown with an independent bounded context, then
close the database. Use an explicit Manager's methods for isolation. timeline.New
provides a standalone writer with explicit checkpoints; timeline/gospan provides a
sealed process-local backend. See the design and the
executable example.
MIT