1+ import { safeString } from "../projectSystemValueUtils.js" ;
2+ import {
3+ GAME_ASSET_MANIFEST_SCHEMA ,
4+ GAME_ASSET_MANIFEST_VERSION
5+ } from "./gameAssetManifestCoordinator.js" ;
6+
7+ const SUPPORTED_DOMAINS = Object . freeze ( [ "sprites" , "tilemaps" , "parallax" , "vectors" ] ) ;
8+
9+ function asObject ( value ) {
10+ return value && typeof value === "object" ? value : { } ;
11+ }
12+
13+ function asArray ( value ) {
14+ return Array . isArray ( value ) ? value : [ ] ;
15+ }
16+
17+ function pushIssue ( issues , message ) {
18+ issues . push ( safeString ( message , "Invalid manifest structure." ) ) ;
19+ }
20+
21+ function inferRuntimeKind ( domain , metadata ) {
22+ const runtimeType = safeString ( asObject ( metadata ) . runtimeType , "" ) . toLowerCase ( ) ;
23+ if ( runtimeType ) {
24+ return runtimeType ;
25+ }
26+ if ( domain === "tilemaps" ) {
27+ return "tilemap" ;
28+ }
29+ if ( domain === "sprites" ) {
30+ return "sprite" ;
31+ }
32+ if ( domain === "vectors" ) {
33+ return "vector" ;
34+ }
35+ if ( domain === "parallax" ) {
36+ return "parallax" ;
37+ }
38+ return "data" ;
39+ }
40+
41+ export function validateGameAssetManifestStructure ( manifestInput , options = { } ) {
42+ const manifest = asObject ( manifestInput ) ;
43+ const issues = [ ] ;
44+ const expectedGameId = safeString ( options . gameId , "" ) . toLowerCase ( ) ;
45+
46+ if ( safeString ( manifest . schema , "" ) !== GAME_ASSET_MANIFEST_SCHEMA ) {
47+ pushIssue ( issues , `manifest.schema must equal ${ GAME_ASSET_MANIFEST_SCHEMA } .` ) ;
48+ }
49+ if ( manifest . version !== GAME_ASSET_MANIFEST_VERSION ) {
50+ pushIssue ( issues , `manifest.version must equal ${ GAME_ASSET_MANIFEST_VERSION } .` ) ;
51+ }
52+
53+ const gameId = safeString ( manifest . gameId , "" ) . toLowerCase ( ) ;
54+ if ( ! gameId ) {
55+ pushIssue ( issues , "manifest.gameId is required." ) ;
56+ }
57+ if ( expectedGameId && gameId && gameId !== expectedGameId ) {
58+ pushIssue ( issues , `manifest.gameId must match expected gameId ${ expectedGameId } .` ) ;
59+ }
60+
61+ const domains = asObject ( manifest . domains ) ;
62+ SUPPORTED_DOMAINS . forEach ( ( domain ) => {
63+ const domainEntries = domains [ domain ] ;
64+ if ( ! Array . isArray ( domainEntries ) ) {
65+ pushIssue ( issues , `manifest.domains.${ domain } must be an array.` ) ;
66+ return ;
67+ }
68+ domainEntries . forEach ( ( entry , index ) => {
69+ const assetId = safeString ( entry ?. assetId , "" ) ;
70+ const runtimePath = safeString ( entry ?. runtimePath , "" ) ;
71+ const toolDataPath = safeString ( entry ?. toolDataPath , "" ) ;
72+ if ( ! assetId ) {
73+ pushIssue ( issues , `manifest.domains.${ domain } [${ index } ].assetId is required.` ) ;
74+ }
75+ if ( ! runtimePath ) {
76+ pushIssue ( issues , `manifest.domains.${ domain } [${ index } ].runtimePath is required.` ) ;
77+ }
78+ if ( ! toolDataPath ) {
79+ pushIssue ( issues , `manifest.domains.${ domain } [${ index } ].toolDataPath is required.` ) ;
80+ }
81+ if ( runtimePath . includes ( "/data/" ) ) {
82+ pushIssue ( issues , `manifest.domains.${ domain } [${ index } ] runtimePath cannot point into /data/.` ) ;
83+ }
84+ } ) ;
85+ } ) ;
86+
87+ return {
88+ valid : issues . length === 0 ,
89+ issues,
90+ manifest
91+ } ;
92+ }
93+
94+ export function discoverRuntimeAssetSourcesFromManifest ( manifestInput , options = { } ) {
95+ const validation = validateGameAssetManifestStructure ( manifestInput , options ) ;
96+ if ( ! validation . valid ) {
97+ return {
98+ status : "invalid" ,
99+ runtimeAssetSources : { } ,
100+ records : [ ] ,
101+ issues : validation . issues ,
102+ gameId : safeString ( asObject ( manifestInput ) . gameId , "" )
103+ } ;
104+ }
105+
106+ const manifest = validation . manifest ;
107+ const sources = { } ;
108+ const records = [ ] ;
109+
110+ SUPPORTED_DOMAINS . forEach ( ( domain ) => {
111+ asArray ( manifest . domains [ domain ] ) . forEach ( ( entry ) => {
112+ const assetId = safeString ( entry ?. assetId , "" ) ;
113+ const runtimePath = safeString ( entry ?. runtimePath , "" ) ;
114+ const toolDataPath = safeString ( entry ?. toolDataPath , "" ) ;
115+ if ( ! assetId || ! runtimePath ) {
116+ return ;
117+ }
118+ const metadata = asObject ( entry ?. metadata ) ;
119+ sources [ assetId ] = {
120+ file : runtimePath ,
121+ path : runtimePath ,
122+ kind : inferRuntimeKind ( domain , metadata ) ,
123+ domain,
124+ sourceToolId : safeString ( entry ?. sourceToolId , "" )
125+ } ;
126+ records . push ( {
127+ domain,
128+ assetId,
129+ runtimePath,
130+ toolDataPath,
131+ sourceToolId : safeString ( entry ?. sourceToolId , "" ) ,
132+ metadata
133+ } ) ;
134+ } ) ;
135+ } ) ;
136+
137+ return {
138+ status : "ready" ,
139+ runtimeAssetSources : sources ,
140+ records,
141+ issues : [ ] ,
142+ gameId : safeString ( manifest . gameId , "" )
143+ } ;
144+ }
0 commit comments