chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
cpSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
realpathSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import ts from "typescript";
|
||||
|
||||
/**
|
||||
* Regression test for declaration-emit portability (customer TS2742).
|
||||
*
|
||||
* Simulates a real consumer: the BUILT package (dist + package.json) is
|
||||
* copied (not symlinked — tsc's module-specifier generation only uses the
|
||||
* exports map for files under node_modules real paths) into a temp
|
||||
* project's node_modules, then a chat-builder agent export is compiled
|
||||
* with `declaration: true`. Every type appearing in the inferred public
|
||||
* surface must be nameable through a public package specifier; a type
|
||||
* declared in a module that isn't reachable via the exports map produces
|
||||
* a relative-path import in the emit and TS2742 for consumers.
|
||||
*/
|
||||
|
||||
const packageRoot = resolve(__dirname, "..");
|
||||
const distDir = join(packageRoot, "dist");
|
||||
const coreRoot = resolve(packageRoot, "../core");
|
||||
|
||||
const FIXTURE_SOURCE = `
|
||||
import { chat } from "@trigger.dev/sdk/ai";
|
||||
import { streamText } from "ai";
|
||||
import type { UIMessage } from "ai";
|
||||
import { z } from "zod";
|
||||
|
||||
type FixtureUIMessage = UIMessage<never, { kind: { value: string } }>;
|
||||
|
||||
export const fixtureAgent = chat
|
||||
.withUIMessage<FixtureUIMessage>()
|
||||
.withClientData({ schema: z.object({ userId: z.string() }) })
|
||||
.agent({
|
||||
id: "fixture-agent",
|
||||
run: async ({ messages, signal }) => {
|
||||
return streamText({ model: "openai/gpt-5" as never, messages, abortSignal: signal });
|
||||
},
|
||||
});
|
||||
`;
|
||||
|
||||
describe("declaration emit portability", () => {
|
||||
it.skipIf(!existsSync(distDir) || !existsSync(join(coreRoot, "dist")))(
|
||||
"emits portable declarations for inferred chat agent types",
|
||||
() => {
|
||||
const consumerDir = mkdtempSync(join(tmpdir(), "sdk-decl-emit-"));
|
||||
try {
|
||||
const scopedDir = join(consumerDir, "node_modules", "@trigger.dev");
|
||||
mkdirSync(scopedDir, { recursive: true });
|
||||
|
||||
for (const [name, root] of [
|
||||
["sdk", packageRoot],
|
||||
["core", coreRoot],
|
||||
] as const) {
|
||||
const target = join(scopedDir, name);
|
||||
mkdirSync(target, { recursive: true });
|
||||
cpSync(join(root, "package.json"), join(target, "package.json"));
|
||||
cpSync(join(root, "dist"), join(target, "dist"), { recursive: true });
|
||||
}
|
||||
|
||||
// Third-party type deps resolve fine through symlinks.
|
||||
for (const dep of ["ai", "zod", "@ai-sdk/provider"]) {
|
||||
const source = realpathSync(join(packageRoot, "node_modules", dep));
|
||||
const target = join(consumerDir, "node_modules", dep);
|
||||
mkdirSync(resolve(target, ".."), { recursive: true });
|
||||
symlinkSync(source, target);
|
||||
}
|
||||
|
||||
const fixturePath = join(consumerDir, "agent.ts");
|
||||
ts.sys.writeFile(fixturePath, FIXTURE_SOURCE);
|
||||
|
||||
const emitted = new Map<string, string>();
|
||||
const host = ts.createCompilerHost({});
|
||||
host.writeFile = (fileName, text) => emitted.set(fileName, text);
|
||||
|
||||
const program = ts.createProgram({
|
||||
rootNames: [fixturePath],
|
||||
options: {
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
module: ts.ModuleKind.NodeNext,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
||||
strict: true,
|
||||
declaration: true,
|
||||
emitDeclarationOnly: true,
|
||||
skipLibCheck: true,
|
||||
outDir: join(consumerDir, "out"),
|
||||
rootDir: consumerDir,
|
||||
},
|
||||
host,
|
||||
});
|
||||
|
||||
const emitResult = program.emit();
|
||||
const diagnostics = [
|
||||
...ts.getPreEmitDiagnostics(program),
|
||||
...emitResult.diagnostics,
|
||||
].filter((d) => d.category === ts.DiagnosticCategory.Error);
|
||||
const formatted = diagnostics.map((d) =>
|
||||
ts.flattenDiagnosticMessageText(d.messageText, "\n")
|
||||
);
|
||||
expect(formatted).toEqual([]);
|
||||
|
||||
const dts = [...emitted.entries()].find(([name]) => name.endsWith("agent.d.ts"))?.[1];
|
||||
expect(dts).toBeDefined();
|
||||
// The payload generic must be named via the public subpath, and the
|
||||
// emit must not fall back to file paths into the package.
|
||||
expect(dts).toContain('import("@trigger.dev/sdk/chat").ChatTaskWirePayload');
|
||||
expect(dts).not.toMatch(/import\("\.{1,2}\//);
|
||||
expect(dts).not.toContain("ai-shared");
|
||||
} finally {
|
||||
rmSync(consumerDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user