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
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
quality:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- run: pnpm install --frozen-lockfile

- name: Lint & format (Biome)
run: pnpm run check

- name: Type check
run: pnpm run typecheck

- name: Schema documentation completeness
run: pnpm run check:docs
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ schemas/
wrangler.jsonc
site/resources/
site/data-types/
site/public/schema/
site/.vitepress/dist/
site/.vitepress/cache/
site/.vitepress/sidebar.json
Expand Down
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ That's it. All contributions are reviewed before merging.
- Follow existing naming conventions and patterns in the TypeScript types.
- If proposing a new resource, look at how existing resources are structured and follow the same approach.

## Code Quality

This project uses [Biome](https://biomejs.dev/) for linting, formatting, and import sorting.

Before opening a PR, run:

```bash
pnpm run check # lint + format + import check (what CI runs)
pnpm run check:fix # auto-fix all issues
pnpm run typecheck # TypeScript type checking
```

CI runs these checks automatically on every pull request.

## Questions or Ideas?

If you want to discuss something before opening a PR, reach out at **contact@bind-standard.org**.
Expand Down
77 changes: 77 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.0/schema.json",
"files": {
"includes": [
"src/**",
"scripts/**/*.ts",
"site/.vitepress/config.mts",
"site/.vitepress/theme/**",
"package.json",
"tsconfig.json",
"biome.json"
]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"lineEnding": "lf"
},
"javascript": {
"formatter": {
"quoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"useImportType": "error",
"useExportType": "error",
"noUnusedTemplateLiteral": "off"
},
"suspicious": {
"noExplicitAny": "warn"
},
"correctness": {
"noUnusedImports": "warn"
}
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"overrides": [
{
"includes": ["scripts/**/*.ts"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
}
}
}
},
{
"includes": ["**/*.vue"],
"linter": {
"rules": {
"correctness": {
"noUnusedVariables": "off",
"noUnusedFunctionParameters": "off",
"noUnusedImports": "off"
}
}
}
}
]
}
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@
"docs:dev": "pnpm run gen && vitepress dev site",
"docs:build": "pnpm run gen && vitepress build site",
"docs:preview": "vitepress preview site",
"clean": "rm -rf dist schemas/resources schemas/supporting schemas/index.json site/resources site/data-types site/.vitepress/dist site/.vitepress/cache",
"clean": "rm -rf dist schemas/resources schemas/supporting schemas/index.json site/resources site/data-types site/public/schema site/.vitepress/dist site/.vitepress/cache",
"wrangler:config": "ts-node scripts/generate-wrangler-config.ts",
"deploy": "pnpm run wrangler:config && pnpm exec wrangler deploy",
"deploy:delete": "pnpm exec wrangler delete --force"
"deploy:delete": "pnpm exec wrangler delete --force",
"lint": "biome lint .",
"lint:fix": "biome lint --fix .",
"format": "biome format --fix .",
"format:check": "biome format .",
"check": "biome check .",
"check:fix": "biome check --fix .",
"typecheck": "tsc --noEmit",
"check:docs": "pnpm run gen:schema && ts-node scripts/check-schema-docs.ts"
},
"keywords": [
"bind",
Expand All @@ -30,6 +38,7 @@
"type": "commonjs",
"packageManager": "pnpm@10.24.0",
"devDependencies": {
"@biomejs/biome": "^2.4.0",
"@types/d3-drag": "^3.0.7",
"@types/d3-force": "^3.0.10",
"@types/d3-selection": "^3.0.11",
Expand Down
91 changes: 91 additions & 0 deletions pnpm-lock.yaml

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

75 changes: 75 additions & 0 deletions scripts/check-schema-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { readdirSync, readFileSync } from "node:fs";
import { basename, join } from "node:path";

const schemasDir = join(__dirname, "..", "schemas");
const schemaDirs = [join(schemasDir, "resources"), join(schemasDir, "supporting")];

interface SchemaProperty {
type?: string;
description?: string;
$ref?: string;
const?: unknown;
properties?: Record<string, SchemaProperty>;
items?: SchemaProperty;
}

interface SchemaDefinition {
type?: string;
description?: string;
properties?: Record<string, SchemaProperty>;
}

interface Schema {
$ref?: string;
definitions?: Record<string, SchemaDefinition>;
}

let errorCount = 0;

function reportError(typeName: string, message: string): void {
console.error(` ✗ ${typeName}: ${message}`);
errorCount++;
}

for (const dir of schemaDirs) {
const files = readdirSync(dir).filter((f) => f.endsWith(".schema.json"));

for (const file of files) {
const typeName = basename(file, ".schema.json");
const schema: Schema = JSON.parse(readFileSync(join(dir, file), "utf-8"));

// Resolve the root definition
const rootRefName = schema.$ref ? schema.$ref.replace("#/definitions/", "") : typeName;
const rootDef = schema.definitions?.[rootRefName];

if (!rootDef) {
reportError(typeName, "root definition not found");
continue;
}

// Check root type has a description
if (!rootDef.description) {
reportError(typeName, "missing root description (add JSDoc to the type)");
}

// Check each property has a description
if (rootDef.properties) {
for (const [propName, prop] of Object.entries(rootDef.properties)) {
// Skip const fields (e.g. resourceType discriminators) — they're self-documenting
if (prop.const !== undefined) continue;

if (!prop.description) {
reportError(typeName, `property "${propName}" is missing a description`);
}
}
}
}
}

if (errorCount > 0) {
console.error(`\n${errorCount} documentation issue(s) found.`);
console.error("Add JSDoc comments to the TypeScript source types to fix these.");
process.exit(1);
} else {
console.log("All schema types and properties are documented.");
}
Loading