workspacemedia

package
v1.138.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package workspacemedia writes model-generated media into the user's workspace as ordinary, visible files — the same way generated code or text lands there. It is a pure naming/collision layer: the caller hands it a workspace root, a requested relative path (or a generic fallback name such as "generated"), the bytes, and the MIME type; it returns the exact workspace-relative path it wrote.

Guarantees:

  • Never overwrites. A completed sibling temp file is hard-linked to the final name, making name selection and publication one atomic no-replace operation. An existing entry of any kind (including a symlink) means "taken", so the writer retries with a dash suffix (name-1.ext, name-2.ext, ...), which also makes concurrent same-name writers safe.
  • Atomic publish. A reader observes either no final entry or the full content, never a placeholder or partial write. Filesystems without hard-link support return an explicit error rather than falling back to a replace-capable rename.
  • Workspace containment. Every directory and file operation goes through os.Root, so absolute paths, ".." traversal, invalid or Windows-reserved segments, and symlink escapes are rejected with ErrPathEscape rather than followed.
  • The final filename's extension always agrees with the MIME type when the type is known; a conflicting requested extension is corrected and reported via Result so the caller can show a notice.

Files use ordinary user-file modes (0o755 directories, 0o644 files, both umask-masked). On Windows modes are ignored and entries inherit the parent directory's ACLs — same convention as pkg/atomicfile.

Index

Constants

This section is empty.

Variables

View Source
var ErrNameExhausted = errors.New("no free filename after exhausting collision suffixes")

ErrNameExhausted classifies collision-suffix exhaustion: every candidate name up to maxNameAttempts already exists. Match with errors.Is when the failure must be explained without echoing the requested path.

View Source
var ErrPathEscape = errors.New("path escapes the workspace or has invalid segments")

ErrPathEscape classifies a requested path the writer refuses to touch: absolute, containing "..", an empty/invalid/Windows-reserved segment, or resolving outside the workspace root through a symlinked parent. Callers can redirect refused targets to a safe workspace basename.

Functions

func RequestedBasename

func RequestedBasename(requested string) string

RequestedBasename returns the final meaningful segment of a requested path — the name to redirect to when the full path is refused — or "" when none exists (empty, separators only, or only "."/".." segments).

Types

type PathClass

type PathClass int

PathClass is the outcome of classifying a model-requested target path before any I/O. Classification is purely lexical: a PathWorkspaceRelative path can still be refused at write time when a symlinked parent resolves outside the root (surfaced as ErrPathEscape by Write).

const (
	// PathInvalid marks a path that names nothing usable: empty, only
	// separators/dots, an invalid or Windows-reserved final segment. It is
	// unusable — callers should fall back to a safe generated name.
	PathInvalid PathClass = iota

	// PathWorkspaceRelative marks a path contained under the workspace
	// root, directly writable via [Write].
	PathWorkspaceRelative

	// PathEscaping marks a path that targets a location outside the
	// workspace: absolute, traversing above the root via "..", or rooted at
	// a home directory via a leading "~" segment.
	PathEscaping
)

func ClassifyRequestedPath

func ClassifyRequestedPath(requested string) (PathClass, string)

ClassifyRequestedPath classifies requested and, for PathWorkspaceRelative, returns the normalized slash-separated relative path to hand to Write (both separator styles accepted; interior "." and ".." segments resolved lexically, so "a/../b.png" is contained rather than escaping). For every other class the second return is "".

type Result

type Result struct {
	// RelPath is the exact final path written, relative to the workspace root
	// and slash-separated. Persist this verbatim.
	RelPath string

	// ExtensionCorrected reports that the requested filename's extension
	// conflicted with the MIME-derived one and was replaced. Callers should
	// surface a notice (e.g. "saved as sunshine.png — the provider returned
	// PNG data"). RequestedExtension holds the original, with leading dot.
	ExtensionCorrected bool
	RequestedExtension string
}

Result describes a completed write.

func Write

func Write(workspaceRoot, requestedPath string, data []byte, mimeType string) (Result, error)

Write stores data under workspaceRoot at requestedPath, sanitized and collision-avoided per the package contract, and returns the exact workspace-relative path written. Prompt-directed subdirectories in requestedPath are created as needed. A rejected path returns an error matching ErrPathEscape, collision-suffix exhaustion one matching ErrNameExhausted; any other failure (unwritable directory, full disk, ...) is returned as-is for the caller to surface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL