refactor(apple): group artifact producers and verification (#952)

* refactor(apple): group artifact producers and verification

* test(apple): run affected native plist CLI regressions
This commit is contained in:
ysopwnable 2026-10-07 21:09:33 +08:00 committed by GitHub
parent a6ba772c08
commit ef1a0cc65a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 149 additions and 92 deletions

View File

@ -311,7 +311,7 @@ jobs:
- name: Real binary metadata and Interface Builder integration
run: npm run verify:apple-dispatch && npm run verify:interface-builder
- name: Native plist CLI selection regressions
run: npm run test:focused -- tests/boundary/cli/nativePlistCli.test.ts
run: npm run test:focused -- tests/boundary/cli/nativePlistCli.test.ts tests/boundary/cli/nativePlistPrototypeKeyCli.test.ts tests/boundary/cli/nativePlistSelectionFailureCli.test.ts
reconstruction-readiness:
name: Reconstruction readiness

View File

@ -356,3 +356,32 @@ repository-root default. Both point to `_reference/firmware-integration/generate
when invoked through the documented npm commands. Optional ext4/Ghidra lanes keep
their own prerequisites. Source-fixture generation and synthetic producer tests
do not establish real Binwalk/Unblob analysis or another host's execution support.
## Apple artifact capability ownership
`src/domain/apple/` owns Apple application anatomy/projection, asset catalog
facts, Interface Builder graphs, keyed archive semantics and plist values.
`src/application/apple/AppleApplicationService.ts` retains the shared workflow
that projects authenticated IPA/macOS inventory Evidence.
`src/artifacts/apple/` owns the actual producer boundary: directory acquisition,
assetutil invocation, plist/NIB decoding, encoded view-parent interpretation and
keyed archive inspection. These helpers are used by ArtifactProvider, retain
its existing typed analysis boundary, injected assetutil seam and original reader
cleanup, and do not require a new port for each helper. Producer code no longer
lives in the application layer. Guards reject application/composition/caller
imports from this owner while admitting pure facts and shared artifact readers.
Adapter-source tests retain their assertions in the existing forked adapter lane.
`scripts/verify/apple/` groups archive, asset catalog, Interface Builder,
Objective-C/Swift dispatch and macOS bundle verification. The macOS bundle fixture
builder lives in `scripts/fixtures/apple/`; NIB byte fixtures stay beside their
artifact decoder. Existing format-specific conformance sources and goldens keep
their locations, and relocated verifiers resolve them against their own file
URLs. Public npm entrypoints and CLI/MCP contracts stay stable; generated-document
cache inputs include the relocated runtime scripts and fixture builder.
Portable decoder and injected producer tests establish their format/boundary
claims. Actual Swift/Xcode, assetutil, native NIB, bundle signing and DMG lifecycle
claims require the real Apple lane. Native binary API/value-flow semantics and
shared cross-domain contract aggregates keep their separate owners.

View File

@ -89,6 +89,12 @@ then checks that `inspect-artifact` plus `project-apple-application-graph`
report the same bundle anatomy for all three through the CLI, with stdio MCP
parity. It also checks that the DMG is detached afterwards. It runs in macOS CI.
Apple artifact verifiers live in `scripts/verify/apple/`, with the macOS bundle
builder under `scripts/fixtures/apple/` and NIB byte fixtures beside the decoder
in `src/artifacts/apple/`. The npm entrypoints are unchanged. Format-specific
Swift/XIB/asset sources and goldens retain their locations; real Apple workflows
resolve them from the verifier file URL and run in the macOS CI lane.
MCP SDK transport tests with recording providers remain integration tests.
They are useful for schema drift and failure projection but do not prove that
Hopper, Ghidra or another substituted engine works. `verify:package` proves

View File

@ -121,17 +121,17 @@
"typecheck": "tsc --noEmit",
"verify:test-temp-hygiene": "npm run build:cached && node scripts/verify-test-temp-hygiene.mjs",
"verify:hopper": "npm run verify:fixtures && npm run build:cached && node scripts/verify-real-hopper.mjs",
"verify:apple-dispatch": "npm run build:cached && node scripts/verify-apple-dispatch.mjs",
"verify:apple-dispatch": "npm run build:cached && node scripts/verify/apple/dispatch.mjs",
"verify:native-ui": "npm run build:cached && node scripts/verify-native-ui.mjs",
"verify:keyed-archive": "npm run build:cached && node scripts/verify-keyed-archive.mjs",
"verify:macos-bundle": "npm run build:cached && node scripts/verify-macos-bundle.mjs",
"verify:keyed-archive": "npm run build:cached && node scripts/verify/apple/keyed-archive.mjs",
"verify:macos-bundle": "npm run build:cached && node scripts/verify/apple/macos-bundle.mjs",
"verify:ghidra": "npm run build:cached && node scripts/verify-real-ghidra.mjs",
"verify:ida": "npm run build:cached && node scripts/verify-real-ida.mjs",
"verify:ghidra:switch": "npm run build:cached && node scripts/verify-real-ghidra-switch.mjs",
"verify:ghidra:dos": "npm run build:cached && node scripts/verify-real-ghidra-dos.mjs",
"verify:ghidra:com": "npm run build:cached && node scripts/verify-real-ghidra-com.mjs",
"verify:ghidra:aarch64-jump-table": "npm run build:cached && node scripts/verify-real-ghidra.mjs --aarch64-jump-table",
"verify:interface-builder": "npm run build:cached && node scripts/verify-interface-builder.mjs",
"verify:interface-builder": "npm run build:cached && node scripts/verify/apple/interface-builder.mjs",
"verify:ghidra:cross-format": "npm run build:cached && node scripts/verify-real-ghidra.mjs --cross-format",
"verify:ghidra:windows": "npm run build:cached && npm run build:ghidra-windows-fixture && node scripts/verify-real-ghidra-windows.mjs",
"verify:browser": "npm run build:cached && node scripts/verify-real-browser.mjs",
@ -171,7 +171,7 @@
"docs:error-schema": "npm run build:cached && node scripts/generate-error-schema.mjs",
"prepack": "node scripts/prepack.mjs",
"prepare": "node scripts/prepare.mjs",
"verify:asset-catalog": "npm run build:cached && node scripts/verify-asset-catalog.mjs",
"verify:asset-catalog": "npm run build:cached && node scripts/verify/apple/asset-catalog.mjs",
"verify:native-ui:permissions": "npm run build:cached && node scripts/verify-native-ui.mjs --allow-permission-denial",
"test:focused": "node scripts/run-development-tests.mjs focused",
"build:windows-native": "node scripts/build-windows-native.mjs",

View File

@ -69,6 +69,7 @@ const failedBoundary = (file, target) => {
return "provider-construction";
if (
(file.startsWith("src/artifacts/javascript/") ||
file.startsWith("src/artifacts/apple/") ||
/^src\/artifacts\/ArtifactHash\.(?:js|ts)$/u.test(file)) &&
(["application", "composition", "server", "cli", "main"].includes(
targetLayer,

View File

@ -10,7 +10,7 @@ import {
artifactCli,
artifactMcpResult,
withArtifactMcp,
} from "./lib/artifact-e2e.mjs";
} from "../../lib/artifact-e2e.mjs";
if (process.platform !== "darwin")
throw new Error(
@ -30,7 +30,7 @@ try {
"clang",
fileURLToPath(
new URL(
"../tests/conformance/interface-builder/fixture.c",
"../../../tests/conformance/interface-builder/fixture.c",
import.meta.url,
),
),
@ -44,7 +44,10 @@ try {
await exec("/usr/bin/xcrun", [
"actool",
fileURLToPath(
new URL("../tests/conformance/native/Assets.xcassets", import.meta.url),
new URL(
"../../../tests/conformance/native/Assets.xcassets",
import.meta.url,
),
),
"--compile",
resources,

View File

@ -4,8 +4,8 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js";
import { inspectAppleDispatchMetadata } from "../dist/native/AppleDispatchMetadata.js";
import { parseBinaryTarget } from "../../../dist/application/BinaryTargetResolver.js";
import { inspectAppleDispatchMetadata } from "../../../dist/native/AppleDispatchMetadata.js";
if (process.platform !== "darwin")
throw new Error(
@ -14,7 +14,7 @@ if (process.platform !== "darwin")
const exec = promisify(execFile);
const source = (name) =>
fileURLToPath(
new URL(`../tests/conformance/native/${name}`, import.meta.url),
new URL(`../../../tests/conformance/native/${name}`, import.meta.url),
);
/** Both link modes: legacy LC_DYLD_INFO binds and the default chained fixups. */

View File

@ -8,8 +8,8 @@ import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { parseBinaryTarget } from "../dist/application/BinaryTargetResolver.js";
import { ArtifactProvider } from "../dist/artifacts/ArtifactProvider.js";
import { parseBinaryTarget } from "../../../dist/application/BinaryTargetResolver.js";
import { ArtifactProvider } from "../../../dist/artifacts/ArtifactProvider.js";
const exec = promisify(execFile);
const fixtureRoot = await mkdtemp(join(tmpdir(), "rea-interface-builder-"));
@ -18,7 +18,7 @@ const resourcesPath = join(appPath, "Contents", "Resources");
const executablePath = join(appPath, "Contents", "MacOS", "Fixture");
const nibPath = join(resourcesPath, "Main.nib");
const sourceRoot = fileURLToPath(
new URL("../tests/conformance/interface-builder/", import.meta.url),
new URL("../../../tests/conformance/interface-builder/", import.meta.url),
);
try {

View File

@ -10,7 +10,7 @@ import {
artifactCli,
artifactMcpResult,
withArtifactMcp,
} from "./lib/artifact-e2e.mjs";
} from "../../lib/artifact-e2e.mjs";
if (process.platform !== "darwin")
throw new Error(
@ -25,7 +25,7 @@ try {
join(root, "modules"),
fileURLToPath(
new URL(
"../tests/conformance/native/keyed-archive.swift",
"../../../tests/conformance/native/keyed-archive.swift",
import.meta.url,
),
),
@ -66,7 +66,7 @@ try {
const expected = JSON.parse(
await readFile(
new URL(
"../tests/fixtures/golden/keyed-archive/graph.json",
"../../../tests/fixtures/golden/keyed-archive/graph.json",
import.meta.url,
),
"utf8",
@ -124,7 +124,7 @@ try {
join(root, "modules"),
fileURLToPath(
new URL(
"../tests/conformance/native/keyed-archive-uid-root.swift",
"../../../tests/conformance/native/keyed-archive-uid-root.swift",
import.meta.url,
),
),

View File

@ -9,11 +9,11 @@ import {
artifactCliEvidence,
artifactMcpResult,
withArtifactMcp,
} from "./lib/artifact-e2e.mjs";
} from "../../lib/artifact-e2e.mjs";
import {
buildMacosBundleFixture,
preflightMacosBundleFixture,
} from "./lib/macos-bundle-fixture.mjs";
} from "../../fixtures/apple/macos-bundle.mjs";
const exec = promisify(execFile);

View File

@ -3,7 +3,7 @@ import { z } from "zod";
import type { AnalysisOperationPort } from "./AnalysisProvider.js";
import type { EnhancedResult } from "./EnhancedToolTypes.js";
import type { enhancedInputSchemas } from "../contracts/enhancedInputs.js";
import { interfaceBuilderAnalysisSchema } from "../domain/interfaceBuilderGraph.js";
import { interfaceBuilderAnalysisSchema } from "../domain/apple/interfaceBuilderGraph.js";
import {
nativeInvestigationGraphSchema,
nativeInvestigationTraceSchema,

View File

@ -1,12 +1,12 @@
import type { AnalysisError } from "../domain/analysisErrorBase.js";
import type { Evidence } from "../domain/evidence.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import type { Evidence } from "../../domain/evidence.js";
import {
appleApplicationProjectionInputSchema,
projectAppleApplication,
} from "../domain/appleApplication.js";
import type { Result } from "../domain/result.js";
import { APPLE_APPLICATION_PROVIDER } from "./InvestigationProviders.js";
import { projectInventoryEvidence } from "./InventoryProjectionEvidence.js";
} from "../../domain/apple/appleApplication.js";
import type { Result } from "../../domain/result.js";
import { APPLE_APPLICATION_PROVIDER } from "../InvestigationProviders.js";
import { projectInventoryEvidence } from "../InventoryProjectionEvidence.js";
const OPERATION = "project_apple_application_graph" as const;

View File

@ -7,12 +7,12 @@ import {
type ProviderIdentity,
type ExecutionOptions,
} from "../application/AnalysisProvider.js";
import { inspectBundleKeyedArchive } from "./KeyedArchiveReader.js";
import { inspectBundleKeyedArchive } from "./apple/KeyedArchiveReader.js";
import { basename, dirname } from "node:path";
import { inventoryArtifact } from "../application/ArtifactInventory.js";
import { extractArtifact } from "../application/ArtifactExtraction.js";
import { analyzeInterfaceBuilderBundle } from "../application/InterfaceBuilderAnalysis.js";
import { analyzeAppleAssetCatalogs } from "../application/AppleAssetCatalogAnalysis.js";
import { analyzeInterfaceBuilderBundle } from "./apple/InterfaceBuilderAnalysis.js";
import { analyzeAppleAssetCatalogs } from "./apple/AppleAssetCatalogAnalysis.js";
import {
ARTIFACT_ANALYSIS_OPERATIONS,
artifactInventoryInputSchema,
@ -24,7 +24,7 @@ import { AnalysisCapabilityUnavailableError } from "../domain/analysisErrorCore.
import { ArtifactOperationError } from "../domain/artifactOperationError.js";
import { type AnalysisError } from "../domain/analysisErrorBase.js";
import type { JsonValue } from "../domain/jsonValue.js";
import { interfaceBuilderLimitsSchema } from "../domain/interfaceBuilderGraph.js";
import { interfaceBuilderLimitsSchema } from "../domain/apple/interfaceBuilderGraph.js";
import { err, ok } from "../domain/result.js";
import { ArtifactReaderFailure } from "./ArtifactReader.js";
import {

View File

@ -1,14 +1,14 @@
import { createHash } from "node:crypto";
import { lstat } from "node:fs/promises";
import { DirectoryArtifactReader } from "../artifacts/DirectoryArtifactReader.js";
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
import { ArtifactReaderFailure } from "../ArtifactReader.js";
import { analyzeInterfaceBuilderBundle } from "./InterfaceBuilderAnalysis.js";
import {
appleAssetCatalogInputSchema,
parseAppleAssetCatalogRecords,
projectAppleAssetCatalogPage,
} from "../domain/appleAssetCatalog.js";
import { execFileOutput } from "../process/ExecFileOutput.js";
} from "../../domain/apple/appleAssetCatalog.js";
import { execFileOutput } from "../../process/ExecFileOutput.js";
const ASSETUTIL = "/usr/bin/assetutil";
const MAX_CATALOG_BYTES = 64 * 1024 * 1024;
@ -105,7 +105,7 @@ export const analyzeAppleAssetCatalogs = async (input: {
const inspectAssetCatalogEntry = async (input: {
readonly reader: DirectoryArtifactReader;
readonly entry: import("../artifacts/ArtifactReader.js").ArtifactEntry;
readonly entry: import("../ArtifactReader.js").ArtifactEntry;
readonly runAssetUtil: (
path: string,
signal?: AbortSignal,
@ -237,7 +237,7 @@ const runAssetUtil = async (
const digestBounded = async (
reader: DirectoryArtifactReader,
entry: import("../artifacts/ArtifactReader.js").ArtifactEntry,
entry: import("../ArtifactReader.js").ArtifactEntry,
signal?: AbortSignal,
): Promise<string> => {
if (entry.declaredSize === null || entry.declaredSize > MAX_CATALOG_BYTES)

View File

@ -1,28 +1,25 @@
import { createHash } from "node:crypto";
import { parseBinary } from "plist";
import { DirectoryArtifactReader } from "../artifacts/DirectoryArtifactReader.js";
import { projectPlistValue } from "../domain/plistValue.js";
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
import { projectPlistValue } from "../../domain/apple/plistValue.js";
import {
omittedPrototypeKeysLimitation,
parseXmlPropertyList,
} from "../domain/propertyListKeys.js";
import {
decodeNibArchive,
type NibArchiveDocument,
} from "../artifacts/NibArchive.js";
import type { ArtifactEntry } from "../artifacts/ArtifactReader.js";
} from "../../domain/propertyListKeys.js";
import { decodeNibArchive, type NibArchiveDocument } from "./NibArchive.js";
import type { ArtifactEntry } from "../ArtifactReader.js";
import {
buildInterfaceBuilderAnalysis,
interfaceBuilderLimitsSchema,
type InterfaceBuilderDocumentInput,
} from "../domain/interfaceBuilderGraph.js";
} from "../../domain/apple/interfaceBuilderGraph.js";
import {
mergeNibHierarchies,
projectNibViewHierarchy,
type NibHierarchyNode,
} from "./NibViewHierarchy.js";
import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js";
import { jsonValueSchema, type JsonValue } from "../../domain/jsonValue.js";
const MAX_DOCUMENT_BYTES = 64 * 1024 * 1024;

View File

@ -2,19 +2,19 @@ import { createHash } from "node:crypto";
import { TextDecoder } from "node:util";
import { parseBinary } from "plist";
import { z } from "zod";
import type { JsonValue } from "../domain/jsonValue.js";
import { projectPlistValue } from "../domain/plistValue.js";
import type { JsonValue } from "../../domain/jsonValue.js";
import { projectPlistValue } from "../../domain/apple/plistValue.js";
import {
omittedPrototypeKeysLimitation,
parseXmlPropertyList,
} from "../domain/propertyListKeys.js";
} from "../../domain/propertyListKeys.js";
import {
keyedArchiveInputSchema,
keyedArchiveResultSchema,
projectKeyedArchive,
} from "../domain/keyedArchive.js";
import { DirectoryArtifactReader } from "./DirectoryArtifactReader.js";
import { ArtifactReaderFailure } from "./ArtifactReader.js";
} from "../../domain/apple/keyedArchive.js";
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
import { ArtifactReaderFailure } from "../ArtifactReader.js";
const MAX_BYTES = 64 * 1024 * 1024;

View File

@ -1,4 +1,4 @@
import type { JsonValue } from "../domain/jsonValue.js";
import type { JsonValue } from "../../domain/jsonValue.js";
import { TextDecoder } from "node:util";
const MAGIC = Buffer.from("NIBArchive", "ascii");

View File

@ -1,5 +1,5 @@
import type { NibArchiveDocument } from "../artifacts/NibArchive.js";
import type { JsonValue } from "../domain/jsonValue.js";
import type { NibArchiveDocument } from "./NibArchive.js";
import type { JsonValue } from "../../domain/jsonValue.js";
export type NibHierarchyNode = {
objectID: string;

View File

@ -2,7 +2,7 @@ import { reconstructionCoverageEvaluationInputSchema } from "../domain/reconstru
import { Cli, z } from "incur";
import { projectAndroidApplicationEvidence } from "../application/android/AndroidApplicationService.js";
import { projectAppleApplicationEvidence } from "../application/AppleApplicationService.js";
import { projectAppleApplicationEvidence } from "../application/apple/AppleApplicationService.js";
import {
compareApplicationVersionsEvidenceValidated,
compareJavaScriptExportShapesEvidenceValidated,
@ -19,7 +19,7 @@ import { AnalysisInputError } from "../domain/analysisErrorCore.js";
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
import { type AnalysisError } from "../domain/analysisErrorBase.js";
import { androidApplicationProjectionInputSchema } from "../domain/android/androidApplication.js";
import { appleApplicationProjectionInputSchema } from "../domain/appleApplication.js";
import { appleApplicationProjectionInputSchema } from "../domain/apple/appleApplication.js";
import { jsonValueSchema, type JsonValue } from "../domain/jsonValue.js";
import type { Logger } from "../logger.js";
import { traceApplicationFeatureInputSchema } from "../domain/javascript/javascriptFeatureTraceSchemas.js";

View File

@ -23,7 +23,7 @@ import {
import {
appleApplicationProjectionInputSchema,
appleApplicationProjectionResultSchema,
} from "../domain/appleApplication.js";
} from "../domain/apple/appleApplication.js";
import type { ToolContract } from "./toolContracts.js";
import { toolContractMetadata } from "./toolEffects.js";
import { evidenceResultOf } from "./toolOutputSchemas.js";

View File

@ -5,8 +5,8 @@ import { artifactOutputSchemas } from "./toolOutputSchemas.js";
import { jsonValueSchema } from "../domain/jsonValue.js";
import { toolContractMetadata } from "./toolEffects.js";
import { requireOutputSchema } from "./toolOutputSchemaPrimitives.js";
import { appleAssetCatalogInputSchema } from "../domain/appleAssetCatalog.js";
import { keyedArchiveInputSchema } from "../domain/keyedArchive.js";
import { appleAssetCatalogInputSchema } from "../domain/apple/appleAssetCatalog.js";
import { keyedArchiveInputSchema } from "../domain/apple/keyedArchive.js";
/** Exact caller boundary for deterministic artifact inventory. */
export const artifactInventoryInputSchema = z.strictObject({
integrity_policy: z.enum(["fail", "record-and-continue"]).default("fail"),

View File

@ -30,9 +30,9 @@ import {
} from "../domain/nativeInspection.js";
import { artifactExtractionResultSchema } from "../domain/artifactGraph.js";
import { artifactInspectionResultSchema } from "../domain/artifactInspection.js";
import { interfaceBuilderAnalysisSchema } from "../domain/interfaceBuilderGraph.js";
import { keyedArchiveResultSchema } from "../domain/keyedArchive.js";
import { appleAssetCatalogResultSchema } from "../domain/appleAssetCatalog.js";
import { interfaceBuilderAnalysisSchema } from "../domain/apple/interfaceBuilderGraph.js";
import { keyedArchiveResultSchema } from "../domain/apple/keyedArchive.js";
import { appleAssetCatalogResultSchema } from "../domain/apple/appleAssetCatalog.js";
import {
managedArtifactInspectionSchema,
managedMemberInspectionSchema,

View File

@ -3,9 +3,9 @@ import { createHash } from "node:crypto";
import { describe, expect, it } from "vitest";
import { projectAppleApplication } from "./appleApplication.js";
import { canonicalDigest } from "./comparisonSemantics.js";
import { createEvidence, type Evidence } from "./evidence.js";
import { jsonValueSchema } from "./jsonValue.js";
import { canonicalDigest } from "../comparisonSemantics.js";
import { createEvidence, type Evidence } from "../evidence.js";
import { jsonValueSchema } from "../jsonValue.js";
type EntryKind = "file" | "directory" | "symlink";
type RootFormat = "ipa" | "zip" | "dmg" | "directory" | "asar";

View File

@ -14,10 +14,10 @@ import {
platformsOf,
type AppleInventoryEntry,
} from "./appleBundleAnatomy.js";
import { parseArtifactInventoryEvidence } from "./artifactInventoryEvidence.js";
import { evidenceSchema } from "./evidence.js";
import { digestSchema } from "./../domain/digests.js";
import { prefixedDigestSchema } from "./../domain/digests.js";
import { parseArtifactInventoryEvidence } from "../artifactInventoryEvidence.js";
import { evidenceSchema } from "../evidence.js";
import { digestSchema } from "../digests.js";
import { prefixedDigestSchema } from "../digests.js";
const evidenceIdSchema = prefixedDigestSchema("ev");
const pathSchema = z.string().min(1);

View File

@ -1,7 +1,7 @@
import { z } from "zod";
import { jsonValueSchema } from "./jsonValue.js";
import { AnalysisOutputError } from "./analysisErrorCore.js";
import { jsonValueSchema } from "../jsonValue.js";
import { AnalysisOutputError } from "../analysisErrorCore.js";
/** Caller-controlled page bounds for compiled Apple asset metadata. */
export const appleAssetCatalogInputSchema = z.strictObject({

View File

@ -1,6 +1,6 @@
import { z } from "zod";
import { digestSchema, prefixedDigestSchema } from "./digests.js";
import { digestSchema, prefixedDigestSchema } from "../digests.js";
const pathSchema = z.string().min(1);

View File

@ -10,7 +10,7 @@ import {
nativeInvestigationGraphSchema,
nativeInvestigationNodeSchema,
nativeInvestigationTraceLimitsSchema,
} from "./nativeInvestigationGraph.js";
} from "../nativeInvestigationGraph.js";
export { parseInterfaceBuilderRecords } from "./interfaceBuilderKeyedArchive.js";

View File

@ -1,6 +1,6 @@
import { z } from "zod";
import { jsonValueSchema } from "./jsonValue.js";
import { jsonValueSchema } from "../jsonValue.js";
const recordSchema = z.record(z.string(), z.unknown());

View File

@ -1,5 +1,5 @@
import { z } from "zod";
import { jsonValueSchema, type JsonValue } from "./jsonValue.js";
import { jsonValueSchema, type JsonValue } from "../jsonValue.js";
/** Select one archive within the active bundle and optionally one named root. */
export const keyedArchiveInputSchema = z.strictObject({

View File

@ -1,6 +1,6 @@
import { z } from "zod";
import type { JsonValue } from "./jsonValue.js";
import type { JsonValue } from "../jsonValue.js";
const MAX_PLIST_DEPTH = 128;

View File

@ -8,7 +8,7 @@ import {
import {
buildInterfaceBuilderAnalysis,
interfaceBuilderLimitsSchema,
} from "./interfaceBuilderGraph.js";
} from "./apple/interfaceBuilderGraph.js";
const evidence = [
{

View File

@ -5,7 +5,7 @@ import type { JsonValue } from "../../domain/jsonValue.js";
import {
projectPlistValue,
type ProjectedPlistValue,
} from "../../domain/plistValue.js";
} from "../../domain/apple/plistValue.js";
import {
omitPrototypeKeys,
omittedPrototypeKeysLimitation,

View File

@ -1,7 +1,7 @@
import type { McpServer } from "@modelcontextprotocol/server";
import { projectAndroidApplicationEvidence } from "../../application/android/AndroidApplicationService.js";
import { projectAppleApplicationEvidence } from "../../application/AppleApplicationService.js";
import { projectAppleApplicationEvidence } from "../../application/apple/AppleApplicationService.js";
import {
APPLICATION_TOOL_CONTRACTS,
applicationToolContract,

View File

@ -7,8 +7,8 @@ import { promisify } from "node:util";
import { buildBinary } from "plist";
import { describe, expect, it } from "vitest";
import { analyzeInterfaceBuilderBundle } from "../../../src/application/InterfaceBuilderAnalysis.js";
import { encodeNibArchiveFixture } from "../../../src/artifacts/NibArchive.fixture.js";
import { analyzeInterfaceBuilderBundle } from "../../../src/artifacts/apple/InterfaceBuilderAnalysis.js";
import { encodeNibArchiveFixture } from "../../../src/artifacts/apple/NibArchive.fixture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const compile = promisify(execFile);

View File

@ -1,10 +1,10 @@
import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import { decodeKeyedArchiveBytes } from "../../../src/artifacts/KeyedArchiveReader.js";
import { decodeKeyedArchiveBytes } from "../../../src/artifacts/apple/KeyedArchiveReader.js";
import {
parseAppleAssetCatalogRecords,
projectAppleAssetCatalogPage,
} from "../../../src/domain/appleAssetCatalog.js";
} from "../../../src/domain/apple/appleAssetCatalog.js";
const golden = (path: string) =>
new URL(`../../fixtures/golden/${path}`, import.meta.url);

View File

@ -6,8 +6,8 @@ import { promisify } from "node:util";
import { describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { decodeNibArchive } from "../../../src/artifacts/NibArchive.js";
import { analyzeInterfaceBuilderBundle } from "../../../src/application/InterfaceBuilderAnalysis.js";
import { decodeNibArchive } from "../../../src/artifacts/apple/NibArchive.js";
import { analyzeInterfaceBuilderBundle } from "../../../src/artifacts/apple/InterfaceBuilderAnalysis.js";
const compile = promisify(execFile);

View File

@ -7,7 +7,7 @@ import { expect, it } from "vitest";
import { z } from "zod";
import { parseConfig } from "../../../src/config.js";
import { createBinarySession } from "../../../src/application/runtime.js";
import { keyedArchiveResultSchema } from "../../../src/domain/keyedArchive.js";
import { keyedArchiveResultSchema } from "../../../src/domain/apple/keyedArchive.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";

View File

@ -5,7 +5,7 @@ import { join } from "node:path";
import { buildBinary, parse } from "plist";
import { expect, it } from "vitest";
import { inspectBundleKeyedArchive } from "../../../../src/artifacts/KeyedArchiveReader.js";
import { inspectBundleKeyedArchive } from "../../../../src/artifacts/apple/KeyedArchiveReader.js";
import { nativeFixture } from "../../../fixtures/nativeCommands.js";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";

View File

@ -16,9 +16,9 @@ import {
requireSuccessfulProjection,
} from "../../support/applicationSessionFixture.js";
import { projectAppleApplicationEvidence } from "../../../src/application/AppleApplicationService.js";
import { projectAppleApplicationEvidence } from "../../../src/application/apple/AppleApplicationService.js";
import { runProviderAnalysis } from "../../../src/application/DirectAnalysis.js";
import { appleApplicationProjectionResultSchema } from "../../../src/domain/appleApplication.js";
import { appleApplicationProjectionResultSchema } from "../../../src/domain/apple/appleApplication.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
// Real ZIP entries cross the former component and bridge-candidate limits.

View File

@ -272,3 +272,22 @@ describe("test lane import boundaries", () => {
if (!allowed) expect(result.stdout).toContain("no-restricted-imports");
});
});
describe("Apple artifact producer ownership", () => {
it.each([
["../../application/Workflow.js", false],
["../../composition/android.js", false],
["../../cli.js", false],
["../../domain/apple/plistValue.js", true],
["../DirectoryArtifactReader.js", true],
])("checks the producer dependency %s", (dependency, allowed) => {
const violations = inspectModuleBoundaries(
"src/artifacts/apple/probe.ts",
`import type { Value } from ${JSON.stringify(dependency)};`,
process.cwd(),
);
expect(violations.length === 0).toBe(allowed);
if (!allowed) expect(violations[0]?.boundary).toBe("artifact-acquisition");
});
});

View File

@ -111,6 +111,8 @@
"scripts/lib/*.mjs",
"scripts/verify/managed/*.mjs",
"scripts/fixtures/managed/*.mjs",
"scripts/verify/apple/*.mjs",
"scripts/fixtures/apple/*.mjs",
"src/generated*.ts",
"src/contracts/**/*.ts",
"docs/**",