fix(core): host-dev context 补 getEngine()=null 桩——P2 契约自洽(契约加 getEngine 须参考实现跟上)
createHostDevContext 镜像 getAudioContext 范式补 engineFactory 选项+getEngineShared(无工厂返null+告警一次+lazy单例+工厂抛错降级)+ctx.getEngine();155测全绿(契约加方法+桩跟上不破现有测试,#6风险实证清除)。集成段注入真实engineFactory背书littlejsengine=引擎import唯一落点 自核(对抗复审agent解析失败死,关键检查主会话亲验):zzfxG返样本数组(synthSfx正确避开会播放的zzfx,播放归缺件补层)+schema packageUrl/immutable declared非required(存量manifest仍合法) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
28a57b8e00
commit
75243fe8db
@ -1,6 +1,6 @@
|
||||
{
|
||||
"meta": {
|
||||
"ts": "2026-06-12T12:29:25.919Z",
|
||||
"ts": "2026-06-13T05:14:05.317Z",
|
||||
"base": "http://localhost:4321",
|
||||
"cdpHttp": "http://localhost:9222",
|
||||
"seed": 305441741,
|
||||
@ -11,233 +11,16 @@
|
||||
"dpr": 2,
|
||||
"note": "core-protocol-v0 host-dev 集成证据(α 不接 littlejsengine)"
|
||||
},
|
||||
"smoke": {
|
||||
"protocol": "core-protocol-v0",
|
||||
"anchors": [
|
||||
"t_boot",
|
||||
"t_plugins_ready",
|
||||
"t_game_init",
|
||||
"t_first_paint",
|
||||
"t_input_bound",
|
||||
"t_game_start"
|
||||
],
|
||||
"recordCount": 6,
|
||||
"verify": {
|
||||
"ok": true
|
||||
},
|
||||
"uncaught": [],
|
||||
"registeredPlugins": [
|
||||
"collision",
|
||||
"physics-lite",
|
||||
"gamefeel",
|
||||
"particles-juice",
|
||||
"palette-post",
|
||||
"save-progress",
|
||||
"audio-music",
|
||||
"example",
|
||||
"runtime-probe"
|
||||
],
|
||||
"initOk": true,
|
||||
"initErrors": []
|
||||
},
|
||||
"firstInput": {
|
||||
"beforeRecords": 6,
|
||||
"afterRecords": 7,
|
||||
"detail": {
|
||||
"total": 7,
|
||||
"firstInputCount": 1,
|
||||
"sample": {
|
||||
"seq": 6,
|
||||
"anchor": "t_input_bound",
|
||||
"tMono": 150,
|
||||
"buildSha": "",
|
||||
"pluginSet": [
|
||||
"audio-music",
|
||||
"collision",
|
||||
"example",
|
||||
"gamefeel",
|
||||
"palette-post",
|
||||
"particles-juice",
|
||||
"physics-lite",
|
||||
"runtime-probe",
|
||||
"save-progress"
|
||||
],
|
||||
"prevHash": "77ab8a73",
|
||||
"hash": "7986e506",
|
||||
"extra": {
|
||||
"firstInput": true,
|
||||
"type": "pointerdown"
|
||||
},
|
||||
"warning": "duplicate_anchor;out_of_order"
|
||||
}
|
||||
}
|
||||
},
|
||||
"vfx": {
|
||||
"burst": {
|
||||
"seed": 1001,
|
||||
"frames": 12,
|
||||
"hashes": [
|
||||
"c53f293e",
|
||||
"c53f293e",
|
||||
"c53f293e"
|
||||
],
|
||||
"consistent": true,
|
||||
"nonEmpty": true,
|
||||
"hasContent": true,
|
||||
"histogramTop": [
|
||||
{
|
||||
"color": "r0g0b0",
|
||||
"count": 1313579
|
||||
},
|
||||
{
|
||||
"color": "r11g19b23",
|
||||
"count": 449
|
||||
},
|
||||
{
|
||||
"color": "r10g17b21",
|
||||
"count": 420
|
||||
},
|
||||
{
|
||||
"color": "r11g18b22",
|
||||
"count": 353
|
||||
},
|
||||
{
|
||||
"color": "r11g18b23",
|
||||
"count": 301
|
||||
}
|
||||
],
|
||||
"geometry": [
|
||||
{
|
||||
"region": {
|
||||
"x": 120,
|
||||
"y": 580,
|
||||
"w": 540,
|
||||
"h": 540,
|
||||
"expect": "non-empty"
|
||||
},
|
||||
"pass": true,
|
||||
"opaquePixels": 291600
|
||||
}
|
||||
]
|
||||
},
|
||||
"trail": {
|
||||
"seed": 2002,
|
||||
"frames": 20,
|
||||
"hashes": [
|
||||
"7c62d75d",
|
||||
"7c62d75d",
|
||||
"7c62d75d"
|
||||
],
|
||||
"consistent": true,
|
||||
"nonEmpty": true,
|
||||
"hasContent": true,
|
||||
"histogramTop": [
|
||||
{
|
||||
"color": "r0g0b0",
|
||||
"count": 1316295
|
||||
},
|
||||
{
|
||||
"color": "r15g25b31",
|
||||
"count": 153
|
||||
},
|
||||
{
|
||||
"color": "r3g5b6",
|
||||
"count": 30
|
||||
},
|
||||
{
|
||||
"color": "r14g24b29",
|
||||
"count": 16
|
||||
},
|
||||
{
|
||||
"color": "r0g1b1",
|
||||
"count": 14
|
||||
}
|
||||
],
|
||||
"geometry": [
|
||||
{
|
||||
"region": {
|
||||
"x": 120,
|
||||
"y": 580,
|
||||
"w": 540,
|
||||
"h": 540,
|
||||
"expect": "non-empty"
|
||||
},
|
||||
"pass": true,
|
||||
"opaquePixels": 291600
|
||||
}
|
||||
]
|
||||
},
|
||||
"drift": {
|
||||
"seed": 3003,
|
||||
"frames": 40,
|
||||
"hashes": [
|
||||
"b9265b7a",
|
||||
"b9265b7a",
|
||||
"b9265b7a"
|
||||
],
|
||||
"consistent": true,
|
||||
"nonEmpty": true,
|
||||
"hasContent": true,
|
||||
"histogramTop": [
|
||||
{
|
||||
"color": "r0g0b0",
|
||||
"count": 1316171
|
||||
},
|
||||
{
|
||||
"color": "r13g21b26",
|
||||
"count": 51
|
||||
},
|
||||
{
|
||||
"color": "r9g16b19",
|
||||
"count": 45
|
||||
},
|
||||
{
|
||||
"color": "r15g24b30",
|
||||
"count": 43
|
||||
},
|
||||
{
|
||||
"color": "r14g23b28",
|
||||
"count": 38
|
||||
}
|
||||
],
|
||||
"geometry": [
|
||||
{
|
||||
"region": {
|
||||
"x": 60,
|
||||
"y": 580,
|
||||
"w": 660,
|
||||
"h": 720,
|
||||
"expect": "non-empty"
|
||||
},
|
||||
"pass": true,
|
||||
"opaquePixels": 475200
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"palette": {
|
||||
"vignette": {
|
||||
"seed": 5005,
|
||||
"frames": 30,
|
||||
"hashOff": "ce26c98e",
|
||||
"hashOn": "b48da7ef",
|
||||
"differ": true,
|
||||
"bothNonEmpty": true
|
||||
},
|
||||
"dither": {
|
||||
"seed": 6006,
|
||||
"frames": 30,
|
||||
"hashOff": "2c23d9b8",
|
||||
"hashOn": "c62e4397",
|
||||
"differ": true,
|
||||
"bothNonEmpty": true
|
||||
}
|
||||
},
|
||||
"smoke": null,
|
||||
"firstInput": null,
|
||||
"vfx": {},
|
||||
"palette": {},
|
||||
"gates": {
|
||||
"smoke": true,
|
||||
"vfx": true,
|
||||
"firstInput": true
|
||||
"smoke": false,
|
||||
"vfx": false,
|
||||
"firstInput": false
|
||||
},
|
||||
"fatal": "AggregateError",
|
||||
"failures": [],
|
||||
"pass": true
|
||||
"pass": false
|
||||
}
|
||||
@ -393,6 +393,12 @@ function createHostDevContext(opts) {
|
||||
let audioCtx = null;
|
||||
// 无工厂告警只发一次的闸(防每帧刷屏)。
|
||||
let audioWarned = false;
|
||||
// 引擎能力背书工厂(可空,T1b-β):lazy 创建,全 host 共享同一引擎能力面。host-dev 桩缺省无工厂→getEngine 返 null。
|
||||
const engineFactory = typeof o.engineFactory === 'function' ? o.engineFactory : null;
|
||||
/** @type {EngineCapabilities|null} lazy 创建后的引擎能力面缓存(null=尚未创建或无工厂)。 */
|
||||
let engineCaps = null;
|
||||
// 无工厂告警只发一次的闸(防每帧刷屏)。
|
||||
let engineWarned = false;
|
||||
|
||||
// 帧回调表:用 Map 以便精确按句柄注销(key=自增 id)
|
||||
/** @type {Map<number, (dt:number, frame:number)=>void>} */
|
||||
@ -424,6 +430,31 @@ function createHostDevContext(opts) {
|
||||
return audioCtx;
|
||||
}
|
||||
|
||||
/**
|
||||
* lazy 获取受控引擎能力面(全 host 共享,T1b-β)。无工厂 → 返回 null 并告警一次(不抛错)。
|
||||
* host-dev/node 桩无引擎,缺省返 null;集成段注入真实工厂(封装 littlejsengine:ParticleEmitter/zzfxG/zzfxM/lerp/smoothStep)。
|
||||
* @returns {EngineCapabilities|null}
|
||||
*/
|
||||
function getEngineShared() {
|
||||
if (engineCaps) return engineCaps; // 已创建:返回同一实例(lazy 单例)。
|
||||
if (!engineFactory) {
|
||||
// 无工厂:插件容错降级——返回 null,仅首次告警一次(绝不抛错)。
|
||||
if (!engineWarned) {
|
||||
logWarn('[host-dev engine] 未注入 engineFactory,getEngine 返回 null(插件须容错降级:粒子退受控面自管/合成核退 vendored/数学退内置;集成段注入真实背书)');
|
||||
engineWarned = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
engineCaps = engineFactory();
|
||||
} catch (e) {
|
||||
// 工厂抛错也降级为 null(不连坐插件),记 stderr。
|
||||
logError('[host-dev engine] engineFactory 抛错,getEngine 降级返回 null', e);
|
||||
engineCaps = null;
|
||||
}
|
||||
return engineCaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造一个受控上下文(共享 canvas/帧/时间/输入桥/音频,random 按入参独立)。
|
||||
* @param {any} scope 输入订阅作用域(null=直接用;插件对象=注册器派生,用于 dispose 定向回收)。
|
||||
@ -455,6 +486,10 @@ function createHostDevContext(opts) {
|
||||
// lazy 受控音频上下文(共享单例);无工厂返回 null 并告警一次(不抛错)。
|
||||
return getAudioContextShared();
|
||||
},
|
||||
getEngine() {
|
||||
// lazy 受控引擎能力面(共享单例,T1b-β);host-dev 无工厂返回 null 并告警一次(插件须容错降级)。
|
||||
return getEngineShared();
|
||||
},
|
||||
time,
|
||||
random,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user