@hology/core 0.0.204 → 0.0.206

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/config/project-config.d.ts +4 -0
  2. package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +28 -1
  3. package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
  4. package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
  5. package/dist/gameplay/index.d.ts +1 -0
  6. package/dist/gameplay/index.js +1 -1
  7. package/dist/gameplay/initiate.d.ts +4 -0
  8. package/dist/gameplay/services/asset-loader.d.ts +3 -0
  9. package/dist/gameplay/services/asset-loader.js +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +1 -1
  12. package/dist/rendering.d.ts +2 -0
  13. package/dist/rendering.js +1 -1
  14. package/dist/scene/asset-resource-loader.js +1 -1
  15. package/dist/scene/custom-param-deserialize.d.ts +1 -0
  16. package/dist/scene/custom-param-deserialize.js +1 -1
  17. package/dist/scene/custom-param-runtime-types.d.ts +3 -1
  18. package/dist/scene/custom-param-runtime-types.js +1 -1
  19. package/dist/scene/landscape/landscape.js +1 -1
  20. package/dist/scene/materializer.d.ts +16 -3
  21. package/dist/scene/materializer.js +1 -1
  22. package/dist/scene/model.d.ts +18 -2
  23. package/dist/scene/model.js +1 -1
  24. package/dist/scene/objects/data-asset.d.ts +10 -0
  25. package/dist/scene/objects/data-asset.js +4 -0
  26. package/dist/shader/parameter.d.ts +64 -3
  27. package/dist/shader/parameter.js +1 -1
  28. package/dist/test/data-asset-definition.test.d.ts +2 -0
  29. package/dist/test/data-asset-definition.test.js +4 -0
  30. package/dist/test/parameter-definition.test.d.ts +2 -0
  31. package/dist/test/parameter-definition.test.js +4 -0
  32. package/dist/test/prefab-instance-params.test.d.ts +2 -0
  33. package/dist/test/prefab-instance-params.test.js +4 -0
  34. package/dist/utils/collections.d.ts +1 -0
  35. package/dist/utils/collections.js +1 -1
  36. package/dist/utils/type.d.ts +1 -1
  37. package/package.json +1 -1
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
- import"reflect-metadata";import{reflect as t,decorateProperty as e}from"@plumier/reflect";import{ArrayMap as r}from"../utils/collections.js";Symbol("format");export function shaderParameterUniformName(t){return`pu_${t}`}class a{constructor(t){this.options=t,this.isParameterDecorator=!0}}new Map;const o=new r;export function Parameter(t){return function(r,n,p){if(null!=r){o.push(r.constructor,{name:n,options:t??{}});try{e(new a(t))(r,n,p)}catch(t){console.warn("Failed to decorate method ",r,n,p)}}}}export function extractShaderParameters(t){if(null==t)return[];const e=o.get(t).map(({name:e,options:r})=>{const a=Reflect.getMetadata("design:type",t.prototype,e),o=r.type??a,n=Reflect.getMetadata("prefab:type",t.prototype,e);return r.prefabOf??(r.prefabOf=n),r.array=a===Array,{name:e,type:o,options:r}});return[...extractShaderParameters(Object.getPrototypeOf(t)).filter(t=>e.every(e=>e.name!==t.name)),...e]}export function ParameterCategory(t){}/*
1
+ import"reflect-metadata";import{reflect as e,decorateProperty as t}from"@plumier/reflect";import{ArrayMap as n}from"../utils/collections.js";Symbol("format");const i=Symbol("parameterDefinition"),r=Symbol("dataAssetDefinition");export function shaderParameterUniformName(e){return`pu_${e}`}class a{constructor(e){this.options=e,this.isParameterDecorator=!0}}new Map;const o=new n,s=new Map,f=new Map,l=new WeakMap,p=new Map,u=new Map,c=new WeakMap;export function ParameterDefinition(e,t={}){return function(n){registerParameterDefinition(e??n.name,n,t)}}export function DataAssetDefinition(e,t={}){return function(n){registerDataAssetDefinition(e??n.name,n,t)}}export function registerParameterDefinition(e,t,n={}){if(null==e||""===e.trim())throw new Error("Parameter definition id must be a non-empty string");const r=s.get(e);null!=r&&r!==t&&console.warn(`Replacing parameter definition "${e}"`,{existing:r,type:t}),s.set(e,t),f.set(e,{id:e,type:t,...!0===n.abstract?{abstract:!0}:{}}),l.set(t,e),Reflect.defineMetadata(i,f.get(e),t)}export function getParameterDefinitionType(e){return s.get(e)}export function registerDataAssetDefinition(e,t,n={}){if(null==e||""===e.trim())throw new Error("Data asset definition id must be a non-empty string");const i=p.get(e);null!=i&&i!==t&&console.warn(`Replacing data asset definition "${e}"`,{existing:i,type:t}),p.set(e,t),u.set(e,{id:e,type:t,...!0===n.abstract?{abstract:!0}:{}}),c.set(t,e),Reflect.defineMetadata(r,u.get(e),t)}export function getDataAssetDefinitionType(e){return p.get(e)}export function getAssignableDataAssetDefinitions(e,t={}){if(null==e)return[];const n=getDataAssetDefinitionId(e);return Array.from(p.entries()).filter(([,t])=>isParameterDefinitionAssignableTo(t,e)).filter(([e])=>!0===t.includeAbstract||!isDataAssetDefinitionAbstract(e)).map(([e,t])=>({id:e,type:t,label:getParameterDefinitionDisplayName(t)})).sort((e,t)=>e.id===n?-1:t.id===n?1:e.label.localeCompare(t.label))}export function getDataAssetDefinitions(e={}){return Array.from(p.entries()).filter(([t])=>!0===e.includeAbstract||!isDataAssetDefinitionAbstract(t)).map(([e,t])=>({id:e,type:t,label:getParameterDefinitionDisplayName(t)})).sort((e,t)=>e.label.localeCompare(t.label))}export function getAssignableParameterDefinitions(e,t={}){if(null==e)return[];const n=getParameterDefinitionId(e);return Array.from(s.entries()).filter(([,t])=>isParameterDefinitionAssignableTo(t,e)).filter(([e])=>!0===t.includeAbstract||!isParameterDefinitionAbstract(e)).map(([e,t])=>({id:e,type:t,label:getParameterDefinitionDisplayName(t)})).sort((e,t)=>e.id===n?-1:t.id===n?1:e.label.localeCompare(t.label))}export function isParameterDefinitionAssignableTo(e,t){return null!=e&&null!=t&&(e===t||e.prototype instanceof t)}export function isParameterDefinitionStructAssignableTo(e,t){return null!=e&&null!=t&&(e===t||isParameterDefinitionAssignableTo(getParameterDefinitionType(e),getParameterDefinitionType(t)))}export function isParameterDefinitionAbstract(e){if(null==e)return!1;const t="string"==typeof e?f.get(e):getParameterDefinitionInfo(e);return!0===t?.abstract}export function isDataAssetDefinitionAbstract(e){if(null==e)return!1;const t="string"==typeof e?u.get(e):getDataAssetDefinitionInfo(e);return!0===t?.abstract}export function getParameterDefinitionDisplayName(e){return("string"==typeof e?e:e.name).replace(/Definition$/,"").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g,"$1 $2").replace(/[_-]+/g," ").trim().split(/\s+/).filter(e=>e.length>0).map(e=>e.length<=2&&e===e.toUpperCase()?e:e.charAt(0).toUpperCase()+e.slice(1)).join(" ")}export function getParameterDefinitionId(e){if(null!=e)return l.get(e)??Reflect.getMetadata(i,e)?.id}export function getDataAssetDefinitionId(e){if(null!=e)return c.get(e)??Reflect.getMetadata(r,e)?.id}export function getParameterDefinitionInfo(e){if(null==e)return;const t=getParameterDefinitionId(e);return null!=t?f.get(t)??Reflect.getMetadata(i,e)??{id:t,type:e}:void 0}export function resolveParameterType(e){if(null!=e)return"function"==typeof e&&null==e.prototype?e():e}function m(e,t){const{enum:n,...i}=e,r=n??t;if(null==r||i.options?.length>0)return i;const a=function(e){return Object.entries(e).filter(([e])=>!function(e){return""!==e.trim()&&String(Number(e))===e}(e)).map(([e,t])=>({name:getParameterDefinitionDisplayName(e),value:t}))}(r);return{...i,type:i.type??g(a),defaultValue:void 0!==i.defaultValue?i.defaultValue:a[0]?.value,options:a}}function g(e){const t=new Set(e.map(e=>typeof e.value));if(1===t.size)return t.has("number")?Number:t.has("string")?String:void 0}export function getDataAssetDefinitionInfo(e){if(null==e)return;const t=getDataAssetDefinitionId(e);return null!=t?u.get(t)??Reflect.getMetadata(r,e)??{id:t,type:e}:void 0}export function Parameter(e){return function(n,i,r){if(null!=n){o.push(n.constructor,{name:i,options:e??{}});try{t(new a(e))(n,i,r)}catch(e){console.warn("Failed to decorate method ",n,i,r)}}}}export function extractShaderParameters(e){if(null==e)return[];const t=o.get(e).map(({name:t,options:n})=>{const i=m(n,Reflect.getMetadata("parameter:enum",e.prototype,t)),r=Reflect.getMetadata("design:type",e.prototype,t),a=resolveParameterType(i.type)??r,o=Reflect.getMetadata("prefab:type",e.prototype,t);return i.prefabOf??(i.prefabOf=o),i.array=!0===i.array||r===Array,{name:t,type:a,options:i,definition:getParameterDefinitionInfo(a)}});return[...extractShaderParameters(Object.getPrototypeOf(e)).filter(e=>t.every(t=>t.name!==e.name)),...t]}export function ParameterCategory(e){}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data-asset-definition.test.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{__decorate as e,__metadata as t}from"tslib";import{describe as a,expect as s,it as i,vi as o}from"vitest";o.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,t)=>(t in e||(e[t]=("string"!=typeof t||!t.startsWith("is"))&&o.fn()),e[t]),set:(e,t,a)=>(e[t]=a,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e}),"undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}})}),o.mock("../gameplay/actors/builtin/index.js",()=>({default:{}})),o.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),o.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),o.mock("../rendering.js",()=>({RenderingView:class{},setRenderingPaused:o.fn()}));import{prepareClassParameters as n,prepareCustomParamsFromType as r,materializeDataAssetRef as d}from"../scene/materializer.js";import{SerializedParamType as m}from"../scene/model.js";import{DataAssetRef as l}from"../scene/objects/data-asset.js";import{DataAssetDefinition as p,getAssignableDataAssetDefinitions as c,getDataAssetDefinitionId as f,getDataAssetDefinitions as u,getDataAssetDefinitionType as y,isDataAssetDefinitionAbstract as v,Parameter as g,ParameterDefinition as B}from"../shader/parameter.js";let w=class{constructor(){this.amount=1}};e([g({type:Number}),t("design:type",Object)],w.prototype,"amount",void 0),w=e([B("test.data.statModifier")],w);let b=class{constructor(){this.displayName="Item"}};e([g({type:String}),t("design:type",Object)],b.prototype,"displayName",void 0),b=e([p("test.data.item",{abstract:!0})],b);let A=class extends b{constructor(){super(...arguments),this.damage=5,this.modifier=new w}};e([g({type:Number}),t("design:type",Object)],A.prototype,"damage",void 0),e([g({type:()=>w}),t("design:type",Object)],A.prototype,"modifier",void 0),A=e([p("test.data.weapon")],A);let j=class extends b{constructor(){super(...arguments),this.charges=1}};e([g({type:Number}),t("design:type",Object)],j.prototype,"charges",void 0),j=e([p("test.data.consumable")],j);class h{constructor(){this.item=null,this.items=[]}}function N(e){const t=new Map(e.map(e=>[e.id,e]));return{getAsset:o.fn(async e=>t.get(e)??null),getAssets:o.fn(async()=>e)}}e([g({type:()=>l,dataAssetOf:()=>b}),t("design:type",l)],h.prototype,"item",void 0),e([g({type:()=>l,dataAssetOf:()=>b,array:!0}),t("design:type",Array)],h.prototype,"items",void 0),a("data asset definitions",()=>{i("registers data asset definitions separately from parameter definitions",()=>{s(y("test.data.item")).toBe(b),s(f(A)).toBe("test.data.weapon"),s(v(b)).toBe(!0),s(v("test.data.item")).toBe(!0);const e=c(b);s(e.map(e=>e.id)).toEqual(["test.data.consumable","test.data.weapon"]);const t=u({includeAbstract:!0});s(t.some(e=>"test.data.item"===e.id)).toBe(!0),s(t.some(e=>"test.data.statModifier"===e.id)).toBe(!1),s(y("test.data.statModifier")).toBeUndefined()}),i("serializes data asset references as asset ids, including arrays",()=>{const e=r(h,{}),t=e.item,a=e.items;s(t.type).toBe(m.DataAsset),s(t.value).toBeNull(),s(a.type).toBe(m.Array),s(a.element).toBe(m.DataAsset),s(a.value).toEqual([])}),i("materializes a data asset reference into a wrapper with nested inline structs",async()=>{const e={id:"asset.weapon",name:"Bronze Sword",type:"data",dataAsset:{definition:"test.data.weapon",params:{displayName:{type:m.String,value:"Bronze Sword"},damage:{type:m.Number,value:12},modifier:{type:m.Struct,struct:"test.data.statModifier",value:{amount:{type:m.Number,value:3}}}}}},t=N([e]),a=await n({item:{type:m.DataAsset,value:e.id},items:{type:m.Array,element:m.DataAsset,value:[e.id]}},h,t,{}),i=a.item;s(i).toBeInstanceOf(l),s(i.id).toBe(e.id),s(i.name).toBe("Bronze Sword"),s(i.asset).toBe(e),s(i.definitionId).toBe("test.data.weapon"),s(i.value).toBeInstanceOf(A),s(i.value.displayName).toBe("Bronze Sword"),s(i.value.damage).toBe(12),s(i.value.modifier).toBeInstanceOf(w),s(i.value.modifier.amount).toBe(3),s(a.items).toHaveLength(1),s(a.items[0].value.damage).toBe(12)}),i("resolves missing and invalid data asset references safely",async()=>{const e=o.spyOn(console,"warn").mockImplementation(()=>{}),t=N([{id:"asset.notData",name:"Texture pretending",type:"texture"},{id:"asset.unknownDefinition",name:"Unknown Definition",type:"data",dataAsset:{definition:"test.data.unknown",params:{}}},{id:"asset.abstractDefinition",name:"Abstract Definition",type:"data",dataAsset:{definition:"test.data.item",params:{}}},{id:"asset.invalidParams",name:"Invalid Params",type:"data",dataAsset:{definition:"test.data.weapon",params:"bad"}}]);await s(d("asset.missing",t,{})).resolves.toBeNull(),await s(d("asset.notData",t,{})).resolves.toBeNull(),await s(d("asset.unknownDefinition",t,{})).resolves.toBeNull(),await s(d("asset.abstractDefinition",t,{})).resolves.toBeNull(),await s(d("asset.invalidParams",t,{})).resolves.toBeNull();const a=await n({item:{type:m.DataAsset,value:"asset.missing"}},h,t,{});s(a.item).toBeUndefined(),s(e).toHaveBeenCalled(),e.mockRestore()})});/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parameter-definition.test.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{__decorate as t,__metadata as e}from"tslib";import{describe as a,expect as s,it as o,vi as i}from"vitest";i.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const t=new Proxy({},{get:(t,e)=>(e in t||(t[e]=("string"!=typeof e||!e.startsWith("is"))&&i.fn()),t[e]),set:(t,e,a)=>(t[e]=a,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>t}),"undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}})}),i.mock("../gameplay/actors/builtin/index.js",()=>({default:{}})),i.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),i.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),i.mock("../rendering.js",()=>({RenderingView:class{},setRenderingPaused:i.fn()}));import{convertConfiguredParamValueToRuntimeType as r}from"../scene/custom-param-runtime-types.js";import{prepareClassParameters as n,prepareCustomParamsFromType as c}from"../scene/materializer.js";import{SerializedParamType as l}from"../scene/model.js";import{extractShaderParameters as u,getAssignableParameterDefinitions as p,isParameterDefinitionAbstract as m,Parameter as y,ParameterDefinition as d}from"../shader/parameter.js";let b=class{constructor(){this.damage=10,this.label="Slash"}kind(){return"attack"}};t([y({type:Number}),e("design:type",Object)],b.prototype,"damage",void 0),t([y({type:String}),e("design:type",Object)],b.prototype,"label",void 0),b=t([d("test.attack")],b);let g=class extends b{constructor(){super(...arguments),this.manaCost=5}kind(){return"magic"}};t([y({type:Number}),e("design:type",Object)],g.prototype,"manaCost",void 0),g=t([d("test.magicAttack")],g);let v=class{constructor(){this.amount=20}};t([y({type:Number}),e("design:type",Object)],v.prototype,"amount",void 0),v=t([d("test.heal")],v);class f{constructor(){this.attacks=[]}}t([y({type:()=>b,array:!0}),e("design:type",Array)],f.prototype,"attacks",void 0);class k{constructor(){this.primaryAttack=new b}}t([y({type:()=>b}),e("design:type",Object)],k.prototype,"primaryAttack",void 0);let B=class{constructor(){this.cooldown=1}};t([y({type:Number}),e("design:type",Object)],B.prototype,"cooldown",void 0),B=t([d("test.ability",{abstract:!0})],B);let h=class extends B{constructor(){super(...arguments),this.manaCost=6}kind(){return"magicAbility"}};t([y({type:Number}),e("design:type",Object)],h.prototype,"manaCost",void 0),h=t([d("test.magicAbility")],h);class A{constructor(){this.ability=void 0}}t([y({type:()=>B}),e("design:type",B)],A.prototype,"ability",void 0);class S{constructor(){this.abilities=[]}}t([y({type:()=>B,array:!0}),e("design:type",Array)],S.prototype,"abilities",void 0);class j{constructor(){this.value=void 0}}var w,O;t([y({type:String}),e("design:type",Object)],j.prototype,"value",void 0),function(t){t[t.good=0]="good",t[t.bad=1]="bad"}(w||(w={})),function(t){t.good="good",t.bad="bad"}(O||(O={}));class N{constructor(){this.faction=w.bad,this.stringFaction=O.good}}t([y({enum:w}),e("design:type",Object)],N.prototype,"faction",void 0),t([y({enum:O}),e("design:type",Object)],N.prototype,"stringFaction",void 0);class C{}t([y({enum:O}),e("design:type",String)],C.prototype,"stringFaction",void 0);class z{constructor(){this.faction=w.good}}t([y(),e("design:type",Number)],z.prototype,"faction",void 0),Reflect.defineMetadata("parameter:enum",w,z.prototype,"faction"),a("parameter definitions",()=>{o("generates editor options from enum decorator options",()=>{const t=u(N),e=t.find(t=>"faction"===t.name),a=t.find(t=>"stringFaction"===t.name);s(e?.type).toBe(Number),s(e?.options.options).toEqual([{name:"Good",value:w.good},{name:"Bad",value:w.bad}]),s(a?.type).toBe(String),s(a?.options.options).toEqual([{name:"Good",value:O.good},{name:"Bad",value:O.bad}])}),o("prepares enum params as number or string custom values",()=>{const t=c(N,{});s(t.faction).toMatchObject({type:l.Number,value:w.bad}),s(t.stringFaction).toMatchObject({type:l.String,value:O.good})}),o("uses the first enum option as the default when no initializer is set",()=>{const t=c(C,{});s(t.stringFaction).toMatchObject({type:l.String,value:O.good})}),o("uses reflected enum metadata for direct enum parameter annotations",()=>{const[t]=u(z),e=c(z,{});s(t.type).toBe(Number),s(t.options.options).toEqual([{name:"Good",value:w.good},{name:"Bad",value:w.bad}]),s(e.faction).toMatchObject({type:l.Number,value:w.good})}),o("lists assignable parameter definitions with display labels",()=>{const t=p(b);s(t.map(t=>t.id)).toEqual(["test.attack","test.magicAttack"]),s(t.map(t=>t.label)).toEqual(["Attack","Magic Attack"]),s(t.some(t=>"test.heal"===t.id)).toBe(!1)}),o("lists concrete assignable definitions for abstract bases",()=>{const t=p(B);s(m(B)).toBe(!0),s(m("test.ability")).toBe(!0),s(t.map(t=>t.id)).toEqual(["test.magicAbility"]),s(t.map(t=>t.label)).toEqual(["Magic Ability"])}),o("serializes arrays of nested parameter definitions with stable definition ids",()=>{const t=c(f,{}).attacks;s(t.type).toBe(l.Array),s(t.element).toBe(l.Struct),s(t.elementStruct).toBe("test.attack"),s(t.value).toEqual([])}),o("serializes nested parameter definition defaults",()=>{const t=c(k,{}).primaryAttack;s(t.type).toBe(l.Struct),s(t.struct).toBe("test.attack"),s(t.value.damage.value).toBe(10),s(t.value.label.value).toBe("Slash")}),o("uses an empty string for string parameters without explicit defaults",()=>{const t=c(j,{}).value;s(t.type).toBe(l.String),s(t.value).toBe("")}),o("serializes nested subclass defaults with concrete definition ids",()=>{class a{constructor(){this.primaryAttack=new g}}t([y({type:()=>b}),e("design:type",Object)],a.prototype,"primaryAttack",void 0);const o=c(a,{}).primaryAttack;s(o.type).toBe(l.Struct),s(o.struct).toBe("test.magicAttack"),s(o.value.damage.value).toBe(10),s(o.value.label.value).toBe("Slash"),s(o.value.manaCost.value).toBe(5)}),o("serializes abstract parameter definitions as unselected structs",()=>{const t=c(A,{}).ability;s(t.type).toBe(l.Struct),s(t.struct).toBe("test.ability"),s(t.value).toEqual({})}),o("serializes abstract base defaults with concrete subclass ids",()=>{class a{constructor(){this.ability=new h}}t([y({type:()=>B}),e("design:type",Object)],a.prototype,"ability",void 0);const o=c(a,{}).ability;s(o.type).toBe(l.Struct),s(o.struct).toBe("test.magicAbility"),s(o.value.cooldown.value).toBe(1),s(o.value.manaCost.value).toBe(6)}),o("preserves previously selected concrete definition ids when preparing params",()=>{const t=c(k,{primaryAttack:{type:l.Struct,struct:"test.magicAttack",value:{damage:{type:l.Number,value:22},manaCost:{type:l.Number,value:7}}}}).primaryAttack;s(t.struct).toBe("test.magicAttack"),s(t.value.damage.value).toBe(22),s(t.value.manaCost.value).toBe(7)}),o("materializes nested parameter definitions as class instances",async()=>{const t=await n({attacks:{type:l.Array,element:l.Struct,elementStruct:"test.attack",value:[{damage:{type:l.Number,value:25},label:{type:l.String,value:"Heavy Slash"}}]}},f,{},{});s(t.attacks).toHaveLength(1),s(t.attacks[0]).toBeInstanceOf(b),s(t.attacks[0].damage).toBe(25),s(t.attacks[0].label).toBe("Heavy Slash")}),o("materializes a selected subclass for a non-array base parameter",async()=>{const t=await n({primaryAttack:{type:l.Struct,struct:"test.magicAttack",value:{damage:{type:l.Number,value:30},label:{type:l.String,value:"Arcane Slash"},manaCost:{type:l.Number,value:12}}}},k,{},{});s(t.primaryAttack).toBeInstanceOf(g),s(t.primaryAttack.kind()).toBe("magic"),s(t.primaryAttack.damage).toBe(30),s(t.primaryAttack.manaCost).toBe(12)}),o("materializes a selected subclass for an abstract base parameter",async()=>{const t=await n({ability:{type:l.Struct,struct:"test.magicAbility",value:{cooldown:{type:l.Number,value:2},manaCost:{type:l.Number,value:9}}}},A,{},{});s(t.ability).toBeInstanceOf(h),s(t.ability.cooldown).toBe(2),s(t.ability.manaCost).toBe(9),s(t.ability.kind()).toBe("magicAbility")}),o("does not instantiate abstract parameter definitions",async()=>{const t=i.spyOn(console,"warn").mockImplementation(()=>{}),e=await n({ability:{type:l.Struct,struct:"test.ability",value:{}}},A,{},{});s(e.ability).toBeUndefined(),s(t).toHaveBeenCalledWith('Can not instantiate abstract parameter definition "test.ability"'),t.mockRestore()}),o("materializes compact and tagged struct array elements",async()=>{const t=await n({attacks:{type:l.Array,element:l.Struct,elementStruct:"test.attack",value:[{damage:{type:l.Number,value:15},label:{type:l.String,value:"Quick Slash"}},{struct:"test.magicAttack",value:{damage:{type:l.Number,value:45},label:{type:l.String,value:"Meteor"},manaCost:{type:l.Number,value:18}}}]}},f,{},{});s(t.attacks).toHaveLength(2),s(t.attacks[0]).toBeInstanceOf(b),s(t.attacks[0]).not.toBeInstanceOf(g),s(t.attacks[1]).toBeInstanceOf(g),s(t.attacks[1].damage).toBe(45),s(t.attacks[1].manaCost).toBe(18)}),o("materializes tagged struct array elements for abstract bases",async()=>{const t=await n({abilities:{type:l.Array,element:l.Struct,elementStruct:"test.ability",value:[{struct:"test.magicAbility",value:{cooldown:{type:l.Number,value:3},manaCost:{type:l.Number,value:11}}}]}},S,{},{});s(t.abilities).toHaveLength(1),s(t.abilities[0]).toBeInstanceOf(h),s(t.abilities[0].cooldown).toBe(3),s(t.abilities[0].manaCost).toBe(11)}),o("preserves compatible selected subclass ids during runtime normalization",()=>{const t=r({type:l.Struct,struct:"test.magicAttack",value:{manaCost:{type:l.Number,value:9}}},{type:l.Struct,struct:"test.attack"});s(t.struct).toBe("test.magicAttack")})});/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=prefab-instance-params.test.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{__decorate as e,__metadata as a}from"tslib";import{describe as t,expect as n,it as r,vi as i}from"vitest";import{Scene as o}from"three";import{Subject as s}from"rxjs";i.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,a)=>(a in e||(e[a]=("string"!=typeof a||!a.startsWith("is"))&&i.fn()),e[a]),set:(e,a,t)=>(e[a]=t,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e}),"undefined"==typeof AudioBuffer&&Object.defineProperty(globalThis,"AudioBuffer",{configurable:!0,value:class{}})}),i.mock("../gameplay/actors/builtin/index.js",()=>({default:{}})),i.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),i.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),i.mock("../rendering.js",()=>({RenderingView:class{},setRenderingPaused:i.fn()}));import{BaseActor as c}from"../gameplay/actors/actor.js";import{ActorComponent as m}from"../gameplay/actors/component.js";import{Parameter as p}from"../shader/parameter.js";import{SceneMaterializer as f}from"../scene/materializer.js";import{SerializedParamType as l}from"../scene/model.js";class d extends m{constructor(){super(...arguments),this.animationPreset="mixamo"}}e([p(),a("design:type",Object)],d.prototype,"animationPreset",void 0);class u extends c{constructor(){super(...arguments),this.animation=new d,this.animationPreset="mixamo"}}async function b(e){const a=(t="prefab-asset",n=[y({id:"prefab-actor",type:"actor",actor:{type:"PrefabParamActor",params:{animationPreset:{type:l.String,value:e.prefabActorValue}},innerParams:null==e.prefabComponentValue?[]:[{path:["animation"],params:{animationPreset:{type:l.String,value:e.prefabComponentValue}}}],components:[]}})],{id:t,name:t,type:"prefab",prefab:{objects:n,mainActorId:"prefab-actor"}});var t,n;const r=y({id:"prefab-instance",type:"prefab",assetId:a.id,prefab:{mainActorId:"prefab-actor",params:null==e.instanceParam?{}:{animationPreset:e.instanceParam},innerParams:null==e.instanceInnerParam?[]:[{path:["animation"],params:{animationPreset:e.instanceInnerParam}}]}}),{materializer:c}=function(e,a){const t={getObjects:()=>e,onCreate:()=>{},onUpdate:()=>{},onRemove:()=>{}},n={onCreate:new s,onDelete:new s,onUpdate:new s,getAsset:i.fn(async e=>a.get(e)??null),getAssets:i.fn(async()=>Array.from(a.values()))},r={create:i.fn(async(e,a)=>{const t=new e;return null!=a&&t.position.copy(a),t}),initActor:i.fn(async e=>{e.__isInitialised=!0})},c={renderer:{shadowMap:{needsUpdate:!1}}},m=new f(new o,t,n,{},c,[],[{name:"PrefabParamActor",type:u}],r);return{materializer:m}}([r],new Map([[a.id,a]]));return await c.materialize(r),await c.initActorsPostInit(),c.materializedActors.get("prefab-instance/prefab-actor")}function y(e){return{name:e.id,position:[0,0,0],rotation:[0,0,0,"XYZ"],scale:[1,1,1],...e}}e([p(),a("design:type",Object)],u.prototype,"animationPreset",void 0),t("prefab instance actor params",()=>{r("does not let generated prefab instance defaults override the prefab actor value",async()=>{const e=await b({prefabActorValue:"basic",instanceParam:{type:l.String,value:"mixamo"}});n(e.animationPreset).toBe("basic")}),r("applies prefab instance params when the override is explicit",async()=>{const e=await b({prefabActorValue:"basic",instanceParam:{type:l.String,value:"mixamo",override:!0}});n(e.animationPreset).toBe("mixamo")}),r("does not let generated prefab instance component defaults override prefab component values",async()=>{const e=await b({prefabActorValue:"basic",prefabComponentValue:"basic",instanceInnerParam:{type:l.String,value:"mixamo"}});n(e.animation.animationPreset).toBe("basic")}),r("applies prefab instance component params when the override is explicit",async()=>{const e=await b({prefabActorValue:"basic",prefabComponentValue:"basic",instanceInnerParam:{type:l.String,value:"mixamo",override:!0}});n(e.animation.animationPreset).toBe("mixamo")})});/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -10,6 +10,7 @@ export declare class DefaultMap<K, V> implements Map<K, V> {
10
10
  private getDefault;
11
11
  get(key: K): V;
12
12
  has(key: K): boolean;
13
+ present(key: K): boolean;
13
14
  set(key: K, value: V): this;
14
15
  get size(): number;
15
16
  entries(): IterableIterator<[K, V]>;
@@ -1,4 +1,4 @@
1
- export function groupBy(t,e){const r=new Map;for(const s of t??[]){const t=e(s);r.has(t)||r.set(t,[]),r.get(t).push(s)}return r}export function indexBy(t,e){const r=new Map;for(const s of t??[])r.set(e(s),s);return r}export class DefaultMap{constructor(t){this.defaultValue=t,this.map=new Map}clear(){this.map.clear()}delete(t){return this.map.delete(t)}forEach(t,e){this.map.forEach(t)}getDefault(){return"function"==typeof this.defaultValue?this.defaultValue():JSON.parse(JSON.stringify(this.defaultValue))}get(t){return this.map.has(t)||this.map.set(t,this.getDefault()),this.map.get(t)}has(t){return!0}set(t,e){return this.map.set(t,e),this}get size(){return this.map.size}entries(){return this.map.entries()}keys(){return this.map.keys()}values(){return this.map.values()}[Symbol.iterator](){return this.map[Symbol.iterator]()}}Symbol.toStringTag;export class ArrayMap extends DefaultMap{constructor(){super(()=>[])}push(t,e){this.get(t).push(e)}}export function partition(t,e){let r=[],s=[];for(const a of t)(e(a)?r:s).push(a);return[r,s]}export function removeObjectUndefined(t){for(const e of Object.keys(t))null==t[e]&&delete t[e];return t}/*
1
+ export function groupBy(t,e){const r=new Map;for(const s of t??[]){const t=e(s);r.has(t)||r.set(t,[]),r.get(t).push(s)}return r}export function indexBy(t,e){const r=new Map;for(const s of t??[])r.set(e(s),s);return r}export class DefaultMap{constructor(t){this.defaultValue=t,this.map=new Map}clear(){this.map.clear()}delete(t){return this.map.delete(t)}forEach(t,e){this.map.forEach(t)}getDefault(){return"function"==typeof this.defaultValue?this.defaultValue():JSON.parse(JSON.stringify(this.defaultValue))}get(t){let e=this.map.get(t);return this.map.has(t)||(e=this.getDefault(),this.map.set(t,e)),e}has(t){return!0}present(t){return this.map.has(t)}set(t,e){return this.map.set(t,e),this}get size(){return this.map.size}entries(){return this.map.entries()}keys(){return this.map.keys()}values(){return this.map.values()}[Symbol.iterator](){return this.map[Symbol.iterator]()}}Symbol.toStringTag;export class ArrayMap extends DefaultMap{constructor(){super(()=>[])}push(t,e){this.get(t).push(e)}}export function partition(t,e){let r=[],s=[];for(const a of t)(e(a)?r:s).push(a);return[r,s]}export function removeObjectUndefined(t){for(const e of Object.keys(t))null==t[e]&&delete t[e];return t}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,6 +1,6 @@
1
1
  export interface Type<T> extends Function {
2
2
  new (...args: any[]): T;
3
3
  }
4
- export type AbstractType<T> = abstract new (...args: any[]) => T;
4
+ export type AbstractType<T> = (abstract new (...args: any[]) => T) & Function;
5
5
  export declare function assertType<T, K extends T>(value: T, type: Type<K>): asserts value is K;
6
6
  //# sourceMappingURL=type.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hology/core",
3
- "version": "0.0.204",
3
+ "version": "0.0.206",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",