@forgeax/engine-app 0.1.4 → 0.1.6
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.
- package/README.md +20 -14
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/create-app-error-retention.integration.test.d.ts +2 -0
- package/dist/__tests__/create-app-error-retention.integration.test.d.ts.map +1 -0
- package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts +2 -0
- package/dist/__tests__/create-app-physics-plugin-failure.integration.test.d.ts.map +1 -0
- package/dist/animation-asset-lookup.d.ts +1 -2
- package/dist/animation-asset-lookup.d.ts.map +1 -1
- package/dist/create-app.d.ts +31 -10
- package/dist/create-app.d.ts.map +1 -1
- package/dist/engine-worker-runtime.mjs +72 -129
- package/dist/engine-worker-runtime.mjs.map +1 -1
- package/dist/errors.d.ts +3 -12
- package/dist/errors.d.ts.map +1 -1
- package/dist/execution/attached-world-swap.d.ts +1 -12
- package/dist/execution/attached-world-swap.d.ts.map +1 -1
- package/dist/execution/bootstrap-url.d.ts +5 -0
- package/dist/execution/bootstrap-url.d.ts.map +1 -0
- package/dist/execution/control.d.ts +0 -2
- package/dist/execution/control.d.ts.map +1 -1
- package/dist/execution/engine-worker.d.ts +1 -0
- package/dist/execution/engine-worker.d.ts.map +1 -1
- package/dist/execution/host-controller.d.ts.map +1 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/protocol.d.ts +4 -1
- package/dist/execution/protocol.d.ts.map +1 -1
- package/dist/execution/types.d.ts +17 -1
- package/dist/execution/types.d.ts.map +1 -1
- package/dist/game-context.d.ts +1 -1
- package/dist/game-context.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +331 -362
- package/dist/index.mjs.map +1 -1
- package/dist/internal/assembled-engine-profile.d.ts.map +1 -1
- package/dist/internal/assets-world-plugin.d.ts +3 -17
- package/dist/internal/assets-world-plugin.d.ts.map +1 -1
- package/dist/internal/browser-remote-bridge.d.ts +1 -2
- package/dist/internal/browser-remote-bridge.d.ts.map +1 -1
- package/dist/internal/browser-rhi-debug-runtime.d.ts +1 -1
- package/dist/internal/browser-rhi-debug-runtime.d.ts.map +1 -1
- package/dist/internal/debug-draw.d.ts +9 -0
- package/dist/internal/debug-draw.d.ts.map +1 -0
- package/dist/internal/engine-profile-common.d.ts +2 -2
- package/dist/internal/engine-profile-common.d.ts.map +1 -1
- package/dist/internal/frame-loop.d.ts +7 -4
- package/dist/internal/frame-loop.d.ts.map +1 -1
- package/dist/internal/main-engine-profile.d.ts +4 -0
- package/dist/internal/main-engine-profile.d.ts.map +1 -1
- package/dist/internal/remote-serve-flag.d.ts +3 -7
- package/dist/internal/remote-serve-flag.d.ts.map +1 -1
- package/dist/internal/rhi-capture.d.ts +1 -1
- package/dist/internal/rhi-capture.d.ts.map +1 -1
- package/dist/internal/worker-engine-profile.d.ts +2 -0
- package/dist/internal/worker-engine-profile.d.ts.map +1 -1
- package/dist/renderer-plugin.d.ts +25 -0
- package/dist/renderer-plugin.d.ts.map +1 -0
- package/dist/tool-preview/bootstrap.d.ts.map +1 -1
- package/dist/types.d.ts +21 -61
- package/dist/types.d.ts.map +1 -1
- package/package.json +25 -26
- package/src/__tests__/callback-inference.test-d.ts +7 -3
- package/src/__tests__/consumer-migration.unit.test.ts +13 -0
- package/src/__tests__/create-app-error-retention.integration.test.ts +125 -0
- package/src/__tests__/create-app-features.test.ts +21 -10
- package/src/__tests__/create-app-listener-sync.test.ts +23 -19
- package/src/__tests__/create-app-physics-plugin-failure.integration.test.ts +142 -0
- package/src/__tests__/create-app-stop.test.ts +4 -12
- package/src/__tests__/create-app.test-d.ts +12 -11
- package/src/__tests__/create-app.test.ts +1 -60
- package/src/__tests__/create-renderer-error.integration.test.ts +3 -2
- package/src/__tests__/create-renderer-lifecycle.integration.test.ts +10 -5
- package/src/__tests__/draw-source-injected-world-update-visibility.test.ts +7 -13
- package/src/__tests__/draw-source-single-world-regression.test.ts +23 -24
- package/src/__tests__/engine-worker-bootstrap-channel.unit.test.ts +8 -0
- package/src/__tests__/engine-worker-world-swap.unit.test.ts +15 -22
- package/src/__tests__/errors-pointer-lock-failed.test.ts +4 -7
- package/src/__tests__/execution-bootstrap-isolation.unit.test.ts +5 -6
- package/src/__tests__/execution-frame-order.unit.test.ts +2 -2
- package/src/__tests__/execution-frame-protocol.unit.test.ts +3 -4
- package/src/__tests__/execution-public-api.test-d.ts +4 -0
- package/src/__tests__/frame-loop-time-elapsed.test.ts +1 -1
- package/src/__tests__/frame-loop-time.unit.test.ts +1 -1
- package/src/__tests__/frame-loop-world-array.test.ts +82 -73
- package/src/__tests__/plugin-profiles.unit.test.ts +3 -3
- package/src/__tests__/profiler-default-off.test.ts +1 -1
- package/src/__tests__/profiler-frame-token.test.ts +17 -30
- package/src/__tests__/profiler-phase-integration.test.ts +15 -27
- package/src/__tests__/surface-handoff.test.ts +1 -4
- package/src/__tests__/visibility-remote.integration.test.ts +2 -5
- package/src/__tests__/worker-execution-stop.test.ts +64 -7
- package/src/animation-asset-lookup.ts +2 -2
- package/src/create-app.ts +197 -296
- package/src/errors.ts +27 -39
- package/src/execution/attached-world-swap.ts +4 -38
- package/src/execution/bootstrap-url.ts +25 -0
- package/src/execution/control.ts +3 -7
- package/src/execution/engine-worker-runtime.ts +23 -40
- package/src/execution/engine-worker.ts +2 -0
- package/src/execution/host-controller.ts +8 -2
- package/src/execution/index.ts +1 -0
- package/src/execution/protocol.ts +4 -1
- package/src/execution/types.ts +18 -1
- package/src/game-context.ts +1 -1
- package/src/index.ts +11 -4
- package/src/internal/assembled-engine-profile.ts +3 -11
- package/src/internal/assets-world-plugin.ts +7 -53
- package/src/internal/browser-remote-bridge.ts +2 -3
- package/src/internal/browser-rhi-debug-runtime.ts +1 -1
- package/src/internal/debug-draw.ts +38 -0
- package/src/internal/engine-profile-common.ts +2 -3
- package/src/internal/frame-loop.ts +77 -63
- package/src/internal/main-engine-profile.ts +21 -17
- package/src/internal/remote-serve-flag.ts +5 -14
- package/src/internal/rhi-capture.ts +1 -1
- package/src/internal/worker-engine-profile.ts +10 -10
- package/src/renderer-plugin.ts +96 -0
- package/src/tool-preview/bootstrap.ts +1 -14
- package/src/types.ts +22 -77
- package/src/vite-env.d.ts +0 -2
- package/dist/__tests__/app-rebuild.test.d.ts +0 -2
- package/dist/__tests__/app-rebuild.test.d.ts.map +0 -1
- package/dist/__tests__/asset-registry-types.test-d.d.ts +0 -2
- package/dist/__tests__/asset-registry-types.test-d.d.ts.map +0 -1
- package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts +0 -2
- package/dist/__tests__/assets-runtime-assembly.integration.test.d.ts.map +0 -1
- package/dist/__tests__/draw-source-headless-full-chain.test.d.ts +0 -2
- package/dist/__tests__/draw-source-headless-full-chain.test.d.ts.map +0 -1
- package/dist/__tests__/ecs-import.unit.test.d.ts +0 -2
- package/dist/__tests__/ecs-import.unit.test.d.ts.map +0 -1
- package/dist/__tests__/execution-world-health-owner.test-d.d.ts +0 -2
- package/dist/__tests__/execution-world-health-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/remote-serve-flag.test.d.ts +0 -2
- package/dist/__tests__/remote-serve-flag.test.d.ts.map +0 -1
- package/dist/assets-runtime-assembly.d.ts +0 -19
- package/dist/assets-runtime-assembly.d.ts.map +0 -1
- package/src/__tests__/app-rebuild.test.ts +0 -31
- package/src/__tests__/asset-registry-types.test-d.ts +0 -16
- package/src/__tests__/assets-runtime-assembly.integration.test.ts +0 -68
- package/src/__tests__/draw-source-headless-full-chain.test.ts +0 -226
- package/src/__tests__/ecs-import.unit.test.ts +0 -40
- package/src/__tests__/execution-world-health-owner.test-d.ts +0 -9
- package/src/__tests__/remote-serve-flag.test.ts +0 -24
- package/src/assets-runtime-assembly.ts +0 -70
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { createRenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
2
3
|
import { createProfiler, type ProfileCapture } from '@forgeax/engine-profiler';
|
|
3
|
-
import type {
|
|
4
|
+
import type { Renderer, RenderFrameInput } from '@forgeax/engine-render';
|
|
4
5
|
import { describe, expect, it } from 'vitest';
|
|
5
6
|
|
|
6
7
|
import { createFrameLoop } from '../internal/frame-loop';
|
|
@@ -32,18 +33,14 @@ function makeScheduler() {
|
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
function makeRenderer(onDraw: (
|
|
36
|
+
function makeRenderer(onDraw: (profileFrame: unknown) => void = () => {}): Renderer {
|
|
36
37
|
return {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
detachWorld: () => {},
|
|
41
|
-
draw: (_worlds: World[], options: DrawOwnerOptions) => {
|
|
42
|
-
onDraw(options);
|
|
38
|
+
attach: (world: World) => ({ ok: true, value: createRenderReadLease(world) }),
|
|
39
|
+
draw: (request: RenderFrameInput) => {
|
|
40
|
+
onDraw(request.profileFrame);
|
|
43
41
|
return { ok: true, value: undefined };
|
|
44
42
|
},
|
|
45
|
-
|
|
46
|
-
onLost: () => () => {},
|
|
43
|
+
subscribe: () => () => {},
|
|
47
44
|
dispose: () => {},
|
|
48
45
|
} as unknown as Renderer;
|
|
49
46
|
}
|
|
@@ -98,42 +95,32 @@ describe('App profiler frame token', () => {
|
|
|
98
95
|
const capture = finish(profiler);
|
|
99
96
|
expect(capture.phaseCatalog.app).toEqual(APP_PHASE_CATALOG);
|
|
100
97
|
expect(capture.captureId).toBe('capture-0001');
|
|
101
|
-
expect(
|
|
102
|
-
capture.records.filter(
|
|
103
|
-
(record: ProfileCapture['records'][number]) => record.source === 'app',
|
|
104
|
-
),
|
|
105
|
-
).toHaveLength(10);
|
|
98
|
+
expect(capture.records.filter((record) => record.source === 'app')).toHaveLength(10);
|
|
106
99
|
expect(
|
|
107
100
|
capture.records
|
|
108
|
-
.filter(
|
|
109
|
-
|
|
110
|
-
record.source === 'app' && record.kind === 'phase',
|
|
111
|
-
)
|
|
112
|
-
.map((record: ProfileCapture['records'][number]) => record.phase),
|
|
101
|
+
.filter((record) => record.source === 'app' && record.kind === 'phase')
|
|
102
|
+
.map((record) => record.phase),
|
|
113
103
|
).toEqual(expect.arrayContaining([...FRAME_LOOP_PHASES]));
|
|
114
104
|
expect(
|
|
115
105
|
new Set(
|
|
116
106
|
capture.records
|
|
117
|
-
.filter(
|
|
118
|
-
|
|
119
|
-
record.source === 'app' && record.kind === 'phase',
|
|
120
|
-
)
|
|
121
|
-
.map((record: ProfileCapture['records'][number]) => record.phase),
|
|
107
|
+
.filter((record) => record.source === 'app' && record.kind === 'phase')
|
|
108
|
+
.map((record) => record.phase),
|
|
122
109
|
),
|
|
123
110
|
).toEqual(new Set(FRAME_LOOP_PHASES));
|
|
124
111
|
expect(
|
|
125
112
|
new Set(
|
|
126
|
-
capture.records
|
|
127
|
-
.filter((record: ProfileCapture['records'][number]) => record.source === 'app')
|
|
128
|
-
.map((record: ProfileCapture['records'][number]) => record.frameId),
|
|
113
|
+
capture.records.filter((record) => record.source === 'app').map((record) => record.frameId),
|
|
129
114
|
),
|
|
130
115
|
).toEqual(new Set([1, 2]));
|
|
131
116
|
expect(drawOptions).toHaveLength(2);
|
|
132
117
|
expect(drawOptions[0]).toMatchObject({
|
|
133
|
-
|
|
118
|
+
captureId: 'capture-0001',
|
|
119
|
+
frameId: 1,
|
|
134
120
|
});
|
|
135
121
|
expect(drawOptions[1]).toMatchObject({
|
|
136
|
-
|
|
122
|
+
captureId: 'capture-0001',
|
|
123
|
+
frameId: 2,
|
|
137
124
|
});
|
|
138
125
|
});
|
|
139
126
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { World } from '@forgeax/engine-ecs';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { createRenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
3
|
+
import { createProfiler } from '@forgeax/engine-profiler';
|
|
4
|
+
import type { Renderer, RenderFrameInput } from '@forgeax/engine-render';
|
|
4
5
|
import { describe, expect, it } from 'vitest';
|
|
5
6
|
|
|
6
7
|
import { createFrameLoop } from '../internal/frame-loop';
|
|
@@ -24,18 +25,14 @@ function makeScheduler() {
|
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
function makeRenderer(draw: (
|
|
28
|
+
function makeRenderer(draw: (profileFrame: unknown) => void): Renderer {
|
|
28
29
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
detachWorld: () => {},
|
|
33
|
-
draw: (_worlds: World[], options: DrawOwnerOptions) => {
|
|
34
|
-
draw(options);
|
|
30
|
+
attach: (world: World) => ({ ok: true, value: createRenderReadLease(world) }),
|
|
31
|
+
draw: (request: RenderFrameInput) => {
|
|
32
|
+
draw(request.profileFrame);
|
|
35
33
|
return { ok: true, value: undefined };
|
|
36
34
|
},
|
|
37
|
-
|
|
38
|
-
onLost: () => () => {},
|
|
35
|
+
subscribe: () => () => {},
|
|
39
36
|
dispose: () => {},
|
|
40
37
|
} as unknown as Renderer;
|
|
41
38
|
}
|
|
@@ -46,7 +43,7 @@ function start(profiler: ReturnType<typeof createProfiler>) {
|
|
|
46
43
|
return result.value;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
function finish(profiler: ReturnType<typeof createProfiler>)
|
|
46
|
+
function finish(profiler: ReturnType<typeof createProfiler>) {
|
|
50
47
|
const session = profiler.activeSession();
|
|
51
48
|
if (session === undefined) {
|
|
52
49
|
const latest = profiler.latestCapture();
|
|
@@ -63,7 +60,7 @@ describe('App and Render profiler integration', () => {
|
|
|
63
60
|
const profiler = createProfiler();
|
|
64
61
|
start(profiler);
|
|
65
62
|
const scheduler = makeScheduler();
|
|
66
|
-
const drawOptions:
|
|
63
|
+
const drawOptions: unknown[] = [];
|
|
67
64
|
const loop = createFrameLoop({
|
|
68
65
|
world: new World(),
|
|
69
66
|
renderer: makeRenderer((options) => drawOptions.push(options)),
|
|
@@ -78,16 +75,11 @@ describe('App and Render profiler integration', () => {
|
|
|
78
75
|
loop.stop().unwrap();
|
|
79
76
|
|
|
80
77
|
const capture = finish(profiler);
|
|
81
|
-
expect(
|
|
82
|
-
|
|
83
|
-
(record: ProfileCapture['records'][number]) => record.source === 'app',
|
|
84
|
-
),
|
|
85
|
-
).toHaveLength(5);
|
|
86
|
-
expect(
|
|
87
|
-
capture.records.every((record: ProfileCapture['records'][number]) => record.frameId === 1),
|
|
88
|
-
).toBe(true);
|
|
78
|
+
expect(capture.records.filter((record) => record.source === 'app')).toHaveLength(5);
|
|
79
|
+
expect(capture.records.every((record) => record.frameId === 1)).toBe(true);
|
|
89
80
|
expect(drawOptions[0]).toMatchObject({
|
|
90
|
-
|
|
81
|
+
captureId: capture.captureId,
|
|
82
|
+
frameId: 1,
|
|
91
83
|
});
|
|
92
84
|
expect(capture.phaseCatalog.app).toEqual(APP_PHASE_CATALOG);
|
|
93
85
|
expect(profiler.phaseCatalog.app).toEqual([]);
|
|
@@ -120,10 +112,6 @@ describe('App and Render profiler integration', () => {
|
|
|
120
112
|
const capture = finish(profiler);
|
|
121
113
|
expect(draws).toBe(2);
|
|
122
114
|
expect(errors).toHaveLength(2);
|
|
123
|
-
expect(
|
|
124
|
-
capture.records.filter(
|
|
125
|
-
(record: ProfileCapture['records'][number]) => record.source === 'app',
|
|
126
|
-
),
|
|
127
|
-
).toHaveLength(10);
|
|
115
|
+
expect(capture.records.filter((record) => record.source === 'app')).toHaveLength(10);
|
|
128
116
|
});
|
|
129
117
|
});
|
|
@@ -30,10 +30,7 @@ describe('createApp presentation surface handoff', () => {
|
|
|
30
30
|
drawSawReleasedSurface = surfaceReleased;
|
|
31
31
|
return ok(undefined);
|
|
32
32
|
},
|
|
33
|
-
|
|
34
|
-
return () => {};
|
|
35
|
-
},
|
|
36
|
-
onLost(): () => void {
|
|
33
|
+
subscribe(): () => void {
|
|
37
34
|
return () => {};
|
|
38
35
|
},
|
|
39
36
|
releaseSurface(): { ok: true; value: undefined } {
|
|
@@ -37,12 +37,11 @@ function makeRendererStub(): Renderer {
|
|
|
37
37
|
onError: () => () => {},
|
|
38
38
|
onLost: () => () => {},
|
|
39
39
|
dispose: () => {},
|
|
40
|
-
visibilityStats: { explicitlyHidden: 1 },
|
|
41
40
|
} as unknown as Renderer;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
describe('createApp to remote visibility discovery', () => {
|
|
45
|
-
it('introspects then evaluates a Query
|
|
44
|
+
it('introspects then evaluates a Query and recovers an invalid write', async () => {
|
|
46
45
|
const previous = process.env.FORGEAX_ENGINE_REMOTE_SERVE;
|
|
47
46
|
process.env.FORGEAX_ENGINE_REMOTE_SERVE = '1';
|
|
48
47
|
const world = new World();
|
|
@@ -77,7 +76,7 @@ describe('createApp to remote visibility discovery', () => {
|
|
|
77
76
|
const query = world.query({ read: [render.Visibility] }).unwrap();
|
|
78
77
|
let current;
|
|
79
78
|
for (const row of query) {
|
|
80
|
-
current =
|
|
79
|
+
current = ['inherited', 'hidden', 'visible'][row.get(render.Visibility).state];
|
|
81
80
|
}
|
|
82
81
|
const effective = render.resolveVisibility(world).effective(entity);
|
|
83
82
|
const invalid = world.set(entity, render.Visibility, { state: 99 });
|
|
@@ -95,7 +94,6 @@ describe('createApp to remote visibility discovery', () => {
|
|
|
95
94
|
},
|
|
96
95
|
recovered: recovered.ok,
|
|
97
96
|
restored: render.resolveVisibility(world).effective(entity),
|
|
98
|
-
visibilityStats: renderer.visibilityStats,
|
|
99
97
|
};
|
|
100
98
|
})()`);
|
|
101
99
|
expect(value).toMatchObject({
|
|
@@ -104,7 +102,6 @@ describe('createApp to remote visibility discovery', () => {
|
|
|
104
102
|
invalid: { code: 'component-field-invalid-value' },
|
|
105
103
|
recovered: true,
|
|
106
104
|
restored: 'visible',
|
|
107
|
-
visibilityStats: { explicitlyHidden: 1 },
|
|
108
105
|
});
|
|
109
106
|
expect((value as { invalid: { hint: string } }).invalid.hint.length).toBeGreaterThan(0);
|
|
110
107
|
} finally {
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { EngineToHostMessage } from '../execution/protocol';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
type SessionListener = (message: EngineToHostMessage) => void;
|
|
5
|
+
|
|
6
|
+
const probes = vi.hoisted(() => {
|
|
7
|
+
const sessionListeners: SessionListener[] = [];
|
|
8
|
+
return {
|
|
9
|
+
audioDispose: vi.fn(),
|
|
10
|
+
inputDetach: vi.fn(),
|
|
11
|
+
sessionDispose: vi.fn(),
|
|
12
|
+
sessionListen: vi.fn((listener: SessionListener) => {
|
|
13
|
+
sessionListeners.push(listener);
|
|
14
|
+
return () => {};
|
|
15
|
+
}),
|
|
16
|
+
sessionListeners,
|
|
17
|
+
sessionPost: vi.fn(),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
10
20
|
|
|
11
21
|
vi.mock('../execution/engine-worker', async () => {
|
|
12
22
|
const { ok } = await import('@forgeax/engine-types');
|
|
@@ -73,6 +83,7 @@ const capabilities = {
|
|
|
73
83
|
describe('Worker ExecutionApp terminal stop', () => {
|
|
74
84
|
beforeEach(() => {
|
|
75
85
|
vi.clearAllMocks();
|
|
86
|
+
probes.sessionListeners.length = 0;
|
|
76
87
|
});
|
|
77
88
|
|
|
78
89
|
it('cleans every host owner once when stopped while paused and cannot restart', async () => {
|
|
@@ -113,4 +124,50 @@ describe('Worker ExecutionApp terminal stop', () => {
|
|
|
113
124
|
if (!restart.ok) expect(restart.error.code).toBe('app-not-started');
|
|
114
125
|
expect(raf).toHaveBeenCalledTimes(1);
|
|
115
126
|
});
|
|
127
|
+
|
|
128
|
+
it('retains the existing terminal fault identity for host inspection', async () => {
|
|
129
|
+
vi.stubGlobal(
|
|
130
|
+
'requestAnimationFrame',
|
|
131
|
+
vi.fn(() => 7),
|
|
132
|
+
);
|
|
133
|
+
vi.stubGlobal('cancelAnimationFrame', vi.fn());
|
|
134
|
+
const result = await createWorkerExecutionApp({
|
|
135
|
+
canvas: {} as HTMLCanvasElement,
|
|
136
|
+
appOptions: { execution: { bootstrap: 'https://example.test/game.js' } } as never,
|
|
137
|
+
capabilities,
|
|
138
|
+
selection: {
|
|
139
|
+
requestedTier: 'engine-worker',
|
|
140
|
+
actualTier: 'engine-worker',
|
|
141
|
+
selectionReason: 'explicit-request',
|
|
142
|
+
missingCapabilities: [],
|
|
143
|
+
sharedEvidencePassed: false,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
expect(result.ok).toBe(true);
|
|
147
|
+
if (!result.ok) return;
|
|
148
|
+
let observed: unknown;
|
|
149
|
+
result.value.onError((error) => {
|
|
150
|
+
observed = error;
|
|
151
|
+
});
|
|
152
|
+
const listener = probes.sessionListeners[0];
|
|
153
|
+
expect(listener).toBeTypeOf('function');
|
|
154
|
+
|
|
155
|
+
listener?.({
|
|
156
|
+
kind: 'fault',
|
|
157
|
+
worldIdentity: 'worker-world',
|
|
158
|
+
source: 'world',
|
|
159
|
+
code: 'app-system-update-failed',
|
|
160
|
+
expected: 'worker world update succeeds',
|
|
161
|
+
hint: 'inspect the retained cause',
|
|
162
|
+
detail: { cause: 'worker boom' },
|
|
163
|
+
partialWrite: true,
|
|
164
|
+
retryable: false,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result.value.lastError).toBe(observed);
|
|
168
|
+
expect(result.value.lastError).toMatchObject({
|
|
169
|
+
code: 'app-system-update-failed',
|
|
170
|
+
detail: { cause: { cause: 'worker boom' } },
|
|
171
|
+
});
|
|
172
|
+
});
|
|
116
173
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AnimationPayloadLookup } from '@forgeax/engine-animation';
|
|
2
|
-
import type { AnimationClip
|
|
2
|
+
import type { AnimationClip } from '@forgeax/engine-types';
|
|
3
3
|
|
|
4
4
|
export interface AnimationPayloadSource {
|
|
5
|
-
lookup<T
|
|
5
|
+
lookup<T = unknown>(guid: string): T | undefined;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
/** Bridge the renderer-owned GUID catalogue into the animation plugin. */
|