Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
714525b
ci: node 24 pin and boundary lint (warn) (#102)
mottihoresh Oct 16, 2026
c010d40
test: tier-1 stable-seam tests, fixtures, services harness (#103)
mottihoresh Oct 16, 2026
28803d8
test: jsdom component harness + tier-1 component tests (1/2) (#104)
mottihoresh Oct 16, 2026
2598104
test: tier-1 component tests (2/2) (#105)
mottihoresh Oct 16, 2026
f833939
refactor(firmware): generated reference to shared/firmware, imports r…
mottihoresh Oct 17, 2026
087f3c5
build(sync): sync USB identity, node bus and calibration timing (#107)
mottihoresh Oct 17, 2026
552b050
build(sync): sync the newly named firmware protocol values (#108)
mottihoresh Oct 17, 2026
ef57688
refactor(ipc): per-topic contracts, break the model↔contract cycles (…
mottihoresh Oct 17, 2026
ca57ba6
refactor(format): shared/format helpers, merge identical copies (#110)
mottihoresh Oct 17, 2026
111e907
refactor(main): topic folders for main's own files, injected paths an…
mottihoresh Oct 17, 2026
64310ec
refactor(main): main-only models move from shared to main (#112)
mottihoresh Oct 17, 2026
afadc63
refactor(nodes): the mapping store service and the JSON store (#113)
mottihoresh Oct 17, 2026
3b9d5da
refactor(gateway): move HID, calibration and event reporting out of D…
mottihoresh Oct 17, 2026
f667979
refactor(gateway): finish the DeviceLink split (#115)
mottihoresh Oct 17, 2026
6228947
refactor(dcs): move the DCS owners out of Session (#116)
mottihoresh Oct 17, 2026
f3cc40a
refactor(config): settings store and the event journal (#117)
mottihoresh Oct 17, 2026
49ec873
refactor(app): session becomes app services (#118)
mottihoresh Oct 17, 2026
e8947b4
test: tier-2 component tests (1/2) (#119)
mottihoresh Oct 17, 2026
424790b
test: tier-2 component tests (2/2) (#120)
mottihoresh Oct 17, 2026
39c052c
refactor(calibration): workflow moves to the hid calibration service …
mottihoresh Oct 17, 2026
9918c51
refactor(nodes): cockpit snapshot built in main (cockpit tab) (#122)
mottihoresh Oct 17, 2026
bde740d
refactor(nodes): cockpit snapshot for overview, connection and settin…
mottihoresh Oct 17, 2026
a40b33b
refactor(renderer): link, relay, HID and settings rules pushed from m…
mottihoresh Oct 17, 2026
4d13321
refactor(renderer): last renderer decisions move to main (#125)
mottihoresh Oct 17, 2026
fbcd181
refactor(renderer): topic folders, per-topic stores, adapter-only IPC…
mottihoresh Oct 17, 2026
8462a6c
refactor(app): one consistent frame per main update (#127)
mottihoresh Oct 17, 2026
19cdcd9
refactor: final cleanup, renames and the architecture guide (#128)
mottihoresh Oct 17, 2026
ee6ca36
refactor(calibration): return the workflow promise without a nullish …
mottihoresh Oct 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
181 changes: 181 additions & 0 deletions .dependency-cruiser.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
// Process + topic boundaries for the structure rewrite (SkyHawkClient#97, arch-phase0-v2 §5).
//
// Seeded from `depcruise --init oneshot` (dependency-cruiser 18.5.0); its default rule set is
// replaced by the boundary rules below, its options are kept.
//
// The rules describe the layout: src/main/<topic>/, src/renderer/src/<topic>/ and
// src/shared/<topic>/{index,ipc}.ts contracts, plus the leaves src/shared/firmware/,
// src/shared/format/ and src/renderer/src/app/kit/.
//
// Severities live ONLY in .dependency-cruiser.severity.json, one key per rule family. Each phase
// flipped its own families from "warn" to "error"; since R3.4b every family is "error".
// eslint-disable-next-line @typescript-eslint/no-require-imports -- CommonJS config file
const SEV = require('./.dependency-cruiser.severity.json')

const MAIN = 'src/main'
const RENDERER = 'src/renderer/src'

const FIRMWARE = '^src/shared/firmware/'
const FORMAT = '^src/shared/format/'
const KIT = `^${RENDERER}/app/kit/`
const CONTRACT = '^src/shared/[a-z-]+(/calibration)?/(index|ipc)\\.ts$'
// Topic directions, the same inside main and inside the renderer. `app` may import everything,
// so it has no rule. Every topic may also import the leaves, the kit and the shared contracts.
const ALLOW = {
nodes: ['gateway', 'events'],
dcs: ['gateway', 'events'],
hid: ['gateway'],
'hid/calibration': ['hid', 'gateway'],
gateway: ['events'],
config: ['nodes', 'hid/calibration'],
events: []
}

// A topic's folder; `hid` must not swallow `hid/calibration`.
const dir = (root, topic) =>
topic === 'hid' ? `^${root}/hid/(?!calibration/)` : `^${root}/${topic}/`

const topicRules = (side, root, severity, extraAllowed) =>
Object.entries(ALLOW).map(([topic, allowed]) => ({
name: `topic-${side}-${topic.replace('/', '-')}`,
severity,
comment: `${side} ${topic} may import only ${allowed.join(', ') || 'no other topic'} (+ firmware, format, kit, contracts)`,
from: { path: dir(root, topic) },
to: {
path: `^${root}/`,
pathNot: [dir(root, topic), ...allowed.map((t) => dir(root, t)), ...extraAllowed]
}
}))

// T2: across topics, import only a topic's index.ts. $1 is the importing topic.
const deepRule = (side, root, severity, extraAllowed) => ({
name: `T2-${side}-no-deep-topic-import`,
severity,
comment: `outside a ${side} topic, import only its index.ts`,
from: { path: `^${root}/([^/]+)/` },
to: {
path: `^${root}/[^/]+/`,
pathNot: [
`^${root}/$1/`,
`^${root}/[^/]+/index\\.ts$`,
`^${root}/hid/calibration/index\\.ts$`,
...extraAllowed
]
}
})

/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
forbidden: [
// ── process (hard boundary) ─────────────────────────────────────────────
{
name: 'P1-renderer-not-main',
severity: SEV.crossProcess,
comment: 'the renderer reaches main only over IPC',
from: { path: '^src/renderer/' },
to: { path: '^src/(main|preload)/' }
},
{
name: 'P2-main-not-renderer',
severity: SEV.crossProcess,
comment: 'main never imports renderer code',
from: { path: '^src/main/' },
to: { path: '^src/renderer/' }
},
{
name: 'P3-shared-is-a-leaf',
severity: SEV.process,
comment: 'shared code imports nothing from a process',
from: { path: '^src/shared/' },
to: { path: '^src/(main|renderer|preload)/' }
},
{
name: 'P4-renderer-imports-contracts-only',
severity: SEV.rendererShared,
comment: 'the renderer imports only shared contracts and the firmware/format leaves',
from: { path: '^src/renderer/' },
to: { path: '^src/shared/', pathNot: [CONTRACT, FIRMWARE, FORMAT] }
},
{
name: 'P6-electron-only-in-app',
severity: SEV.electron,
comment: 'electron is imported only by main/app and preload',
from: { pathNot: '^src/(main/app|preload)/' },
to: { path: '^node_modules/electron/' }
},
// ── contracts + leaves ──────────────────────────────────────────────────
{
name: 'C1-contracts-import-contracts',
severity: SEV.contracts,
comment: 'a contract imports only other contracts and the leaves',
from: { path: CONTRACT },
to: { path: '^src/', pathNot: [CONTRACT, FIRMWARE, FORMAT] }
},
{
name: 'L-firmware-leaf',
severity: SEV.firmwareLeaf,
comment: 'shared/firmware imports nothing else in src',
from: { path: FIRMWARE },
to: { path: '^src/', pathNot: FIRMWARE }
},
{
name: 'L-format-leaf',
severity: SEV.formatLeaf,
comment: 'shared/format imports nothing else in src',
from: { path: FORMAT },
to: { path: '^src/', pathNot: FORMAT }
},
{
name: 'L-kit-leaf',
severity: SEV.kitLeaf,
comment: 'app/kit imports no other renderer code',
from: { path: KIT },
to: { path: '^src/renderer/', pathNot: KIT }
},
// ── topics (soft boundary) ──────────────────────────────────────────────
...topicRules('main', MAIN, SEV.topicsMain, []),
...topicRules('renderer', RENDERER, SEV.topicsRenderer, [KIT]),
deepRule('main', MAIN, SEV.deepMain, []),
deepRule('renderer', RENDERER, SEV.deepRenderer, [KIT]),
// ── cycles ──────────────────────────────────────────────────────────────
{
name: 'C2-no-circular',
severity: SEV.cycles,
comment: 'no circular dependencies anywhere (type-only imports included)',
from: {},
to: { circular: true }
}
],
options: {
doNotFollow: { path: ['node_modules'] },
exclude: {
path: [
'\\.test\\.tsx?$',
'^src/test-fixtures/',
'^src/renderer/test/',
'^src/main/test-harness/'
]
},
// Type-only imports count: today's model <-> contract cycles are type cycles.
tsPreCompilationDeps: true,
// tsconfig.json carries both the @shared and @renderer paths; main and preload use only
// @shared, which resolves the same way.
tsConfig: { fileName: 'tsconfig.json' },
detectProcessBuiltinModuleCalls: true,
enhancedResolveOptions: {
exportsFields: ['exports'],
conditionNames: ['import', 'require', 'node', 'default', 'types'],
extensions: ['.ts', '.tsx', '.d.ts'],
mainFields: ['main', 'types', 'typings']
},
skipAnalysisNotInRules: true,
reporterOptions: {
dot: { collapsePattern: 'node_modules/(?:@[^/]+/[^/]+|[^/]+)' },
archi: {
collapsePattern:
'^(?:packages|src|lib(s?)|app(s?)|bin|test(s?)|spec(s?))/[^/]+|node_modules/(?:@[^/]+/[^/]+|[^/]+)'
},
text: { highlightFocused: true }
}
}
}
15 changes: 15 additions & 0 deletions .dependency-cruiser.severity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"process": "error",
"crossProcess": "error",
"electron": "error",
"rendererShared": "error",
"contracts": "error",
"cycles": "error",
"firmwareLeaf": "error",
"formatLeaf": "error",
"kitLeaf": "error",
"topicsMain": "error",
"topicsRenderer": "error",
"deepMain": "error",
"deepRenderer": "error"
}
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ jobs:
- name: Sync freshness check
run: |
npm run sync
git diff --exit-code -- src/main/reference \
git diff --exit-code -- src/shared/firmware \
|| (echo "::error::generated reference files are stale — run 'npm run sync' and commit" && exit 1)
- run: npm run lint
- name: Boundary lint
run: npm run lint:deps
- run: npm run typecheck
- run: npm test

Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
Loading
Loading