Files
Michał Pierzchała 7a1640e53f refactor: move errors/redaction/device into src/kernel — Phase 5 slice 3 (#940)
* refactor: move errors/redaction/device into src/kernel — Phase 5 slice 3

Relocates the foundational primitive trio from src/utils/ into the kernel/ layer
(joining snapshot.ts from slice 2), per the target folder DAG in
plans/perfect-shape.md §5.5. A pure path codemod, no behavior change.

They form a closed cluster — device -> errors -> redaction, with redaction a
leaf — so kernel/ takes no upward dependency, and every importer becomes a clean
downward import toward kernel. errors.ts is the most-imported module in the
tree; device.ts the §5.5-named headliner. Moving all three atomically avoids a
half-state where one would import another across the utils/kernel boundary.

Imports rewritten by a resolve-based codemod (compares each specifier's resolved
path to the moved files, so the unrelated commands/management/device.ts and
other same-named files are untouched): 483 sites across 402 files. The two
platform-descriptor doc comments and the fallow health baseline key for
device.ts are updated to the new path; the contracts-schema-public guard that
asserts the error helpers pull no diagnostics/node: deps now reads kernel/.

Verified: tsc --noEmit, oxfmt + oxlint --deny-warnings, rslib build, full vitest
suite (2877 pass), fallow audit clean (411 changed files), Layering Guard empty;
kernel/ files import only within kernel.

* docs: update guidance references to kernel/{device,errors} after the move

AGENTS.md (Apple-family sync rule + normalizeError), ADR-0009, and
plans/apple-platform-consolidation.md still named the old src/utils/ paths.
Point them at src/kernel/. plans/perfect-shape.md's utils/device.ts mention is
left as-is — it describes the pre-move diagnosis.
2026-06-30 07:25:02 +02:00

133 lines
3.9 KiB
TypeScript

import assert from 'node:assert/strict';
import { isDeepStrictEqual } from 'node:util';
import type { Platform } from '../../../src/kernel/device.ts';
export interface ProviderScenarioProviderScope {
deviceId?: string;
platform?: Platform;
}
export interface ProviderScenarioProviderEntry<
TResult = unknown,
> extends ProviderScenarioProviderScope {
command: string;
request?: unknown;
result?: TResult;
error?: Error | string;
}
export interface ProviderScenarioProviderCall<
TResult = unknown,
> extends ProviderScenarioProviderScope {
command: string;
request?: unknown;
result?: TResult;
}
export interface ProviderScenarioTranscript {
readonly calls: readonly ProviderScenarioProviderCall[];
readonly remaining: readonly ProviderScenarioProviderEntry[];
next<TResult = unknown>(
command: string,
request?: unknown,
scope?: ProviderScenarioProviderScope,
): TResult;
assertComplete(): void;
}
export function createProviderTranscript(
entries: readonly ProviderScenarioProviderEntry[],
options: { ordered?: boolean } = {},
): ProviderScenarioTranscript {
const pending = [...entries];
const calls: ProviderScenarioProviderCall[] = [];
return {
get calls() {
return [...calls];
},
get remaining() {
return [...pending];
},
next<TResult = unknown>(
command: string,
request?: unknown,
scope: ProviderScenarioProviderScope = {},
): TResult {
const entryIndex = options.ordered
? 0
: pending.findIndex((candidate) =>
providerEntryMatches(candidate, command, request, scope),
);
const entry = entryIndex >= 0 ? pending.splice(entryIndex, 1)[0] : undefined;
assert.ok(entry, `Unexpected provider call: ${formatCall(command, scope)}`);
assert.equal(command, entry.command, 'Provider command mismatch');
assertScope(scope, entry);
if (Object.hasOwn(entry, 'request')) {
assert.deepEqual(request, entry.request, 'Provider request mismatch');
}
const call = {
command,
request,
deviceId: scope.deviceId,
platform: scope.platform,
result: entry.result as TResult,
};
calls.push(call);
if (entry.error) {
throw entry.error instanceof Error ? entry.error : new Error(entry.error);
}
return entry.result as TResult;
},
assertComplete() {
assert.equal(
pending.length,
0,
`Unconsumed provider transcript entries: ${pending.map(formatEntry).join(', ')}`,
);
},
};
}
export function createOrderedProviderTranscript(
entries: readonly ProviderScenarioProviderEntry[],
): ProviderScenarioTranscript {
return createProviderTranscript(entries, { ordered: true });
}
function providerEntryMatches(
entry: ProviderScenarioProviderEntry,
command: string,
request: unknown,
scope: ProviderScenarioProviderScope,
): boolean {
if (entry.command !== command) return false;
if (entry.deviceId && entry.deviceId !== scope.deviceId) return false;
if (entry.platform && entry.platform !== scope.platform) return false;
return !Object.hasOwn(entry, 'request') || isDeepStrictEqual(request, entry.request);
}
function assertScope(
actual: ProviderScenarioProviderScope,
expected: ProviderScenarioProviderEntry,
): void {
if (expected.deviceId) {
assert.equal(actual.deviceId, expected.deviceId, 'Provider device id mismatch');
}
if (expected.platform) {
assert.equal(actual.platform, expected.platform, 'Provider platform mismatch');
}
}
function formatCall(command: string, scope: ProviderScenarioProviderScope): string {
return formatEntry({ command, ...scope });
}
function formatEntry(entry: { command: string; deviceId?: string; platform?: Platform }): string {
const scope = [entry.platform, entry.deviceId].filter(Boolean).join(':');
return scope ? `${scope}.${entry.command}` : entry.command;
}