refactor(platform): unify routing and session dispatch

This commit is contained in:
2026-09-05 05:23:58 +08:00
parent e47d9d3007
commit 1c124cf216
10 changed files with 916 additions and 171 deletions
@@ -1,8 +1,11 @@
import { afterEach, test } from 'node:test';
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import ts from 'typescript';
import { scanImportBoundaries } from '../../scripts/lib/import-boundaries.mjs';
const roots = [];
@@ -30,6 +33,85 @@ function scan(root) {
});
}
const frameworkRoot = fileURLToPath(new URL('../../YouleNexus/assets/framework/', import.meta.url));
const modernRuntimeEntries = [
'net/wire-client.ts',
'platform/runtime-session.ts',
'protocol/platform-handlers.ts',
'protocol/router.ts',
// Task 10 creates this entry; the same assertion starts covering it as soon as it exists.
'platform/runtime.ts',
];
const legacyRuntimeFiles = new Set([
'net/net-client.ts',
'platform/session.ts',
'platform/startup.ts',
'platform/room-rpc-bus.ts',
'platform/readonly.ts',
'platform/stores/app-store.ts',
'platform/stores/player-store.ts',
'platform/stores/room-store.ts',
'platform/stores/types.ts',
'protocol/room-handlers.ts',
].map((file) => resolve(frameworkRoot, file)));
function relativeImports(file) {
const source = readFileSync(file, 'utf8');
const sourceFile = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const imports = [];
const visit = (node) => {
let specifier;
if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node))
&& node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
specifier = node.moduleSpecifier.text;
} else if (ts.isImportTypeNode(node)
&& ts.isLiteralTypeNode(node.argument)
&& ts.isStringLiteral(node.argument.literal)) {
specifier = node.argument.literal.text;
} else if (ts.isCallExpression(node)
&& node.expression.kind === ts.SyntaxKind.ImportKeyword
&& node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0])) {
specifier = node.arguments[0].text;
}
if (specifier?.startsWith('.')) {
const imported = resolve(dirname(file), specifier);
const candidates = [imported, `${imported}.ts`, join(imported, 'index.ts')];
const resolvedImport = candidates.find((candidate) => existsSync(candidate));
if (resolvedImport) imports.push(resolvedImport);
}
ts.forEachChild(node, visit);
};
ts.forEachChild(sourceFile, visit);
return imports;
}
function findLegacyDependency(entry, path = [], visited = new Set()) {
if (legacyRuntimeFiles.has(entry)) return [...path, entry];
if (visited.has(entry)) return null;
visited.add(entry);
for (const imported of relativeImports(entry)) {
const found = findLegacyDependency(imported, [...path, entry], visited);
if (found) return found;
}
return null;
}
test('every existing modern runtime entry is transitively isolated from the legacy compatibility island', () => {
const entries = modernRuntimeEntries
.map((file) => resolve(frameworkRoot, file))
.filter((file) => existsSync(file));
assert.ok(entries.some((file) => file.endsWith(`${join('protocol', 'router.ts')}`)));
for (const entry of entries) {
const dependencyPath = findLegacyDependency(entry);
assert.equal(
dependencyPath,
null,
dependencyPath?.map((file) => file.slice(frameworkRoot.length)).join(' -> '),
);
}
});
test('scanner rejects sdk contracts importing a framework platform path', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "import '../platform/session.ts'\n");