Add typed artifact codec foundation

This commit is contained in:
2026-07-17 05:41:20 +00:00
parent 075888c97f
commit fc1b57bde2
22 changed files with 795 additions and 33 deletions

View File

@@ -0,0 +1,67 @@
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"`
}
// ArtifactCodec owns the stable encoding for one concrete artifact type.
type ArtifactCodec[T any] interface {
Kind() ArtifactKind
Schema() ArtifactSchema
MediaType() string
Encode(T) ([]byte, error)
Decode([]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
}
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
}

View File

@@ -0,0 +1,45 @@
package contracts
import "testing"
func TestDigestArtifactSchemaUsesExactBytes(t *testing.T) {
first := ArtifactSchema{JSONSchema: []byte(`{"type":"object"}`)}
second := ArtifactSchema{JSONSchema: []byte("{\n \"type\": \"object\"\n}")}
if got := DigestArtifactSchema(first); got != "sha256:a2c799262a3ce3c19ef5cdd983bf3d12b43ab3c426227091b909dcb7054738c0" {
t.Fatalf("DigestArtifactSchema() = %q, want stable SHA-256", got)
}
if DigestArtifactSchema(first) == DigestArtifactSchema(second) {
t.Fatal("schema digests match for different exact bytes")
}
}
func TestArtifactCloneHelpersOwnSlicesAndMaps(t *testing.T) {
schema := ArtifactSchema{ID: "notes.v1", Name: "notes", Version: "v1", JSONSchema: []byte(`{"type":"object"}`)}
artifact := SerializedArtifact{
Kind: "test/notes",
Schema: schema,
MediaType: "application/json",
Content: []byte(`{"items":["one"]}`),
Metadata: map[string]any{"origin": "test"},
}
clonedSchema := CloneArtifactSchema(schema)
cloned := CloneSerializedArtifact(artifact)
schema.JSONSchema[0] = '['
artifact.Content[0] = '['
artifact.Metadata["origin"] = "changed"
if string(clonedSchema.JSONSchema) != `{"type":"object"}` {
t.Fatalf("cloned schema = %q, want original bytes", clonedSchema.JSONSchema)
}
if string(cloned.Schema.JSONSchema) != `{"type":"object"}` {
t.Fatalf("serialized artifact schema = %q, want original bytes", cloned.Schema.JSONSchema)
}
if string(cloned.Content) != `{"items":["one"]}` {
t.Fatalf("cloned content = %q, want original bytes", cloned.Content)
}
if cloned.Metadata["origin"] != "test" {
t.Fatalf("cloned metadata = %#v, want independent map", cloned.Metadata)
}
}