@appstrate/validation
v1.4.0
Published
Shared validation library for the Appstrate ecosystem. Provides manifest schemas (Zod), package ZIP parsing, naming helpers, dependency extraction, and integrity hashing — used by both the [Appstrate platform](https://github.com/appstrate/strate) and the
Readme
@appstrate/validation
Shared validation library for the Appstrate ecosystem. Provides manifest schemas (Zod), package ZIP parsing, naming helpers, dependency extraction, and integrity hashing — used by both the Appstrate platform and the Appstrate [registry].
Installation
# From npm
bun add @appstrate/validationExports
The package exposes five entry points:
| Import path | Description |
| ------------------------------------ | ----------------------------------------------------- |
| @appstrate/validation | Manifest schemas, validators, skill/extension helpers |
| @appstrate/validation/zip | ZIP parsing, artifact creation, package extraction |
| @appstrate/validation/naming | Scoped name helpers, slug regex, packageId conversion |
| @appstrate/validation/dependencies | Registry dependency extraction from manifests |
| @appstrate/validation/integrity | SHA256 integrity hash computation |
Usage
Manifest Validation
import { validateManifest, baseManifestSchema, flowManifestSchema } from "@appstrate/validation";
// Validate any manifest (auto-dispatches by type)
const result = validateManifest(rawJson);
if (result.valid) {
console.log(result.manifest); // typed BaseManifest | FlowManifest
} else {
console.error(result.errors); // string[]
}Two manifest schemas are available:
baseManifestSchema— Common fields for all types:name(@scope/package-name),version(semver),type(flow|skill|extension), optionaldescription,keywords,license,registryDependenciesflowManifestSchema— Extends base with flow-specific fields:schemaVersion,displayName,author,requires(services, skills, extensions),input/output/configschemas,executionoptions
Skill & Extension Validation
import { extractSkillMeta, validateExtensionSource } from "@appstrate/validation";
// Extract YAML frontmatter from SKILL.md
const meta = extractSkillMeta(skillMdContent);
// { name: "my-skill", description: "...", warnings: [] }
// Validate TypeScript extension source
const result = validateExtensionSource(tsSource);
// { valid: true, errors: [], warnings: [] }ZIP Parsing
import { parsePackageZip, PackageZipError } from "@appstrate/validation/zip";
try {
const parsed = parsePackageZip(zipBuffer);
// { manifest, content, files, type }
} catch (err) {
if (err instanceof PackageZipError) {
console.error(err.code, err.message);
// Codes: FILE_TOO_LARGE, ZIP_INVALID, ZIP_BOMB, MISSING_MANIFEST,
// INVALID_MANIFEST, MISSING_CONTENT, INVALID_CONTENT
}
}Lower-level ZIP utilities:
zipArtifact(entries, level?)— Create a ZIP from file entriesunzipArtifact(buffer)— Decompress with path sanitization (filters..,__MACOSX, absolute paths)detectFolderWrapper(entries)— Detect single-folder wrapping in ZIP
Naming Helpers
import {
SLUG_REGEX,
normalizeScope,
stripScope,
scopedNameToPackageId,
packageIdToScopedName,
depEntryToPackageId,
} from "@appstrate/validation/naming";
normalizeScope("myscope"); // "@myscope"
stripScope("@myscope"); // "myscope"
scopedNameToPackageId("@scope/name"); // "scope--name"
packageIdToScopedName("scope--name"); // "@scope/name"
packageIdToScopedName("local-slug"); // null
depEntryToPackageId("@scope", "name"); // "scope--name"Dependency Extraction
import { extractDependencies } from "@appstrate/validation/dependencies";
import type { DepEntry } from "@appstrate/validation/dependencies";
const deps: DepEntry[] = extractDependencies(manifest);
// [{ depScope: "@scope", depName: "tool", depType: "skill", versionRange: "^1.0.0" }]Integrity
import { computeIntegrity } from "@appstrate/validation/integrity";
const sri = computeIntegrity(zipBuffer);
// "sha256-abc123..."Project Structure
@appstrate/validation/
├── src/
│ ├── index.ts # Manifest schemas (Zod), validateManifest, extractSkillMeta, validateExtensionSource
│ ├── zip.ts # ZIP parse/create, parsePackageZip, PackageZipError
│ ├── naming.ts # SLUG_REGEX, normalizeScope, stripScope, scopedNameToPackageId, packageIdToScopedName
│ ├── dependencies.ts # extractDependencies, DepEntry type
│ └── integrity.ts # computeIntegrity (SHA256 SRI hash)
│
├── tests/ # bun:test (66 tests across 5 files)
│ ├── index.test.ts # Manifest validation (flow, skill, extension, base)
│ ├── zip.test.ts # ZIP parsing, folder detection, error codes, security
│ ├── naming.test.ts # Naming helpers, packageId conversion
│ ├── dependencies.test.ts # Dependency extraction from manifests
│ └── integrity.test.ts # SHA256 integrity computation
│
├── package.json
├── tsconfig.json
└── eslint.config.jsDevelopment
bun test # Run all tests (66 tests, 5 files)
bun run check # TypeScript type-check + ESLint + Prettier
bun run lint # ESLint only
bun run format # Prettier formatTech Stack
- Runtime: Bun
- Validation: Zod 4
- Semver: semver (range validation, version parsing)
- ZIP: fflate (compression/decompression)
- Testing: bun:test (built-in)
- Code quality: ESLint + Prettier + TypeScript strict mode
License
MIT
