diff --git a/README.md b/README.md index 504028b..1ffb6fb 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,11 @@ uv run hackproof discover uv run hackproof scan uv run hackproof reconcile uv run hackproof export -``` +uv run hackproof export-schemas +` + +All dataset exports and public API response models are versioned and validated against schemas published under [pps/web/public/schema/](apps/web/public/schema/): +- **[Schema Index](apps/web/public/schema/index.json)**: Complete catalog of dataset export schemas (v1.0, v1.1, v1.2) and public API response schemas.`` ## Checks diff --git a/apps/api/src/hackproof/cli.py b/apps/api/src/hackproof/cli.py index bc004a2..7e4bf2c 100644 --- a/apps/api/src/hackproof/cli.py +++ b/apps/api/src/hackproof/cli.py @@ -2,7 +2,7 @@ import asyncio import logging -from hackproof.export.jobs import run_export +from hackproof.export.jobs import export_schemas, run_export from hackproof.github.jobs import run_discover, run_scan from hackproof.indexer.sync import run_sync from hackproof.linking.reconcile import run_reconcile @@ -23,6 +23,7 @@ def main() -> None: ) commands.add_parser("reconcile", help="link chain activity to repos and build projects") commands.add_parser("export", help="write the json and yaml export and its schema") + commands.add_parser("export-schemas", help="export JSON schemas for API models and index") args = parser.parse_args() logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") @@ -38,3 +39,5 @@ def main() -> None: asyncio.run(run_reconcile()) case "export": asyncio.run(run_export()) + case "export-schemas": + export_schemas() diff --git a/apps/api/src/hackproof/export/jobs.py b/apps/api/src/hackproof/export/jobs.py index 4fcd605..16c87b6 100644 --- a/apps/api/src/hackproof/export/jobs.py +++ b/apps/api/src/hackproof/export/jobs.py @@ -7,7 +7,15 @@ from hackproof.db import open_pool, pool from hackproof.export.build import build_export from hackproof.export.models import SCHEMA_VERSION -from hackproof.export.render import json_schema, to_json, to_yaml +from hackproof.export.render import ( + API_MODELS, + API_SCHEMA_VERSION, + SITE, + api_json_schema, + json_schema, + to_json, + to_yaml, +) from hackproof.hackathon import get_hackathon log = logging.getLogger("hackproof.export") @@ -17,7 +25,65 @@ def _write(path, content: str) -> None: path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content) + path.write_text(content, encoding="utf-8") + + +def export_schemas() -> None: + schemas = PUBLIC / "schema" + schemas.mkdir(parents=True, exist_ok=True) + + schema = json_schema() + _write(schemas / f"hackathon-export-{SCHEMA_VERSION}.json", json.dumps(schema, indent=2) + "\n") + _write( + schemas / f"hackathon-export-{SCHEMA_VERSION}.yml", + yaml.safe_dump(schema, sort_keys=False, allow_unicode=True, width=100), + ) + + index_entries = [ + { + "name": "hackathon-export", + "category": "export", + "versions": ["1.0", "1.1", "1.2"], + "latest": "1.2", + "files": [ + "hackathon-export-1.0.json", + "hackathon-export-1.0.yml", + "hackathon-export-1.1.json", + "hackathon-export-1.1.yml", + "hackathon-export-1.2.json", + "hackathon-export-1.2.yml", + ], + "url": f"{SITE}/schema/hackathon-export-{SCHEMA_VERSION}.json", + } + ] + + for slug, model in API_MODELS.items(): + model_schema = api_json_schema(model, slug) + filename = f"{slug}-{API_SCHEMA_VERSION}.schema.json" + _write(schemas / filename, json.dumps(model_schema, indent=2) + "\n") + index_entries.append( + { + "name": slug, + "category": "api", + "model": model.__name__, + "versions": [API_SCHEMA_VERSION], + "latest": API_SCHEMA_VERSION, + "files": [filename], + "url": f"{SITE}/schema/{filename}", + } + ) + + index_data = { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": f"{SITE}/schema/index.json", + "name": "HackProof Schema Index", + "description": ( + "Index of all published schemas for HackProof datasets and public API models." + ), + "schemas": index_entries, + } + _write(schemas / "index.json", json.dumps(index_data, indent=2) + "\n") + log.info("wrote %s schemas and index to %s", len(index_entries), schemas) async def run_export() -> None: @@ -30,11 +96,5 @@ async def run_export() -> None: _write(data / "projects.json", to_json(export)) _write(data / "projects.yml", to_yaml(export)) - schema = json_schema() - schemas = PUBLIC / "schema" - _write(schemas / f"hackathon-export-{SCHEMA_VERSION}.json", json.dumps(schema, indent=2) + "\n") - _write( - schemas / f"hackathon-export-{SCHEMA_VERSION}.yml", - yaml.safe_dump(schema, sort_keys=False, allow_unicode=True, width=100), - ) + export_schemas() log.info("wrote %s projects to %s", len(export.projects), data) diff --git a/apps/api/src/hackproof/export/render.py b/apps/api/src/hackproof/export/render.py index 57e1afa..f23bcea 100644 --- a/apps/api/src/hackproof/export/render.py +++ b/apps/api/src/hackproof/export/render.py @@ -1,11 +1,38 @@ from typing import Any import yaml +from pydantic import BaseModel from hackproof.export.models import SCHEMA_VERSION, HackathonExport +from hackproof.schemas import ( + Builder, + Contract, + CountryCount, + FailedTx, + Integration, + Project, + ProjectDetail, + Stats, + TimelinePoint, + WasmUpload, +) SITE = "https://hackproof.vercel.app" SCHEMA_ID = f"{SITE}/schema/hackathon-export-{SCHEMA_VERSION}.json" +API_SCHEMA_VERSION = "1.0" + +API_MODELS: dict[str, type[BaseModel]] = { + "stats": Stats, + "project": Project, + "project-detail": ProjectDetail, + "builder": Builder, + "contract": Contract, + "wasm-upload": WasmUpload, + "failed-tx": FailedTx, + "timeline-point": TimelinePoint, + "integration": Integration, + "country-count": CountryCount, +} def to_json(export: HackathonExport) -> str: @@ -23,3 +50,13 @@ def json_schema() -> dict[str, Any]: "$id": SCHEMA_ID, **HackathonExport.model_json_schema(), } + + +def api_json_schema( + model: type[BaseModel], slug: str, version: str = API_SCHEMA_VERSION +) -> dict[str, Any]: + return { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": f"{SITE}/schema/{slug}-{version}.schema.json", + **model.model_json_schema(), + } diff --git a/apps/api/tests/test_api_schemas.py b/apps/api/tests/test_api_schemas.py new file mode 100644 index 0000000..4991d4e --- /dev/null +++ b/apps/api/tests/test_api_schemas.py @@ -0,0 +1,227 @@ +import json +from datetime import UTC, datetime + +import jsonschema + +from hackproof.export.jobs import PUBLIC, export_schemas +from hackproof.export.render import API_MODELS, API_SCHEMA_VERSION +from hackproof.github.models import DependencyFingerprint +from hackproof.schemas import ( + Builder, + Contract, + Contributor, + CountryCount, + FailedTx, + Integration, + IntegrationUse, + Link, + Project, + ProjectDetail, + Stats, + TimelinePoint, + WasmUpload, +) + + +def test_schema_index_lists_existing_export_and_api_versions(): + export_schemas() + index_file = PUBLIC / "schema" / "index.json" + assert index_file.exists() + + index_data = json.loads(index_file.read_text(encoding="utf-8")) + assert "$schema" in index_data + assert index_data.get("name") == "HackProof Schema Index" + + schemas_by_name = {s["name"]: s for s in index_data["schemas"]} + + # Verify export schema versions are listed + assert "hackathon-export" in schemas_by_name + export_meta = schemas_by_name["hackathon-export"] + assert export_meta["versions"] == ["1.0", "1.1", "1.2"] + assert export_meta["latest"] == "1.2" + for file in export_meta["files"]: + assert (PUBLIC / "schema" / file).exists() + + # Verify every API model is listed + for slug, model in API_MODELS.items(): + assert slug in schemas_by_name + entry = schemas_by_name[slug] + assert entry["model"] == model.__name__ + assert entry["versions"] == [API_SCHEMA_VERSION] + assert (PUBLIC / "schema" / f"{slug}-{API_SCHEMA_VERSION}.schema.json").exists() + + +def test_api_models_validate_against_their_schemas(): + export_schemas() + now = datetime(2026, 10, 31, 12, 0, 0, tzinfo=UTC) + + # Representative sample instances for each model + sample_stats = Stats( + projects=10, + verified_projects=5, + likely_projects=3, + possible_projects=2, + unattributed_accounts=1, + contracts=8, + builders=12, + contributors=15, + countries=6, + wasm_uploads=9, + failed_txs=1, + ) + + sample_project = Project( + project_id="repo:stellar/wallet", + full_name="stellar/wallet", + url="https://github.com/stellar/wallet", + footprint="contracts", + tier="verified", + contracts=2, + classic_ops=10, + assets=1, + builders=3, + wasm_uploads=2, + failed_txs=0, + first_activity=now, + last_activity=now, + description="A stellar testnet wallet", + homepage="https://wallet.stellar.org", + stars=42, + forks=5, + language="TypeScript", + license="MIT", + owner="stellar", + owner_avatar="https://avatars.githubusercontent.com/u/1", + country="PT", + dependencies=DependencyFingerprint( + stellar_packages=["@stellar/stellar-sdk"], + network_strings=["testnet.stellar.org"], + soroban_imports=[], + wasm_references=[], + ), + window_commits=14, + total_commits=35, + stellar_added_in_window=True, + created_at=now, + topics=["stellar", "soroban"], + track="genesis", + integrations=["privy"], + award="first", + ) + + sample_contributor = Contributor( + login="evandro", + url="https://github.com/evandro", + avatar_url="https://avatars.githubusercontent.com/u/2", + contributions=12, + country="BR", + ) + + sample_link = Link( + subject_kind="contract", + subject="CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + method="contract_in_repo", + path="src/contract.rs", + ) + + sample_contract = Contract( + contract_id="CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + deployer="GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + wasm_hash="1111111111111111111111111111111111111111111111111111111111111111", + tx_hash="2222222222222222222222222222222222222222222222222222222222222222", + ledger=123456, + deployed_at=now, + project_id="repo:stellar/wallet", + ) + + sample_wasm = WasmUpload( + wasm_hash="1111111111111111111111111111111111111111111111111111111111111111", + uploader="GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + tx_hash="3333333333333333333333333333333333333333333333333333333333333333", + size=40960, + source_repo="stellar/wallet", + full_name="stellar/wallet", + contracts=1, + uploaded_at=now, + ) + + sample_failed_tx = FailedTx( + tx_hash="4444444444444444444444444444444444444444444444444444444444444444", + source_account="GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + kind="invokeHostFunction", + result_code="txFAILED", + ledger=123457, + created_at=now, + ) + + sample_integration_use = IntegrationUse( + slug="privy", + name="Privy", + method="package", + evidence="@privy-io/node in package.json", + path="package.json", + ) + + sample_project_detail = ProjectDetail( + project=sample_project, + links=[sample_link], + integration_uses=[sample_integration_use], + contributors=[sample_contributor], + contracts=[sample_contract], + wasm_uploads=[sample_wasm], + failed_txs=[sample_failed_tx], + ) + + sample_builder = Builder( + address="GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + projects=[sample_project], + contracts=[sample_contract], + wasm_uploads=[sample_wasm], + failed_txs=[sample_failed_tx], + ) + + sample_timeline = TimelinePoint( + hour=now, + contracts=4, + ) + + sample_integration = Integration( + slug="privy", + name="Privy", + category="Auth", + site="https://privy.io", + projects=5, + ) + + sample_country = CountryCount( + country="PT", + contributors=7, + ) + + samples = { + "stats": sample_stats, + "project": sample_project, + "project-detail": sample_project_detail, + "builder": sample_builder, + "contract": sample_contract, + "wasm-upload": sample_wasm, + "failed-tx": sample_failed_tx, + "timeline-point": sample_timeline, + "integration": sample_integration, + "country-count": sample_country, + } + + schemas_dir = PUBLIC / "schema" + + for slug, sample_obj in samples.items(): + schema_file = schemas_dir / f"{slug}-{API_SCHEMA_VERSION}.schema.json" + assert schema_file.exists(), f"Missing schema file: {schema_file}" + + schema_json = json.loads(schema_file.read_text(encoding="utf-8")) + + # Verify it conforms to JSON Schema Draft 2020-12 + jsonschema.Draft202012Validator.check_schema(schema_json) + + # Validate serialized sample against the published schema + sample_data = sample_obj.model_dump(mode="json") + jsonschema.validate(instance=sample_data, schema=schema_json) diff --git a/apps/web/public/schema/builder-1.0.schema.json b/apps/web/public/schema/builder-1.0.schema.json new file mode 100644 index 0000000..deedafc --- /dev/null +++ b/apps/web/public/schema/builder-1.0.schema.json @@ -0,0 +1,559 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/builder-1.0.schema.json", + "$defs": { + "Contract": { + "properties": { + "contract_id": { + "title": "Contract Id", + "type": "string" + }, + "deployer": { + "title": "Deployer", + "type": "string" + }, + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "deployed_at": { + "format": "date-time", + "title": "Deployed At", + "type": "string" + }, + "project_id": { + "title": "Project Id", + "type": "string" + } + }, + "required": [ + "contract_id", + "deployer", + "wasm_hash", + "tx_hash", + "ledger", + "deployed_at", + "project_id" + ], + "title": "Contract", + "type": "object" + }, + "DependencyFingerprint": { + "properties": { + "ecosystems": { + "items": { + "type": "string" + }, + "title": "Ecosystems", + "type": "array" + }, + "packages": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "title": "Packages", + "type": "object" + } + }, + "title": "DependencyFingerprint", + "type": "object" + }, + "FailedTx": { + "properties": { + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "source_account": { + "title": "Source Account", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "result_code": { + "title": "Result Code", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + } + }, + "required": [ + "tx_hash", + "source_account", + "kind", + "result_code", + "ledger", + "created_at" + ], + "title": "FailedTx", + "type": "object" + }, + "Project": { + "properties": { + "project_id": { + "title": "Project Id", + "type": "string" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + }, + "footprint": { + "enum": [ + "contracts", + "classic", + "github" + ], + "title": "Footprint", + "type": "string" + }, + "tier": { + "enum": [ + "verified", + "likely", + "possible", + "unattributed" + ], + "title": "Tier", + "type": "string" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "classic_ops": { + "title": "Classic Ops", + "type": "integer" + }, + "assets": { + "title": "Assets", + "type": "integer" + }, + "builders": { + "title": "Builders", + "type": "integer" + }, + "wasm_uploads": { + "title": "Wasm Uploads", + "type": "integer" + }, + "failed_txs": { + "title": "Failed Txs", + "type": "integer" + }, + "first_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First Activity" + }, + "last_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Activity" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "homepage": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Homepage" + }, + "stars": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Stars" + }, + "forks": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Forks" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + }, + "license": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "License" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner" + }, + "owner_avatar": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner Avatar" + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Country" + }, + "dependencies": { + "anyOf": [ + { + "$ref": "#/$defs/DependencyFingerprint" + }, + { + "type": "null" + } + ] + }, + "window_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Window Commits" + }, + "total_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Total Commits" + }, + "stellar_added_in_window": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stellar Added In Window" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Topics" + }, + "track": { + "anyOf": [ + { + "enum": [ + "genesis", + "scale" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Track" + }, + "integrations": { + "default": [], + "items": { + "type": "string" + }, + "title": "Integrations", + "type": "array" + }, + "award": { + "anyOf": [ + { + "enum": [ + "first", + "runner-up" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Award" + } + }, + "required": [ + "project_id", + "full_name", + "url", + "footprint", + "tier", + "contracts", + "classic_ops", + "assets", + "builders", + "wasm_uploads", + "failed_txs", + "first_activity", + "last_activity", + "description", + "homepage", + "stars", + "forks", + "language", + "license", + "owner", + "owner_avatar", + "country", + "dependencies", + "window_commits", + "total_commits", + "stellar_added_in_window", + "created_at", + "topics" + ], + "title": "Project", + "type": "object" + }, + "WasmUpload": { + "properties": { + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "uploader": { + "title": "Uploader", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "size": { + "title": "Size", + "type": "integer" + }, + "source_repo": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Repo" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "uploaded_at": { + "format": "date-time", + "title": "Uploaded At", + "type": "string" + } + }, + "required": [ + "wasm_hash", + "uploader", + "tx_hash", + "size", + "source_repo", + "full_name", + "contracts", + "uploaded_at" + ], + "title": "WasmUpload", + "type": "object" + } + }, + "properties": { + "address": { + "title": "Address", + "type": "string" + }, + "projects": { + "items": { + "$ref": "#/$defs/Project" + }, + "title": "Projects", + "type": "array" + }, + "contracts": { + "items": { + "$ref": "#/$defs/Contract" + }, + "title": "Contracts", + "type": "array" + }, + "wasm_uploads": { + "items": { + "$ref": "#/$defs/WasmUpload" + }, + "title": "Wasm Uploads", + "type": "array" + }, + "failed_txs": { + "items": { + "$ref": "#/$defs/FailedTx" + }, + "title": "Failed Txs", + "type": "array" + } + }, + "required": [ + "address", + "projects", + "contracts", + "wasm_uploads", + "failed_txs" + ], + "title": "Builder", + "type": "object" +} diff --git a/apps/web/public/schema/contract-1.0.schema.json b/apps/web/public/schema/contract-1.0.schema.json new file mode 100644 index 0000000..a86b385 --- /dev/null +++ b/apps/web/public/schema/contract-1.0.schema.json @@ -0,0 +1,46 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/contract-1.0.schema.json", + "properties": { + "contract_id": { + "title": "Contract Id", + "type": "string" + }, + "deployer": { + "title": "Deployer", + "type": "string" + }, + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "deployed_at": { + "format": "date-time", + "title": "Deployed At", + "type": "string" + }, + "project_id": { + "title": "Project Id", + "type": "string" + } + }, + "required": [ + "contract_id", + "deployer", + "wasm_hash", + "tx_hash", + "ledger", + "deployed_at", + "project_id" + ], + "title": "Contract", + "type": "object" +} diff --git a/apps/web/public/schema/country-count-1.0.schema.json b/apps/web/public/schema/country-count-1.0.schema.json new file mode 100644 index 0000000..ae866d2 --- /dev/null +++ b/apps/web/public/schema/country-count-1.0.schema.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/country-count-1.0.schema.json", + "properties": { + "country": { + "title": "Country", + "type": "string" + }, + "contributors": { + "title": "Contributors", + "type": "integer" + } + }, + "required": [ + "country", + "contributors" + ], + "title": "CountryCount", + "type": "object" +} diff --git a/apps/web/public/schema/failed-tx-1.0.schema.json b/apps/web/public/schema/failed-tx-1.0.schema.json new file mode 100644 index 0000000..fb084bd --- /dev/null +++ b/apps/web/public/schema/failed-tx-1.0.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/failed-tx-1.0.schema.json", + "properties": { + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "source_account": { + "title": "Source Account", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "result_code": { + "title": "Result Code", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + } + }, + "required": [ + "tx_hash", + "source_account", + "kind", + "result_code", + "ledger", + "created_at" + ], + "title": "FailedTx", + "type": "object" +} diff --git a/apps/web/public/schema/index.json b/apps/web/public/schema/index.json new file mode 100644 index 0000000..a4dbb4e --- /dev/null +++ b/apps/web/public/schema/index.json @@ -0,0 +1,157 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/index.json", + "name": "HackProof Schema Index", + "description": "Index of all published schemas for HackProof datasets and public API models.", + "schemas": [ + { + "name": "hackathon-export", + "category": "export", + "versions": [ + "1.0", + "1.1", + "1.2" + ], + "latest": "1.2", + "files": [ + "hackathon-export-1.0.json", + "hackathon-export-1.0.yml", + "hackathon-export-1.1.json", + "hackathon-export-1.1.yml", + "hackathon-export-1.2.json", + "hackathon-export-1.2.yml" + ], + "url": "https://hackproof.vercel.app/schema/hackathon-export-1.2.json" + }, + { + "name": "stats", + "category": "api", + "model": "Stats", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "stats-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/stats-1.0.schema.json" + }, + { + "name": "project", + "category": "api", + "model": "Project", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "project-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/project-1.0.schema.json" + }, + { + "name": "project-detail", + "category": "api", + "model": "ProjectDetail", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "project-detail-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/project-detail-1.0.schema.json" + }, + { + "name": "builder", + "category": "api", + "model": "Builder", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "builder-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/builder-1.0.schema.json" + }, + { + "name": "contract", + "category": "api", + "model": "Contract", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "contract-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/contract-1.0.schema.json" + }, + { + "name": "wasm-upload", + "category": "api", + "model": "WasmUpload", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "wasm-upload-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/wasm-upload-1.0.schema.json" + }, + { + "name": "failed-tx", + "category": "api", + "model": "FailedTx", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "failed-tx-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/failed-tx-1.0.schema.json" + }, + { + "name": "timeline-point", + "category": "api", + "model": "TimelinePoint", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "timeline-point-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/timeline-point-1.0.schema.json" + }, + { + "name": "integration", + "category": "api", + "model": "Integration", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "integration-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/integration-1.0.schema.json" + }, + { + "name": "country-count", + "category": "api", + "model": "CountryCount", + "versions": [ + "1.0" + ], + "latest": "1.0", + "files": [ + "country-count-1.0.schema.json" + ], + "url": "https://hackproof.vercel.app/schema/country-count-1.0.schema.json" + } + ] +} diff --git a/apps/web/public/schema/integration-1.0.schema.json b/apps/web/public/schema/integration-1.0.schema.json new file mode 100644 index 0000000..867d7e5 --- /dev/null +++ b/apps/web/public/schema/integration-1.0.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/integration-1.0.schema.json", + "properties": { + "slug": { + "title": "Slug", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "category": { + "title": "Category", + "type": "string" + }, + "site": { + "title": "Site", + "type": "string" + }, + "projects": { + "title": "Projects", + "type": "integer" + } + }, + "required": [ + "slug", + "name", + "category", + "site", + "projects" + ], + "title": "Integration", + "type": "object" +} diff --git a/apps/web/public/schema/project-1.0.schema.json b/apps/web/public/schema/project-1.0.schema.json new file mode 100644 index 0000000..26dd3ee --- /dev/null +++ b/apps/web/public/schema/project-1.0.schema.json @@ -0,0 +1,368 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/project-1.0.schema.json", + "$defs": { + "DependencyFingerprint": { + "properties": { + "ecosystems": { + "items": { + "type": "string" + }, + "title": "Ecosystems", + "type": "array" + }, + "packages": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "title": "Packages", + "type": "object" + } + }, + "title": "DependencyFingerprint", + "type": "object" + } + }, + "properties": { + "project_id": { + "title": "Project Id", + "type": "string" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + }, + "footprint": { + "enum": [ + "contracts", + "classic", + "github" + ], + "title": "Footprint", + "type": "string" + }, + "tier": { + "enum": [ + "verified", + "likely", + "possible", + "unattributed" + ], + "title": "Tier", + "type": "string" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "classic_ops": { + "title": "Classic Ops", + "type": "integer" + }, + "assets": { + "title": "Assets", + "type": "integer" + }, + "builders": { + "title": "Builders", + "type": "integer" + }, + "wasm_uploads": { + "title": "Wasm Uploads", + "type": "integer" + }, + "failed_txs": { + "title": "Failed Txs", + "type": "integer" + }, + "first_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First Activity" + }, + "last_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Activity" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "homepage": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Homepage" + }, + "stars": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Stars" + }, + "forks": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Forks" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + }, + "license": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "License" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner" + }, + "owner_avatar": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner Avatar" + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Country" + }, + "dependencies": { + "anyOf": [ + { + "$ref": "#/$defs/DependencyFingerprint" + }, + { + "type": "null" + } + ] + }, + "window_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Window Commits" + }, + "total_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Total Commits" + }, + "stellar_added_in_window": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stellar Added In Window" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Topics" + }, + "track": { + "anyOf": [ + { + "enum": [ + "genesis", + "scale" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Track" + }, + "integrations": { + "default": [], + "items": { + "type": "string" + }, + "title": "Integrations", + "type": "array" + }, + "award": { + "anyOf": [ + { + "enum": [ + "first", + "runner-up" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Award" + } + }, + "required": [ + "project_id", + "full_name", + "url", + "footprint", + "tier", + "contracts", + "classic_ops", + "assets", + "builders", + "wasm_uploads", + "failed_txs", + "first_activity", + "last_activity", + "description", + "homepage", + "stars", + "forks", + "language", + "license", + "owner", + "owner_avatar", + "country", + "dependencies", + "window_commits", + "total_commits", + "stellar_added_in_window", + "created_at", + "topics" + ], + "title": "Project", + "type": "object" +} diff --git a/apps/web/public/schema/project-detail-1.0.schema.json b/apps/web/public/schema/project-detail-1.0.schema.json new file mode 100644 index 0000000..8554a91 --- /dev/null +++ b/apps/web/public/schema/project-detail-1.0.schema.json @@ -0,0 +1,693 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/project-detail-1.0.schema.json", + "$defs": { + "Contract": { + "properties": { + "contract_id": { + "title": "Contract Id", + "type": "string" + }, + "deployer": { + "title": "Deployer", + "type": "string" + }, + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "deployed_at": { + "format": "date-time", + "title": "Deployed At", + "type": "string" + }, + "project_id": { + "title": "Project Id", + "type": "string" + } + }, + "required": [ + "contract_id", + "deployer", + "wasm_hash", + "tx_hash", + "ledger", + "deployed_at", + "project_id" + ], + "title": "Contract", + "type": "object" + }, + "Contributor": { + "properties": { + "login": { + "title": "Login", + "type": "string" + }, + "url": { + "title": "Url", + "type": "string" + }, + "avatar_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Avatar Url" + }, + "contributions": { + "title": "Contributions", + "type": "integer" + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Country" + } + }, + "required": [ + "login", + "url", + "avatar_url", + "contributions", + "country" + ], + "title": "Contributor", + "type": "object" + }, + "DependencyFingerprint": { + "properties": { + "ecosystems": { + "items": { + "type": "string" + }, + "title": "Ecosystems", + "type": "array" + }, + "packages": { + "additionalProperties": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "title": "Packages", + "type": "object" + } + }, + "title": "DependencyFingerprint", + "type": "object" + }, + "FailedTx": { + "properties": { + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "source_account": { + "title": "Source Account", + "type": "string" + }, + "kind": { + "title": "Kind", + "type": "string" + }, + "result_code": { + "title": "Result Code", + "type": "string" + }, + "ledger": { + "title": "Ledger", + "type": "integer" + }, + "created_at": { + "format": "date-time", + "title": "Created At", + "type": "string" + } + }, + "required": [ + "tx_hash", + "source_account", + "kind", + "result_code", + "ledger", + "created_at" + ], + "title": "FailedTx", + "type": "object" + }, + "IntegrationUse": { + "properties": { + "slug": { + "title": "Slug", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "method": { + "enum": [ + "package", + "endpoint" + ], + "title": "Method", + "type": "string" + }, + "evidence": { + "title": "Evidence", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + } + }, + "required": [ + "slug", + "name", + "method", + "evidence", + "path" + ], + "title": "IntegrationUse", + "type": "object" + }, + "Link": { + "properties": { + "subject_kind": { + "title": "Subject Kind", + "type": "string" + }, + "subject": { + "title": "Subject", + "type": "string" + }, + "method": { + "title": "Method", + "type": "string" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Path" + } + }, + "required": [ + "subject_kind", + "subject", + "method", + "path" + ], + "title": "Link", + "type": "object" + }, + "Project": { + "properties": { + "project_id": { + "title": "Project Id", + "type": "string" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Url" + }, + "footprint": { + "enum": [ + "contracts", + "classic", + "github" + ], + "title": "Footprint", + "type": "string" + }, + "tier": { + "enum": [ + "verified", + "likely", + "possible", + "unattributed" + ], + "title": "Tier", + "type": "string" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "classic_ops": { + "title": "Classic Ops", + "type": "integer" + }, + "assets": { + "title": "Assets", + "type": "integer" + }, + "builders": { + "title": "Builders", + "type": "integer" + }, + "wasm_uploads": { + "title": "Wasm Uploads", + "type": "integer" + }, + "failed_txs": { + "title": "Failed Txs", + "type": "integer" + }, + "first_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First Activity" + }, + "last_activity": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Activity" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "homepage": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Homepage" + }, + "stars": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Stars" + }, + "forks": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Forks" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + }, + "license": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "License" + }, + "owner": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner" + }, + "owner_avatar": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Owner Avatar" + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Country" + }, + "dependencies": { + "anyOf": [ + { + "$ref": "#/$defs/DependencyFingerprint" + }, + { + "type": "null" + } + ] + }, + "window_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Window Commits" + }, + "total_commits": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Total Commits" + }, + "stellar_added_in_window": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stellar Added In Window" + }, + "created_at": { + "anyOf": [ + { + "format": "date-time", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "topics": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Topics" + }, + "track": { + "anyOf": [ + { + "enum": [ + "genesis", + "scale" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Track" + }, + "integrations": { + "default": [], + "items": { + "type": "string" + }, + "title": "Integrations", + "type": "array" + }, + "award": { + "anyOf": [ + { + "enum": [ + "first", + "runner-up" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Award" + } + }, + "required": [ + "project_id", + "full_name", + "url", + "footprint", + "tier", + "contracts", + "classic_ops", + "assets", + "builders", + "wasm_uploads", + "failed_txs", + "first_activity", + "last_activity", + "description", + "homepage", + "stars", + "forks", + "language", + "license", + "owner", + "owner_avatar", + "country", + "dependencies", + "window_commits", + "total_commits", + "stellar_added_in_window", + "created_at", + "topics" + ], + "title": "Project", + "type": "object" + }, + "WasmUpload": { + "properties": { + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "uploader": { + "title": "Uploader", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "size": { + "title": "Size", + "type": "integer" + }, + "source_repo": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Repo" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "uploaded_at": { + "format": "date-time", + "title": "Uploaded At", + "type": "string" + } + }, + "required": [ + "wasm_hash", + "uploader", + "tx_hash", + "size", + "source_repo", + "full_name", + "contracts", + "uploaded_at" + ], + "title": "WasmUpload", + "type": "object" + } + }, + "properties": { + "project": { + "$ref": "#/$defs/Project" + }, + "links": { + "items": { + "$ref": "#/$defs/Link" + }, + "title": "Links", + "type": "array" + }, + "integration_uses": { + "items": { + "$ref": "#/$defs/IntegrationUse" + }, + "title": "Integration Uses", + "type": "array" + }, + "contributors": { + "items": { + "$ref": "#/$defs/Contributor" + }, + "title": "Contributors", + "type": "array" + }, + "contracts": { + "items": { + "$ref": "#/$defs/Contract" + }, + "title": "Contracts", + "type": "array" + }, + "wasm_uploads": { + "items": { + "$ref": "#/$defs/WasmUpload" + }, + "title": "Wasm Uploads", + "type": "array" + }, + "failed_txs": { + "items": { + "$ref": "#/$defs/FailedTx" + }, + "title": "Failed Txs", + "type": "array" + } + }, + "required": [ + "project", + "links", + "integration_uses", + "contributors", + "contracts", + "wasm_uploads", + "failed_txs" + ], + "title": "ProjectDetail", + "type": "object" +} diff --git a/apps/web/public/schema/stats-1.0.schema.json b/apps/web/public/schema/stats-1.0.schema.json new file mode 100644 index 0000000..c8a7c42 --- /dev/null +++ b/apps/web/public/schema/stats-1.0.schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/stats-1.0.schema.json", + "properties": { + "projects": { + "title": "Projects", + "type": "integer" + }, + "verified_projects": { + "title": "Verified Projects", + "type": "integer" + }, + "likely_projects": { + "title": "Likely Projects", + "type": "integer" + }, + "possible_projects": { + "title": "Possible Projects", + "type": "integer" + }, + "unattributed_accounts": { + "title": "Unattributed Accounts", + "type": "integer" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "builders": { + "title": "Builders", + "type": "integer" + }, + "contributors": { + "title": "Contributors", + "type": "integer" + }, + "countries": { + "title": "Countries", + "type": "integer" + }, + "wasm_uploads": { + "title": "Wasm Uploads", + "type": "integer" + }, + "failed_txs": { + "title": "Failed Txs", + "type": "integer" + } + }, + "required": [ + "projects", + "verified_projects", + "likely_projects", + "possible_projects", + "unattributed_accounts", + "contracts", + "builders", + "contributors", + "countries", + "wasm_uploads", + "failed_txs" + ], + "title": "Stats", + "type": "object" +} diff --git a/apps/web/public/schema/timeline-point-1.0.schema.json b/apps/web/public/schema/timeline-point-1.0.schema.json new file mode 100644 index 0000000..4a2ff81 --- /dev/null +++ b/apps/web/public/schema/timeline-point-1.0.schema.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/timeline-point-1.0.schema.json", + "properties": { + "hour": { + "format": "date-time", + "title": "Hour", + "type": "string" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + } + }, + "required": [ + "hour", + "contracts" + ], + "title": "TimelinePoint", + "type": "object" +} diff --git a/apps/web/public/schema/wasm-upload-1.0.schema.json b/apps/web/public/schema/wasm-upload-1.0.schema.json new file mode 100644 index 0000000..14785ba --- /dev/null +++ b/apps/web/public/schema/wasm-upload-1.0.schema.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://hackproof.vercel.app/schema/wasm-upload-1.0.schema.json", + "properties": { + "wasm_hash": { + "title": "Wasm Hash", + "type": "string" + }, + "uploader": { + "title": "Uploader", + "type": "string" + }, + "tx_hash": { + "title": "Tx Hash", + "type": "string" + }, + "size": { + "title": "Size", + "type": "integer" + }, + "source_repo": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Source Repo" + }, + "full_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Full Name" + }, + "contracts": { + "title": "Contracts", + "type": "integer" + }, + "uploaded_at": { + "format": "date-time", + "title": "Uploaded At", + "type": "string" + } + }, + "required": [ + "wasm_hash", + "uploader", + "tx_hash", + "size", + "source_repo", + "full_name", + "contracts", + "uploaded_at" + ], + "title": "WasmUpload", + "type": "object" +}