@absolutejs/manifest
Advanced tools
+82
-12
@@ -421,4 +421,4 @@ #!/usr/bin/env bun | ||
| ].some((candidate) => build.includes(candidate)); | ||
| var parseBuildExternals = (value) => { | ||
| if (value === undefined) | ||
| var parseStringList = (value, optional = false) => { | ||
| if (value === undefined && optional) | ||
| return []; | ||
@@ -438,8 +438,9 @@ if (!Array.isArray(value) || !value.every((specifier) => typeof specifier === "string" && specifier.length > 0)) | ||
| } | ||
| const { buildExternals, optional, range, tested } = value; | ||
| const externalList = parseBuildExternals(buildExternals); | ||
| if (runtime.length === 0 || typeof range !== "string" || range.length === 0 || typeof tested !== "string" || !EXACT_VERSION.test(tested) || optional !== undefined && typeof optional !== "boolean" || externalList === undefined || new Set(externalList).size !== externalList.length) { | ||
| const { artifactImports, buildExternals, optional, range, tested } = value; | ||
| const artifactImportList = parseStringList(artifactImports); | ||
| const externalList = parseStringList(buildExternals, true); | ||
| if (runtime.length === 0 || typeof range !== "string" || range.length === 0 || typeof tested !== "string" || !EXACT_VERSION.test(tested) || optional !== undefined && typeof optional !== "boolean" || artifactImportList === undefined || externalList === undefined || new Set(artifactImportList).size !== artifactImportList.length || new Set(externalList).size !== externalList.length) { | ||
| issues.push({ | ||
| code: "invalid_policy", | ||
| message: `absolutejs.runtimePeers["${runtime}"] requires a non-empty range, exact tested version, optional boolean, and unique buildExternals`, | ||
| message: `absolutejs.runtimePeers["${runtime}"] requires a non-empty range, exact tested version, optional boolean, explicit unique artifactImports, and unique buildExternals`, | ||
| runtime | ||
@@ -450,2 +451,3 @@ }); | ||
| return { | ||
| artifactImports: artifactImportList, | ||
| buildExternals: externalList, | ||
@@ -457,2 +459,3 @@ optional: optional === true, | ||
| }; | ||
| var hasArtifactImport = (artifacts, specifier) => Object.values(artifacts).some((contents) => contents.includes(`"${specifier}"`) || contents.includes(`'${specifier}'`)); | ||
| var validateDeclaredRuntime = (packageJson, runtime, policy, issues) => { | ||
@@ -515,5 +518,33 @@ if (packageJson.dependencies?.[runtime] !== undefined) | ||
| }; | ||
| var validatePackageArtifactPolicy = (packageJson, artifacts) => { | ||
| const packagePolicy = validatePackageRuntimePolicy(packageJson); | ||
| if (!packagePolicy.ok) | ||
| return packagePolicy; | ||
| const configured = packageJson.absolutejs?.runtimePeers; | ||
| if (!isRecord(configured)) | ||
| return packagePolicy; | ||
| const issues = []; | ||
| for (const [runtime, value] of Object.entries(configured)) { | ||
| const policy = parsedPolicy(runtime, value, issues); | ||
| if (!policy) | ||
| continue; | ||
| for (const specifier of policy.artifactImports) | ||
| if (!hasArtifactImport(artifacts, specifier)) | ||
| issues.push({ | ||
| code: "artifact_import_missing", | ||
| message: `built JavaScript must retain an import of ${specifier} for host-owned runtime ${runtime}`, | ||
| runtime | ||
| }); | ||
| } | ||
| return issues.length === 0 ? { issues: [], ok: true } : { issues, ok: false }; | ||
| }; | ||
| // src/cli.ts | ||
| var TAGLINE_MAX_LENGTH = 80; | ||
| var IGNORED_PACKAGE_DIRECTORIES = new Set([ | ||
| ".git", | ||
| "build", | ||
| "dist", | ||
| "node_modules" | ||
| ]); | ||
@@ -529,2 +560,18 @@ class CliError extends Error { | ||
| }; | ||
| var readBuiltJavaScript = async (directory) => { | ||
| const glob = new Bun.Glob("dist/**/*.js"); | ||
| const relativePaths = await Array.fromAsync(glob.scan({ cwd: directory, onlyFiles: true })); | ||
| const entries = await Promise.all(relativePaths.map(async (relativePath) => [ | ||
| relativePath, | ||
| await readFile(join(directory, relativePath), "utf8") | ||
| ])); | ||
| return Object.fromEntries(entries); | ||
| }; | ||
| var validatePackageAt = async (directory, artifacts) => { | ||
| const packageJson = await readPackageJson(directory); | ||
| if (!artifacts) | ||
| return validatePackageRuntimePolicy(packageJson); | ||
| const builtJavaScript = await readBuiltJavaScript(directory); | ||
| return validatePackageArtifactPolicy(packageJson, builtJavaScript); | ||
| }; | ||
| var resolveEntry = (cwd, explicit) => { | ||
@@ -578,2 +625,6 @@ if (explicit !== undefined) { | ||
| const outDir = join(cwd, "dist"); | ||
| const artifactPolicy = await validatePackageAt(cwd, true); | ||
| if (!artifactPolicy.ok) | ||
| throw new CliError(artifactPolicy.issues.map(({ message }) => message).join(` | ||
| `)); | ||
| await mkdir(outDir, { recursive: true }); | ||
@@ -585,6 +636,5 @@ const outPath = join(outDir, "manifest.json"); | ||
| }; | ||
| var verifyPackage = async (explicitDirectory) => { | ||
| var verifyPackage = async (explicitDirectory, artifacts) => { | ||
| const directory = resolve(process.cwd(), explicitDirectory ?? "."); | ||
| const packageJson = await readPackageJson(directory); | ||
| const result = validatePackageRuntimePolicy(packageJson); | ||
| const result = await validatePackageAt(directory, artifacts); | ||
| if (!result.ok) | ||
@@ -595,2 +645,18 @@ throw new CliError(result.issues.map(({ message }) => message).join(` | ||
| }; | ||
| var packagePathIsInspectable = (relativePath) => !relativePath.split("/").some((segment) => IGNORED_PACKAGE_DIRECTORIES.has(segment)); | ||
| var verifyTree = async (explicitDirectory, artifacts) => { | ||
| const root = resolve(process.cwd(), explicitDirectory ?? "."); | ||
| const glob = new Bun.Glob("**/package.json"); | ||
| const packagePaths = (await Array.fromAsync(glob.scan({ cwd: root, onlyFiles: true }))).filter(packagePathIsInspectable); | ||
| const results = await Promise.all(packagePaths.map(async (packagePath) => { | ||
| const directory = resolve(root, packagePath, ".."); | ||
| const result = await validatePackageAt(directory, artifacts); | ||
| return { packagePath, result }; | ||
| })); | ||
| const problems = results.flatMap(({ packagePath, result }) => result.ok ? [] : result.issues.map(({ message }) => `${packagePath}: ${message}`)); | ||
| if (problems.length > 0) | ||
| throw new CliError(problems.join(` | ||
| `)); | ||
| console.log(`absolute-manifest: ${packagePaths.length} package policies valid in ${root}`); | ||
| }; | ||
| var scaffoldTemplate = (name, description) => `import { Type } from '@sinclair/typebox'; | ||
@@ -637,2 +703,4 @@ import { defineManifest } from '@absolutejs/manifest'; | ||
| const [, , command, ...rest] = process.argv; | ||
| const artifacts = rest.includes("--artifacts"); | ||
| const directory = rest.find((argument) => argument !== "--artifacts"); | ||
| if (command === "emit") | ||
@@ -643,5 +711,7 @@ await emit(rest[0]); | ||
| else if (command === "verify-package") | ||
| await verifyPackage(rest[0]); | ||
| await verifyPackage(directory, artifacts); | ||
| else if (command === "verify-tree") | ||
| await verifyTree(directory, artifacts); | ||
| else | ||
| throw new CliError(`unknown command "${command ?? ""}" \u2014 use: emit [entry] | scaffold | verify-package [directory]`); | ||
| throw new CliError(`unknown command "${command ?? ""}" \u2014 use: emit [entry] | scaffold | verify-package [directory] [--artifacts] | verify-tree [directory] [--artifacts]`); | ||
| }; | ||
@@ -655,3 +725,3 @@ try { | ||
| //# debugId=0A6FF21A1239870064756E2164756E21 | ||
| //# debugId=F56BCE7F3FA7E4C864756E2164756E21 | ||
| //# sourceMappingURL=cli.js.map |
+2
-2
@@ -11,4 +11,4 @@ export { defineImplementation, defineManifest } from "./defineManifest"; | ||
| export type { LoadManifestResult } from "./load"; | ||
| export { validatePackageRuntimePolicy } from "./packagePolicy"; | ||
| export type { PackageRuntimePolicyInput, PackageRuntimePolicyIssue, PackageRuntimePolicyResult, RuntimePeerPolicy, } from "./packagePolicy"; | ||
| export { validatePackageArtifactPolicy, validatePackageRuntimePolicy, } from "./packagePolicy"; | ||
| export type { PackageArtifactFiles, PackageRuntimePolicyInput, PackageRuntimePolicyIssue, PackageRuntimePolicyResult, RuntimePeerPolicy, } from "./packagePolicy"; | ||
| export { TOOL_NAME_PATTERN } from "./types"; | ||
@@ -15,0 +15,0 @@ export { digestToolInput, inspectManifestSecurity } from "./security"; |
+33
-7
@@ -631,4 +631,4 @@ // @bun | ||
| ].some((candidate) => build.includes(candidate)); | ||
| var parseBuildExternals = (value) => { | ||
| if (value === undefined) | ||
| var parseStringList = (value, optional = false) => { | ||
| if (value === undefined && optional) | ||
| return []; | ||
@@ -648,8 +648,9 @@ if (!Array.isArray(value) || !value.every((specifier) => typeof specifier === "string" && specifier.length > 0)) | ||
| } | ||
| const { buildExternals, optional, range, tested } = value; | ||
| const externalList = parseBuildExternals(buildExternals); | ||
| if (runtime.length === 0 || typeof range !== "string" || range.length === 0 || typeof tested !== "string" || !EXACT_VERSION.test(tested) || optional !== undefined && typeof optional !== "boolean" || externalList === undefined || new Set(externalList).size !== externalList.length) { | ||
| const { artifactImports, buildExternals, optional, range, tested } = value; | ||
| const artifactImportList = parseStringList(artifactImports); | ||
| const externalList = parseStringList(buildExternals, true); | ||
| if (runtime.length === 0 || typeof range !== "string" || range.length === 0 || typeof tested !== "string" || !EXACT_VERSION.test(tested) || optional !== undefined && typeof optional !== "boolean" || artifactImportList === undefined || externalList === undefined || new Set(artifactImportList).size !== artifactImportList.length || new Set(externalList).size !== externalList.length) { | ||
| issues.push({ | ||
| code: "invalid_policy", | ||
| message: `absolutejs.runtimePeers["${runtime}"] requires a non-empty range, exact tested version, optional boolean, and unique buildExternals`, | ||
| message: `absolutejs.runtimePeers["${runtime}"] requires a non-empty range, exact tested version, optional boolean, explicit unique artifactImports, and unique buildExternals`, | ||
| runtime | ||
@@ -660,2 +661,3 @@ }); | ||
| return { | ||
| artifactImports: artifactImportList, | ||
| buildExternals: externalList, | ||
@@ -667,2 +669,3 @@ optional: optional === true, | ||
| }; | ||
| var hasArtifactImport = (artifacts, specifier) => Object.values(artifacts).some((contents) => contents.includes(`"${specifier}"`) || contents.includes(`'${specifier}'`)); | ||
| var validateDeclaredRuntime = (packageJson, runtime, policy, issues) => { | ||
@@ -725,4 +728,27 @@ if (packageJson.dependencies?.[runtime] !== undefined) | ||
| }; | ||
| var validatePackageArtifactPolicy = (packageJson, artifacts) => { | ||
| const packagePolicy = validatePackageRuntimePolicy(packageJson); | ||
| if (!packagePolicy.ok) | ||
| return packagePolicy; | ||
| const configured = packageJson.absolutejs?.runtimePeers; | ||
| if (!isRecord(configured)) | ||
| return packagePolicy; | ||
| const issues = []; | ||
| for (const [runtime, value] of Object.entries(configured)) { | ||
| const policy = parsedPolicy(runtime, value, issues); | ||
| if (!policy) | ||
| continue; | ||
| for (const specifier of policy.artifactImports) | ||
| if (!hasArtifactImport(artifacts, specifier)) | ||
| issues.push({ | ||
| code: "artifact_import_missing", | ||
| message: `built JavaScript must retain an import of ${specifier} for host-owned runtime ${runtime}`, | ||
| runtime | ||
| }); | ||
| } | ||
| return issues.length === 0 ? { issues: [], ok: true } : { issues, ok: false }; | ||
| }; | ||
| export { | ||
| validatePackageRuntimePolicy, | ||
| validatePackageArtifactPolicy, | ||
| validateManifest, | ||
@@ -747,3 +773,3 @@ toolFactory, | ||
| //# debugId=BE6A2950E52A0F2664756E2164756E21 | ||
| //# debugId=F9264E832F51A9E264756E2164756E21 | ||
| //# sourceMappingURL=index.js.map |
| export type RuntimePeerPolicy = { | ||
| artifactImports: readonly string[]; | ||
| buildExternals?: readonly string[]; | ||
@@ -8,3 +9,3 @@ optional?: boolean; | ||
| export type PackageRuntimePolicyIssue = { | ||
| code: "dependency_conflict" | "dev_dependency_mismatch" | "external_missing" | "invalid_policy" | "optional_mismatch" | "peer_range_mismatch"; | ||
| code: "artifact_import_missing" | "dependency_conflict" | "dev_dependency_mismatch" | "external_missing" | "invalid_policy" | "optional_mismatch" | "peer_range_mismatch"; | ||
| message: string; | ||
@@ -32,3 +33,4 @@ runtime?: string; | ||
| }; | ||
| export declare const validatePackageRuntimePolicy: (packageJson: PackageRuntimePolicyInput) => { | ||
| export type PackageArtifactFiles = Readonly<Record<string, string>>; | ||
| declare const validatePackageRuntimePolicy: (packageJson: PackageRuntimePolicyInput) => { | ||
| issues: []; | ||
@@ -40,1 +42,9 @@ ok: true; | ||
| }; | ||
| declare const validatePackageArtifactPolicy: (packageJson: PackageRuntimePolicyInput, artifacts: PackageArtifactFiles) => { | ||
| issues: []; | ||
| ok: true; | ||
| } | { | ||
| issues: PackageRuntimePolicyIssue[]; | ||
| ok: false; | ||
| }; | ||
| export { validatePackageArtifactPolicy, validatePackageRuntimePolicy }; |
+6
-2
| { | ||
| "name": "@absolutejs/manifest", | ||
| "version": "0.6.0", | ||
| "version": "0.6.1", | ||
| "description": "The AbsoluteJS package manifest contract. Every @absolutejs/* package exports a typed manifest (settings schema, env requirements, adapter slots, wiring recipes, AI tools) from its ./manifest subpath; this package is the contract those manifests are written against, plus bridges that turn any manifest into an AI tool map or a remote MCP tool registry.", | ||
@@ -35,2 +35,6 @@ "repository": { | ||
| "@sinclair/typebox": { | ||
| "artifactImports": [ | ||
| "@sinclair/typebox", | ||
| "@sinclair/typebox/value" | ||
| ], | ||
| "range": ">=0.34.0 <0.35.0", | ||
@@ -57,3 +61,3 @@ "tested": "0.34.52", | ||
| "lint": "eslint . --max-warnings 0", | ||
| "check:package": "bun run typecheck && bun run lint && bun run verify-package && bun run build && bun run test", | ||
| "check:package": "bun run typecheck && bun run lint && bun run verify-package && bun run build && bun run verify-package --artifacts && bun run test", | ||
| "verify-package": "bun src/cli.ts verify-package", | ||
@@ -60,0 +64,0 @@ "release": "bun run format && bun run check:package && bun publish" |
+11
-4
@@ -158,2 +158,3 @@ # @absolutejs/manifest | ||
| "@absolutejs/agency": { | ||
| "artifactImports": ["@absolutejs/agency"], | ||
| "range": ">=0.7.1 <0.8.0", | ||
@@ -188,7 +189,13 @@ "tested": "0.7.1", | ||
| range or optionality mismatch, a dev dependency that differs from the exact | ||
| tested version, and any missing build external. Keep compatibility windows | ||
| conservative; supporting a new pre-1.0 minor requires a deliberate package | ||
| release. Run `absolute-manifest verify-package` directly for packages that want | ||
| the package policy gate without emitting a manifest. | ||
| tested version, any missing build external, and any `artifactImports` entry | ||
| that disappeared from the emitted JavaScript because it was bundled. An empty | ||
| `artifactImports` array is an explicit declaration that the peer is type-only. | ||
| Keep compatibility windows conservative; supporting a new pre-1.0 minor | ||
| requires a deliberate package release. | ||
| Run `absolute-manifest verify-package --artifacts` directly for packages that | ||
| want both package and emitted-artifact gates without emitting a manifest. | ||
| `absolute-manifest verify-tree [directory] [--artifacts]` recursively checks a | ||
| workspace while excluding generated and dependency directories. | ||
| ## Consuming manifests | ||
@@ -195,0 +202,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
297153
5.51%2308
4.91%242
2.98%0
-100%