2026-03-18 00:21:08 +00:00
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// Embedded assets for the Core framework.
|
2026-03-18 00:21:08 +00:00
|
|
|
//
|
2026-03-18 09:37:34 +00:00
|
|
|
// Embed provides scoped filesystem access for go:embed and any fs.FS.
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// Also includes build-time asset packing (AST scanner + compressor)
|
|
|
|
|
// and template-based directory extraction.
|
2026-03-18 00:21:08 +00:00
|
|
|
//
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// Usage (mount):
|
2026-03-18 00:21:08 +00:00
|
|
|
//
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// sub, _ := core.Mount(myFS, "lib/persona")
|
|
|
|
|
// content, _ := sub.ReadString("secops/developer.md")
|
|
|
|
|
//
|
|
|
|
|
// Usage (extract):
|
2026-03-18 00:21:08 +00:00
|
|
|
//
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// core.Extract(fsys, "/tmp/workspace", data)
|
2026-03-18 00:21:08 +00:00
|
|
|
//
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// Usage (pack):
|
|
|
|
|
//
|
|
|
|
|
// refs, _ := core.ScanAssets([]string{"main.go"})
|
|
|
|
|
// source, _ := core.GeneratePack(refs)
|
2026-03-18 00:21:08 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-18 01:12:10 +00:00
|
|
|
"bytes"
|
2026-03-18 00:21:08 +00:00
|
|
|
"compress/gzip"
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
"embed"
|
2026-03-18 00:21:08 +00:00
|
|
|
"encoding/base64"
|
|
|
|
|
"fmt"
|
|
|
|
|
"go/ast"
|
|
|
|
|
"go/parser"
|
|
|
|
|
"go/token"
|
|
|
|
|
"io"
|
2026-03-18 01:12:10 +00:00
|
|
|
"io/fs"
|
2026-03-18 00:21:08 +00:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"sync"
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
"text/template"
|
2026-03-18 00:21:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// --- Runtime: Asset Registry ---
|
|
|
|
|
|
|
|
|
|
// AssetGroup holds a named collection of packed assets.
|
|
|
|
|
type AssetGroup struct {
|
|
|
|
|
assets map[string]string // name → compressed data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
assetGroups = make(map[string]*AssetGroup)
|
|
|
|
|
assetGroupsMu sync.RWMutex
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AddAsset registers a packed asset at runtime (called from generated init()).
|
|
|
|
|
func AddAsset(group, name, data string) {
|
|
|
|
|
assetGroupsMu.Lock()
|
|
|
|
|
defer assetGroupsMu.Unlock()
|
|
|
|
|
|
|
|
|
|
g, ok := assetGroups[group]
|
|
|
|
|
if !ok {
|
2026-03-18 01:12:10 +00:00
|
|
|
g = &AssetGroup{assets: make(map[string]string)}
|
2026-03-18 00:21:08 +00:00
|
|
|
assetGroups[group] = g
|
|
|
|
|
}
|
|
|
|
|
g.assets[name] = data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAsset retrieves and decompresses a packed asset.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := core.GetAsset("mygroup", "greeting")
|
|
|
|
|
// if r.OK { content := r.Value.(string) }
|
|
|
|
|
func GetAsset(group, name string) Result {
|
2026-03-18 00:21:08 +00:00
|
|
|
assetGroupsMu.RLock()
|
|
|
|
|
g, ok := assetGroups[group]
|
|
|
|
|
assetGroupsMu.RUnlock()
|
|
|
|
|
if !ok {
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
return Result{}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
data, ok := g.assets[name]
|
|
|
|
|
if !ok {
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
return Result{}
|
|
|
|
|
}
|
|
|
|
|
s, err := decompress(data)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{s, true}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAssetBytes retrieves a packed asset as bytes.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := core.GetAssetBytes("mygroup", "file")
|
|
|
|
|
// if r.OK { data := r.Value.([]byte) }
|
|
|
|
|
func GetAssetBytes(group, name string) Result {
|
|
|
|
|
r := GetAsset(group, name)
|
|
|
|
|
if !r.OK {
|
|
|
|
|
return r
|
|
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{[]byte(r.Value.(string)), true}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Build-time: AST Scanner ---
|
|
|
|
|
|
|
|
|
|
// AssetRef is a reference to an asset found in source code.
|
|
|
|
|
type AssetRef struct {
|
2026-03-18 01:12:10 +00:00
|
|
|
Name string
|
|
|
|
|
Path string
|
|
|
|
|
Group string
|
|
|
|
|
FullPath string
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ScannedPackage holds all asset references from a set of source files.
|
|
|
|
|
type ScannedPackage struct {
|
fix: AX audit round 5 — full naming, Result returns throughout
Renames (via GoLand refactor):
- Option.K → Key, Option.V → Value
- Err.Op → Operation, Err.Msg → Message, Err.Err → Error
- CrashSystem.OS → OperatingSystem, Arch → Architecture
- TaskID → TaskIdentifier, TaskWithID → TaskWithIdentifier
- Ipc → IPC, BaseDir → BaseDirectory
- ServiceRuntime.Opts → Options
Return type changes:
- Options.Get, Config.Get → Result (was (any, bool))
- Embed.ReadDir → Result (was ([]fs.DirEntry, error))
- Translator.Translate, I18n.Translate → Result (was string)
Rule 6:
- data.go: propagate opts.Get failure, typed error for bad fs.FS
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 16:32:43 +00:00
|
|
|
PackageName string
|
|
|
|
|
BaseDirectory string
|
|
|
|
|
Groups []string
|
|
|
|
|
Assets []AssetRef
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ScanAssets parses Go source files and finds asset references.
|
|
|
|
|
// Looks for calls to: core.GetAsset("group", "name"), core.AddAsset, etc.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
func ScanAssets(filenames []string) Result {
|
2026-03-18 00:21:08 +00:00
|
|
|
packageMap := make(map[string]*ScannedPackage)
|
2026-03-18 01:12:10 +00:00
|
|
|
var scanErr error
|
2026-03-18 00:21:08 +00:00
|
|
|
|
|
|
|
|
for _, filename := range filenames {
|
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
|
node, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
baseDir := filepath.Dir(filename)
|
|
|
|
|
pkg, ok := packageMap[baseDir]
|
|
|
|
|
if !ok {
|
fix: AX audit round 5 — full naming, Result returns throughout
Renames (via GoLand refactor):
- Option.K → Key, Option.V → Value
- Err.Op → Operation, Err.Msg → Message, Err.Err → Error
- CrashSystem.OS → OperatingSystem, Arch → Architecture
- TaskID → TaskIdentifier, TaskWithID → TaskWithIdentifier
- Ipc → IPC, BaseDir → BaseDirectory
- ServiceRuntime.Opts → Options
Return type changes:
- Options.Get, Config.Get → Result (was (any, bool))
- Embed.ReadDir → Result (was ([]fs.DirEntry, error))
- Translator.Translate, I18n.Translate → Result (was string)
Rule 6:
- data.go: propagate opts.Get failure, typed error for bad fs.FS
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 16:32:43 +00:00
|
|
|
pkg = &ScannedPackage{BaseDirectory: baseDir}
|
2026-03-18 00:21:08 +00:00
|
|
|
packageMap[baseDir] = pkg
|
|
|
|
|
}
|
|
|
|
|
pkg.PackageName = node.Name.Name
|
|
|
|
|
|
|
|
|
|
ast.Inspect(node, func(n ast.Node) bool {
|
2026-03-18 01:12:10 +00:00
|
|
|
if scanErr != nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
call, ok := n.(*ast.CallExpr)
|
|
|
|
|
if !ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
|
|
|
|
if !ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ident, ok := sel.X.(*ast.Ident)
|
|
|
|
|
if !ok {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look for core.GetAsset or mewn.String patterns
|
|
|
|
|
if ident.Name == "core" || ident.Name == "mewn" {
|
|
|
|
|
switch sel.Sel.Name {
|
|
|
|
|
case "GetAsset", "GetAssetBytes", "String", "MustString", "Bytes", "MustBytes":
|
|
|
|
|
if len(call.Args) >= 1 {
|
|
|
|
|
if lit, ok := call.Args[len(call.Args)-1].(*ast.BasicLit); ok {
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
path := TrimPrefix(TrimSuffix(lit.Value, "\""), "\"")
|
2026-03-18 00:21:08 +00:00
|
|
|
group := "."
|
|
|
|
|
if len(call.Args) >= 2 {
|
|
|
|
|
if glit, ok := call.Args[0].(*ast.BasicLit); ok {
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
group = TrimPrefix(TrimSuffix(glit.Value, "\""), "\"")
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-18 01:12:10 +00:00
|
|
|
fullPath, err := filepath.Abs(filepath.Join(baseDir, group, path))
|
|
|
|
|
if err != nil {
|
2026-03-20 13:38:53 +00:00
|
|
|
scanErr = Wrap(err, "core.ScanAssets", Join(" ", "could not determine absolute path for asset", path, "in group", group))
|
2026-03-18 01:12:10 +00:00
|
|
|
return false
|
|
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
pkg.Assets = append(pkg.Assets, AssetRef{
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
Name: path,
|
|
|
|
|
|
2026-03-18 00:21:08 +00:00
|
|
|
Group: group,
|
|
|
|
|
FullPath: fullPath,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "Group":
|
|
|
|
|
// Variable assignment: g := core.Group("./assets")
|
|
|
|
|
if len(call.Args) == 1 {
|
|
|
|
|
if lit, ok := call.Args[0].(*ast.BasicLit); ok {
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
path := TrimPrefix(TrimSuffix(lit.Value, "\""), "\"")
|
2026-03-18 01:12:10 +00:00
|
|
|
fullPath, err := filepath.Abs(filepath.Join(baseDir, path))
|
|
|
|
|
if err != nil {
|
2026-03-20 13:38:53 +00:00
|
|
|
scanErr = Wrap(err, "core.ScanAssets", Join(" ", "could not determine absolute path for group", path))
|
2026-03-18 01:12:10 +00:00
|
|
|
return false
|
|
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
pkg.Groups = append(pkg.Groups, fullPath)
|
|
|
|
|
// Track for variable resolution
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
})
|
2026-03-18 01:12:10 +00:00
|
|
|
if scanErr != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{scanErr, false}
|
2026-03-18 01:12:10 +00:00
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var result []ScannedPackage
|
|
|
|
|
for _, pkg := range packageMap {
|
|
|
|
|
result = append(result, *pkg)
|
|
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{result, true}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GeneratePack creates Go source code that embeds the scanned assets.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
func GeneratePack(pkg ScannedPackage) Result {
|
2026-03-20 15:36:33 +00:00
|
|
|
b := NewBuilder()
|
2026-03-18 00:21:08 +00:00
|
|
|
|
|
|
|
|
b.WriteString(fmt.Sprintf("package %s\n\n", pkg.PackageName))
|
|
|
|
|
b.WriteString("// Code generated by core pack. DO NOT EDIT.\n\n")
|
|
|
|
|
|
|
|
|
|
if len(pkg.Assets) == 0 && len(pkg.Groups) == 0 {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{b.String(), true}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b.WriteString("import \"forge.lthn.ai/core/go/pkg/core\"\n\n")
|
|
|
|
|
b.WriteString("func init() {\n")
|
|
|
|
|
|
|
|
|
|
// Pack groups (entire directories)
|
|
|
|
|
packed := make(map[string]bool)
|
|
|
|
|
for _, groupPath := range pkg.Groups {
|
|
|
|
|
files, err := getAllFiles(groupPath)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
for _, file := range files {
|
|
|
|
|
if packed[file] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
data, err := compressFile(file)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
localPath := TrimPrefix(file, groupPath+"/")
|
fix: AX audit round 5 — full naming, Result returns throughout
Renames (via GoLand refactor):
- Option.K → Key, Option.V → Value
- Err.Op → Operation, Err.Msg → Message, Err.Err → Error
- CrashSystem.OS → OperatingSystem, Arch → Architecture
- TaskID → TaskIdentifier, TaskWithID → TaskWithIdentifier
- Ipc → IPC, BaseDir → BaseDirectory
- ServiceRuntime.Opts → Options
Return type changes:
- Options.Get, Config.Get → Result (was (any, bool))
- Embed.ReadDir → Result (was ([]fs.DirEntry, error))
- Translator.Translate, I18n.Translate → Result (was string)
Rule 6:
- data.go: propagate opts.Get failure, typed error for bad fs.FS
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 16:32:43 +00:00
|
|
|
relGroup, err := filepath.Rel(pkg.BaseDirectory, groupPath)
|
2026-03-18 01:12:10 +00:00
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 01:12:10 +00:00
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
b.WriteString(fmt.Sprintf("\tcore.AddAsset(%q, %q, %q)\n", relGroup, localPath, data))
|
|
|
|
|
packed[file] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pack individual assets
|
|
|
|
|
for _, asset := range pkg.Assets {
|
|
|
|
|
if packed[asset.FullPath] {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
data, err := compressFile(asset.FullPath)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
b.WriteString(fmt.Sprintf("\tcore.AddAsset(%q, %q, %q)\n", asset.Group, asset.Name, data))
|
|
|
|
|
packed[asset.FullPath] = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b.WriteString("}\n")
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{b.String(), true}
|
2026-03-18 00:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Compression ---
|
|
|
|
|
|
|
|
|
|
func compressFile(path string) (string, error) {
|
|
|
|
|
data, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return compress(string(data))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func compress(input string) (string, error) {
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
b64 := base64.NewEncoder(base64.StdEncoding, &buf)
|
|
|
|
|
gz, err := gzip.NewWriterLevel(b64, gzip.BestCompression)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if _, err := gz.Write([]byte(input)); err != nil {
|
2026-03-18 01:12:10 +00:00
|
|
|
_ = gz.Close()
|
|
|
|
|
_ = b64.Close()
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if err := gz.Close(); err != nil {
|
|
|
|
|
_ = b64.Close()
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
if err := b64.Close(); err != nil {
|
2026-03-18 00:21:08 +00:00
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return buf.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func decompress(input string) (string, error) {
|
2026-03-20 15:36:33 +00:00
|
|
|
b64 := base64.NewDecoder(base64.StdEncoding, NewReader(input))
|
2026-03-18 00:21:08 +00:00
|
|
|
gz, err := gzip.NewReader(b64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
|
2026-03-18 00:21:08 +00:00
|
|
|
data, err := io.ReadAll(gz)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
2026-03-18 01:12:10 +00:00
|
|
|
if err := gz.Close(); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
2026-03-18 00:21:08 +00:00
|
|
|
return string(data), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getAllFiles(dir string) ([]string, error) {
|
|
|
|
|
var result []string
|
2026-03-18 01:12:10 +00:00
|
|
|
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
2026-03-18 00:21:08 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-03-18 01:12:10 +00:00
|
|
|
if !d.IsDir() {
|
2026-03-18 00:21:08 +00:00
|
|
|
result = append(result, path)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return result, err
|
|
|
|
|
}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
|
2026-03-18 09:37:34 +00:00
|
|
|
// --- Embed: Scoped Filesystem Mount ---
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
|
2026-03-18 09:37:34 +00:00
|
|
|
// Embed wraps an fs.FS with a basedir for scoped access.
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
// All paths are relative to basedir.
|
|
|
|
|
type Embed struct {
|
|
|
|
|
basedir string
|
|
|
|
|
fsys fs.FS
|
|
|
|
|
embedFS *embed.FS // original embed.FS for type-safe access via EmbedFS()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mount creates a scoped view of an fs.FS anchored at basedir.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := core.Mount(myFS, "lib/prompts")
|
|
|
|
|
// if r.OK { emb := r.Value.(*Embed) }
|
|
|
|
|
func Mount(fsys fs.FS, basedir string) Result {
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
s := &Embed{fsys: fsys, basedir: basedir}
|
|
|
|
|
|
|
|
|
|
if efs, ok := fsys.(embed.FS); ok {
|
|
|
|
|
s.embedFS = &efs
|
|
|
|
|
}
|
|
|
|
|
|
fix: AX audit round 5 — full naming, Result returns throughout
Renames (via GoLand refactor):
- Option.K → Key, Option.V → Value
- Err.Op → Operation, Err.Msg → Message, Err.Err → Error
- CrashSystem.OS → OperatingSystem, Arch → Architecture
- TaskID → TaskIdentifier, TaskWithID → TaskWithIdentifier
- Ipc → IPC, BaseDir → BaseDirectory
- ServiceRuntime.Opts → Options
Return type changes:
- Options.Get, Config.Get → Result (was (any, bool))
- Embed.ReadDir → Result (was ([]fs.DirEntry, error))
- Translator.Translate, I18n.Translate → Result (was string)
Rule 6:
- data.go: propagate opts.Get failure, typed error for bad fs.FS
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 16:32:43 +00:00
|
|
|
if r := s.ReadDir("."); !r.OK {
|
|
|
|
|
return r
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{s, true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MountEmbed creates a scoped view of an embed.FS.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := core.MountEmbed(myFS, "testdata")
|
|
|
|
|
func MountEmbed(efs embed.FS, basedir string) Result {
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
return Mount(efs, basedir)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Embed) path(name string) string {
|
|
|
|
|
return filepath.ToSlash(filepath.Join(s.basedir, name))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open opens the named file for reading.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := emb.Open("test.txt")
|
|
|
|
|
// if r.OK { file := r.Value.(fs.File) }
|
|
|
|
|
func (s *Embed) Open(name string) Result {
|
|
|
|
|
f, err := s.fsys.Open(s.path(name))
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{f, true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadDir reads the named directory.
|
fix: AX audit round 5 — full naming, Result returns throughout
Renames (via GoLand refactor):
- Option.K → Key, Option.V → Value
- Err.Op → Operation, Err.Msg → Message, Err.Err → Error
- CrashSystem.OS → OperatingSystem, Arch → Architecture
- TaskID → TaskIdentifier, TaskWithID → TaskWithIdentifier
- Ipc → IPC, BaseDir → BaseDirectory
- ServiceRuntime.Opts → Options
Return type changes:
- Options.Get, Config.Get → Result (was (any, bool))
- Embed.ReadDir → Result (was ([]fs.DirEntry, error))
- Translator.Translate, I18n.Translate → Result (was string)
Rule 6:
- data.go: propagate opts.Get failure, typed error for bad fs.FS
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 16:32:43 +00:00
|
|
|
func (s *Embed) ReadDir(name string) Result {
|
|
|
|
|
return Result{}.Result(fs.ReadDir(s.fsys, s.path(name)))
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadFile reads the named file.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := emb.ReadFile("test.txt")
|
|
|
|
|
// if r.OK { data := r.Value.([]byte) }
|
|
|
|
|
func (s *Embed) ReadFile(name string) Result {
|
|
|
|
|
data, err := fs.ReadFile(s.fsys, s.path(name))
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{data, true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadString reads the named file as a string.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := emb.ReadString("test.txt")
|
|
|
|
|
// if r.OK { content := r.Value.(string) }
|
|
|
|
|
func (s *Embed) ReadString(name string) Result {
|
|
|
|
|
r := s.ReadFile(name)
|
|
|
|
|
if !r.OK {
|
|
|
|
|
return r
|
|
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{string(r.Value.([]byte)), true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 09:37:34 +00:00
|
|
|
// Sub returns a new Embed anchored at a subdirectory within this mount.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
//
|
|
|
|
|
// r := emb.Sub("testdata")
|
|
|
|
|
// if r.OK { sub := r.Value.(*Embed) }
|
|
|
|
|
func (s *Embed) Sub(subDir string) Result {
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
sub, err := fs.Sub(s.fsys, s.path(subDir))
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{&Embed{fsys: sub, basedir: "."}, true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FS returns the underlying fs.FS.
|
|
|
|
|
func (s *Embed) FS() fs.FS {
|
|
|
|
|
return s.fsys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EmbedFS returns the underlying embed.FS if mounted from one.
|
|
|
|
|
// Returns zero embed.FS if mounted from a non-embed source.
|
|
|
|
|
func (s *Embed) EmbedFS() embed.FS {
|
|
|
|
|
if s.embedFS != nil {
|
|
|
|
|
return *s.embedFS
|
|
|
|
|
}
|
|
|
|
|
return embed.FS{}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 09:37:34 +00:00
|
|
|
// BaseDir returns the basedir this Embed is anchored at.
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
func (s *Embed) BaseDir() string {
|
|
|
|
|
return s.basedir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Template Extraction ---
|
|
|
|
|
|
|
|
|
|
// ExtractOptions configures template extraction.
|
|
|
|
|
type ExtractOptions struct {
|
|
|
|
|
// TemplateFilters identifies template files by substring match.
|
|
|
|
|
// Default: [".tmpl"]
|
|
|
|
|
TemplateFilters []string
|
|
|
|
|
|
|
|
|
|
// IgnoreFiles is a set of filenames to skip during extraction.
|
|
|
|
|
IgnoreFiles map[string]struct{}
|
|
|
|
|
|
|
|
|
|
// RenameFiles maps original filenames to new names.
|
|
|
|
|
RenameFiles map[string]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Extract copies a template directory from an fs.FS to targetDir,
|
|
|
|
|
// processing Go text/template in filenames and file contents.
|
|
|
|
|
//
|
|
|
|
|
// Files containing a template filter substring (default: ".tmpl") have
|
|
|
|
|
// their contents processed through text/template with the given data.
|
|
|
|
|
// The filter is stripped from the output filename.
|
|
|
|
|
//
|
|
|
|
|
// Directory and file names can contain Go template expressions:
|
|
|
|
|
// {{.Name}}/main.go → myproject/main.go
|
|
|
|
|
//
|
|
|
|
|
// Data can be any struct or map[string]string for template substitution.
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
func Extract(fsys fs.FS, targetDir string, data any, opts ...ExtractOptions) Result {
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
opt := ExtractOptions{
|
|
|
|
|
TemplateFilters: []string{".tmpl"},
|
|
|
|
|
IgnoreFiles: make(map[string]struct{}),
|
|
|
|
|
RenameFiles: make(map[string]string),
|
|
|
|
|
}
|
|
|
|
|
if len(opts) > 0 {
|
|
|
|
|
if len(opts[0].TemplateFilters) > 0 {
|
|
|
|
|
opt.TemplateFilters = opts[0].TemplateFilters
|
|
|
|
|
}
|
|
|
|
|
if opts[0].IgnoreFiles != nil {
|
|
|
|
|
opt.IgnoreFiles = opts[0].IgnoreFiles
|
|
|
|
|
}
|
|
|
|
|
if opts[0].RenameFiles != nil {
|
|
|
|
|
opt.RenameFiles = opts[0].RenameFiles
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure target directory exists
|
|
|
|
|
targetDir, err := filepath.Abs(targetDir)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Categorise files
|
|
|
|
|
var dirs []string
|
|
|
|
|
var templateFiles []string
|
|
|
|
|
var standardFiles []string
|
|
|
|
|
|
|
|
|
|
err = fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if path == "." {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if d.IsDir() {
|
|
|
|
|
dirs = append(dirs, path)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
filename := filepath.Base(path)
|
|
|
|
|
if _, ignored := opt.IgnoreFiles[filename]; ignored {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if isTemplate(filename, opt.TemplateFilters) {
|
|
|
|
|
templateFiles = append(templateFiles, path)
|
|
|
|
|
} else {
|
|
|
|
|
standardFiles = append(standardFiles, path)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create directories (names may contain templates)
|
|
|
|
|
for _, dir := range dirs {
|
|
|
|
|
target := renderPath(filepath.Join(targetDir, dir), data)
|
|
|
|
|
if err := os.MkdirAll(target, 0755); err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Process template files
|
|
|
|
|
for _, path := range templateFiles {
|
|
|
|
|
tmpl, err := template.ParseFS(fsys, path)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
targetFile := renderPath(filepath.Join(targetDir, path), data)
|
|
|
|
|
|
|
|
|
|
// Strip template filters from filename
|
|
|
|
|
dir := filepath.Dir(targetFile)
|
|
|
|
|
name := filepath.Base(targetFile)
|
|
|
|
|
for _, filter := range opt.TemplateFilters {
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
name = Replace(name, filter, "")
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
if renamed := opt.RenameFiles[name]; renamed != "" {
|
|
|
|
|
name = renamed
|
|
|
|
|
}
|
|
|
|
|
targetFile = filepath.Join(dir, name)
|
|
|
|
|
|
|
|
|
|
f, err := os.Create(targetFile)
|
|
|
|
|
if err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
if err := tmpl.Execute(f, data); err != nil {
|
|
|
|
|
f.Close()
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
f.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy standard files
|
|
|
|
|
for _, path := range standardFiles {
|
|
|
|
|
targetPath := path
|
|
|
|
|
name := filepath.Base(path)
|
|
|
|
|
if renamed := opt.RenameFiles[name]; renamed != "" {
|
|
|
|
|
targetPath = filepath.Join(filepath.Dir(path), renamed)
|
|
|
|
|
}
|
|
|
|
|
target := renderPath(filepath.Join(targetDir, targetPath), data)
|
|
|
|
|
if err := copyFile(fsys, path, target); err != nil {
|
2026-03-20 15:36:33 +00:00
|
|
|
return Result{err, false}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: embed.go and data.go return Result throughout
Mount, MountEmbed, Open, ReadFile, ReadString, Sub, GetAsset,
GetAssetBytes, ScanAssets, GeneratePack, Extract → all return Result.
Data.ReadFile, ReadString, List, ListNames, Extract → Result.
Data.New uses Mount's Result internally.
Internal helpers (WalkDir callback, copyFile) stay error — they're
not public API.
231 tests, 77.4% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 14:13:47 +00:00
|
|
|
return Result{OK: true}
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isTemplate(filename string, filters []string) bool {
|
|
|
|
|
for _, f := range filters {
|
refactor: AX audit fixes — no direct strings/fmt, full type names
Direct strings import removed from: data.go, error.go, fs.go
→ uses Split, SplitN, TrimPrefix, TrimSuffix, HasPrefix, Replace, Contains, Join
Direct fmt import removed from: fs.go
→ uses Print() from utils.go
fmt.Errorf in panic recovery → NewError(fmt.Sprint("panic: ", r))
Abbreviated type names renamed:
ConfigOpts → ConfigOptions
LogOpts → LogOptions
RotationLogOpts → RotationLogOptions
embed.go keeps strings import (strings.NewReader, strings.Builder).
error.go keeps fmt import (fmt.Sprint for panic values).
232 tests, 77.8% coverage.
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-20 13:47:23 +00:00
|
|
|
if Contains(filename, f) {
|
feat: restructure Core as unified struct with DTO pattern
Complete architectural overhaul of pkg/core:
- All subsystem types renamed to idiomatic Go (no stutter)
- Core struct: App, Embed, Fs, Config, ErrPan, ErrLog, Cli, Service, Lock, Ipc, I18n
- Exports consolidated in core.go, contracts/options in contract.go
- Service() unified get/register: c.Service(), c.Service("name"), c.Service("name", svc)
- Lock() named mutex map: c.Lock("srv"), c.Lock("ipc")
- Error system: Err/ErrLog/ErrPan + Log/LogErr/LogPan (shared ErrSink interface)
- CoreCommand with optional description (i18n resolves from command path)
- Tests moved to tests/ directory (black-box package core_test)
- Removed: ServiceFor/MustServiceFor, global instance, Display/Workspace/Crypt interfaces
- New files: app.go, fs.go, ipc.go, lock.go, i18n.go, task.go, runtime.go, contract.go
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-18 09:12:29 +00:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderPath(path string, data any) string {
|
|
|
|
|
if data == nil {
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
tmpl, err := template.New("path").Parse(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
if err := tmpl.Execute(&buf, data); err != nil {
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func copyFile(fsys fs.FS, source, target string) error {
|
|
|
|
|
s, err := fsys.Open(source)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer s.Close()
|
|
|
|
|
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d, err := os.Create(target)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer d.Close()
|
|
|
|
|
|
|
|
|
|
_, err = io.Copy(d, s)
|
|
|
|
|
return err
|
|
|
|
|
}
|