Skip to content
Open
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
50 changes: 41 additions & 9 deletions .github/workflows/validate-sdk-compliance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ on:
description: Comma-separated search paths for griffe relative to repo root (python only)
type: string
default: ""
entrypoint:
description: TypeScript entrypoint file relative to SDK root (TypeScript SDKs only)
type: string
default: src/index.ts

jobs:
validate:
Expand Down Expand Up @@ -99,6 +103,43 @@ jobs:
run: npm ci
working-directory: _sdk-spec/scripts/capability-matrix

- name: Install PR SDK dependencies (TypeScript)
if: inputs.language == 'javascript'
run: npm ci
working-directory: _sdk-pr

- name: Generate TypeDoc JSON — PR branch
if: inputs.language == 'javascript'
run: |
npx --yes typedoc@0.27 \
--json "$GITHUB_WORKSPACE/pr-raw.json" \
--excludePrivate --excludeProtected \
"${{ inputs.entrypoint }}"
working-directory: _sdk-pr

- name: Install base SDK dependencies (TypeScript)
if: inputs.language == 'javascript'
run: npm ci
working-directory: _sdk-base

- name: Generate TypeDoc JSON — base branch
if: inputs.language == 'javascript'
run: |
npx --yes typedoc@0.27 \
--json "$GITHUB_WORKSPACE/base-raw.json" \
--excludePrivate --excludeProtected \
"${{ inputs.entrypoint }}"
working-directory: _sdk-base

- name: Normalize TypeDoc output (TypeScript)
if: inputs.language == 'javascript'
run: |
npm run --silent normalize-typedoc -- \
"$GITHUB_WORKSPACE/pr-raw.json" "$GITHUB_WORKSPACE/pr-symbols.json"
npm run --silent normalize-typedoc -- \
"$GITHUB_WORKSPACE/base-raw.json" "$GITHUB_WORKSPACE/base-symbols.json"
working-directory: _sdk-spec/scripts/capability-matrix

- name: Set up Python
if: inputs.language == 'python'
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
Expand Down Expand Up @@ -184,15 +225,6 @@ jobs:
done
working-directory: _sdk-spec/scripts/capability-matrix

- name: Parse JavaScript symbols
if: inputs.language == 'javascript'
run: |
for b in pr base; do
npm run --silent parse-ts -- "$GITHUB_WORKSPACE/_sdk-$b" \
> "$GITHUB_WORKSPACE/$b-symbols.json"
done
working-directory: _sdk-spec/scripts/capability-matrix

