Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/validate-schemas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ on:
- 'schemas/**'
- 'examples/**'
- 'scripts/**'
- 'spec/**'
- 'package.json'
- '.github/workflows/validate-schemas.yml'
pull_request:
paths:
- 'schemas/**'
- 'examples/**'
- 'scripts/**'
- 'spec/**'
- 'package.json'
- '.github/workflows/validate-schemas.yml'

Expand All @@ -30,8 +32,17 @@ jobs:
- name: Install dependencies
run: npm ci

- name: TypeScript type check
run: npx tsc --noEmit

- name: Validate schemas and examples
run: npm test

- name: Check spec-schema sync
run: npm run check:sync

- name: Validate cross-references
run: npm run check:refs

- name: Check example coverage
run: npm run check:coverage
111 changes: 111 additions & 0 deletions examples/comprehensive-document/assets/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"version": "0.1",
"families": [
{
"name": "Inter",
"fonts": [
{
"id": "font-inter-regular",
"weight": 400,
"style": "normal"
},
{
"id": "font-inter-bold",
"weight": 700,
"style": "normal"
},
{
"id": "font-inter-italic",
"weight": 400,
"style": "italic"
}
]
}
],
"assets": [
{
"id": "hero-image",
"path": "images/hero.png",
"type": "image/png",
"size": 245760,
"hash": "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"metadata": {
"width": 1920,
"height": 1080,
"colorSpace": "sRGB",
"hasAlpha": false,
"dpi": 72
},
"license": {
"name": "CC BY 4.0",
"url": "https://creativecommons.org/licenses/by/4.0/"
},
"variants": [
{
"path": "images/hero-thumb.png",
"width": 480,
"size": 20480
},
{
"path": "images/hero-medium.png",
"width": 960,
"size": 81920
}
]
},
{
"id": "diagram-svg",
"path": "images/architecture.svg",
"type": "image/svg+xml",
"size": 8192,
"hash": "sha256:b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3"
},
{
"id": "font-inter-regular",
"path": "fonts/Inter-Regular.woff2",
"type": "font/woff2",
"size": 98304,
"hash": "sha256:c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"metadata": {
"family": "Inter",
"weight": 400,
"style": "normal"
}
},
{
"id": "font-inter-bold",
"path": "fonts/Inter-Bold.woff2",
"type": "font/woff2",
"size": 102400,
"hash": "sha256:d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5",
"metadata": {
"family": "Inter",
"weight": 700,
"style": "normal"
}
},
{
"id": "font-inter-italic",
"path": "fonts/Inter-Italic.woff2",
"type": "font/woff2",
"size": 100352,
"hash": "sha256:e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6",
"metadata": {
"family": "Inter",
"weight": 400,
"style": "italic"
}
},
{
"id": "supplementary-pdf",
"path": "embeds/appendix-data.pdf",
"type": "application/pdf",
"size": 524288,
"hash": "sha256:f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1",
"metadata": {
"filename": "appendix-data.pdf",
"description": "Supplementary data tables"
}
}
]
}
36 changes: 36 additions & 0 deletions examples/comprehensive-document/provenance/lineage.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": "0.1",
"documentId": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"created": "2025-01-28T14:00:00Z",
"creator": {
"name": "Document Author",
"identifier": "https://orcid.org/0000-0002-1825-0097",
"organization": "Example University"
},
"lineage": {
"parent": null,
"ancestors": [],
"depth": 1
},
"merkle": {
"root": "sha256:f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3",
"blockCount": 24,
"algorithm": "sha256"
},
"timestamps": [
{
"type": "rfc3161",
"time": "2025-01-28T14:05:00Z",
"hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"authority": "http://timestamp.digicert.com",
"token": "MIIEjDAVBgkqhkiG9w0BCQUxCDAGBgQBMjM0..."
}
],
"derivedFrom": [
{
"documentId": "sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"relationship": "revision",
"blocks": ["section-2", "section-3"]
}
]
}
38 changes: 38 additions & 0 deletions examples/signed-document/security/annotations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"version": "0.1",
"annotations": [
{
"id": "ann-001",
"type": "comment",
"anchor": {
"blockId": "para-1",
"start": 0,
"end": 45
},
"author": "Jane Reviewer",
"created": "2025-01-12T09:15:00Z",
"content": "This section needs a stronger opening statement."
},
{
"id": "ann-002",
"type": "highlight",
"anchor": {
"blockId": "para-3"
},
"author": "John Editor",
"created": "2025-01-13T11:30:00Z",
"content": "Key conclusion paragraph"
},
{
"id": "ann-003",
"type": "note",
"anchor": {
"blockId": "heading-2",
"offset": 0
},
"author": "Jane Reviewer",
"created": "2025-01-14T08:00:00Z",
"content": "Consider restructuring this section before signing."
}
]
}
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions scripts/check-spec-schema-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,13 @@ function extractTypesFromSchema(filePath: string): BlockType[] {

// Also check allOf conditionals in block definitions
if (schema.$defs?.block?.allOf) {
const allOf = schema.$defs.block.allOf as Array<Record<string, unknown>>;
const allOf = schema.$defs.block.allOf as Array<{
if?: { properties?: { type?: { const?: string } } };
then?: unknown;
}>;
for (const condition of allOf) {
if (condition.if?.properties?.type?.const) {
const typeName = condition.if.properties.type.const as string;
const typeName = condition.if.properties.type.const;
// Skip 'text' as it's ubiquitous (matches spec extraction behavior)
if (typeName !== 'text' && !extractedTypes.includes(typeName)) {
extractedTypes.push(typeName);
Expand Down
25 changes: 25 additions & 0 deletions scripts/lib/ajv-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Shared AJV utilities for schema validation scripts.
*/

import Ajv2020 from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
import * as fs from 'fs';
import * as path from 'path';

const schemasDir = path.join(__dirname, '..', '..', 'schemas');

export function createAjv(): Ajv2020 {
const ajv = new Ajv2020({
strict: false,
allErrors: true,
});
addFormats(ajv);
return ajv;
}

export function loadSchema(filename: string): object {
const filepath = path.join(schemasDir, filename);
const content = fs.readFileSync(filepath, 'utf8');
return JSON.parse(content);
}
24 changes: 6 additions & 18 deletions scripts/validate-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
* Validates example documents against their corresponding schemas.
*/

import Ajv2020, { ValidateFunction } from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
import { ValidateFunction } from 'ajv/dist/2020';
import * as fs from 'fs';
import * as path from 'path';
import { createAjv, loadSchema } from './lib/ajv-utils.js';

const schemasDir = path.join(__dirname, '..', 'schemas');
const examplesDir = path.join(__dirname, '..', 'examples');

interface Validation {
Expand All @@ -20,21 +19,6 @@ interface Validation {
// Schema validators (compiled once)
const validators: Record<string, ValidateFunction> = {};

function createAjv(): Ajv2020 {
const ajv = new Ajv2020({
strict: false,
allErrors: true,
});
addFormats(ajv);
return ajv;
}

function loadSchema(filename: string): object {
const filepath = path.join(schemasDir, filename);
const content = fs.readFileSync(filepath, 'utf8');
return JSON.parse(content);
}

function loadJson(filepath: string): unknown {
const content = fs.readFileSync(filepath, 'utf8');
return JSON.parse(content);
Expand All @@ -46,6 +30,7 @@ const schemaDependencies: Record<string, string[]> = {
'collaboration.schema.json': ['anchor.schema.json'],
'phantoms.schema.json': ['anchor.schema.json'],
'security.schema.json': ['anchor.schema.json'],
'annotations.schema.json': ['anchor.schema.json'],
};

function getValidator(schemaName: string): ValidateFunction {
Expand Down Expand Up @@ -76,6 +61,9 @@ const extensionValidations: Validation[] = [
{ schema: 'collaboration.schema.json', file: 'collaboration/changes.json' },
{ schema: 'forms.schema.json', file: 'forms/data.json' },
{ schema: 'phantoms.schema.json', file: 'phantoms/clusters.json' },
{ schema: 'annotations.schema.json', file: 'security/annotations.json' },
{ schema: 'asset-index.schema.json', file: 'assets/index.json' },
{ schema: 'provenance.schema.json', file: 'provenance/lineage.json' },
];

let hasErrors = false;
Expand Down
22 changes: 1 addition & 21 deletions scripts/validate-schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
* Validates that all JSON schemas compile correctly.
*/

import Ajv2020 from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
import * as fs from 'fs';
import * as path from 'path';

const schemasDir = path.join(__dirname, '..', 'schemas');
import { createAjv, loadSchema } from './lib/ajv-utils.js';

interface DependentSchema {
schema: string;
Expand Down Expand Up @@ -42,21 +37,6 @@ const dependentSchemas: DependentSchema[] = [

let hasErrors = false;

function createAjv(): Ajv2020 {
const ajv = new Ajv2020({
strict: false,
allErrors: true,
});
addFormats(ajv);
return ajv;
}

function loadSchema(filename: string): object {
const filepath = path.join(schemasDir, filename);
const content = fs.readFileSync(filepath, 'utf8');
return JSON.parse(content);
}

console.log('Validating JSON schemas...\n');

// Validate standalone schemas
Expand Down