Refactor VPN proxy client implementation
Build and Deploy Gateway / build-and-push (push) Failing after 2s
Build and Deploy Gateway / deploy (push) Has been skipped

This commit is contained in:
2026-08-09 00:41:52 +03:00
parent 32be4380a3
commit 34d8b681ad
190 changed files with 20064 additions and 9761 deletions
+115
View File
@@ -0,0 +1,115 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
checkImportBoundaries,
importBoundaryViolation,
} from '../../scripts/check-import-boundaries.mjs';
test('architecture rejects reverse ownership imports', () => {
assert.equal(
importBoundaryViolation('src/shared/contracts/state.ts', '../../server/main.ts'),
'shared cannot import server or web',
);
assert.equal(
importBoundaryViolation('src/server/http/routes/state.ts', '../../infrastructure/filesystem/store.ts'),
'server/http cannot import infrastructure directly',
);
assert.equal(
importBoundaryViolation('src/web/ui/Button.tsx', '../api/harborClient.ts'),
'web/ui cannot import api or features',
);
assert.equal(
importBoundaryViolation('src/web/ui/Button.tsx', '../api.js'),
'web/ui cannot import api or features',
);
});
test('architecture requires cross-feature imports to use the public index', () => {
assert.equal(
importBoundaryViolation(
'src/web/features/connection/controller.ts',
'../servers/private/selectors.ts',
),
'cross-feature imports must use the feature index',
);
assert.equal(
importBoundaryViolation('src/web/features/connection/controller.ts', '../servers/index.ts'),
null,
);
});
test('strict TypeScript source tree passes the active boundaries', () => {
const result = checkImportBoundaries();
assert.equal(result.violations.length, 0, JSON.stringify(result.violations, null, 2));
assert.ok(result.filesChecked > 0);
});
test('state views cannot read flat v0 wire aliases or a parallel state transport', () => {
const root = path.resolve(import.meta.dirname, '../..');
const page = fs.readFileSync(path.join(root, 'src/web/components/ClientOverviewPage.tsx'), 'utf8');
const app = fs.readFileSync(path.join(root, 'src/web/App.tsx'), 'utf8');
const client = fs.readFileSync(path.join(root, 'src/web/api/harborClient.ts'), 'utf8');
const server = fs.readFileSync(path.join(root, 'src/server/index.ts'), 'utf8');
const consumers = [
'src/web/App.tsx',
'src/web/components/ClientOverviewPage.tsx',
'src/web/features/diagnostics/ConnectivityDiagnosticsPanel.tsx',
'src/web/features/devices/DevicesPanel.tsx',
'src/web/features/servers/ServerPicker.tsx',
'src/web/features/connection/ConnectionPanel.tsx',
'src/web/features/subscription/SubscriptionFeature.tsx',
'src/web/features/routing/RoutingFeature.tsx',
'src/web/features/instructions/InstructionsFeature.tsx',
].map((file) => fs.readFileSync(path.join(root, file), 'utf8')).join('\n');
const componentSources = fs.readdirSync(path.join(root, 'src/web/components'))
.filter((file) => /\.[jt]sx?$/.test(file))
.map((file) => fs.readFileSync(path.join(root, 'src/web/components', file), 'utf8'))
.concat([
fs.readFileSync(path.join(root, 'src/web/features/connection/ConnectionPanel.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/servers/ServerPicker.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/subscription/SubscriptionFeature.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/routing/RoutingFeature.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/devices/DevicesFeature.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/devices/DevicesPanel.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/diagnostics/DiagnosticsFeature.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/diagnostics/ConnectivityDiagnosticsPanel.tsx'), 'utf8'),
fs.readFileSync(path.join(root, 'src/web/features/instructions/InstructionsFeature.tsx'), 'utf8'),
])
.join('\n');
const flatRead = /state\??\.(?:hasSubscription|subscriptionHost|singboxRunning|singboxStartedAt|configExists|proxyPort|userInfo|gatewayAuto)\b/;
assert.doesNotMatch(page, flatRead);
assert.doesNotMatch(app, flatRead);
assert.doesNotMatch(client, /\bstate:\s*\(\)\s*=>\s*request\(['"]\/api\/state/);
assert.equal(fs.existsSync(path.join(root, 'src/web/api.js')), false);
assert.doesNotMatch(consumers, /from ['"][^'"]*\/api\.js['"]/);
assert.doesNotMatch(componentSources, /from ['"][^'"]*\/api(?:\/|\.js)/);
assert.match(client, /export class HarborApiError/);
assert.match(client, /export async function request/);
assert.match(client, /export const api =/);
assert.match(client, /export function parseHarborState/);
assert.doesNotMatch(server, /function\s+publicState\b/);
assert.doesNotMatch(server, /req\.url\s*===\s*['"]\/api\/state['"]/);
assert.match(app, /const displayState = previewReady \? \{[\s\S]*mode: 'client' as const/);
});
test('comments, strings, and property names are not treated as imports', (t) => {
const repository = fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-boundaries-'));
t.after(() => fs.rmSync(repository, { recursive: true, force: true }));
const file = path.join(repository, 'src/web/ui/example.ts');
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, [
'// import value from "../../server/private.ts";',
'const example = \'import value from "../../server/private.ts";\';',
'const metadata = { import: "../../server/private.ts" };',
'const loader = { import() {} };',
'loader.import("../../server/private.ts");',
'export { example };',
'',
].join('\n'));
assert.equal(checkImportBoundaries(repository).violations.length, 0);
});