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
1 change: 1 addition & 0 deletions packages/plugin-api-map/scripts/sdk-api-inventory.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export interface SdkPublicApiInventory {
}

export const INVENTORY_PATH: string;
export function hashDeclarationTokens(source: string): string;
export function createSdkPublicApiInventory(): SdkPublicApiInventory;
export function readSdkPublicApiInventory(): SdkPublicApiInventory;
38 changes: 19 additions & 19 deletions packages/plugin-api-map/scripts/sdk-api-inventory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,27 @@ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const SDK_ROOT = resolve(PACKAGE_ROOT, "../plugin-sdk");
export const INVENTORY_PATH = join(PACKAGE_ROOT, "sdk-public-api.json");

function canonicalDeclaration(source, fileName) {
const sourceFile = ts.createSourceFile(
fileName,
source,
export function hashDeclarationTokens(source) {
// build:types has already parsed and normalized these declarations. Scanning
// their tokens keeps the inventory insensitive to comments and formatting
// without repeating a full TypeScript parse and print for every subpath.
const hash = createHash("sha256");
const scanner = ts.createScanner(
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS,
ts.LanguageVariant.Standard,
source,
);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
removeComments: true,
});
return sourceFile.statements
.map((statement) =>
printer.printNode(ts.EmitHint.Unspecified, statement, sourceFile),
)
.join("\n");
for (
let token = scanner.scan();
token !== ts.SyntaxKind.EndOfFileToken;
token = scanner.scan()
) {
const text = scanner.getTokenText();
hash.update(`${token}:${Buffer.byteLength(text)}:`);
hash.update(text);
}
return hash.digest("hex");
}

function publicTypeEntries() {
Expand Down Expand Up @@ -55,15 +59,11 @@ export function createSdkPublicApiInventory() {
schemaVersion: 1,
entries: Object.fromEntries(
publicTypeEntries().map(([subpath, entry]) => {
const canonical = canonicalDeclaration(
readFileSync(entry.path, "utf8"),
entry.path,
);
return [
subpath,
{
types: entry.types,
sha256: createHash("sha256").update(canonical).digest("hex"),
sha256: hashDeclarationTokens(readFileSync(entry.path, "utf8")),
},
];
}),
Expand Down
20 changes: 10 additions & 10 deletions packages/plugin-api-map/sdk-public-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,43 @@
"entries": {
".": {
"types": "bundled-types/bb-plugin-sdk.d.ts",
"sha256": "840e5e196f66d9b0ba57097c9eb23e75ed638d8483d5fcb6f17a9d9dddfe516a"
"sha256": "7994e5e3cc8a3f743d1098cb09334201ae359aac5148bd945fb1ac429908c80f"
},
"./ai-services": {
"types": "bundled-types/bb-plugin-sdk-ai-services.d.ts",
"sha256": "97b4294f28f476c4c2fbe5db621686ae66ddee616f2ba6bed2fc29e7cb122317"
"sha256": "da4f251124361cf0d1bf1d21168d81a4f064bbc71aee1e15d89d351e92b34234"
},
"./app": {
"types": "bundled-types/bb-plugin-sdk-app.d.ts",
"sha256": "2b9516df9eacc9b8e6f0b9695c9a1117bcc135e62e0541434dc53817939c5c85"
"sha256": "d5a5ca8bb14f04dbe5d53dc47470412337ce1fcf8a8019c692479620b05ef7c2"
},
"./host": {
"types": "bundled-types/bb-plugin-sdk-host.d.ts",
"sha256": "4b07e41aecd8f0668324c8016633237037bf4b325f00d12c8dbb5e1bf568385f"
"sha256": "355d8480a6d834206efbd02f1e8537d146a4d0dcbe73a9ae99c86c4929d4f7ea"
},
"./provider-bridge": {
"types": "bundled-types/bb-plugin-sdk-provider-bridge.d.ts",
"sha256": "1ef4800cd3125f662d6dde23ede7c58713ada026124e47af7cf07f03e2b0e1ff"
"sha256": "92fe1b5ad8514c8b9f758f7119821137433c1d7fd27e6109e94de6beed053243"
},
"./provider-bridge/acp": {
"types": "bundled-types/bb-plugin-sdk-provider-bridge-acp.d.ts",
"sha256": "b0f99e3f95243581363f764f34036c1ca4ef65f12f940ac53c8b897789960aaa"
"sha256": "45536c1752a316b4fe3c4267efe6178114a3a534d1af38e1980a503b164aa5e1"
},
"./provider-bridge/testing": {
"types": "bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts",
"sha256": "1b722f069b8da3519c840bbfa964a29452227a7017095116f0f52186290abf7a"
"sha256": "7c97c6976876e450ecc9dab36314a48215aae11054e410f985433cd322f9cde8"
},
"./testing": {
"types": "bundled-types/bb-plugin-sdk-testing.d.ts",
"sha256": "693ff804ff2a80f4ae1041f623ca01b58e48ff833a82b952c989c6589afbbbf7"
"sha256": "3fb82277e547add8ba4df1014809d4b0aae89bcbed3263a7ae1da1d5fb2021f4"
},
"./testing/app": {
"types": "bundled-types/bb-plugin-sdk-testing-app.d.ts",
"sha256": "c9de1acc9d5d24da0dccab82577975d58bee9a99c48ed5c2063a1301d723eac4"
"sha256": "e618f89997828f11b10520e9dcd00b4b26e0fad7f49fb6030b55189c3247b3ba"
},
"./testing/host": {
"types": "bundled-types/bb-plugin-sdk-testing-host.d.ts",
"sha256": "4a03019a60ef5950c9df363e330995f928b5f73a2c6b9ccf93c033717c27a297"
"sha256": "5c3f3e6971ce1e0bf9e46db7bbada5090e51442e9f2951ecfb84058d3db1b80b"
}
}
}
20 changes: 20 additions & 0 deletions packages/plugin-api-map/test/api-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { describe, expect, it } from "vitest";
import { SURFACE_GROUPS } from "../src/index";
import {
createSdkPublicApiInventory,
hashDeclarationTokens,
readSdkPublicApiInventory,
} from "../scripts/sdk-api-inventory.mjs";

Expand Down Expand Up @@ -52,6 +53,25 @@ const EXPORTED = new Set(
const SURFACES = SURFACE_GROUPS.flatMap((group) => group.surfaces);

describe("public SDK inventory", () => {
it("ignores declaration trivia but preserves every API token", () => {
const compact =
"export interface PluginApi { run(input: string): Promise<void>; }";
const formatted = `
/** Public plugin API. */
export interface PluginApi {
// Run the plugin.
run(input: string): Promise<void>;
}
`;

expect(hashDeclarationTokens(formatted)).toBe(
hashDeclarationTokens(compact),
);
expect(hashDeclarationTokens(compact.replace("string", "number"))).not.toBe(
hashDeclarationTokens(compact),
);
});

it("matches every non-internal published declaration subpath", () => {
// This is intentionally an exact declaration-shape gate, not only an
// export-name list: adding a BbPluginApi property or an interface method
Expand Down
Loading