@@ -10,6 +10,39 @@ export class BaseType {
1010 this . systemPath = systemPath ;
1111 }
1212
13+ // ---- Instance extra deps (per derived spec id) ----
14+ _instanceExtraDeps = new Map ( ) ;
15+
16+ /** @param {string } specId @param {string[] } deps */
17+ setInstanceDeps ( specId , deps ) {
18+ const arr = Array . isArray ( deps ) ? deps . filter ( Boolean ) : [ ] ;
19+ this . _instanceExtraDeps . set ( specId , arr ) ;
20+ }
21+
22+ /** @param {string } specId */
23+ clearInstanceDeps ( specId ) {
24+ this . _instanceExtraDeps . delete ( specId ) ;
25+ }
26+
27+ /** @param {string } specId @returns {string[] } */
28+ getInstanceDeps ( specId ) {
29+ return this . _instanceExtraDeps . get ( specId ) ?? [ ] ;
30+ }
31+
32+ /**
33+ * Merge instance deps into the provided specs.
34+ * Call this at the end of getDerivedFlowSpecs() in subclasses.
35+ *
36+ * @param {Array<{id:string,deps:string[],mods:string[],compute:Function}> } specs
37+ */
38+ _mergeInstanceDeps ( specs ) {
39+ return ( specs ?? [ ] ) . map ( s => {
40+ const extra = this . getInstanceDeps ( s . id ) ;
41+ if ( ! extra . length ) return s ;
42+ return { ...s , deps : [ ...( s . deps ?? [ ] ) , ...extra ] } ;
43+ } ) ;
44+ }
45+
1346 _get ( relPath , fallback = undefined ) {
1447 return (
1548 foundry . utils . getProperty ( this . actor , `${ this . systemPath } .${ relPath } ` ) ?? fallback
@@ -44,7 +77,12 @@ export class BaseType {
4477 throw new Error ( `${ this . constructor . name } .applyDerived() not implemented.` ) ;
4578 }
4679
47- /** Structure defaults used for migrations */
80+ /** Common fields shared by all typed nodes */
81+ static commonDefaults ( ) {
82+ return { key : '' } ;
83+ }
84+
85+ /** Type-specific defaults used for migrations */
4886 static defaults ( ) {
4987 return { } ;
5088 }
@@ -63,13 +101,18 @@ export class BaseType {
63101
64102 const normalized = this . normalizeInflateInput ( combined ) ;
65103
66- const merged = foundry . utils . mergeObject ( this . defaults ( ) , normalized , {
104+ const base = foundry . utils . mergeObject ( BaseType . commonDefaults ( ) , this . defaults ( ) , {
105+ inplace : false ,
106+ insertKeys : true ,
107+ insertValues : true
108+ } ) ;
109+
110+ const merged = foundry . utils . mergeObject ( base , normalized , {
67111 inplace : false ,
68112 insertKeys : true ,
69113 insertValues : true
70114 } ) ;
71115
72- // IMPORTANT: remove legacy keys like "value" or "__type"
73116 return this . pruneToDefaults ( merged ) ;
74117 }
75118
@@ -82,7 +125,11 @@ export class BaseType {
82125 static pruneToDefaults ( obj ) {
83126 if ( ! obj || typeof obj !== 'object' ) return obj ;
84127
85- const allowed = new Set ( Object . keys ( this . defaults ( ) ) ) ;
128+ const allowed = new Set ( [
129+ ...Object . keys ( BaseType . commonDefaults ( ) ) ,
130+ ...Object . keys ( this . defaults ( ) )
131+ ] ) ;
132+
86133 for ( const k of Object . keys ( obj ) ) {
87134 if ( ! allowed . has ( k ) ) delete obj [ k ] ;
88135 }
@@ -96,11 +143,102 @@ export class BaseType {
96143 * @returns {object }
97144 */
98145 static sanitize ( data ) {
99- const merged = foundry . utils . mergeObject ( this . defaults ( ) , data ?? { } , {
146+ const base = foundry . utils . mergeObject ( BaseType . commonDefaults ( ) , this . defaults ( ) , {
100147 inplace : false ,
101148 insertKeys : true ,
102149 insertValues : true
103150 } ) ;
151+
152+ const merged = foundry . utils . mergeObject ( base , data ?? { } , {
153+ inplace : false ,
154+ insertKeys : true ,
155+ insertValues : true
156+ } ) ;
157+
104158 return this . pruneToDefaults ( merged ) ;
105159 }
160+
161+ getByKey ( type , key ) {
162+ return this . actor . typedRepo ?. get ( type ) ?. get ( key ) ?? null ;
163+ }
164+
165+ getValueByKey ( type , key , relPath ) {
166+ const node = this . getByKey ( type , key ) ;
167+ if ( ! node ) return undefined ;
168+ return foundry . utils . getProperty ( this . actor , `${ node . systemPath } .${ relPath } ` ) ;
169+ }
170+
171+ get key ( ) {
172+ // If key is not stored, default to the last segment of the system path
173+ const stored = this . _get ( 'key' , '' ) ;
174+ if ( stored ) return stored ;
175+
176+ const parts = String ( this . systemPath ?? '' ) . split ( '.' ) ;
177+ return parts . length ? parts [ parts . length - 1 ] : '' ;
178+ }
179+
180+ set key ( v ) {
181+ this . _set ( 'key' , String ( v ?? '' ) ) ;
182+ }
183+
184+ /**
185+ * Persist the default key if it's missing or empty.
186+ * This writes into the actor's system data (in-memory). Persisting to the document
187+ * should be done by the caller (e.g. ABFActor.updateSource in prepareDerivedData).
188+ *
189+ * @returns {string } The key after ensuring.
190+ */
191+ ensureDefaultKey ( ) {
192+ const current = this . _get ( 'key' , undefined ) ;
193+ if ( current == null || current === '' ) {
194+ const derived = BaseType . deriveKeyFromSystemPath ( this . systemPath ) ;
195+ if ( derived ) this . _set ( 'key' , derived ) ;
196+ return derived ;
197+ }
198+ return String ( current ) ;
199+ }
200+
201+ /**
202+ * Static helper to persist a default key on raw system data, without needing an instance.
203+ * Useful if you prefer to patch this.system directly in ABFActor.
204+ *
205+ * @param {object } systemRoot Usually actor.system
206+ * @param {string } systemPath Path to the node (e.g. "system.characteristics.primaries.agility")
207+ * @returns {boolean } True if it changed something
208+ */
209+ static ensureDefaultKeyOnNodeData ( systemRoot , systemPath ) {
210+ if ( ! systemRoot || typeof systemRoot !== 'object' ) return false ;
211+ if ( ! systemPath || typeof systemPath !== 'string' ) return false ;
212+
213+ const keyPath = `${ systemPath } .key` ;
214+ const current = foundry . utils . getProperty ( systemRoot , keyPath ) ;
215+
216+ if ( current == null || current === '' ) {
217+ const derived = BaseType . deriveKeyFromSystemPath ( systemPath ) ;
218+ if ( ! derived ) return false ;
219+ foundry . utils . setProperty ( systemRoot , keyPath , derived ) ;
220+ return true ;
221+ }
222+
223+ return false ;
224+ }
225+
226+ static deriveKeyFromSystemPath ( systemPath ) {
227+ const parts = String ( systemPath ?? '' ) . split ( '.' ) ;
228+ return parts . length ? parts [ parts . length - 1 ] : '' ;
229+ }
230+
231+ static editorConfig ( ) {
232+ return {
233+ readonly : [ 'key' ] ,
234+ hidden : [ ] ,
235+ labels : { key : 'Key' } ,
236+ order : [ ] ,
237+ overrides : {
238+ // Example:
239+ // key: { readonly: false },
240+ // 'someField.value': { hidden: true }
241+ }
242+ } ;
243+ }
106244}
0 commit comments