6ad82167bb
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
163 lines
5.7 KiB
Go
163 lines
5.7 KiB
Go
package registry
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// timestampRecord holds preserved timestamps and hash for an existing entry.
|
|
type timestampRecord struct {
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
ContentHash string
|
|
}
|
|
|
|
// ComputeFunctionHash computes a deterministic hash of all content fields of a Function
|
|
// (excluding created_at, updated_at, and content_hash itself).
|
|
func ComputeFunctionHash(f *Function) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s|%s|%s|%s|%s",
|
|
f.ID, f.Name, f.Kind, f.Lang, f.Domain, f.Version, f.Purity, f.Signature, f.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Tags))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.UsesFunctions))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.UsesTypes))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Returns))
|
|
fmt.Fprintf(h, "|%t|%s", f.ReturnsOptional, f.ErrorType)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Imports))
|
|
fmt.Fprintf(h, "|%s|%t", f.Example, f.Tested)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Tests))
|
|
fmt.Fprintf(h, "|%s|%s", f.TestFilePath, f.FilePath)
|
|
fmt.Fprintf(h, "|%s", marshalProps(f.Props))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Emits))
|
|
if f.HasState != nil {
|
|
fmt.Fprintf(h, "|%t", *f.HasState)
|
|
}
|
|
fmt.Fprintf(h, "|%s", f.Framework)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(f.Variant))
|
|
fmt.Fprintf(h, "|%s|%s|%s", f.Notes, f.Documentation, f.Code)
|
|
fmt.Fprintf(h, "|%s|%s|%s", f.SourceRepo, f.SourceLicense, f.SourceFile)
|
|
fmt.Fprintf(h, "|%s", f.ParamsSchema)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeTypeHash computes a deterministic hash of all content fields of a Type.
|
|
func ComputeTypeHash(t *Type) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s|%s|%s|%s",
|
|
t.ID, t.Name, t.Lang, t.Domain, t.Version, t.Algebraic, t.Definition, t.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(t.Tags))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(t.UsesTypes))
|
|
fmt.Fprintf(h, "|%s|%s|%s|%s|%s", t.FilePath, t.Examples, t.Notes, t.Documentation, t.Code)
|
|
fmt.Fprintf(h, "|%s|%s|%s", t.SourceRepo, t.SourceLicense, t.SourceFile)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeAppHash computes a deterministic hash of all content fields of an App.
|
|
func ComputeAppHash(a *App) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s",
|
|
a.ID, a.Name, a.Lang, a.Domain, a.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.Tags))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesFunctions))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesTypes))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesModules))
|
|
fmt.Fprintf(h, "|%s|%s|%s|%s|%s|%s", a.Framework, a.EntryPoint, a.Documentation, a.Notes, a.DirPath, a.RepoURL)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeAnalysisHash computes a deterministic hash of all content fields of an Analysis.
|
|
func ComputeAnalysisHash(a *Analysis) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s",
|
|
a.ID, a.Name, a.Lang, a.Domain, a.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.Tags))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesFunctions))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesTypes))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(a.UsesModules))
|
|
fmt.Fprintf(h, "|%s|%s|%s|%s|%s|%s", a.Framework, a.EntryPoint, a.Documentation, a.Notes, a.DirPath, a.RepoURL)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeModuleHash computes a deterministic hash of all content fields of a Module.
|
|
func ComputeModuleHash(m *Module) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s",
|
|
m.ID, m.Name, m.Version, m.Lang, m.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(m.Members))
|
|
fmt.Fprintf(h, "|%s", marshalStrings(m.Tags))
|
|
fmt.Fprintf(h, "|%s|%s|%s|%s", m.DirPath, m.RepoURL, m.Documentation, m.Notes)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeProjectHash computes a deterministic hash of all content fields of a Project.
|
|
func ComputeProjectHash(p *Project) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s",
|
|
p.ID, p.Name, p.Description)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(p.Tags))
|
|
fmt.Fprintf(h, "|%s|%s|%s|%s", p.RepoURL, p.DirPath, p.Documentation, p.Notes)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// ComputeVaultHash computes a deterministic hash of all content fields of a Vault.
|
|
func ComputeVaultHash(v *Vault) string {
|
|
h := sha256.New()
|
|
fmt.Fprintf(h, "%s|%s|%s|%s|%s|%t",
|
|
v.ID, v.Name, v.ProjectID, v.Description, v.Path, v.Symlink)
|
|
fmt.Fprintf(h, "|%s", marshalStrings(v.Tags))
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// LoadTimestamps reads existing id → {created_at, updated_at, content_hash} from all tables.
|
|
// Called before Purge so we can preserve dates across reindexing.
|
|
func (db *DB) LoadTimestamps() (funcs, types, apps, analysis, projects, vaults, modules map[string]timestampRecord, err error) {
|
|
funcs, err = loadTable(db, "functions")
|
|
if err != nil {
|
|
return
|
|
}
|
|
types, err = loadTable(db, "types")
|
|
if err != nil {
|
|
return
|
|
}
|
|
apps, err = loadTable(db, "apps")
|
|
if err != nil {
|
|
return
|
|
}
|
|
analysis, err = loadTable(db, "analysis")
|
|
if err != nil {
|
|
return
|
|
}
|
|
projects, err = loadTable(db, "projects")
|
|
if err != nil {
|
|
return
|
|
}
|
|
vaults, err = loadTable(db, "vaults")
|
|
if err != nil {
|
|
return
|
|
}
|
|
modules, err = loadTable(db, "modules")
|
|
return
|
|
}
|
|
|
|
func loadTable(db *DB, table string) (map[string]timestampRecord, error) {
|
|
rows, err := db.conn.Query(fmt.Sprintf("SELECT id, created_at, updated_at, content_hash FROM %s", table))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
m := make(map[string]timestampRecord)
|
|
for rows.Next() {
|
|
var id, ca, ua, ch string
|
|
if err := rows.Scan(&id, &ca, &ua, &ch); err != nil {
|
|
return nil, err
|
|
}
|
|
rec := timestampRecord{ContentHash: ch}
|
|
rec.CreatedAt, _ = time.Parse(time.RFC3339, ca)
|
|
rec.UpdatedAt, _ = time.Parse(time.RFC3339, ua)
|
|
m[id] = rec
|
|
}
|
|
return m, rows.Err()
|
|
}
|