






















1+#!/usr/bin/env node
2+3+import path from "node:path";
4+import ts from "typescript";
5+import {
6+collectFileViolations,
7+resolveRepoRoot,
8+resolveSourceRoots,
9+runAsScript,
10+toLine,
11+unwrapExpression,
12+} from "./lib/ts-guard-utils.mjs";
13+14+const legacyReaderNames = new Set(["loadSessionStore", "readSessionEntries"]);
15+16+export const migratedSessionAccessorFiles = new Set([
17+"src/config/sessions/combined-store-gateway.ts",
18+"src/gateway/session-utils.ts",
19+"src/gateway/sessions-resolve.ts",
20+"src/gateway/server-methods/sessions.ts",
21+]);
22+23+function normalizeRelativePath(filePath) {
24+return filePath.replaceAll(path.sep, "/");
25+}
26+27+function propertyAccessName(expression) {
28+const unwrapped = unwrapExpression(expression);
29+if (ts.isIdentifier(unwrapped)) {
30+return unwrapped.text;
31+}
32+if (ts.isPropertyAccessExpression(unwrapped)) {
33+return unwrapped.name.text;
34+}
35+if (ts.isElementAccessExpression(unwrapped) && ts.isStringLiteral(unwrapped.argumentExpression)) {
36+return unwrapped.argumentExpression.text;
37+}
38+return null;
39+}
40+41+function bindingName(node) {
42+if (node.propertyName && ts.isIdentifier(node.propertyName)) {
43+return node.propertyName.text;
44+}
45+if (ts.isIdentifier(node.name)) {
46+return node.name.text;
47+}
48+return null;
49+}
50+51+export function findSessionAccessorBoundaryViolations(content, fileName = "source.ts") {
52+const sourceFile = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true);
53+const violations = [];
54+55+const visit = (node) => {
56+if (ts.isImportDeclaration(node)) {
57+const namedBindings = node.importClause?.namedBindings;
58+if (namedBindings && ts.isNamedImports(namedBindings)) {
59+for (const specifier of namedBindings.elements) {
60+const importedName = specifier.propertyName?.text ?? specifier.name.text;
61+if (legacyReaderNames.has(importedName)) {
62+violations.push({
63+line: toLine(sourceFile, specifier),
64+reason: `imports legacy session store reader "${importedName}"`,
65+});
66+}
67+}
68+}
69+}
70+71+if (ts.isBindingElement(node)) {
72+const name = bindingName(node);
73+if (name && legacyReaderNames.has(name)) {
74+violations.push({
75+line: toLine(sourceFile, node),
76+reason: `aliases legacy session store reader "${name}"`,
77+});
78+}
79+}
80+81+if (ts.isPropertyAccessExpression(node) && legacyReaderNames.has(node.name.text)) {
82+violations.push({
83+line: toLine(sourceFile, node.name),
84+reason: `references legacy session store reader "${node.name.text}"`,
85+});
86+}
87+88+if (
89+ts.isElementAccessExpression(node) &&
90+ts.isStringLiteral(node.argumentExpression) &&
91+legacyReaderNames.has(node.argumentExpression.text)
92+) {
93+violations.push({
94+line: toLine(sourceFile, node.argumentExpression),
95+reason: `references legacy session store reader "${node.argumentExpression.text}"`,
96+});
97+}
98+99+if (ts.isCallExpression(node)) {
100+const calleeName = propertyAccessName(node.expression);
101+if (
102+calleeName &&
103+legacyReaderNames.has(calleeName) &&
104+ts.isIdentifier(unwrapExpression(node.expression))
105+) {
106+violations.push({
107+line: toLine(sourceFile, node.expression),
108+reason: `calls legacy session store reader "${calleeName}"`,
109+});
110+}
111+}
112+113+ts.forEachChild(node, visit);
114+};
115+116+visit(sourceFile);
117+return violations;
118+}
119+120+export async function main() {
121+const repoRoot = resolveRepoRoot(import.meta.url);
122+const sourceRoots = resolveSourceRoots(repoRoot, ["src/config/sessions", "src/gateway"]);
123+const violations = await collectFileViolations({
124+ repoRoot,
125+ sourceRoots,
126+skipFile: (filePath) =>
127+!migratedSessionAccessorFiles.has(normalizeRelativePath(path.relative(repoRoot, filePath))),
128+findViolations: findSessionAccessorBoundaryViolations,
129+});
130+131+if (violations.length === 0) {
132+console.log("session accessor boundary guard passed.");
133+return;
134+}
135+136+console.error("Found legacy session store reader usage in session-accessor migrated files:");
137+for (const violation of violations) {
138+console.error(`- ${violation.path}:${violation.line}: ${violation.reason}`);
139+}
140+console.error(
141+"Use src/config/sessions/session-accessor.ts helpers for migrated read/projection paths. Expand this ratchet only after a slice migrates more files.",
142+);
143+process.exit(1);
144+}
145+146+runAsScript(import.meta.url, main);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。