@@ -202,7 +202,7 @@ export class HttpDispatcher {
202202 // GET /metadata/types
203203 if ( parts [ 0 ] === 'types' ) {
204204 // Try protocol service for dynamic types
205- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
205+ const protocol = await this . resolveService ( 'protocol' ) ;
206206 if ( protocol && typeof protocol . getMetaTypes === 'function' ) {
207207 const result = await protocol . getMetaTypes ( { } ) ;
208208 return { handled : true , response : this . success ( result ) } ;
@@ -242,7 +242,7 @@ export class HttpDispatcher {
242242 // PUT /metadata/:type/:name (Save)
243243 if ( method === 'PUT' && body ) {
244244 // Try to get the protocol service directly
245- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
245+ const protocol = await this . resolveService ( 'protocol' ) ;
246246
247247 if ( protocol && typeof protocol . saveMetaItem === 'function' ) {
248248 try {
@@ -275,7 +275,7 @@ export class HttpDispatcher {
275275 const singularType = type . endsWith ( 's' ) ? type . slice ( 0 , - 1 ) : type ;
276276
277277 // Try Protocol Service First (Preferred)
278- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
278+ const protocol = await this . resolveService ( 'protocol' ) ;
279279 if ( protocol && typeof protocol . getMetaItem === 'function' ) {
280280 try {
281281 const data = await protocol . getMetaItem ( { type : singularType , name } ) ;
@@ -304,7 +304,7 @@ export class HttpDispatcher {
304304 const packageId = query ?. package || undefined ;
305305
306306 // Try protocol service first for any type
307- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
307+ const protocol = await this . resolveService ( 'protocol' ) ;
308308 if ( protocol && typeof protocol . getMetaItems === 'function' ) {
309309 try {
310310 const data = await protocol . getMetaItems ( { type : typeOrName , packageId } ) ;
@@ -343,7 +343,7 @@ export class HttpDispatcher {
343343 // GET /metadata — return available metadata types
344344 if ( parts . length === 0 ) {
345345 // Try protocol service for dynamic types
346- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
346+ const protocol = await this . resolveService ( 'protocol' ) ;
347347 if ( protocol && typeof protocol . getMetaTypes === 'function' ) {
348348 const result = await protocol . getMetaTypes ( { } ) ;
349349 return { handled : true , response : this . success ( result ) } ;
@@ -708,7 +708,7 @@ export class HttpDispatcher {
708708 // Support both path param /view/obj/list AND query param /view/obj?type=list
709709 const type = parts [ 2 ] || query ?. type || 'list' ;
710710
711- const protocol = this . kernel ?. context ?. getService ? await this . kernel . context . getService ( 'protocol' ) : null ;
711+ const protocol = await this . resolveService ( 'protocol' ) ;
712712
713713 if ( protocol && typeof protocol . getUiView === 'function' ) {
714714 try {
@@ -852,19 +852,38 @@ export class HttpDispatcher {
852852 }
853853
854854 private async getService ( name : CoreServiceName ) {
855- // Prefer async resolution to support factory-based services (e.g. auth, analytics)
855+ return this . resolveService ( name ) ;
856+ }
857+
858+ /**
859+ * Resolve any service by name, supporting async factories.
860+ * Fallback chain: getServiceAsync → getService (sync) → context.getService → services map.
861+ * Only returns when a non-null service is found; otherwise falls through to the next step.
862+ */
863+ private async resolveService ( name : string ) {
864+ // Prefer async resolution to support factory-based services (e.g. auth, analytics, protocol)
856865 if ( typeof this . kernel . getServiceAsync === 'function' ) {
857866 try {
858- return await this . kernel . getServiceAsync ( name ) ;
867+ const svc = await this . kernel . getServiceAsync ( name ) ;
868+ if ( svc != null ) return svc ;
859869 } catch {
860- // Service not registered or async resolution failed — fall through to sync/map lookup
870+ // Service not registered or async resolution failed — fall through
861871 }
862872 }
863873 if ( typeof this . kernel . getService === 'function' ) {
864874 try {
865- return await this . kernel . getService ( name ) ;
875+ const svc = await this . kernel . getService ( name ) ;
876+ if ( svc != null ) return svc ;
877+ } catch {
878+ // Service not registered or sync resolution threw "is async" — fall through
879+ }
880+ }
881+ if ( this . kernel ?. context ?. getService ) {
882+ try {
883+ const svc = await this . kernel . context . getService ( name ) ;
884+ if ( svc != null ) return svc ;
866885 } catch {
867- // Service not registered or sync resolution threw "is async" — fall through to map lookup
886+ // Service not registered — fall through
868887 }
869888 }
870889 const services = this . getServicesMap ( ) ;
@@ -876,30 +895,11 @@ export class HttpDispatcher {
876895 * Tries multiple access patterns since kernel structure varies.
877896 */
878897 private async getObjectQLService ( ) : Promise < any > {
879- // 1. Try via kernel.getServiceAsync (supports factory-based services)
880- if ( typeof this . kernel . getServiceAsync === 'function' ) {
881- try {
882- const svc = await this . kernel . getServiceAsync ( 'objectql' ) ;
883- if ( svc ?. registry ) return svc ;
884- } catch { /* service not registered or not yet available */ }
885- }
886- // 2. Try via kernel.getService (sync fallback)
887- if ( typeof this . kernel . getService === 'function' ) {
888- try {
889- const svc = await this . kernel . getService ( 'objectql' ) ;
890- if ( svc ?. registry ) return svc ;
891- } catch { /* ignore */ }
892- }
893- // 3. Try via kernel context
894- if ( this . kernel ?. context ?. getService ) {
895- try {
896- const svc = await this . kernel . context . getService ( 'objectql' ) ;
897- if ( svc ?. registry ) return svc ;
898- } catch { /* ignore */ }
899- }
900- // 4. Try via services map
901- const services = this . getServicesMap ( ) ;
902- if ( services [ 'objectql' ] ?. registry ) return services [ 'objectql' ] ;
898+ // 1. Try via resolveService (handles async factories, sync, context, and map)
899+ try {
900+ const svc = await this . resolveService ( 'objectql' ) ;
901+ if ( svc ?. registry ) return svc ;
902+ } catch { /* service not available */ }
903903 return null ;
904904 }
905905
0 commit comments