Refine generated app-server import boundaries

This commit is contained in:
murashit 2026-06-26 08:52:12 +09:00
parent d7bb49efbe
commit f71f40b61a
6 changed files with 16 additions and 196 deletions

View file

@ -17,28 +17,10 @@
},
{
"path": "./scripts/lint/no-generated-app-server-boundary-imports.grit",
"includes": [
"**/src/**/*.ts",
"**/src/**/*.tsx",
"!**/src/app-server/connection/**",
"!**/src/app-server/protocol/turn.ts",
"!**/src/app-server/protocol/server-requests.ts"
]
"includes": ["**/src/**/*.ts", "**/src/**/*.tsx", "!**/src/app-server/connection/**"]
},
// Shared source layering boundaries.
{
"path": "./scripts/lint/no-generated-app-server-thread-alias-imports.grit",
"includes": ["**/src/app-server/connection/**/*.ts", "**/src/app-server/connection/**/*.tsx"]
},
{
"path": "./scripts/lint/no-generated-app-server-turn-protocol-imports.grit",
"includes": ["**/src/app-server/protocol/turn.ts"]
},
{
"path": "./scripts/lint/no-generated-app-server-server-request-imports.grit",
"includes": ["**/src/app-server/protocol/server-requests.ts"]
},
{
"path": "./scripts/lint/no-lower-level-boundary-imports.grit",
"includes": [

View file

@ -11,6 +11,12 @@ private pattern js_module_reference() {
}
js_module_reference() as $stmt where {
$stmt <: contains `$source` where { $source <: r"^[\"'].*(?:src/)?generated/app-server(?:/.*)?[\"']$" },
$stmt <: contains `$source` where {
$source <: r"^[\"'].*(?:src/)?generated/app-server(?:/.*)?[\"']$",
not {
$filename <: r".*/src/app-server/protocol/server-requests\.ts$",
$source <: r"^[\"'].*generated/app-server/(?:RequestId|ServerRequest)[\"']$"
}
},
register_diagnostic(span=$stmt, message="Keep generated app-server types behind src/app-server adapters; expose Panel-owned models outside raw app-server boundaries.", severity="error")
}

View file

@ -1,19 +0,0 @@
language js
private pattern js_module_reference() {
or {
JsImport(),
JsExportNamedFromClause(),
JsExportFromClause(),
TsImportType(),
JsImportCallExpression()
}
}
js_module_reference() as $stmt where {
$stmt <: contains `$source` where {
$source <: r"^[\"'].*generated/app-server/.*[\"']$",
not { $source <: r"^[\"'].*generated/app-server/(?:RequestId|ServerRequest)[\"']$" }
},
register_diagnostic(span=$stmt, message="Only generated RequestId and ServerRequest are allowed in the server request protocol exception.", severity="error")
}

View file

@ -1,22 +0,0 @@
language js
private pattern js_non_import_module_reference() {
or {
JsExportNamedFromClause(),
JsExportFromClause(),
TsImportType(),
JsImportCallExpression()
}
}
or {
JsImport() as $stmt where {
$stmt <: contains `$source` where { $source <: r"^[\"'].*generated/app-server/v2/Thread[\"']$" },
not { $stmt <: r".*\bThread\s+as\s+AppServerThread\b.*" },
register_diagnostic(span=$stmt, message="Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.", severity="error")
},
js_non_import_module_reference() as $stmt where {
$stmt <: contains `$source` where { $source <: r"^[\"'].*generated/app-server/v2/Thread[\"']$" },
register_diagnostic(span=$stmt, message="Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.", severity="error")
}
}

View file

@ -1,17 +0,0 @@
language js
private pattern js_static_module_reference() {
or {
JsImport(),
JsExportNamedFromClause(),
JsExportFromClause()
}
}
js_static_module_reference() as $stmt where {
$stmt <: contains `$source` where {
$source <: r"^[\"'].*generated/app-server/.*[\"']$",
not { $source <: r"^[\"'].*generated/app-server/v2/ThreadItem[\"']$" }
},
register_diagnostic(span=$stmt, message="Only generated ThreadItem is allowed in the turn protocol exception. Model other turn payload shapes locally.", severity="error")
}

View file

@ -399,8 +399,10 @@ export type TurnItem = GeneratedTurnItem;
path.join(cwd, "src/app-server/protocol/server-requests.ts"),
`
import type { ServerRequest } from "../../generated/app-server/ServerRequest";
import type { ToolRequestUserInputParams } from "../../generated/app-server/v2/ToolRequestUserInputParams";
export type Request = ServerRequest;
export type Params = ToolRequestUserInputParams;
`.trimStart(),
);
@ -432,8 +434,12 @@ export type Request = ServerRequest;
expect(pluginMessages(report, "src/app-server/services/runtime-overrides.ts")).toEqual([
"Keep generated app-server types behind src/app-server adapters; expose Panel-owned models outside raw app-server boundaries.",
]);
expect(pluginDiagnostics(report, "src/app-server/protocol/turn.ts")).toEqual([]);
expect(pluginDiagnostics(report, "src/app-server/protocol/server-requests.ts")).toEqual([]);
expect(pluginMessages(report, "src/app-server/protocol/turn.ts")).toEqual([
"Keep generated app-server types behind src/app-server adapters; expose Panel-owned models outside raw app-server boundaries.",
]);
expect(pluginMessages(report, "src/app-server/protocol/server-requests.ts")).toEqual([
"Keep generated app-server types behind src/app-server adapters; expose Panel-owned models outside raw app-server boundaries.",
]);
});
it("keeps app-server protocol modules behind app-server and chat ingestion boundaries", async () => {
@ -599,122 +605,6 @@ export const format = formatDate;
expect(pluginDiagnostics(report, "src/domain/threads/format.ts")).toEqual([]);
});
it("keeps generated app-server Thread imports behind the app-server alias", async () => {
const cwd = await tempBiomeWorkspace(["no-generated-app-server-thread-alias-imports.grit"]);
await writeFile(
path.join(cwd, "src/app-server/connection/thread.ts"),
`
import type { Thread } from "../../generated/app-server/v2/Thread";
export type ConnectionThread = Thread;
`.trimStart(),
);
await writeFile(
path.join(cwd, "src/app-server/connection/aliased-thread.ts"),
`
import type { Thread as AppServerThread } from "../../generated/app-server/v2/Thread";
export type ConnectionThread = AppServerThread;
`.trimStart(),
);
await writeFile(
path.join(cwd, "src/app-server/connection/record-thread.ts"),
`
import type { Thread as ThreadRecord } from "../../generated/app-server/v2/Thread";
export type ConnectionThread = ThreadRecord;
`.trimStart(),
);
await writeFile(
path.join(cwd, "src/app-server/connection/import-type-thread.ts"),
`
export type ConnectionThread = import("../../generated/app-server/v2/Thread").Thread;
`.trimStart(),
);
await writeFile(
path.join(cwd, "src/app-server/connection/export-thread.ts"),
`
export type { Thread } from "../../generated/app-server/v2/Thread";
`.trimStart(),
);
const report = biomeLint(
[
"src/app-server/connection/thread.ts",
"src/app-server/connection/aliased-thread.ts",
"src/app-server/connection/record-thread.ts",
"src/app-server/connection/import-type-thread.ts",
"src/app-server/connection/export-thread.ts",
],
cwd,
);
expect(pluginMessages(report, "src/app-server/connection/thread.ts")).toEqual([
"Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.",
]);
expect(pluginDiagnostics(report, "src/app-server/connection/aliased-thread.ts")).toEqual([]);
expect(pluginMessages(report, "src/app-server/connection/record-thread.ts")).toEqual([
"Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.",
]);
expect(pluginMessages(report, "src/app-server/connection/import-type-thread.ts")).toEqual([
"Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.",
]);
expect(pluginMessages(report, "src/app-server/connection/export-thread.ts")).toEqual([
"Import generated app-server Thread as AppServerThread, or use the Panel-owned domain Thread model.",
]);
});
it("keeps generated app-server turn protocol imports behind the ThreadItem exception", async () => {
const cwd = await tempBiomeWorkspace(["no-generated-app-server-turn-protocol-imports.grit"]);
await writeFile(
path.join(cwd, "src/app-server/protocol/turn.ts"),
`
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Thread } from "../../generated/app-server/v2/Thread";
export type TurnItem = ThreadItem;
export type TurnThread = Thread;
`.trimStart(),
);
const report = biomeLint(["src/app-server/protocol/turn.ts"], cwd);
expect(pluginMessages(report, "src/app-server/protocol/turn.ts")).toEqual([
"Only generated ThreadItem is allowed in the turn protocol exception. Model other turn payload shapes locally.",
]);
});
it("keeps generated app-server server request imports behind narrow exceptions", async () => {
const cwd = await tempBiomeWorkspace(["no-generated-app-server-server-request-imports.grit"]);
await writeFile(
path.join(cwd, "src/app-server/protocol/server-requests.ts"),
`
import type { ServerRequest } from "../../generated/app-server/ServerRequest";
import type { ToolRequestUserInputParams } from "../../generated/app-server/v2/ToolRequestUserInputParams";
export type Request = ServerRequest;
export type Params = ToolRequestUserInputParams;
export type InputParams = import("../../generated/app-server/v2/ToolRequestUserInputParams").ToolRequestUserInputParams;
export async function loadParams() {
return import("../../generated/app-server/v2/ToolRequestUserInputParams");
}
const params = await import("../../generated/app-server/v2/ToolRequestUserInputParams");
export const loadedParams = params;
`.trimStart(),
);
const report = biomeLint(["src/app-server/protocol/server-requests.ts"], cwd);
expect(pluginMessages(report, "src/app-server/protocol/server-requests.ts")).toEqual([
"Only generated RequestId and ServerRequest are allowed in the server request protocol exception.",
"Only generated RequestId and ServerRequest are allowed in the server request protocol exception.",
"Only generated RequestId and ServerRequest are allowed in the server request protocol exception.",
"Only generated RequestId and ServerRequest are allowed in the server request protocol exception.",
]);
});
it("keeps chat application app-server projection RPCs behind facades", async () => {
const cwd = await tempBiomeWorkspace(["no-app-server-projection-rpcs.grit"]);
await writeFile(