2026-07-05 02:08:15 +04:00
import { Injectable } from '@angular/core' ;
import { HttpClient } from '@angular/common/http' ;
feat: dead-config sweep, test suite foundation, widget settingsSchema validation
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:47:13 +04:00
import { Observable , catchError , map , of , shareReplay , switchMap , take , tap } from 'rxjs' ;
2026-07-05 02:08:15 +04:00
import { WidgetManifestEntry , WidgetManifestFile } from '../contracts/widget-manifest.contract' ;
2026-07-05 04:17:31 +04:00
import { ConfigService } from '../../core/config/config.service' ;
2026-07-05 02:08:15 +04:00
@Injectable ( { providedIn : 'root' } )
export class WidgetManifestService {
2026-07-05 04:17:31 +04:00
private readonly fallbackManifestUrl = '/assets/mock/bootstrap/widget-manifest.json' ;
private readonly manifestByUrl = new Map < string , Observable < WidgetManifestFile > > ( ) ;
feat: dead-config sweep, test suite foundation, widget settingsSchema validation
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:47:13 +04:00
/** Last manifest resolved by getManifest(), for synchronous readers (e.g. ProjectValidator) that can't await an Observable. Mirrors ConfigService.getBootstrapSnapshot(). */
private manifestSnapshot : WidgetManifestFile | null = null ;
2026-07-05 02:08:15 +04:00
2026-07-05 04:17:31 +04:00
constructor (
private readonly http : HttpClient ,
private readonly configService : ConfigService
) { }
2026-07-05 02:08:15 +04:00
getManifest ( ) : Observable < WidgetManifestFile > {
2026-07-05 04:17:31 +04:00
return this . resolveManifestUrl ( ) . pipe (
switchMap ( ( manifestUrl ) = > {
const cached = this . manifestByUrl . get ( manifestUrl ) ;
if ( cached ) {
return cached ;
}
const manifest $ = this . http . get < WidgetManifestFile > ( manifestUrl ) . pipe (
feat: dead-config sweep, test suite foundation, widget settingsSchema validation
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:47:13 +04:00
tap ( manifest = > { this . manifestSnapshot = manifest ; } ) ,
2026-07-05 04:17:31 +04:00
shareReplay ( { bufferSize : 1 , refCount : true } ) ,
catchError ( ( ) = > of ( { widgets : [ ] } ) )
) ;
2026-07-05 02:08:15 +04:00
2026-07-05 04:17:31 +04:00
this . manifestByUrl . set ( manifestUrl , manifest $ ) ;
return manifest $ ;
} )
) ;
2026-07-05 02:08:15 +04:00
}
getWidgets ( ) : Observable < WidgetManifestEntry [ ] > {
return this . getManifest ( ) . pipe ( map ( ( manifest ) = > manifest . widgets ? ? [ ] ) ) ;
}
getWidget ( type : string ) : Observable < WidgetManifestEntry | undefined > {
return this . getWidgets ( ) . pipe ( map ( ( widgets ) = > widgets . find ( ( widget ) = > widget . type === type ) ) ) ;
}
2026-07-05 04:17:31 +04:00
feat: dead-config sweep, test suite foundation, widget settingsSchema validation
Sprint G: audited every BootstrapConfig field for a real runtime consumer
(docs/DEAD-CONFIG-AUDIT.md). Wired 3 previously-dead editable fields:
footer.logoUrl, company.address.street/contacts.phone, catalog.suggestionsEnabled.
Remaining dead fields needing a business/design decision tracked in
PRODUCT_BACKLOG.md/KNOWN-ISSUES.md, not silently left.
Sprint H: 6 new spec files (test count 57 -> 83), covering ProjectEditorFacade
(undo/redo, draft persistence, publish gating), AdminAnalyticsFacade
(never-fabricate-a-number contract), and regression coverage for this
session's carousel/hero/profile-toggle fixes.
Sprint I: widget settingsSchema (declared in widget-manifest.json, never
validated) now enforced via a new lightweight schema check in
ProjectValidator, surfaced through the existing issuesByField pipeline.
Same check reused in diagnostics so editor and diagnostics can't disagree.
Verification: tsc clean, ng build clean, 83/83 tests pass, barry-cache
validate clean (2 pre-existing unrelated warnings only).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 20:47:13 +04:00
/** Synchronous accessor for the last-resolved manifest, or null before it's loaded once. */
getManifestSnapshot ( ) : WidgetManifestFile | null {
return this . manifestSnapshot ;
}
2026-07-05 04:17:31 +04:00
private resolveManifestUrl ( ) : Observable < string > {
const snapshotUrl = this . configService . getBootstrapSnapshot ( ) ? . widgetRegistry ? . manifestUrl ;
if ( snapshotUrl ) {
return of ( snapshotUrl ) ;
}
return this . configService . loadBootstrap ( ) . pipe (
take ( 1 ) ,
map ( ( bootstrap ) = > bootstrap . widgetRegistry ? . manifestUrl || this . fallbackManifestUrl ) ,
catchError ( ( ) = > of ( this . fallbackManifestUrl ) )
) ;
}
2026-07-05 02:08:15 +04:00
}