Files
notarius/internal/framework/contracts/artifact.go

104 lines
3.4 KiB
Go

package contracts
import (
"crypto/sha256"
"encoding/hex"
)
// ArtifactKind is the stable logical identity of a domain artifact.
type ArtifactKind string
// ArtifactSchema describes the durable representation owned by an artifact
// codec. JSONSchema is cloned whenever framework ownership changes.
type ArtifactSchema struct {
ID string `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
JSONSchema []byte `json:"-"`
}
// SerializedArtifact is the domain-neutral representation of a typed
// artifact at an explicit serialization boundary.
type SerializedArtifact struct {
Kind ArtifactKind `json:"kind"`
Schema ArtifactSchema `json:"schema"`
MediaType string `json:"media_type"`
Content []byte `json:"-"`
Metadata map[string]any `json:"metadata,omitempty"`
}
// SerializedOutput associates a domain-neutral artifact with the pipeline
// operation that produced it. Provenance remains outside codec-owned bytes.
type SerializedOutput struct {
StepID string `json:"step_id,omitempty"`
LaneID string `json:"lane_id"`
NormalizerKey string `json:"normalizer_key"`
SourceID string `json:"source_id,omitempty"`
Artifact SerializedArtifact `json:"artifact"`
}
// ArtifactCodec owns the stable encoding for one concrete artifact type.
type ArtifactCodec[T any] interface {
Kind() ArtifactKind
Schema() ArtifactSchema
MediaType() string
// EncodeCandidate serializes a stage result before semantic validation. It
// must not apply validity checks owned by typed validators; Encode remains
// the strict final-artifact boundary used after validation succeeds.
EncodeCandidate(T) ([]byte, error)
Encode(T) ([]byte, error)
Decode([]byte) (T, error)
}
// CandidateArtifactCodec extends an artifact codec with strict representation
// decoding for values that have not yet passed semantic validation.
type CandidateArtifactCodec[T any] interface {
ArtifactCodec[T]
DecodeCandidate([]byte) (T, error)
}
// DigestArtifactSchema returns the SHA-256 digest of the exact JSON Schema
// bytes. Schema formatting is therefore part of the registered identity.
func DigestArtifactSchema(schema ArtifactSchema) string {
sum := sha256.Sum256(schema.JSONSchema)
return "sha256:" + hex.EncodeToString(sum[:])
}
func CloneArtifactSchema(schema ArtifactSchema) ArtifactSchema {
schema.JSONSchema = append([]byte(nil), schema.JSONSchema...)
return schema
}
func CloneSerializedArtifact(artifact SerializedArtifact) SerializedArtifact {
artifact.Schema = CloneArtifactSchema(artifact.Schema)
artifact.Content = append([]byte(nil), artifact.Content...)
artifact.Metadata = cloneArtifactMetadata(artifact.Metadata)
return artifact
}
// CloneSerializedArtifactPointer returns an independently owned artifact when
// one is present.
func CloneSerializedArtifactPointer(artifact *SerializedArtifact) *SerializedArtifact {
if artifact == nil {
return nil
}
cloned := CloneSerializedArtifact(*artifact)
return &cloned
}
func CloneSerializedOutput(output SerializedOutput) SerializedOutput {
output.Artifact = CloneSerializedArtifact(output.Artifact)
return output
}
func cloneArtifactMetadata(metadata map[string]any) map[string]any {
if len(metadata) == 0 {
return nil
}
out := make(map[string]any, len(metadata))
for key, value := range metadata {
out[key] = value
}
return out
}