18d307f52SEvan Baconimport { JSONObject } from '@expo/json-file'; 28d307f52SEvan Baconimport fs from 'fs'; 38d307f52SEvan Baconimport schemaDerefSync from 'json-schema-deref-sync'; 48d307f52SEvan Baconimport path from 'path'; 58d307f52SEvan Bacon 6*8a424bebSJames Ideimport { createCachedFetch } from './rest/client'; 7814b6fafSEvan Baconimport { env } from '../utils/env'; 88d307f52SEvan Baconimport { CommandError } from '../utils/errors'; 98d307f52SEvan Bacon 108d307f52SEvan Baconexport type Schema = any; 118d307f52SEvan Bacon 128d307f52SEvan Baconexport type AssetSchema = { 138d307f52SEvan Bacon fieldPath: string; 148d307f52SEvan Bacon}; 158d307f52SEvan Bacon 168d307f52SEvan Baconconst schemaJson: { [sdkVersion: string]: Schema } = {}; 178d307f52SEvan Bacon 188d307f52SEvan Bacon// TODO: Maybe move json-schema-deref-sync out of api (1.58MB -- lodash) 198d307f52SEvan Bacon// https://packagephobia.com/result?p=json-schema-deref-sync 208d307f52SEvan Baconasync function getSchemaAsync(sdkVersion: string): Promise<Schema> { 218d307f52SEvan Bacon const json = await getSchemaJSONAsync(sdkVersion); 228d307f52SEvan Bacon return schemaDerefSync(json.schema); 238d307f52SEvan Bacon} 248d307f52SEvan Bacon 258d307f52SEvan Bacon/** 268d307f52SEvan Bacon * Array of schema nodes that refer to assets along with their field path (eg. 'notification.icon') 278d307f52SEvan Bacon * 288d307f52SEvan Bacon * @param sdkVersion 298d307f52SEvan Bacon */ 308d307f52SEvan Baconexport async function getAssetSchemasAsync(sdkVersion: string = 'UNVERSIONED'): Promise<string[]> { 318d307f52SEvan Bacon // If no SDK version is available then fall back to unversioned 328d307f52SEvan Bacon const schema = await getSchemaAsync(sdkVersion); 338d307f52SEvan Bacon const assetSchemas: string[] = []; 348d307f52SEvan Bacon const visit = (node: Schema, fieldPath: string) => { 358d307f52SEvan Bacon if (node.meta && node.meta.asset) { 368d307f52SEvan Bacon assetSchemas.push(fieldPath); 378d307f52SEvan Bacon } 388d307f52SEvan Bacon const properties = node.properties; 398d307f52SEvan Bacon if (properties) { 408d307f52SEvan Bacon Object.keys(properties).forEach((property) => 418d307f52SEvan Bacon visit(properties[property], `${fieldPath}${fieldPath.length > 0 ? '.' : ''}${property}`) 428d307f52SEvan Bacon ); 438d307f52SEvan Bacon } 448d307f52SEvan Bacon }; 458d307f52SEvan Bacon visit(schema, ''); 468d307f52SEvan Bacon 478d307f52SEvan Bacon return assetSchemas; 488d307f52SEvan Bacon} 498d307f52SEvan Bacon 508d307f52SEvan Baconasync function getSchemaJSONAsync(sdkVersion: string): Promise<{ schema: Schema }> { 51814b6fafSEvan Bacon if (env.EXPO_UNIVERSE_DIR) { 528d307f52SEvan Bacon return JSON.parse( 538d307f52SEvan Bacon fs 548d307f52SEvan Bacon .readFileSync( 55814b6fafSEvan Bacon path.join( 56814b6fafSEvan Bacon env.EXPO_UNIVERSE_DIR, 57814b6fafSEvan Bacon 'server', 58814b6fafSEvan Bacon 'www', 59814b6fafSEvan Bacon 'xdl-schemas', 60814b6fafSEvan Bacon 'UNVERSIONED-schema.json' 61814b6fafSEvan Bacon ) 628d307f52SEvan Bacon ) 638d307f52SEvan Bacon .toString() 648d307f52SEvan Bacon ); 658d307f52SEvan Bacon } 668d307f52SEvan Bacon 678d307f52SEvan Bacon if (!schemaJson[sdkVersion]) { 688d307f52SEvan Bacon try { 698d307f52SEvan Bacon schemaJson[sdkVersion] = await getConfigurationSchemaAsync(sdkVersion); 708d307f52SEvan Bacon } catch (e: any) { 718d307f52SEvan Bacon if (e.code === 'INVALID_JSON') { 728d307f52SEvan Bacon throw new CommandError('INVALID_JSON', `Couldn't read schema from server`); 738d307f52SEvan Bacon } 748d307f52SEvan Bacon 758d307f52SEvan Bacon throw e; 768d307f52SEvan Bacon } 778d307f52SEvan Bacon } 788d307f52SEvan Bacon 798d307f52SEvan Bacon return schemaJson[sdkVersion]; 808d307f52SEvan Bacon} 818d307f52SEvan Bacon 828d307f52SEvan Baconasync function getConfigurationSchemaAsync(sdkVersion: string): Promise<JSONObject> { 838d307f52SEvan Bacon // Reconstruct the cached fetch since caching could be disabled. 848d307f52SEvan Bacon const fetchAsync = createCachedFetch({ 858d307f52SEvan Bacon cacheDirectory: 'schema-cache', 868d307f52SEvan Bacon // We'll use a 1 week cache for versions so older versions get flushed out eventually. 878d307f52SEvan Bacon ttl: 1000 * 60 * 60 * 24 * 7, 888d307f52SEvan Bacon }); 898d307f52SEvan Bacon const response = await fetchAsync(`project/configuration/schema/${sdkVersion}`); 908d307f52SEvan Bacon const { data } = await response.json(); 918d307f52SEvan Bacon return data; 928d307f52SEvan Bacon} 93