2026-09-12 15:58:06 +00:00
package engine
import (
"bytes"
"errors"
"io/fs"
"strconv"
"strings"
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
)
const (
maxImportDepth = 64
maxImportSources = 1024
maxGlobEntries = 1_000_000
)
type importLimits struct {
depth int
sources int
bytes int64
nodes int
entries int
}
func defaultImportLimits ( ) importLimits {
return importLimits {
depth : maxImportDepth , sources : maxImportSources ,
bytes : maxTotalBytes , nodes : maxDocumentNodes , entries : maxGlobEntries ,
}
}
type importRoute struct {
canonical string
locations [ ] Location
}
2026-09-29 17:29:01 +00:00
type importAccounting struct {
sources int
bytes int64
nodes int
entries int
}
2026-09-12 15:58:06 +00:00
type importTraversal struct {
2026-10-15 15:33:40 +00:00
access * filesystemAccess
accounting * importAccounting
limits importLimits
rule * VersionRule
versionState * sourceVersionState
2026-09-12 15:58:06 +00:00
2026-10-16 19:23:43 +00:00
scope acquisitionScope
itemScope bool
itemPrefix Path
itemFallback Path
sources [ ] SourceInfo
active map [ resourceIdentity ] int
visited map [ resourceIdentity ] importRoute
stack [ ] importRoute
2026-09-12 15:58:06 +00:00
}
func loadImportedDocuments ( source Source , context * FilesystemContext , main parsedSource , rule * VersionRule , limits importLimits ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-14 13:19:31 +00:00
return loadImportedDocumentsTracked ( source , context , main , Path { } , rule , limits , & importAccounting { } , nil , nil )
2026-09-29 17:52:20 +00:00
}
2026-10-14 13:19:31 +00:00
func loadImportedDocumentsTracked ( source Source , context * FilesystemContext , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , hostToken * namespaceToken ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-15 15:33:40 +00:00
return loadImportedDocumentsTrackedWithVersion ( source , context , main , destination , rule , limits , accounting , visited , hostToken , nil )
}
func loadImportedDocumentsTrackedWithVersion ( source Source , context * FilesystemContext , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , hostToken * namespaceToken , versionState * sourceVersionState ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-15 15:40:11 +00:00
var processing * sourceVersionProcessingResult
if versionState != nil && versionState . mode != sourceVersionModeDefault {
processing = versionState . mainProcessing
}
2026-10-16 18:59:52 +00:00
return loadImportedDocumentsTrackedWithProcessing ( source , context , main , destination , rule , limits , accounting , visited , hostToken , versionState , processing , sequencePolicy { } )
2026-10-15 15:40:11 +00:00
}
2026-10-16 18:59:52 +00:00
func loadImportedDocumentsTrackedWithProcessing ( source Source , context * FilesystemContext , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , hostToken * namespaceToken , versionState * sourceVersionState , processing * sourceVersionProcessingResult , policy sequencePolicy ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-09-29 17:36:20 +00:00
if nativeContext ( source , context ) {
access , basePath , mainPath , diagnostic := nativeFilesystemAccess ( source , context )
if diagnostic != nil {
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { * diagnostic }
}
2026-10-16 18:59:52 +00:00
return loadImportedWithAccessTrackedAndProcessingPolicy ( access , basePath , mainPath , main , destination , rule , limits , accounting , visited , versionState , processing , policy )
2026-09-29 17:29:01 +00:00
}
2026-09-12 15:58:06 +00:00
rootPath , basePath , mainPath , diagnostic := importFilesystem ( source , context , main )
if diagnostic != nil {
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { * diagnostic }
}
2026-09-29 17:29:01 +00:00
access , err := openHostAccess ( rootPath )
2026-09-12 15:58:06 +00:00
if err != nil {
diagnostic := globFilesystemDiagnostic ( err , "configuration root could not be opened" , Location { } )
diagnostic . Primary = nil
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { diagnostic }
}
2026-09-29 17:52:20 +00:00
if hostToken != nil {
access . token = hostToken
}
2026-10-16 18:59:52 +00:00
return loadImportedWithAccessTrackedAndProcessingPolicy ( access , basePath , mainPath , main , destination , rule , limits , accounting , visited , versionState , processing , policy )
2026-09-29 17:29:01 +00:00
}
2026-09-12 15:58:06 +00:00
2026-09-29 17:29:01 +00:00
func loadImportedWithAccess ( access * filesystemAccess , basePath , mainPath string , main parsedSource , rule * VersionRule , limits importLimits , accounting * importAccounting ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-14 13:19:31 +00:00
return loadImportedWithAccessTracked ( access , basePath , mainPath , main , Path { } , rule , limits , accounting , nil )
2026-09-29 17:52:20 +00:00
}
2026-10-14 13:19:31 +00:00
func loadImportedWithAccessTracked ( access * filesystemAccess , basePath , mainPath string , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-15 15:33:40 +00:00
return loadImportedWithAccessTrackedWithVersion ( access , basePath , mainPath , main , destination , rule , limits , accounting , visited , nil )
}
func loadImportedWithAccessTrackedWithVersion ( access * filesystemAccess , basePath , mainPath string , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , versionState * sourceVersionState ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-15 15:40:11 +00:00
var processing * sourceVersionProcessingResult
if versionState != nil && versionState . mode != sourceVersionModeDefault {
processing = versionState . mainProcessing
}
return loadImportedWithAccessTrackedAndProcessing ( access , basePath , mainPath , main , destination , rule , limits , accounting , visited , versionState , processing )
}
func loadImportedWithAccessTrackedAndProcessing ( access * filesystemAccess , basePath , mainPath string , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , versionState * sourceVersionState , processing * sourceVersionProcessingResult ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-10-16 18:59:52 +00:00
return loadImportedWithAccessTrackedAndProcessingPolicy ( access , basePath , mainPath , main , destination , rule , limits , accounting , visited , versionState , processing , sequencePolicy { } )
}
func loadImportedWithAccessTrackedAndProcessingPolicy ( access * filesystemAccess , basePath , mainPath string , main parsedSource , destination Path , rule * VersionRule , limits importLimits , accounting * importAccounting , visited map [ resourceIdentity ] importRoute , versionState * sourceVersionState , processing * sourceVersionProcessingResult , policy sequencePolicy ) ( * composedDocument , [ ] SourceInfo , [ ] Diagnostic ) {
2026-09-29 17:29:01 +00:00
if ! access . contains ( basePath ) || ( mainPath != "" && ! access . contains ( mainPath ) ) {
_ = access . close ( )
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { importDiagnostic ( CodeImportOutsideRoot , "selected source and import base must stay inside the configuration root" , nil ) }
}
for _ , directory := range [ ] string { access . rootPath , basePath } {
info , err := access . stat ( directory )
if err != nil || ! info . IsDir ( ) {
_ = access . close ( )
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { importDiagnostic ( CodeImportInvalid , "configuration root and import base must be directories" , nil ) }
}
}
2026-09-12 15:58:06 +00:00
mainInfo := cloneSourceInfo ( * main . source )
2026-10-14 13:19:31 +00:00
mainInfo . Destination = destination . clone ( )
2026-10-16 16:20:14 +00:00
mainIsItem := main . source . placementMode == placementModeSequenceItem
2026-09-12 15:58:06 +00:00
if mainPath != "" {
mainInfo . Path = mainPath
}
2026-09-29 17:52:20 +00:00
mainKey := access . identity ( mainPath )
if mainPath == "" {
mainKey = access . syntheticIdentity ( )
}
if previous , repeated := visited [ mainKey ] ; repeated {
_ = access . close ( )
return nil , sourceInfos ( main . source ) , [ ] Diagnostic { importRepeatedDiagnostic ( Location { SourceID : main . source . ID } , previous , importRoute { } ) }
}
2026-09-29 17:29:01 +00:00
accounting . sources ++
accounting . bytes += main . bytes
accounting . nodes += countInputNodes ( main . document )
2026-09-12 15:58:06 +00:00
state := importTraversal {
2026-10-15 15:33:40 +00:00
access : access , accounting : accounting , limits : limits , rule : rule , versionState : versionState ,
2026-10-16 15:48:36 +00:00
scope : acquisitionScope { destination : destination . clone ( ) } ,
sources : [ ] SourceInfo { mainInfo } ,
active : make ( map [ resourceIdentity ] int ) , visited : visited ,
2026-09-29 17:52:20 +00:00
}
2026-10-16 16:20:14 +00:00
if mainIsItem {
state . itemScope = true
state . itemPrefix = destination . clone ( )
2026-10-16 19:23:43 +00:00
state . itemFallback = destination . clone ( )
2026-10-16 16:20:14 +00:00
state . scope . destination = Path { }
state . sources [ 0 ] = cloneSourceInfo ( mainInfo )
}
2026-09-29 17:52:20 +00:00
if state . visited == nil {
state . visited = make ( map [ resourceIdentity ] importRoute )
2026-09-12 15:58:06 +00:00
}
if diagnostic := state . checkInitialLimits ( ) ; diagnostic != nil {
2026-09-29 17:29:01 +00:00
_ = access . close ( )
2026-09-12 15:58:06 +00:00
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { * diagnostic }
}
mainIdentity := mainPath
if mainIdentity == "" {
mainIdentity = "<selected-source>"
}
mainRoute := importRoute { canonical : mainIdentity }
2026-09-29 17:29:01 +00:00
state . active [ mainKey ] = 0
state . visited [ mainKey ] = mainRoute
2026-09-12 15:58:06 +00:00
state . stack = append ( state . stack , mainRoute )
2026-10-15 15:33:40 +00:00
var body * document . Node
var declarations [ ] importDeclaration
var diagnostics [ ] Diagnostic
2026-10-15 15:40:11 +00:00
if processing != nil {
body = processing . payload
declarations = processing . imports
2026-10-15 15:33:40 +00:00
} else {
body , declarations , diagnostics = splitImportDirectives ( main . document )
}
2026-09-12 15:58:06 +00:00
if len ( diagnostics ) == 0 {
2026-10-16 16:20:14 +00:00
if mainIsItem {
2026-10-16 15:48:36 +00:00
state . scope . contributions = append ( state . scope . contributions , acquiredContribution {
2026-10-16 16:20:14 +00:00
source : cloneSourceInfo ( mainInfo ) , payload : body , document : body , processing : processing ,
2026-10-16 15:48:36 +00:00
} )
2026-10-16 16:20:14 +00:00
diagnostics = state . walkDeclarations ( basePath , declarations , 0 , Path { } )
} else {
_ , placed , err := placeDocument ( body , destination , body . Location , maxDocumentDepth , maxDocumentNodes )
placementNodes := destinationWrapperNodeCount ( destination )
if err != nil {
diagnostics = [ ] Diagnostic { sourceDiagnostic ( CodeSourceLimitExceeded , main . source . ID , "configuration exceeds the source depth or node limit after destination placement" ) }
} else if accounting . nodes + placementNodes > limits . nodes {
diagnostics = [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , nil ) }
} else {
accounting . nodes += placementNodes
state . scope . contributions = append ( state . scope . contributions , acquiredContribution {
source : cloneSourceInfo ( mainInfo ) , payload : body , document : placed ,
destination : destination . clone ( ) , processing : processing ,
} )
diagnostics = state . walkDeclarations ( basePath , declarations , 0 , destination )
}
2026-10-14 13:19:31 +00:00
}
2026-09-12 15:58:06 +00:00
}
2026-09-29 17:29:01 +00:00
closeErr := access . close ( )
2026-09-12 15:58:06 +00:00
if len ( diagnostics ) != 0 {
return nil , cloneSourceInfos ( state . sources ) , diagnostics
}
if closeErr != nil {
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { importDiagnostic ( CodeImportRead , "configuration root could not be closed" , nil ) }
}
2026-10-16 16:20:14 +00:00
if mainIsItem {
localScope := state . scope
documents := make ( [ ] * document . Node , 0 , len ( localScope . contributions ) )
for _ , contribution := range localScope . contributions {
documents = append ( documents , contribution . document )
}
completed , failures := composeDocuments ( documents ... )
if len ( failures ) != 0 {
return nil , cloneSourceInfos ( state . sources ) , prependDiagnosticPaths ( failures , destination . Index ( 0 ) )
}
2026-10-16 19:11:08 +00:00
if versionState != nil {
if diagnostic := importedVersionClaim ( documents , completed , versionState . localRule , 0 ) ; diagnostic != nil {
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { * diagnostic }
}
}
2026-10-16 16:20:14 +00:00
completed . acquisition = [ ] acquisitionScope { cloneAcquisitionScope ( localScope ) }
projectNestedEvidence ( completed , localScope )
_ , placed , err := placeDocumentWithMode ( completed . root , destination , placementModeSequenceItem , body . Location , maxDocumentDepth , maxDocumentNodes )
if err != nil {
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { sourceDiagnostic ( CodeSourceLimitExceeded , main . source . ID , "configuration exceeds the source depth or node limit after destination placement" ) }
}
placementNodes := destinationWrapperNodeCountForMode ( destination , placementModeSequenceItem )
if accounting . nodes + placementNodes > limits . nodes {
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , nil ) }
}
accounting . nodes += placementNodes
2026-10-16 18:59:52 +00:00
composed , failures := composeDocumentsWithPolicy ( policy , placed )
2026-10-16 16:20:14 +00:00
if composed != nil {
synthetic := acquisitionScope { contributions : [ ] acquiredContribution { {
source : cloneSourceInfo ( mainInfo ) , payload : completed . root , document : placed ,
destination : destination . clone ( ) , processing : processing , nested : completed ,
} } }
projectNestedEvidence ( composed , synthetic )
projectAcquiredDestinations ( composed , synthetic , state . sources )
composed . acquisition = [ ] acquisitionScope { cloneAcquisitionScope ( synthetic ) }
composed . inputBytes = accounting . bytes
composed . inputNodes = accounting . nodes
}
return composed , cloneSourceInfos ( state . sources ) , failures
}
2026-10-16 15:48:36 +00:00
documents := make ( [ ] * document . Node , 0 , len ( state . scope . contributions ) )
for _ , contribution := range state . scope . contributions {
documents = append ( documents , contribution . document )
}
2026-10-16 18:59:52 +00:00
composed , diagnostics := composeDocumentsWithPolicy ( policy , documents ... )
2026-10-16 19:11:08 +00:00
if composed != nil {
if diagnostic := importedVersionClaim ( documents , composed , rule , 0 ) ; diagnostic != nil {
return nil , cloneSourceInfos ( state . sources ) , [ ] Diagnostic { * diagnostic }
}
}
2026-09-12 15:58:06 +00:00
if composed != nil {
2026-10-16 16:20:14 +00:00
projectNestedEvidence ( composed , state . scope )
projectAcquiredDestinations ( composed , state . scope , state . sources )
2026-10-16 15:48:36 +00:00
composed . acquisition = [ ] acquisitionScope { cloneAcquisitionScope ( state . scope ) }
2026-09-29 17:29:01 +00:00
composed . inputBytes = accounting . bytes
composed . inputNodes = accounting . nodes
2026-09-12 15:58:06 +00:00
}
return composed , cloneSourceInfos ( state . sources ) , diagnostics
}
func ( s * importTraversal ) checkInitialLimits ( ) * Diagnostic {
2026-09-29 17:29:01 +00:00
if s . accounting . sources > s . limits . sources {
2026-09-12 15:58:06 +00:00
value := importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the source-count limit" , nil )
return & value
}
2026-09-29 17:29:01 +00:00
if s . accounting . bytes > s . limits . bytes {
2026-09-12 15:58:06 +00:00
value := importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate byte limit" , nil )
return & value
}
2026-09-29 17:29:01 +00:00
if s . accounting . nodes > s . limits . nodes {
2026-09-12 15:58:06 +00:00
value := importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , nil )
return & value
}
return nil
}
2026-10-14 17:21:07 +00:00
func ( s * importTraversal ) walkDeclarations ( basePath string , declarations [ ] importDeclaration , depth int , parentDestination Path ) [ ] Diagnostic {
2026-09-12 15:58:06 +00:00
for _ , declaration := range declarations {
2026-10-14 17:21:07 +00:00
childDestination := appendDestination ( parentDestination , declaration . destination )
2026-09-12 15:58:06 +00:00
if declaration . glob != "" {
matches , diagnostics := s . expandImportGlob ( basePath , declaration )
if len ( diagnostics ) != 0 {
return diagnostics
}
if len ( matches ) == 0 {
if declaration . optional {
continue
}
location := declaration . location
return [ ] Diagnostic { importDiagnostic ( CodeImportMissing , "required import glob matched no files" , & location ) }
}
for _ , match := range matches {
matched := declaration
matched . path = match
matched . glob = ""
2026-10-16 15:48:36 +00:00
if diagnostics := s . visitPath ( basePath , matched , childDestination , declaration . destination , depth + 1 ) ; len ( diagnostics ) != 0 {
2026-09-12 15:58:06 +00:00
return diagnostics
}
}
continue
}
2026-10-16 15:48:36 +00:00
if diagnostics := s . visitPath ( basePath , declaration , childDestination , declaration . destination , depth + 1 ) ; len ( diagnostics ) != 0 {
2026-09-12 15:58:06 +00:00
return diagnostics
}
}
return nil
}
2026-10-16 15:48:36 +00:00
func ( s * importTraversal ) visitPath ( basePath string , declaration importDeclaration , destination , relativeDestination Path , depth int ) [ ] Diagnostic {
2026-09-12 15:58:06 +00:00
location := declaration . location
2026-10-16 16:20:14 +00:00
if declaration . placement == placementModeSequenceItem && len ( destination . segments ) == 0 {
return [ ] Diagnostic { {
Code : CodeImportInvalid , Severity : SeverityError ,
Path : declaration . placementPath . clone ( ) ,
Message : "sequence-item placement requires a nonempty destination" ,
Primary : cloneLocation ( declaration . placementLocation ) ,
} }
}
2026-09-12 15:58:06 +00:00
if depth > s . limits . depth {
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the active import-depth limit" , & location ) }
}
if ! validImportPath ( declaration . path ) {
return [ ] Diagnostic { importDiagnostic ( CodeImportInvalid , "import path must be a nonempty slash-relative path without a network scheme" , & location ) }
}
2026-09-29 17:29:01 +00:00
candidate := s . access . join ( basePath , declaration . path )
if ! s . access . contains ( candidate ) {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importDiagnostic ( CodeImportOutsideRoot , "import target is outside the configuration root" , & location ) }
}
2026-09-29 17:29:01 +00:00
canonical , err := s . access . canonical ( candidate )
2026-09-12 15:58:06 +00:00
if err != nil {
if declaration . optional && errors . Is ( err , fs . ErrNotExist ) {
return nil
}
code := CodeImportInvalid
message := "import target could not be canonicalized"
if errors . Is ( err , fs . ErrNotExist ) {
code , message = CodeImportMissing , "required import target does not exist"
} else if errors . Is ( err , fs . ErrPermission ) {
message = "import target cannot be accessed"
}
return [ ] Diagnostic { importDiagnostic ( code , message , & location ) }
}
2026-09-29 17:29:01 +00:00
if ! s . access . contains ( canonical ) {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importDiagnostic ( CodeImportOutsideRoot , "import target resolves outside the configuration root" , & location ) }
}
currentRoute := s . currentRoute ( location )
2026-09-29 17:29:01 +00:00
identity := s . access . identity ( canonical )
if activeIndex , ok := s . active [ identity ] ; ok {
2026-09-12 15:58:06 +00:00
cycle := append ( [ ] importRoute ( nil ) , s . stack [ activeIndex : ] ... )
return [ ] Diagnostic { importCycleDiagnostic ( canonical , location , cycle ) }
}
2026-09-29 17:29:01 +00:00
if previous , ok := s . visited [ identity ] ; ok {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importRepeatedDiagnostic ( location , previous , currentRoute ) }
}
2026-09-29 17:29:01 +00:00
if s . accounting . sources + 1 > s . limits . sources {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the source-count limit" , & location ) }
}
2026-09-29 17:29:01 +00:00
if _ , err := s . access . relative ( canonical ) ; err != nil {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importDiagnostic ( CodeImportOutsideRoot , "import target could not be addressed inside the configuration root" , & location ) }
}
2026-09-29 17:29:01 +00:00
sourceID := "source-" + strconv . Itoa ( s . accounting . sources + 1 )
2026-09-29 17:36:20 +00:00
kind := SourceKindFile
label := declaration . path
if s . access . native != nil {
kind = SourceKindFilesystem
label = s . access . label + "/" + canonical
}
2026-10-16 16:20:14 +00:00
info := SourceInfo { ID : sourceID , Label : label , Path : canonical , Destination : s . sourceDestination ( destination ) , Layer : LayerBase , Kind : kind , ImportedFrom : & location }
if declaration . placement == placementModeSequenceItem {
info . placementMode = placementModeSequenceItem
}
2026-10-16 19:23:43 +00:00
// A source's own item placement still has an exact sequence-container
// destination. Only an unresolved enclosing item index makes the path a
// fallback container for this snapshot.
info . destinationUnresolved = s . itemScope
2026-09-29 17:58:15 +00:00
if kind == SourceKindFilesystem {
info . namespace = s . access . token
}
2026-09-12 15:58:06 +00:00
s . sources = append ( s . sources , info )
2026-09-29 17:29:01 +00:00
data , skipped , diagnostic := s . readImport ( canonical , sourceID , declaration . optional )
2026-09-12 15:58:06 +00:00
if diagnostic != nil {
return [ ] Diagnostic { * diagnostic }
}
if skipped {
s . sources = s . sources [ : len ( s . sources ) - 1 ]
return nil
}
2026-09-29 17:29:01 +00:00
s . accounting . sources ++
2026-09-12 15:58:06 +00:00
root , issues := yamladapter . Parse ( bytes . NewReader ( data ) , sourceID , yamladapter . Limits {
Depth : maxDocumentDepth , Nodes : maxDocumentNodes ,
} )
if len ( issues ) != 0 {
return parserDiagnostics ( sourceID , issues )
}
nodeCount := countInputNodes ( root )
2026-09-29 17:29:01 +00:00
if s . accounting . nodes + nodeCount > s . limits . nodes {
2026-09-12 15:58:06 +00:00
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , & location ) }
}
2026-09-29 17:29:01 +00:00
s . accounting . nodes += nodeCount
2026-09-12 15:58:06 +00:00
2026-10-15 15:33:40 +00:00
var body * document . Node
var declarations [ ] importDeclaration
var diagnostics [ ] Diagnostic
2026-10-16 15:48:36 +00:00
var processing * sourceVersionProcessingResult
2026-10-15 15:33:40 +00:00
if s . versionState != nil && s . versionState . mode != sourceVersionModeDefault {
2026-10-16 19:11:08 +00:00
versionDestination := destination
if s . itemScope {
versionDestination = appendDestination ( s . itemPrefix . Index ( 0 ) , destination )
}
processed , failures := s . versionState . process ( root , sourceVersionContribution , versionDestination , sourceID , declaration . placement )
2026-10-15 15:33:40 +00:00
if len ( failures ) != 0 {
return failures
}
2026-10-16 15:48:36 +00:00
processing = & processed
2026-10-15 15:33:40 +00:00
body , declarations = processed . payload , processed . imports
} else {
body , declarations , diagnostics = splitImportDirectives ( root )
if len ( diagnostics ) != 0 {
return diagnostics
}
2026-09-12 15:58:06 +00:00
}
2026-10-16 16:20:14 +00:00
placementDestination := destination
mode := placementModeMapping
if declaration . placement == placementModeSequenceItem {
mode = placementModeSequenceItem
}
if mode == placementModeSequenceItem {
route := importRoute { canonical : canonical , locations : currentRoute . locations }
s . active [ identity ] = len ( s . stack )
s . visited [ identity ] = route
s . stack = append ( s . stack , route )
diagnostics := s . visitItemSource ( canonical , declaration , destination , relativeDestination , depth , info , body , declarations , processing )
delete ( s . active , identity )
s . stack = s . stack [ : len ( s . stack ) - 1 ]
return diagnostics
}
_ , placed , err := placeDocument ( body , placementDestination , body . Location , maxDocumentDepth , maxDocumentNodes )
2026-10-14 13:19:31 +00:00
if err != nil {
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the source depth or node limit after destination placement" , & location ) }
}
2026-10-16 16:20:14 +00:00
placementNodes := destinationWrapperNodeCount ( placementDestination )
2026-10-14 13:31:32 +00:00
if s . accounting . nodes + placementNodes > s . limits . nodes {
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , & location ) }
}
s . accounting . nodes += placementNodes
2026-09-12 15:58:06 +00:00
route := importRoute { canonical : canonical , locations : currentRoute . locations }
2026-10-16 15:48:36 +00:00
s . scope . contributions = append ( s . scope . contributions , acquiredContribution {
source : cloneSourceInfo ( info ) , payload : body , document : placed ,
2026-10-16 16:20:14 +00:00
destination : placementDestination . clone ( ) , relativeDestination : relativeDestination . clone ( ) , processing : processing ,
2026-10-16 15:48:36 +00:00
} )
2026-09-29 17:29:01 +00:00
s . active [ identity ] = len ( s . stack )
s . visited [ identity ] = route
2026-09-12 15:58:06 +00:00
s . stack = append ( s . stack , route )
2026-10-14 17:21:07 +00:00
diagnostics = s . walkDeclarations ( s . access . dir ( canonical ) , declarations , depth , destination )
2026-09-29 17:29:01 +00:00
delete ( s . active , identity )
2026-09-12 15:58:06 +00:00
s . stack = s . stack [ : len ( s . stack ) - 1 ]
return diagnostics
}
2026-10-16 16:20:14 +00:00
func ( s * importTraversal ) sourceDestination ( destination Path ) Path {
if ! s . itemScope {
return destination . clone ( )
}
2026-10-16 19:23:43 +00:00
// Until the enclosing item receives its assigned index, only the collection
// container is exact. Keep the local suffix in the acquisition projection.
return s . itemFallback . clone ( )
2026-10-16 16:20:14 +00:00
}
func ( s * importTraversal ) visitItemSource ( canonical string , declaration importDeclaration , destination , relativeDestination Path , depth int , info SourceInfo , body * document . Node , declarations [ ] importDeclaration , processing * sourceVersionProcessingResult ) [ ] Diagnostic {
parentScope := s . scope
2026-10-16 19:23:43 +00:00
parentItemScope , parentPrefix , parentFallback := s . itemScope , s . itemPrefix , s . itemFallback
2026-10-16 16:20:14 +00:00
s . scope = acquisitionScope { }
s . itemScope = true
if parentItemScope {
s . itemPrefix = appendDestination ( parentPrefix , destination )
} else {
s . itemPrefix = destination . clone ( )
2026-10-16 19:23:43 +00:00
s . itemFallback = destination . clone ( )
2026-10-16 16:20:14 +00:00
}
s . scope . contributions = append ( s . scope . contributions , acquiredContribution {
source : cloneSourceInfo ( info ) , payload : body , document : body ,
processing : processing ,
} )
diagnostics := s . walkDeclarations ( s . access . dir ( canonical ) , declarations , depth , Path { } )
itemScope := s . scope
2026-10-16 19:23:43 +00:00
s . scope , s . itemScope , s . itemPrefix , s . itemFallback = parentScope , parentItemScope , parentPrefix , parentFallback
2026-10-16 16:20:14 +00:00
if len ( diagnostics ) != 0 {
return diagnostics
}
documents := make ( [ ] * document . Node , 0 , len ( itemScope . contributions ) )
for _ , contribution := range itemScope . contributions {
documents = append ( documents , contribution . document )
}
completed , diagnostics := composeDocuments ( documents ... )
if len ( diagnostics ) != 0 {
prefix := destination . Index ( 0 )
if parentItemScope {
prefix = appendDestination ( parentPrefix , prefix )
}
return prependDiagnosticPaths ( diagnostics , prefix )
}
itemScope . destination = Path { }
completed . acquisition = [ ] acquisitionScope { cloneAcquisitionScope ( itemScope ) }
projectNestedEvidence ( completed , itemScope )
pl := destination
_ , placed , err := placeDocumentWithMode ( completed . root , pl , placementModeSequenceItem , body . Location , maxDocumentDepth , maxDocumentNodes )
if err != nil {
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the source depth or node limit after destination placement" , & declaration . location ) }
}
placementNodes := destinationWrapperNodeCountForMode ( pl , placementModeSequenceItem )
if s . accounting . nodes + placementNodes > s . limits . nodes {
return [ ] Diagnostic { importDiagnostic ( CodeImportLimitExceeded , "configuration exceeds the aggregate node limit" , & declaration . location ) }
}
s . accounting . nodes += placementNodes
s . scope . contributions = append ( s . scope . contributions , acquiredContribution {
source : cloneSourceInfo ( info ) , payload : completed . root , document : placed ,
destination : pl . clone ( ) , relativeDestination : relativeDestination . clone ( ) , processing : processing ,
nested : completed ,
} )
return nil
}
2026-10-16 19:11:08 +00:00
func importedVersionClaim ( documents [ ] * document . Node , composed * composedDocument , rule * VersionRule , mainIndex int ) * Diagnostic {
if composed == nil || rule == nil {
return nil
}
for index , contribution := range documents {
if index == mainIndex {
continue
}
if node := fragmentVersionClaimProjected ( contribution , rule , index , composed . sequenceRanges ) ; node != nil {
location := publicLocation ( node . Location )
return & Diagnostic {
Code : CodeImportVersion , Severity : SeverityError , Path : rule . Path . clone ( ) ,
Message : "imported fragments cannot contribute to the schema-version path" , Primary : & location ,
}
}
}
return nil
}
2026-10-16 16:20:14 +00:00
func prependDiagnosticPaths ( diagnostics [ ] Diagnostic , prefix Path ) [ ] Diagnostic {
cloned := append ( [ ] Diagnostic ( nil ) , diagnostics ... )
for index := range cloned {
cloned [ index ] . Path = appendDestination ( prefix , cloned [ index ] . Path )
cloned [ index ] . Primary = cloneLocation ( cloned [ index ] . Primary )
cloned [ index ] . Related = cloneLocations ( cloned [ index ] . Related )
}
return cloned
}
func projectNestedEvidence ( composed * composedDocument , scope acquisitionScope ) {
if composed == nil {
return
}
for sourceIndex , contribution := range scope . contributions {
if contribution . nested == nil {
continue
}
start := 0
for _ , assigned := range composed . sequenceRanges {
if assigned . sourceIndex == sourceIndex && assigned . path . String ( ) == contribution . destination . String ( ) {
start = assigned . start
break
}
}
prefix := contribution . destination . Index ( start )
if sourceIndex < len ( composed . applications ) {
outer := composed . applications [ sourceIndex ]
structural := document . Clone ( outer . value )
if node , found := documentAtPath ( structural , contribution . destination ) ; found && node . Kind == document . Sequence && len ( node . Sequence ) > start {
node . Sequence [ start ] = & document . Node { Kind : document . Mapping , Location : node . Sequence [ start ] . Location }
}
outer . value = structural
composed . applications [ sourceIndex ] = outer
for _ , nested := range contribution . nested . applications {
projected := nested
projected . path = appendDestination ( prefix , nested . path )
projected . sequences = cloneSequenceHistoryContributions ( nested . sequences )
for index := range projected . sequences {
projected . sequences [ index ] . path = appendDestination ( prefix , projected . sequences [ index ] . path )
}
composed . applications = append ( composed . applications , projected )
}
}
projectNestedOrigins ( composed . origins , contribution . nested . root , contribution . nested . origins , prefix )
}
}
func projectNestedOrigins ( target map [ string ] Origin , root * document . Node , origins map [ string ] Origin , prefix Path ) {
var visit func ( * document . Node , Path )
visit = func ( node * document . Node , path Path ) {
if node == nil {
return
}
if origin , ok := origins [ path . String ( ) ] ; ok {
target [ appendDestination ( prefix , path ) . String ( ) ] = cloneOrigin ( origin )
}
for _ , entry := range node . Mapping {
visit ( entry . Value , path . Key ( entry . Key ) )
}
for index , child := range node . Sequence {
visit ( child , path . Index ( index ) )
}
}
visit ( root , Path { } )
}
func projectAcquiredDestinations ( composed * composedDocument , scope acquisitionScope , sources [ ] SourceInfo ) {
byID := make ( map [ string ] int , len ( sources ) )
for index := range sources {
byID [ sources [ index ] . ID ] = index
}
var projectScope func ( * composedDocument , acquisitionScope , Path , bool , string )
projectScope = func ( currentComposition * composedDocument , current acquisitionScope , prefix Path , insideItem bool , parentSourceID string ) {
for sourceIndex , contribution := range current . contributions {
if contribution . source . ID == parentSourceID {
continue
}
contributionPrefix := prefix
contributionInsideItem := insideItem
start := 0
for _ , assigned := range currentComposition . sequenceRanges {
if assigned . sourceIndex == sourceIndex && assigned . path . String ( ) == contribution . destination . String ( ) {
start = assigned . start
break
}
}
if contribution . source . placementMode == placementModeSequenceItem {
container := appendDestination ( contributionPrefix , contribution . destination )
contribution . source . Destination = container . clone ( )
contribution . source . destinationUnresolved = false
current . contributions [ sourceIndex ] . source = cloneSourceInfo ( contribution . source )
if position , ok := byID [ contribution . source . ID ] ; ok {
sources [ position ] . Destination = container
sources [ position ] . destinationUnresolved = false
}
contributionPrefix = appendDestination ( container , Path { } . Index ( start ) )
contributionInsideItem = true
2026-10-17 04:42:23 +00:00
projectSourceVersionEvidence ( contribution . processing , contributionPrefix , nil , sourceIndex )
2026-10-16 16:20:14 +00:00
} else if contributionInsideItem {
exactDestination := appendDestination ( contributionPrefix , contribution . destination )
contribution . source . Destination = exactDestination . clone ( )
contribution . source . destinationUnresolved = false
current . contributions [ sourceIndex ] . source = cloneSourceInfo ( contribution . source )
if position , ok := byID [ contribution . source . ID ] ; ok {
sources [ position ] . Destination = exactDestination
sources [ position ] . destinationUnresolved = false
}
2026-10-17 04:42:23 +00:00
projectSourceVersionEvidence ( contribution . processing , exactDestination , nil , sourceIndex )
} else {
projectSourceVersionEvidence ( contribution . processing , contribution . destination , currentComposition . sequenceRanges , sourceIndex )
2026-10-16 16:20:14 +00:00
}
if contribution . nested != nil && contribution . nested . acquisition != nil {
for _ , nestedScope := range contribution . nested . acquisition {
projectScope ( contribution . nested , nestedScope , contributionPrefix , contributionInsideItem , contribution . source . ID )
}
}
}
}
projectScope ( composed , scope , Path { } , false , "" )
}
2026-10-17 04:42:23 +00:00
func projectSourceVersionEvidence ( processing * sourceVersionProcessingResult , destination Path , ranges [ ] sequenceRange , sourceIndex int ) {
2026-10-16 19:11:08 +00:00
if processing != nil && processing . evidence != nil {
2026-10-17 04:42:23 +00:00
evidence := processing . evidence
evidence . destination = destination . clone ( )
// Rebuild from source-local paths on each handoff: an input can be
// composed first on its own and then at a different offset in its tier.
evidence . projectedPaths = nil
paths := evidence . sensitivityPaths ( )
for index , path := range paths {
for _ , assigned := range ranges {
if assigned . sourceIndex != sourceIndex || ! pathContains ( assigned . path , path ) || len ( path . segments ) <= len ( assigned . path . segments ) {
continue
}
position := len ( assigned . path . segments )
item , ok := schemaIndex ( path . segments [ position ] )
if ok && path . segments [ position ] . kind != pathSegmentKey {
path . segments [ position ] = pathSegment { kind : pathSegmentIndex , index : assigned . start + item }
}
}
paths [ index ] = path
}
evidence . projectedPaths = paths
2026-10-16 19:11:08 +00:00
}
}
2026-09-29 17:29:01 +00:00
func ( s * importTraversal ) readImport ( name , sourceID string , optional bool ) ( [ ] byte , bool , * Diagnostic ) {
remaining := s . limits . bytes - s . accounting . bytes
2026-09-12 15:58:06 +00:00
limit := min ( int64 ( maxSourceBytes ) , remaining )
if limit < 0 {
limit = 0
}
2026-09-29 17:29:01 +00:00
data , failure := readRegularFile ( name , s . access . stat , s . access . open , limit )
2026-09-12 15:58:06 +00:00
if failure != nil {
if optional && failure . missingTarget {
return nil , true , nil
}
value := sourceReadDiagnostic ( failure , sourceID , CodeImportLimitExceeded , "configuration exceeds a source or aggregate byte limit" )
return nil , false , & value
}
2026-09-29 17:29:01 +00:00
s . accounting . bytes += int64 ( len ( data ) )
2026-09-12 15:58:06 +00:00
return data , false , nil
}
func ( s * importTraversal ) currentRoute ( location Location ) importRoute {
locations := make ( [ ] Location , 0 , len ( s . stack ) + 1 )
for _ , route := range s . stack {
if len ( route . locations ) != 0 {
locations = append ( locations , route . locations [ len ( route . locations ) - 1 ] )
}
}
locations = append ( locations , location )
return importRoute { locations : locations }
}
func sourceInfos ( source * SourceInfo ) [ ] SourceInfo {
if source == nil {
return nil
}
return [ ] SourceInfo { cloneSourceInfo ( * source ) }
}
func importDiagnostic ( code Code , message string , location * Location ) Diagnostic {
return Diagnostic { Code : code , Severity : SeverityError , Message : message , Primary : cloneLocation ( location ) }
}
func importCycleDiagnostic ( target string , location Location , cycle [ ] importRoute ) Diagnostic {
paths := make ( [ ] string , 0 , len ( cycle ) + 1 )
related := make ( [ ] Location , 0 , len ( cycle ) )
for _ , route := range cycle {
paths = append ( paths , route . canonical )
if len ( route . locations ) != 0 {
related = append ( related , route . locations [ len ( route . locations ) - 1 ] )
}
}
paths = append ( paths , target )
return Diagnostic {
Code : CodeImportCycle , Severity : SeverityError ,
Message : "import cycle detected: " + strings . Join ( paths , " -> " ) ,
Primary : & location , Related : related ,
}
}
func importRepeatedDiagnostic ( location Location , previous , current importRoute ) Diagnostic {
related := append ( [ ] Location ( nil ) , previous . locations ... )
if len ( current . locations ) > 1 {
related = append ( related , current . locations [ : len ( current . locations ) - 1 ] ... )
}
return Diagnostic {
Code : CodeImportRepeated , Severity : SeverityError ,
Message : "configuration source is included by more than one import route" ,
Primary : & location , Related : related ,
}
}