- name: Check new symbols against capability matrix
run: |
npm run check-api-symbols -- \
Expand Down
7 changes: 3 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,11 @@ capabilities/*.yaml → validate (AJV schema) → aggregate (GitHub API fetc
- `aggregate.ts` — Fetches compliance files from all SDK repos via Octokit
- `generate-site.ts` — Builds the static HTML matrix site
- `report.ts` — Calculates parity percentages per feature/area/language
- `ts-parser.ts` — Syntactic TypeScript AST walker; extracts public symbols without requiring `node_modules`
- `swift-parser.ts` — Line-by-line Swift scanner; extracts public/open symbols from classes, structs, actors, enums, extensions
- `parse-ts.ts` — CLI wrapper for `ts-parser.ts`; takes an SDK root path and emits `ParseResult` JSON
- `parse-swift.ts` — CLI wrapper for `swift-parser.ts`; same contract as `parse-ts.ts`
- `normalize-typedoc.ts` — TypeDoc JSON normalizer; maps TypeDoc reflection kinds to `ParseResult`; defines `ParsedSymbol` and `ParseResult` types
- `normalize-typedoc-cli.ts` — CLI wrapper; reads TypeDoc JSON, calls normalizer, writes `ParseResult` JSON
- `scripts/dart_symbol_extractor/` (sibling Dart package) — Small `package:analyzer` tool that walks `lib/**.dart` syntactically and emits the same `ParseResult` JSON; run directly with `dart run bin/extract.dart <sdk-root>`. Parses without `pub get`; supports extension types and enhanced enums
- `parse-ignore.ts` — Loads `.sdk-parse-ignore` (gitignore syntax) to exclude paths from symbol parsing
- `parse-ignore.ts` — Loads `.sdk-parse-ignore` (gitignore syntax) to exclude paths from Swift symbol parsing (TypeScript uses TypeDoc entrypoint resolution instead)
- `api-check.ts` — Diff logic: `checkNewSymbols(base, pr, compliance)` returns symbols added in PR not in the compliance file
- `check-api-symbols.ts` — CLI; compares two `ParseResult` files against `sdk-compliance.yaml`, exits 1 with a clear error on uncovered symbols

Expand Down
2 changes: 1 addition & 1 deletion scripts/capability-matrix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"validate": "tsx src/cli.ts validate",
"validate:online": "tsx src/cli.ts validate --online",
"validate-compliance": "tsx src/compliance-cli.ts",
"parse-ts": "tsx src/parse-ts.ts",
"normalize-typedoc": "tsx src/normalize-typedoc-cli.ts",
"normalize-symbolgraph": "tsx src/normalize-symbolgraph-cli.ts",
"normalize-griffe": "tsx src/normalize-griffe-cli.ts",
"check-api-symbols": "tsx src/check-api-symbols.ts",
Expand Down
2 changes: 1 addition & 1 deletion scripts/capability-matrix/src/api-check.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildSymbolIndex } from "./compliance.js";
import type { RawCompliance } from "./compliance.js";
import type { ParsedSymbol } from "./ts-parser.js";
import type { ParsedSymbol } from "./normalize-typedoc.js";

export interface CheckResult {
newSymbols: string[];
Expand Down
2 changes: 1 addition & 1 deletion scripts/capability-matrix/src/check-api-symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { resolve } from "node:path";
import { parse } from "yaml";
import { checkNewSymbols, formatErrorMessage, formatRemovedMessage } from "./api-check.js";
import type { RawCompliance } from "./compliance.js";
import type { ParseResult } from "./ts-parser.js";
import type { ParseResult } from "./normalize-typedoc.js";

async function main(): Promise<void> {
const [prFile, baseFile, compliancePath] = process.argv.slice(2);
Expand Down
13 changes: 13 additions & 0 deletions scripts/capability-matrix/src/normalize-typedoc-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { readFileSync, writeFileSync } from "node:fs";
import { normalize } from "./normalize-typedoc.js";

const [, , inputPath, outputPath] = process.argv;

if (!inputPath || !outputPath) {
console.error("Usage: normalize-typedoc <input.json> <output.json>");
process.exit(1);
}

const json = JSON.parse(readFileSync(inputPath, "utf8"));
const result = normalize(json);
writeFileSync(outputPath, JSON.stringify(result, null, 2));
97 changes: 97 additions & 0 deletions scripts/capability-matrix/src/normalize-typedoc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
export interface ParsedSymbol {
name: string;
kind: "class" | "method" | "property" | "function" | "variable";
file: string;
}

export interface ParseResult {
symbols: ParsedSymbol[];
}

const Kind = {
Module: 2,
Namespace: 4,
Enum: 8,
EnumMember: 16,
Variable: 32,
Function: 64,
Class: 128,
Interface: 256,
Constructor: 512,
Property: 1024,
Method: 2048,
Accessor: 262144,
TypeAlias: 2097152,
Reference: 4194304,
} as const;

interface TdReflection {
name: string;
kind: number;
flags?: { isPrivate?: boolean; isProtected?: boolean };
sources?: Array<{ fileName: string }>;
children?: TdReflection[];
}

function fileOf(r: TdReflection): string {
return r.sources?.[0]?.fileName ?? "";
}

function isExcluded(r: TdReflection): boolean {
return !!(r.flags?.isPrivate || r.flags?.isProtected);
}

function extractMembers(
parent: string,
children: TdReflection[],
out: ParsedSymbol[],
): void {
for (const child of children) {
if (isExcluded(child)) continue;
if (child.kind === Kind.Constructor) continue;
const qualName = `${parent}.${child.name}`;
const file = fileOf(child);
if (child.kind === Kind.Method) {
out.push({ name: qualName, kind: "method", file });
} else if (child.kind === Kind.Property) {
out.push({ name: qualName, kind: "property", file });
} else if (child.kind === Kind.Accessor) {
out.push({ name: qualName, kind: "method", file });
} else if (child.kind === Kind.EnumMember) {
out.push({ name: qualName, kind: "property", file });
}
}
}

function extractDeclarations(
children: TdReflection[],
out: ParsedSymbol[],
): void {
for (const child of children) {
if (isExcluded(child)) continue;
const file = fileOf(child);
if (child.kind === Kind.Module || child.kind === Kind.Namespace) {
if (child.children) extractDeclarations(child.children, out);
} else if (child.kind === Kind.Reference) {
continue;
} else if (
child.kind === Kind.Class ||
child.kind === Kind.Interface ||
child.kind === Kind.Enum
) {
out.push({ name: child.name, kind: "class", file });
if (child.children) extractMembers(child.name, child.children, out);
} else if (child.kind === Kind.Function) {
out.push({ name: child.name, kind: "function", file });
} else if (child.kind === Kind.Variable || child.kind === Kind.TypeAlias) {
out.push({ name: child.name, kind: "variable", file });
}
}
}

export function normalize(json: unknown): ParseResult {
const project = json as TdReflection;
const symbols: ParsedSymbol[] = [];
if (project.children) extractDeclarations(project.children, symbols);
return { symbols };
}
19 changes: 0 additions & 19 deletions scripts/capability-matrix/src/parse-ts.ts

This file was deleted.

137 changes: 0 additions & 137 deletions scripts/capability-matrix/src/ts-parser.ts

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/capability-matrix/test/api-check.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect } from "vitest";
import { checkNewSymbols, formatErrorMessage, formatRemovedMessage } from "../src/api-check";
import type { ParsedSymbol } from "../src/ts-parser";
import type { ParsedSymbol } from "../src/normalize-typedoc";

function sym(name: string): ParsedSymbol {
return { name, kind: "method", file: "src/index.ts" };
Expand Down
Loading
Loading