Complete bounded glob imports

This commit is contained in:
2026-09-06 20:51:21 +00:00
parent 9f04c09be3
commit 71a73bcfd8
15 changed files with 679 additions and 82 deletions

View File

@@ -22,9 +22,10 @@ File sources can observe file changes between consumptions; byte sources cannot.
Each source is limited to 8 MiB. An imported load is limited to 64 MiB across all
accepted sources, 1,024 accepted sources, 64 active import edges, and 1,000,000
input nodes in aggregate. Reads request at most the remaining allowance plus one byte
so excess data is rejected rather than silently truncated. Data returned together
with a read error is not accepted as partial input.
input nodes in aggregate. Glob expansion separately permits 1,000,000 visited
directory entries, including nonmatches. Reads request at most the remaining
allowance plus one byte so excess data is rejected rather than silently truncated.
Data returned together with a read error is not accepted as partial input.
## Single-source loading
@@ -50,10 +51,10 @@ contributes no source, and continues through defaults, decode, and projection. A
missing file remains an error; it is never interpreted as no input.
The current `Options` boundary accepts a defaults factory, `VersionRule`, resolver,
semantic validator, explicit path imports, and import filesystem context. Active
profiles, standalone filesystem context, overrides, and dynamic secret classification
return `options.unavailable` because their lifecycle stages are not implemented.
The reserved profile spelling `default` selects no active profile. `NoSource`
semantic validator, explicit path and glob imports, and filesystem context. Active
profiles, overrides, and dynamic secret classification return `options.unavailable`
because their lifecycle stages are not implemented. The reserved profile spelling
`default` selects no active profile. `NoSource`
additionally rejects imports, active profiles, and filesystem context as invalid
combinations. Input containing a reserved `_configkit` mapping key is rejected while
imports are disabled.
@@ -180,16 +181,14 @@ traversal order. Traversal order never authorizes replacement. Composition clone
the resulting tree and its ordered claim records rather than mutating or aliasing an
input document.
## Explicit path imports
## Explicit imports
Imports are opt-in. With `Options.Imports` enabled, a document may reserve its root
`_configkit` mapping for an `imports` sequence. ConfigKit removes that mapping before
application decoding. Each sequence item is a mapping with exactly one nonempty
string `path` or `glob` plus an optional boolean `optional`; all other shapes and
fields are `import.invalid`. `glob` is recognized but temporarily returns
`import.unsupported` until bounded glob expansion is implemented. A `_configkit`
key below the root is invalid. With imports disabled, any occurrence remains
`import.disabled`.
fields are `import.invalid`. A `_configkit` key below the root is invalid. With
imports disabled, any occurrence remains `import.disabled`.
Path declarations use slash-separated relative paths. Absolute paths and network
URLs are invalid. Each path resolves from its declaring file's canonical directory,
@@ -199,16 +198,29 @@ permits replacement. Required missing paths fail; `optional: true` suppresses on
target nonexistence, not invalid paths, containment failures, directories,
permissions, parse errors, or composition errors.
Glob declarations use the slash-based Go `path.Match` grammar: `*`, `?`, character
classes, and backslash escapes are supported, while recursive `**` syntax is invalid.
Backslashes escape pattern characters rather than acting as directory separators.
ConfigKit validates the complete pattern before filesystem traversal, walks only
directories selected by preceding segments, and reads wildcard directories in
bounded batches. Every examined entry consumes the separate visit budget, including
nonmatches. Matches are sorted lexicographically by slash-relative spelling within
each declaration before depth-first source traversal. A required zero-match glob is
`import.missing`; `optional: true` suppresses only zero matches. Invalid patterns,
walk failures, matched directories, invalid documents, escapes, and repeated targets
remain errors. Final candidates are bounded by the remaining source limit before
their contents are accumulated.
The configuration root defaults to a selected file's canonical directory. A file
source may supply `FilesystemContext.Root` to widen that root but may not supply
`BaseDir`. Byte and reader sources must supply both `BaseDir` and `Root` as absolute
existing directories when imports are enabled; their diagnostic labels, including
URI-looking labels, never imply filesystem paths. The base and selected file must be
inside the canonical root. ConfigKit canonicalizes symlinks, checks containment with
relative paths, and opens the canonical root-relative target through a library-owned
root handle. Contained relative or absolute symlink targets are accepted; escapes
are rejected. Canonical paths define identity, so hard links at distinct paths remain
distinct sources.
`BaseDir`. Whenever byte and reader sources supply filesystem context, they must
provide both `BaseDir` and `Root` as absolute existing directories; their diagnostic
labels, including URI-looking labels, never imply filesystem paths. The base and
selected file must be inside the canonical root. ConfigKit canonicalizes symlinks,
checks containment with relative paths, and opens the canonical root-relative target
through a library-owned root handle. Contained relative or absolute symlink targets
are accepted; escapes are rejected. Canonical paths define identity, so hard links at
distinct paths remain distinct sources.
An active-stack repeat is `import.cycle` and reports the complete cycle's import
sites. A previously visited canonical path is `import.repeated` and reports both