JSON Schema validation, canonicalization, and integrity hashing for AlignTrue Align aligns.
JSON Schema validation, canonicalization, and integrity hashing for AlignTrue Align aligns.
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
Important: Canonicalization is ONLY performed at boundaries where determinism is required:
- Lockfile generation (aligntrue lock in 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.
``bash`
pnpm add @aligntrue/schema
#### parseYamlToJson(yaml: string): unknown
Parse YAML string to JavaScript object. Resolves anchors and aliases.
`typescript
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.
`typescript
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.
`typescript
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:
1. Parses YAML to object
2. Sets integrity.value to "
3. Applies JCS canonicalization
4. Computes SHA-256 hash
`typescript
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: ";
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.
`typescript
import { verifyAlignHash } from "@aligntrue/schema";
const isValid = verifyAlignHash(alignYaml, storedHash);
`
#### validateAlignSchema(obj: unknown): ValidationResult
Validate an Align align object against the JSON Schema.
`typescript
import { validateAlignSchema } from "@aligntrue/schema";
const result = validateAlignSchema(alignObject);
if (!result.valid) {
console.error("Schema validation failed:", result.errors);
}
`
ValidationResult:
`typescript
interface ValidationResult {
valid: boolean;
errors?: ValidationError[];
}
interface ValidationError {
path: string;
message: string;
keyword?: string;
params?: Record
}
`
#### validateAlignIntegrity(alignYaml: string): IntegrityResult
Validate the integrity hash of an Align align.
`typescript
import { validateAlignIntegrity } from "@aligntrue/schema";
const result = validateAlignIntegrity(alignYaml);
if (!result.valid) {
console.error("Hash mismatch!");
console.error(Stored: ${result.storedHash});Computed: ${result.computedHash}
console.error();`
}
IntegrityResult:
`typescript`
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.
`typescript
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);
}
`
Export types for Align align structure:
`typescript
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: "
},
};
`
`bash`
pnpm validate path/to/rule.md
Output shows schema validation, integrity validation, and overall status.
`bash`
node --import tsx --no-warnings scripts/compute-basealign-hashes.ts
This script processes all .yaml files in the basealigns/ directory and updates their integrity hashes.
#### cloneDeep
Deep clone an object using native structuredClone(). Preferred over JSON.parse(JSON.stringify()).
`typescript
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 1
`
Benefits:
- Uses native structuredClone() for better performance
- Handles more types (Date, Map, Set, etc.)
- Explicit intent in code
#### parseJsonSafe(str: string): Result
Parse JSON string with type-safe error handling. Returns Result type instead of throwing.
`typescript
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).
`typescript
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.
`typescript
import { computeContentHash } from "@aligntrue/schema";
const hash = computeContentHash({ rules: [...] });
// Returns: hex-encoded SHA-256 hash
`
#### compareCanonical(a: unknown, b: unknown): boolean
Compare two objects using canonical JSON. More reliable than deep equality.
`typescript
import { compareCanonical } from "@aligntrue/schema";
compareCanonical({ b: 2, a: 1 }, { a: 1, b: 2 }); // true
`
#### type Result
Result type for operations that may fail.
`typescript
import type { Result } from "@aligntrue/schema";
type ParseResult = Result
`
`bash`
pnpm test
`bash`
pnpm test:watch
`bash`
pnpm typecheck
`bash`
pnpm build
This package ensures deterministic hashing through:
1. JCS canonicalization (RFC 8785): Stable key ordering, precise float representation
2. YAML normalization: Anchors and aliases resolved before hashing
3. Placeholder handling: integrity.value set to "
4. 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
- Align Spec v2-preview (CLI-first)
- Align Spec v1 (superseded)
- JCS (RFC 8785)
- JSON Schema 2020-12
MIT