2026-10-11 14:09:21 +02:00
import { z } from "zod" ;
2026-10-13 07:38:14 +08:00
import {
hasCompleteInventoryCoverage ,
2026-10-23 06:40:25 +08:00
parseArtifactInventoryEvidence ,
ArtifactInventoryCarrierError ,
2026-10-13 07:38:14 +08:00
type ParsedInventorySet ,
} from "./artifactInventoryEvidence.js" ;
2026-10-23 06:40:25 +08:00
import { evidenceSchema } from "./evidence.js" ;
import { AnalysisInputError } from "./analysisErrorCore.js" ;
import { EvidenceIntegrityError } from "./evidenceErrors.js" ;
import { analysisInputErrorFromIssues } from "./inputIssueProjection.js" ;
import { err , ok , type Result } from "./result.js" ;
2026-10-25 10:29:17 +07:00
import { digestSchema , prefixedDigestSchema } from "./digests.js" ;
2026-10-11 14:09:21 +02:00
/** Evidence-scoped identifier schemas shared by application graph projections. */
export const projectionEvidenceIdSchema = prefixedDigestSchema ( "ev" );
export const projectionPathSchema = z . string (). min ( 1 );
/** Inventory pages accepted by every application graph projection. */
export const applicationInventoryProjectionInputSchema = z . strictObject ({
inventory_evidence : z.array ( evidenceSchema ). min ( 1 ),
});
2026-10-23 06:40:25 +08:00
const applicationInventoryRequestSchema =
applicationInventoryProjectionInputSchema . extend ({
inventory_evidence : z.array ( z . unknown ()). min ( 1 ),
});
/** Resolve the request and authenticate inventory pages once for all projections. */
export const readApplicationInventoryProjection = (
input : unknown ,
operation : string ,
) : Result < ParsedInventorySet , AnalysisInputError | EvidenceIntegrityError > => {
const frame = applicationInventoryRequestSchema . safeParse ( input );
if ( ! frame . success )
return err (
analysisInputErrorFromIssues ( operation , frame . error . issues , input , {
cause : frame.error ,
}),
);
try {
return ok ( parseArtifactInventoryEvidence ( frame . data . inventory_evidence ));
} catch ( cause : unknown ) {
if ( cause instanceof EvidenceIntegrityError ) return err ( cause );
if ( ! ( cause instanceof ArtifactInventoryCarrierError )) throw cause ;
return err (
new AnalysisInputError ( operation , { cause , userMessage : cause.message }, [
{
path : [
"inventory_evidence" ,
...( cause . index === undefined ? [] : [ cause . index , "operation" ]),
],
reason : "invalid_value" ,
message : cause.message ,
},
]),
);
}
};
2026-10-11 14:09:21 +02:00
/** One authenticated inventory entry retained by application graph projections. */
export const projectedComponentSchema = z . strictObject ({
path : projectionPathSchema ,
artifact_id : prefixedDigestSchema ( "art" ),
sha256 : digestSchema ,
format : z.string (). min ( 1 ),
});
2026-10-14 08:32:15 +08:00
/** Inventoried paths whose content identity is unavailable, retained as unknown. */
export const unavailableComponentSchema = z . strictObject ({
path : projectionPathSchema ,
artifact_id : z.null (),
sha256 : z.null (),
format : z.string (). min ( 1 ),
encrypted : z.boolean (),
limitations : z.array ( z . string ()),
});
2026-10-11 14:09:21 +02:00
export type ProjectedComponent = z . infer < typeof projectedComponentSchema >;
/** Path-pair hypotheses connecting managed content to a native library. */
export const projectedBridgeCandidateSchema = <
Basis extends readonly [ string , ... string []],
> (
basis : Basis ,
) =>
z . strictObject ({
managed_path : projectionPathSchema ,
native_path : projectionPathSchema ,
basis : z.enum ( basis ),
});
/** Coverage status combining inventory completeness with a projection budget. */
export const projectionCoverage = (
inventory : ParsedInventorySet [ "inventory" ],
budgetComplete : boolean ,
) : {
status : "complete-within-inventory" | "partial" ;
2026-10-13 07:38:14 +08:00
/** True when all manifest graph-member counts are present, independent of source coverage. */
2026-10-11 14:09:21 +02:00
inventory_complete : boolean ;
} => ({
status :
2026-10-14 04:00:27 +09:00
hasCompleteInventoryCoverage ( inventory , "container-entries" ) &&
budgetComplete
2026-10-11 14:09:21 +02:00
? ( "complete-within-inventory" as const )
: ( "partial" as const ),
inventory_complete : inventory.complete ,
});
2026-10-23 06:40:25 +08:00
/** One occurrence with resolved content, unavailable identity, or an unresolved node reference. */
2026-10-17 19:45:54 +08:00
export type ProjectedInventoryEntry = {
readonly path : string ;
readonly kind : ParsedInventorySet [ "inventory" ][ "occurrences" ][ number ][ "entry_kind" ];
} & (
| { readonly component : ProjectedComponent ; readonly unavailable : undefined }
| {
readonly component : undefined ;
readonly unavailable : z.infer < typeof unavailableComponentSchema >;
}
2026-10-23 06:40:25 +08:00
| { readonly component : undefined ; readonly unavailable : undefined }
2026-10-17 19:45:54 +08:00
);
2026-10-23 06:40:25 +08:00
/** Resolve available identities and report references outside the supplied pages. */
2026-10-17 19:45:54 +08:00
export const projectedInventoryEntries = (
2026-10-11 14:09:21 +02:00
inventory : ParsedInventorySet [ "inventory" ],
2026-10-23 06:40:25 +08:00
) : {
readonly entries : ProjectedInventoryEntry [];
readonly limitations : string [];
} => {
2026-10-11 14:09:21 +02:00
const nodes = new Map (
inventory . nodes . map (( node ) => [ node . artifact_id , node ]),
);
2026-10-23 06:40:25 +08:00
const limitations : string [] = [];
const entries = inventory . occurrences
2026-10-17 19:45:54 +08:00
. filter (({ logical_path }) => logical_path !== "." )
. map (( occurrence ) : ProjectedInventoryEntry => {
const path = occurrence . logical_path ;
const kind = occurrence . entry_kind ;
if ( occurrence . artifact_id === null )
return {
path ,
kind ,
component : undefined ,
unavailable : {
path ,
artifact_id : null ,
sha256 : null ,
format : occurrence.artifact_format ,
encrypted : occurrence.encrypted ,
limitations : occurrence.limitations ,
},
};
const node = nodes . get ( occurrence . artifact_id );
2026-10-23 06:40:25 +08:00
if ( node === undefined ) {
limitations . push (
`Inventory occurrence ${ JSON . stringify ( path ) } references ${ occurrence . artifact_id } ( ${ occurrence . artifact_format } ), whose node is outside the supplied pages. Its content SHA-256 and component classification remain unknown; the source Evidence retains the occurrence.` ,
2026-10-11 14:09:21 +02:00
);
2026-10-23 06:40:25 +08:00
return { path , kind , component : undefined , unavailable : undefined };
}
2026-10-11 14:09:21 +02:00
return {
2026-10-17 19:45:54 +08:00
path ,
kind ,
component : {
path ,
artifact_id : node.artifact_id ,
sha256 : node.sha256 ,
format : occurrence.artifact_format ,
},
unavailable : undefined ,
};
2026-10-11 14:09:21 +02:00
});
2026-10-23 06:40:25 +08:00
return { entries , limitations };
2026-10-11 14:09:21 +02:00
};
2026-10-17 19:58:18 +08:00
/** Directory trees and links retain inventory identity but are not file components. */
export const projectedFileComponents = (
entries : readonly ProjectedInventoryEntry [],
) : ProjectedComponent [] =>
entries . flatMap (({ kind , component }) =>
( kind === "file" || kind === "slice" ) && component !== undefined
? [ component ]
: [],
);