@@ -406,6 +406,118 @@ describe('external runtime field hydration', () => {
406406 ) . resolves . toEqual ( [ ] ) ;
407407 } ) ;
408408
409+ it ( 'merges manifest schema into an already-registered external class with the same qualified key' , ( ) => {
410+ const packageName = '@happyvertical/smrt-runtime-fixture-recaps' ;
411+ const registrationKey = createQualifiedName (
412+ packageName ,
413+ 'FixtureRuntimeMeetingRecap' ,
414+ ) ;
415+
416+ @smrt ( {
417+ packageName,
418+ tableStrategy : 'sti' ,
419+ tableName : 'fixture_runtime_contents' ,
420+ api : false ,
421+ cli : false ,
422+ mcp : false ,
423+ } )
424+ class FixtureRuntimeContent extends SmrtObject { }
425+
426+ @smrt ( {
427+ packageName,
428+ api : false ,
429+ cli : false ,
430+ mcp : false ,
431+ } )
432+ class FixtureRuntimeMeetingRecap extends FixtureRuntimeContent {
433+ @field ( )
434+ meetingId : string = '' ;
435+ }
436+
437+ const before = ObjectRegistry . findClass ( registrationKey ) ;
438+ expect ( before ) . toBeDefined ( ) ;
439+ expect ( before ?. schema ?. tableName ) . toBe ( 'fixture_runtime_contents' ) ;
440+ expect ( before ?. fields . has ( 'meetingId' ) ) . toBe ( true ) ;
441+ expect ( before ?. fields . has ( 'councilId' ) ) . toBe ( false ) ;
442+ expect ( before ?. fields . has ( 'body' ) ) . toBe ( false ) ;
443+
444+ const beforeDefinitions = ObjectRegistry . getAllSchemasAsDefinitions ( ) ;
445+ expect (
446+ beforeDefinitions . fixture_runtime_contents . columns . meeting_id ,
447+ ) . toBeDefined ( ) ;
448+ expect (
449+ beforeDefinitions . fixture_runtime_contents . columns . council_id ,
450+ ) . toBeUndefined ( ) ;
451+ expect (
452+ beforeDefinitions . fixture_runtime_contents . columns . body ,
453+ ) . toBeUndefined ( ) ;
454+
455+ ObjectRegistry . registerFromManifest (
456+ 'FixtureRuntimeMeetingRecap' ,
457+ {
458+ className : 'FixtureRuntimeMeetingRecap' ,
459+ extends : 'FixtureRuntimeContent' ,
460+ decoratorConfig : {
461+ tableStrategy : 'sti' ,
462+ tableName : 'fixture_runtime_contents' ,
463+ api : false ,
464+ cli : false ,
465+ mcp : false ,
466+ } ,
467+ fields : {
468+ meetingId : { type : 'text' } ,
469+ councilId : { type : 'text' } ,
470+ body : { type : 'text' } ,
471+ } ,
472+ schema : {
473+ tableName : 'fixture_runtime_contents' ,
474+ ddl : `CREATE TABLE IF NOT EXISTS "fixture_runtime_contents" (
475+ "id" TEXT PRIMARY KEY NOT NULL,
476+ "slug" TEXT NOT NULL,
477+ "context" TEXT NOT NULL DEFAULT '',
478+ "created_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
479+ "updated_at" TIMESTAMP NOT NULL DEFAULT current_timestamp,
480+ "_meta_type" TEXT NOT NULL DEFAULT '',
481+ "_meta_data" JSON,
482+ "meeting_id" TEXT DEFAULT '',
483+ "council_id" TEXT DEFAULT '',
484+ "body" TEXT DEFAULT ''
485+ );` ,
486+ columns : {
487+ meeting_id : { type : 'TEXT' , defaultValue : '' } ,
488+ council_id : { type : 'TEXT' , defaultValue : '' } ,
489+ body : { type : 'TEXT' , defaultValue : '' } ,
490+ } ,
491+ indexes : [ ] ,
492+ triggers : [ ] ,
493+ foreignKeys : [ ] ,
494+ dependencies : [ ] ,
495+ version : '1.0.0' ,
496+ } ,
497+ } ,
498+ packageName ,
499+ ) ;
500+
501+ const after = ObjectRegistry . findClass ( registrationKey ) ;
502+ expect ( after ) . toBeDefined ( ) ;
503+ expect ( after ?. schema ?. tableName ) . toBe ( 'fixture_runtime_contents' ) ;
504+ expect ( after ?. qualifiedName ) . toBe ( registrationKey ) ;
505+ expect ( after ?. fields . has ( 'meetingId' ) ) . toBe ( true ) ;
506+ expect ( after ?. fields . has ( 'councilId' ) ) . toBe ( true ) ;
507+ expect ( after ?. fields . has ( 'body' ) ) . toBe ( true ) ;
508+
509+ const definitions = ObjectRegistry . getAllSchemasAsDefinitions ( ) ;
510+ expect ( definitions . fixture_runtime_contents ) . toBeDefined ( ) ;
511+ expect (
512+ definitions . fixture_runtime_contents . columns . meeting_id ,
513+ ) . toBeDefined ( ) ;
514+ expect (
515+ definitions . fixture_runtime_contents . columns . council_id ,
516+ ) . toBeDefined ( ) ;
517+ expect ( definitions . fixture_runtime_contents . columns . body ) . toBeDefined ( ) ;
518+ expect ( definitions . fixture_runtime_meeting_recaps ) . toBeUndefined ( ) ;
519+ } ) ;
520+
409521 it ( 'hydrates STI sibling field types before save so missing booleans do not serialize as empty strings' , async ( ) => {
410522 const packageName = '@happyvertical/smrt-runtime-fixture-events' ;
411523
0 commit comments