11import type { RuntimeRunArtifactRef } from "./run-registry.js"
22
33export const MATERIALIZATION_RESULT_SCHEMA = "wp-codebox/materialization-result/v1" as const
4+ export const BROWSER_ARTIFACT_PERSISTENCE_PROJECTION_SCHEMA = "wp-codebox/browser-artifact-persistence-projection/v1" as const
5+ export const ARTIFACT_BUNDLE_FILE_MANIFEST_SCHEMA = "wp-codebox/artifact-bundle-file-manifest/v1" as const
46
57export interface MaterializationArtifactRef {
68 kind : string
@@ -91,14 +93,21 @@ export interface BrowserArtifactProjectionInput {
9193}
9294
9395export interface BrowserArtifactPersistenceProjection {
94- schema : "wp-codebox/browser-artifact-persistence-projection/v1"
96+ schema : typeof BROWSER_ARTIFACT_PERSISTENCE_PROJECTION_SCHEMA
9597 artifact ?: Record < string , unknown >
9698 artifacts : Record < string , unknown > [ ]
9799 artifactBundle ?: Record < string , unknown >
98100 materialization ?: Record < string , unknown >
99101 artifactRefs : MaterializationArtifactRef [ ]
100102}
101103
104+ export interface ArtifactBundleFileManifest {
105+ schema : typeof ARTIFACT_BUNDLE_FILE_MANIFEST_SCHEMA
106+ bundle ?: MaterializationArtifactRef
107+ files : MaterializationArtifactRef [ ]
108+ paths : string [ ]
109+ }
110+
102111export function materializationPhaseResult ( input : Omit < MaterializationPhaseResult , "schema" | "artifactRefs" > & { artifactRefs ?: MaterializationArtifactRef [ ] } ) : MaterializationPhaseResult {
103112 return stripUndefined ( {
104113 schema : "wp-codebox/materialization-phase-result/v1" as const ,
@@ -124,7 +133,7 @@ export function materializationResultEnvelope(input: {
124133} ) : MaterializationResultEnvelope {
125134 const phases = input . phases ?? [ ]
126135 const status = input . status ?? ( input . error || phases . some ( ( phase ) => phase . status === "failed" ) ? "failed" : "completed" )
127- const artifactRefs = [ ...( input . artifactRefs ?? [ ] ) , ...phases . flatMap ( ( phase ) => phase . artifactRefs ) ]
136+ const artifactRefs = normalizeMaterializationArtifactRefs ( [ ...( input . artifactRefs ?? [ ] ) , ...phases . flatMap ( ( phase ) => phase . artifactRefs ) ] )
128137 const diagnostics = input . diagnostics ?? phases . flatMap ( phaseDiagnostics )
129138 const base = stripUndefined ( {
130139 schema : MATERIALIZATION_RESULT_SCHEMA ,
@@ -233,12 +242,71 @@ export function browserArtifactPersistenceProjection(input: BrowserArtifactProje
233242 const artifactRefs = browserArtifactProjectionRefs ( { artifactBundle, artifacts, materialization } )
234243
235244 return stripUndefined ( {
236- schema : "wp-codebox/browser-artifact-persistence-projection/v1" as const ,
245+ schema : BROWSER_ARTIFACT_PERSISTENCE_PROJECTION_SCHEMA ,
237246 artifact,
238247 artifacts,
239248 artifactBundle,
240249 materialization,
241- artifactRefs,
250+ artifactRefs : normalizeMaterializationArtifactRefs ( artifactRefs ) ,
251+ } )
252+ }
253+
254+ export function normalizeMaterializationArtifactRef ( input : unknown , defaults : Partial < MaterializationArtifactRef > = { } ) : MaterializationArtifactRef | undefined {
255+ const source = asRecord ( input )
256+ if ( ! source ) {
257+ return undefined
258+ }
259+
260+ const kind = stringValue ( source . kind ?? source . artifact_type ?? source . role ) || defaults . kind
261+ const id = stringValue ( source . id ?? source . artifact_id ?? source . artifactId ) || defaults . id
262+ const path = stringValue ( source . path ?? source . artifacts_path ?? source . artifactsPath ?? source . directory ) || defaults . path
263+ const digest = normalizeDigest ( source . digest ?? source . sha256 ?? source . contentDigest ?? source . content_digest ) ?? defaults . digest
264+
265+ if ( ! id && ! path && ! digest && ! stringValue ( source . kind ?? source . artifact_type ?? source . role ) ) {
266+ return undefined
267+ }
268+
269+ return stripUndefined ( {
270+ kind : kind ?? "artifact" ,
271+ id,
272+ path,
273+ digest,
274+ } )
275+ }
276+
277+ export function normalizeMaterializationArtifactRefs ( inputs : unknown [ ] | undefined ) : MaterializationArtifactRef [ ] {
278+ const refs : MaterializationArtifactRef [ ] = [ ]
279+ const seen = new Set < string > ( )
280+ for ( const input of inputs ?? [ ] ) {
281+ const ref = normalizeMaterializationArtifactRef ( input )
282+ if ( ! ref ) {
283+ continue
284+ }
285+ const key = `${ ref . kind } \u0000${ ref . id ?? "" } \u0000${ ref . path ?? "" } \u0000${ ref . digest ?. algorithm ?? "" } \u0000${ ref . digest ?. value ?? "" } `
286+ if ( ! seen . has ( key ) ) {
287+ refs . push ( ref )
288+ seen . add ( key )
289+ }
290+ }
291+ return refs
292+ }
293+
294+ export function persistedBrowserArtifactRefs ( input : BrowserArtifactProjectionInput | MaterializationResultEnvelope | unknown ) : MaterializationArtifactRef [ ] {
295+ return browserArtifactPersistenceProjection ( input ) . artifactRefs
296+ }
297+
298+ export function artifactBundleFileManifest ( input : BrowserArtifactProjectionInput | MaterializationResultEnvelope | BrowserArtifactPersistenceProjection | unknown ) : ArtifactBundleFileManifest {
299+ const projection = isRecord ( input ) && input . schema === BROWSER_ARTIFACT_PERSISTENCE_PROJECTION_SCHEMA
300+ ? input as unknown as BrowserArtifactPersistenceProjection
301+ : browserArtifactPersistenceProjection ( input )
302+ const refs = normalizeMaterializationArtifactRefs ( projection . artifactRefs )
303+ const bundle = refs . find ( ( ref ) => ref . kind === "artifact-bundle" )
304+ const files = refs . filter ( ( ref ) => ref . path && ref . kind !== "artifact-bundle" && ref . kind !== "materialization" )
305+ return stripUndefined ( {
306+ schema : ARTIFACT_BUNDLE_FILE_MANIFEST_SCHEMA ,
307+ bundle,
308+ files,
309+ paths : files . map ( ( file ) => file . path as string ) ,
242310 } )
243311}
244312
@@ -272,29 +340,16 @@ function materializationProjectionSource(input: unknown): Record<string, unknown
272340
273341function browserArtifactProjectionRefs ( input : { artifactBundle ?: Record < string , unknown > ; artifacts : Record < string , unknown > [ ] ; materialization ?: Record < string , unknown > } ) : MaterializationArtifactRef [ ] {
274342 const refs : MaterializationArtifactRef [ ] = [ ]
275- const bundleId = stringValue ( input . artifactBundle ?. id ?? input . artifactBundle ?. artifact_id )
276- const bundleDigest = normalizeDigest ( input . artifactBundle ?. contentDigest ?? input . artifactBundle ?. content_digest ?? input . artifactBundle ?. digest ?? input . artifactBundle ?. sha256 )
277- if ( bundleId || bundleDigest || stringValue ( input . artifactBundle ?. path ) ) {
278- refs . push ( stripUndefined ( {
279- kind : "artifact-bundle" ,
280- id : bundleId || undefined ,
281- path : stringValue ( input . artifactBundle ?. path ) || stringValue ( input . artifactBundle ?. directory ) || undefined ,
282- digest : bundleDigest ,
283- } ) )
343+ const bundle = normalizeMaterializationArtifactRef ( input . artifactBundle , { kind : "artifact-bundle" } )
344+ if ( bundle ) {
345+ refs . push ( bundle )
284346 }
285347
286348 for ( const artifact of input . artifacts ) {
287- const path = stringValue ( artifact . path )
288- const id = stringValue ( artifact . id ?? artifact . artifact_id )
289- if ( ! path && ! id ) {
290- continue
349+ const ref = normalizeMaterializationArtifactRef ( artifact , { kind : "browser-artifact" } )
350+ if ( ref ?. path || ref ?. id ) {
351+ refs . push ( ref )
291352 }
292- refs . push ( stripUndefined ( {
293- kind : stringValue ( artifact . kind ?? artifact . artifact_type ?? artifact . role ) || "browser-artifact" ,
294- id : id || undefined ,
295- path : path || undefined ,
296- digest : normalizeDigest ( artifact . digest ?? artifact . sha256 ?? artifact . contentDigest ?? artifact . content_digest ) ,
297- } ) )
298353 }
299354
300355 const materializationId = stringValue ( input . materialization ?. id ?? input . materialization ?. artifact_id )
0 commit comments