@aligntrue/schema
v0.9.3
Published
JSON Schema validation, canonicalization, and integrity hashing for AlignTrue Align aligns.
Downloads
79
Readme
@aligntrue/schema
JSON Schema validation, canonicalization, and integrity hashing for AlignTrue Align aligns.
Overview
This package provides the core validation and canonicalization utilities for Align Spec v2-preview:
- JSON Schema validation using Ajv in strict mode
- JCS (RFC 8785) canonicalization for lockfile and catalog publishing only
- SHA-256 integrity hashing with verification
- TypeScript types for Align align structure
Canonicalization Strategy
Important: Canonicalization is ONLY performed at boundaries where determinism is required:
- Lockfile generation (
aligntrue lockin team mode) - Catalog publishing (
aligntrue publish- removed from roadmap)
NOT used during: init, sync, export, import, or normal file operations.
Why: Solo developers don't need canonicalization overhead for local files. Team mode only needs determinism for lockfile-based drift detection. Running canonicalization on every operation adds unnecessary cost.
Installation
pnpm add @aligntrue/schemaAPI Reference
Canonicalization
parseYamlToJson(yaml: string): unknown
Parse YAML string to JavaScript object. Resolves anchors and aliases.
import { parseYamlToJson } from "@aligntrue/schema";
const yaml = 'id: "aligns/test/example"\nversion: "1.0.0"';
const obj = parseYamlToJson(yaml);canonicalizeJson(obj: unknown): string
Apply JCS (RFC 8785) canonicalization to produce stable JSON string with deterministic key ordering.
import { canonicalizeJson } from "@aligntrue/schema";
const obj = { z: 1, a: 2, m: 3 };
const canonical = canonicalizeJson(obj);
// Result: '{"a":2,"m":3,"z":1}'computeHash(data: string): string
Compute SHA-256 hash of a string and return hex-encoded result.
import { computeHash } from "@aligntrue/schema";
const hash = computeHash("hello world");
// Result: 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'computeAlignHash(alignYaml: string): string
Compute integrity hash for an Align align YAML document. This function:
- Parses YAML to object
- Sets
integrity.valueto"<pending>" - Applies JCS canonicalization
- Computes SHA-256 hash
import { computeAlignHash } from "@aligntrue/schema";
const yaml = `
id: "aligns/test/example"
version: "1.0.0"
profile: "align"
spec_version: "1"
...
integrity:
algo: "jcs-sha256"
value: "<computed>"
`;
const hash = computeAlignHash(yaml);
// Result: hex-encoded SHA-256 hash (64 characters)verifyAlignHash(alignYaml: string, storedHash: string): boolean
Verify that a stored hash matches the computed hash.
import { verifyAlignHash } from "@aligntrue/schema";
const isValid = verifyAlignHash(alignYaml, storedHash);Validation
validateAlignSchema(obj: unknown): ValidationResult
Validate an Align align object against the JSON Schema.
import { validateAlignSchema } from "@aligntrue/schema";
const result = validateAlignSchema(alignObject);
if (!result.valid) {
console.error("Schema validation failed:", result.errors);
}ValidationResult:
interface ValidationResult {
valid: boolean;
errors?: ValidationError[];
}
interface ValidationError {
path: string;
message: string;
keyword?: string;
params?: Record<string, unknown>;
}validateAlignIntegrity(alignYaml: string): IntegrityResult
Validate the integrity hash of an Align align.
import { validateAlignIntegrity } from "@aligntrue/schema";
const result = validateAlignIntegrity(alignYaml);
if (!result.valid) {
console.error("Hash mismatch!");
console.error(`Stored: ${result.storedHash}`);
console.error(`Computed: ${result.computedHash}`);
}IntegrityResult:
interface IntegrityResult {
valid: boolean;
storedHash?: string;
computedHash?: string;
error?: string;
}validateAlign(alignYaml: string)
Validate both schema and integrity of an Align align in one call.
import { validateAlign } from "@aligntrue/schema";
const result = validateAlign(alignYaml);
if (!result.schema.valid) {
console.error("Schema errors:", result.schema.errors);
}
if (!result.integrity.valid) {
console.error("Integrity error:", result.integrity.error);
}TypeScript Types
Export types for Align align structure:
import type {
Align,
AlignScope,
AlignSection,
AlignIntegrity,
} from "@aligntrue/schema";
const align: Align = {
id: "aligns/test/example",
version: "1.0.0",
spec_version: "1",
summary: "Example align",
tags: ["test"],
sections: [
{
id: "section-1",
heading: "Getting started",
level: 1,
content: "Introduction to the align",
},
],
integrity: {
algo: "jcs-sha256",
value: "<computed>",
},
};CLI Scripts
Validate an Align align
pnpm validate path/to/rule.mdOutput shows schema validation, integrity validation, and overall status.
Compute hashes for multiple files
node --import tsx --no-warnings scripts/compute-basealign-hashes.tsThis script processes all .yaml files in the basealigns/ directory and updates their integrity hashes.
JSON Utilities
cloneDeep<T>(obj: T): T
Deep clone an object using native structuredClone(). Preferred over JSON.parse(JSON.stringify()).
import { cloneDeep } from "@aligntrue/schema";
const original = { nested: { value: 1 }, arr: [1, 2] };
const cloned = cloneDeep(original);
cloned.nested.value = 2; // original.nested.value still 1Benefits:
- Uses native
structuredClone()for better performance - Handles more types (Date, Map, Set, etc.)
- Explicit intent in code
parseJsonSafe(str: string): Result<unknown, Error>
Parse JSON string with type-safe error handling. Returns Result type instead of throwing.
import { parseJsonSafe } from "@aligntrue/schema";
const result = parseJsonSafe('{"valid": "json"}');
if (result.ok) {
console.log(result.value);
} else {
console.error(result.error.message);
}stringifyCanonical(obj: unknown): string
Stringify object using canonical JSON (JCS/RFC 8785).
import { stringifyCanonical } from "@aligntrue/schema";
const canonical = stringifyCanonical({ b: 2, a: 1 });
// Result: '{"a":1,"b":2}'computeContentHash(obj: unknown): string
Convenience wrapper combining canonicalization and hashing for objects.
import { computeContentHash } from "@aligntrue/schema";
const hash = computeContentHash({ rules: [...] });
// Returns: hex-encoded SHA-256 hashcompareCanonical(a: unknown, b: unknown): boolean
Compare two objects using canonical JSON. More reliable than deep equality.
import { compareCanonical } from "@aligntrue/schema";
compareCanonical({ b: 2, a: 1 }, { a: 1, b: 2 }); // truetype Result<T, E>
Result type for operations that may fail.
import type { Result } from "@aligntrue/schema";
type ParseResult = Result<unknown, Error>;Development
Run tests
pnpm testWatch mode
pnpm test:watchType check
pnpm typecheckBuild
pnpm buildDeterminism Guarantees
This package ensures deterministic hashing through:
- JCS canonicalization (RFC 8785): Stable key ordering, precise float representation
- YAML normalization: Anchors and aliases resolved before hashing
- Placeholder handling:
integrity.valueset to"<pending>"during hash computation - UTF-8 encoding: Consistent byte representation
The same Align align content will always produce the same hash, regardless of:
- Key ordering in YAML
- Whitespace or formatting
- YAML anchors vs explicit duplication
- Machine or environment
References
License
MIT
