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 changelog.d/fixes/11582-known-symbols-async-executor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **fix(check):** the `known-symbols` executor conformance gate awaits the now-async `getExecutor()`, so it stops reporting all 142 registered aliases as dead symbols and can detect a lazy import that fails ([#11582](https://github.com/diegosouzapw/OmniRoute/pull/11582))
37 changes: 27 additions & 10 deletions scripts/check/check-known-symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,34 @@ export type ExecutorLike = {
* Dada a lista de aliases e um resolvedor (getExecutor), retorna os aliases que NÃO
* resolvem para um BaseExecutor válido (não é instância, ou falta execute/getProvider).
* isInstance é injetado para manter a função pura/testável com inputs sintéticos.
*
* O resolvedor é aguardado: desde #11421 o registro é lazy e `getExecutor()` devolve
* uma Promise (a classe só é importada e construída no primeiro uso). Sem o await,
* TODO alias reprova — uma Promise nunca é `instanceof BaseExecutor` — e a checagem
* deixa de proteger qualquer coisa. Uma rejeição também conta como não-conforme: com
* carregamento lazy o import de um alias pode falhar em runtime, e esse é exatamente
* o símbolo morto que esta sub-checagem existe para achar.
*/
export function findNonConformingExecutors(
export async function findNonConformingExecutors(
aliases: string[],
resolve: (alias: string) => ExecutorLike | null | undefined,
resolve: (
alias: string
) => PromiseLike<ExecutorLike | null | undefined> | ExecutorLike | null | undefined,
isInstance: (value: unknown) => boolean
): string[] {
return aliases.filter((alias) => {
const ex = resolve(alias);
if (!ex || !isInstance(ex)) return true;
return typeof ex.execute !== "function" || typeof ex.getProvider !== "function";
});
): Promise<string[]> {
const verdicts = await Promise.all(
aliases.map(async (alias) => {
let ex: ExecutorLike | null | undefined;
try {
ex = await resolve(alias);
} catch {
return true; // o alias não carrega — símbolo morto
}
if (!ex || !isInstance(ex)) return true;
return typeof ex.execute !== "function" || typeof ex.getProvider !== "function";
})
);
return aliases.filter((_alias, index) => verdicts[index]);
}

// ───────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -460,7 +477,7 @@ async function main(): Promise<void> {

// ── (1) Executor conformance ──────────────────────────────────────────────
const executorsMod = await import("@omniroute/open-sse/executors/index.ts");
const getExecutor = executorsMod.getExecutor as (alias: string) => ExecutorLike;
const getExecutor = executorsMod.getExecutor as (alias: string) => Promise<ExecutorLike>;
const BaseExecutor = executorsMod.BaseExecutor as new (...args: never[]) => unknown;
const indexSource = readFileSync(resolvePath(REPO_ROOT, "open-sse/executors/index.ts"), "utf8");
const aliases = extractExecutorAliases(indexSource);
Expand All @@ -470,7 +487,7 @@ async function main(): Promise<void> {
);
}
const isExecutorInstance = (value: unknown) => value instanceof BaseExecutor;
const badExecutors = findNonConformingExecutors(aliases, getExecutor, isExecutorInstance);
const badExecutors = await findNonConformingExecutors(aliases, getExecutor, isExecutorInstance);
if (badExecutors.length) {
failures.push(
`[executor] ${badExecutors.length} alias(es) registrado(s) não resolvem para um BaseExecutor válido (instância + execute() + getProvider()):\n` +
Expand Down
63 changes: 44 additions & 19 deletions tests/unit/check-known-symbols.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,16 @@ test("combo dispatch registry (runtime import) covers the canonical strategy set
// (1) EXECUTOR CONFORMANCE — extractExecutorAliases + findNonConformingExecutors
// ───────────────────────────────────────────────────────────────────────────

test("extractExecutorAliases parses quoted and bare keys from the executors literal", () => {
// The literal is `const lazyExecutors` since #11421 — every value is a thunk that
// imports and constructs on first use, so the fixture has to carry that shape.
test("extractExecutorAliases parses quoted and bare keys from the lazy executors literal", () => {
const src = [
'import { Foo } from "./foo.ts";',
"const executors = {",
" antigravity: new Foo(),",
" agy: new Foo(), // Alias",
' "amazon-q": new Foo("amazon-q"),',
"const lazyExecutors: Record<string, () => Promise<BaseExecutor>> = {",
' antigravity: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()),',
' agy: () => import("./antigravity.ts").then((m) => new m.AntigravityExecutor()), // Alias',
' "amazon-q": () => import("./amazon-q.ts").then((m) => new m.AmazonQExecutor()),',
"};",
"export function getExecutor() {}",
"export async function getExecutor() {}",
].join("\n");
assert.deepEqual(extractExecutorAliases(src), ["antigravity", "agy", "amazon-q"]);
});
Expand All @@ -144,36 +145,60 @@ test("extractExecutorAliases throws when the executors map cannot be located", (
assert.throws(() => extractExecutorAliases("const other = { a: 1 };"), /could not find/);
});

test("findNonConformingExecutors returns [] when every alias resolves to a valid executor", () => {
test("findNonConformingExecutors returns [] when every alias resolves to a valid executor", async () => {
const good = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const resolve = (_alias: string) => good;
const resolve = async (_alias: string) => good;
const isInstance = (_value: unknown) => true;
assert.deepEqual(findNonConformingExecutors(["a", "b"], resolve, isInstance), []);
assert.deepEqual(await findNonConformingExecutors(["a", "b"], resolve, isInstance), []);
});

test("findNonConformingExecutors flags an alias that does not resolve at all", () => {
// The real resolver is async (#11421). Awaiting is the whole point: a Promise is
// never `instanceof BaseExecutor`, so a sync call reports every alias as broken and
// the sub-check silently stops guarding anything.
test("findNonConformingExecutors awaits an async resolver instead of judging the Promise", async () => {
const good = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const resolve = (alias: string) => (alias === "ghost" ? null : good);
const resolve = (_alias: string) => Promise.resolve(good);
const isInstance = (value: unknown) => value === good;
assert.deepEqual(await findNonConformingExecutors(["a", "b"], resolve, isInstance), []);
});

test("findNonConformingExecutors flags an alias whose lazy load rejects", async () => {
const good = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const resolve = async (alias: string) => {
if (alias === "boom") throw new Error("module not found");
return good;
};
const isInstance = (_value: unknown) => true;
assert.deepEqual(findNonConformingExecutors(["a", "ghost", "b"], resolve, isInstance), ["ghost"]);
assert.deepEqual(await findNonConformingExecutors(["a", "boom", "b"], resolve, isInstance), [
"boom",
]);
});

test("findNonConformingExecutors flags an alias that does not resolve at all", async () => {
const good = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const resolve = async (alias: string) => (alias === "ghost" ? null : good);
const isInstance = (_value: unknown) => true;
assert.deepEqual(await findNonConformingExecutors(["a", "ghost", "b"], resolve, isInstance), [
"ghost",
]);
});

test("findNonConformingExecutors flags an alias resolving to a non-BaseExecutor instance", () => {
test("findNonConformingExecutors flags an alias resolving to a non-BaseExecutor instance", async () => {
const stray = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const resolve = (_alias: string) => stray;
const resolve = async (_alias: string) => stray;
// Simulate `instanceof BaseExecutor` returning false for the stray object.
const isInstance = (_value: unknown) => false;
assert.deepEqual(findNonConformingExecutors(["stray"], resolve, isInstance), ["stray"]);
assert.deepEqual(await findNonConformingExecutors(["stray"], resolve, isInstance), ["stray"]);
});

test("findNonConformingExecutors flags an executor missing execute() or getProvider()", () => {
test("findNonConformingExecutors flags an executor missing execute() or getProvider()", async () => {
const noExecute = { getProvider: () => "x" } as ExecutorLike;
const noProvider = { execute: () => {} } as ExecutorLike;
const valid = { execute: () => {}, getProvider: () => "x" } as ExecutorLike;
const map: Record<string, ExecutorLike> = { ne: noExecute, np: noProvider, ok: valid };
const resolve = (alias: string) => map[alias];
const resolve = async (alias: string) => map[alias];
const isInstance = (_value: unknown) => true;
assert.deepEqual(findNonConformingExecutors(["ne", "np", "ok"], resolve, isInstance), [
assert.deepEqual(await findNonConformingExecutors(["ne", "np", "ok"], resolve, isInstance), [
"ne",
"np",
]);
Expand Down
Loading