@forgeax/engine-app 0.1.3 → 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 +332 -366
- 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 +12 -10
- 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,226 +0,0 @@
|
|
|
1
|
-
// feat-20260709-editor-world-partition-editorworld-super-composite / M2 / w10
|
|
2
|
-
// (RED — impl lands in w12). Full-chain consumer test on the rhi-null headless
|
|
3
|
-
// backend: the drawSource seam drives a REAL Renderer (createRenderer +
|
|
4
|
-
// RhiNull) end-to-end — update -> extract -> record — with no GPU.
|
|
5
|
-
//
|
|
6
|
-
// Unlike w8 (spy renderer, call-shape) and w9 (spy renderer, stale-matrix), this
|
|
7
|
-
// test exercises the actual runtime render pipeline through the seam. The camera
|
|
8
|
-
// + light + visible mesh live ONLY in the injected world; the frame-loop's own
|
|
9
|
-
// world is empty. So a non-zero draw count proves three things at once:
|
|
10
|
-
// 1. drawSource is consumed (else the empty loop world is drawn -> 0 draws),
|
|
11
|
-
// 2. the injected world was world.update(1 / 60).unwrap()'d (propagate ran; extract finds the
|
|
12
|
-
// camera + resolved Transform.world),
|
|
13
|
-
// 3. the full URP record path fired headlessly (bookkeeper counts draws).
|
|
14
|
-
//
|
|
15
|
-
// rhi-null needs no GPU: navigator.gpu is stubbed undefined so createRenderer
|
|
16
|
-
// selects the injected RhiNull instance, and the RhiNullDevice bookkeeper counts
|
|
17
|
-
// draw / bind-group / pipeline records. File suffix `.test.ts` runs in the node
|
|
18
|
-
// vitest project (no dawn / no browser).
|
|
19
|
-
//
|
|
20
|
-
// The frame-loop is driven directly (createFrameLoop + injected now/raf/caf)
|
|
21
|
-
// rather than through createApp, because createApp's assemble form does not (yet
|
|
22
|
-
// — that is w11) forward drawSource; w12 threads it into the loop. Driving the
|
|
23
|
-
// loop directly keeps this test focused on the seam's runtime contract. The
|
|
24
|
-
// drawSource option is reached through a typed alias before w12 widens the type.
|
|
25
|
-
//
|
|
26
|
-
// Anchors:
|
|
27
|
-
// plan-strategy §2 D-3 (drawSource pull -> update injected worlds -> draw)
|
|
28
|
-
// research F1 (frame-loop is the update -> draw driver; the seam insertion pt)
|
|
29
|
-
// feat-20260623 rhi-null-command-flow.unit.test.ts (headless full-chain
|
|
30
|
-
// precedent: createRenderer + RhiNull + bookkeeper counters)
|
|
31
|
-
|
|
32
|
-
import { World } from '@forgeax/engine-ecs';
|
|
33
|
-
import { createPrimitiveMesh } from '@forgeax/engine-geometry';
|
|
34
|
-
import {
|
|
35
|
-
Camera,
|
|
36
|
-
DirectionalLight,
|
|
37
|
-
MeshFilter,
|
|
38
|
-
MeshRenderer,
|
|
39
|
-
type Renderer,
|
|
40
|
-
} from '@forgeax/engine-render';
|
|
41
|
-
import type { RhiNullDevice } from '@forgeax/engine-rhi-null';
|
|
42
|
-
import { rhi } from '@forgeax/engine-rhi-null';
|
|
43
|
-
import { createRenderer } from '@forgeax/engine-runtime';
|
|
44
|
-
import { registerPropagateTransforms, Transform } from '@forgeax/engine-scene';
|
|
45
|
-
|
|
46
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
47
|
-
import { createFrameLoop, type FrameLoopOptions } from '../internal/frame-loop';
|
|
48
|
-
|
|
49
|
-
function makeStubCanvas(w = 800, h = 600): HTMLCanvasElement {
|
|
50
|
-
return {
|
|
51
|
-
width: w,
|
|
52
|
-
height: h,
|
|
53
|
-
getContext(_kind: string): unknown {
|
|
54
|
-
return null;
|
|
55
|
-
},
|
|
56
|
-
addEventListener: () => undefined,
|
|
57
|
-
removeEventListener: () => undefined,
|
|
58
|
-
} as unknown as HTMLCanvasElement;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function buildManifestDataUrl(): string {
|
|
62
|
-
const manifest = {
|
|
63
|
-
schemaVersion: '1.0.0',
|
|
64
|
-
entries: [
|
|
65
|
-
{ hash: 'pbr00000', wgsl: '/* pbr stub - calls f_schlick( */', glsl: '', bindings: '' },
|
|
66
|
-
{ hash: 'unlit000', wgsl: '/* unlit stub */', glsl: '', bindings: '' },
|
|
67
|
-
{
|
|
68
|
-
hash: 'tonemap0',
|
|
69
|
-
wgsl: '/* tonemap stub - struct TonemapParams { exposure: f32 }; */',
|
|
70
|
-
glsl: '',
|
|
71
|
-
bindings: '',
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
};
|
|
75
|
-
return `data:application/json,${encodeURIComponent(JSON.stringify(manifest))}`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function identityTransform(
|
|
79
|
-
overrides: Partial<Record<string, number[]>> = {},
|
|
80
|
-
): Record<string, number[]> {
|
|
81
|
-
return {
|
|
82
|
-
pos: [0, 0, 0],
|
|
83
|
-
quat: [0, 0, 0, 1],
|
|
84
|
-
scale: [1, 1, 1],
|
|
85
|
-
...overrides,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// The injected world carries the FULL renderable set (camera + light + visible
|
|
90
|
-
// mesh). It also registers propagateTransforms so the frame-loop's world.update
|
|
91
|
-
// resolves Transform.world before extract reads it.
|
|
92
|
-
function makeInjectedWorld(): World {
|
|
93
|
-
const world = new World();
|
|
94
|
-
const cube = createPrimitiveMesh('cube').unwrap();
|
|
95
|
-
const cubeHandle = world.internSharedRef('MeshAsset', cube);
|
|
96
|
-
registerPropagateTransforms(world);
|
|
97
|
-
world.spawn(
|
|
98
|
-
{ component: Transform, data: identityTransform({ pos: [0, 0, 5] }) },
|
|
99
|
-
{ component: Camera, data: { fov: 60, near: 0.1, far: 1000, tonemap: 4 } },
|
|
100
|
-
);
|
|
101
|
-
world.spawn(
|
|
102
|
-
{ component: Transform, data: identityTransform() },
|
|
103
|
-
{
|
|
104
|
-
component: DirectionalLight,
|
|
105
|
-
data: { direction: [0.5, -1, -0.5], castShadow: true, intensity: 1 },
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
world.spawn(
|
|
109
|
-
{ component: Transform, data: identityTransform() },
|
|
110
|
-
{ component: MeshFilter, data: { assetHandle: cubeHandle } },
|
|
111
|
-
{ component: MeshRenderer, data: {} },
|
|
112
|
-
);
|
|
113
|
-
return world;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function makeSyncScheduler() {
|
|
117
|
-
let pending: ((t: number) => void) | null = null;
|
|
118
|
-
let clock = 0;
|
|
119
|
-
const raf = (cb: (t: number) => void): number => {
|
|
120
|
-
pending = cb;
|
|
121
|
-
return 1;
|
|
122
|
-
};
|
|
123
|
-
const caf = (): void => {
|
|
124
|
-
pending = null;
|
|
125
|
-
};
|
|
126
|
-
const now = (): number => {
|
|
127
|
-
clock += 16;
|
|
128
|
-
return clock;
|
|
129
|
-
};
|
|
130
|
-
const pump = (frames: number): void => {
|
|
131
|
-
for (let i = 0; i < frames; i++) {
|
|
132
|
-
const cb = pending;
|
|
133
|
-
pending = null;
|
|
134
|
-
if (cb === null) break;
|
|
135
|
-
cb(clock);
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
return { raf, caf, now, pump };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
type DrawSourceCallback = () =>
|
|
142
|
-
| { worlds: readonly World[]; cameraOwner: number; resourceOwner: number }
|
|
143
|
-
| undefined;
|
|
144
|
-
|
|
145
|
-
type FrameLoopOptionsWithDrawSource = FrameLoopOptions & {
|
|
146
|
-
drawSource?: DrawSourceCallback;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const createFrameLoopWithDrawSource = createFrameLoop as unknown as (
|
|
150
|
-
opts: FrameLoopOptionsWithDrawSource,
|
|
151
|
-
) => ReturnType<typeof createFrameLoop>;
|
|
152
|
-
|
|
153
|
-
// The RhiNull instance is a structural stand-in for a real RhiInstance in this
|
|
154
|
-
// headless test; cast the options bag to RendererOptions so the direct
|
|
155
|
-
// createRenderer import (strict RendererOptions) accepts the injected backend.
|
|
156
|
-
const rhiOptions = {
|
|
157
|
-
rhi: rhi as unknown as import('@forgeax/engine-render').RendererOptions['rhi'],
|
|
158
|
-
} as import('@forgeax/engine-render').RendererOptions;
|
|
159
|
-
|
|
160
|
-
let renderer: Renderer | null = null;
|
|
161
|
-
|
|
162
|
-
beforeEach(() => {
|
|
163
|
-
vi.stubGlobal('navigator', { gpu: undefined });
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
afterEach(() => {
|
|
167
|
-
vi.unstubAllGlobals();
|
|
168
|
-
if (renderer) {
|
|
169
|
-
try {
|
|
170
|
-
renderer.dispose();
|
|
171
|
-
} catch {
|
|
172
|
-
// ignore
|
|
173
|
-
}
|
|
174
|
-
renderer = null;
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('drawSource full chain on rhi-null headless (w10)', () => {
|
|
179
|
-
it('drives update -> extract -> record for an injected world with no GPU', async () => {
|
|
180
|
-
const canvas = makeStubCanvas();
|
|
181
|
-
renderer = await createRenderer(canvas, rhiOptions, {
|
|
182
|
-
shaderManifestUrl: buildManifestDataUrl(),
|
|
183
|
-
});
|
|
184
|
-
await renderer.ready;
|
|
185
|
-
|
|
186
|
-
const rhiNullDevice = renderer.device as unknown as RhiNullDevice;
|
|
187
|
-
|
|
188
|
-
// The frame-loop's own world is EMPTY — it has no camera / mesh, so it
|
|
189
|
-
// records zero draws on its own. Only the injected world is renderable.
|
|
190
|
-
const loopWorld = new World();
|
|
191
|
-
const injectedWorld = makeInjectedWorld();
|
|
192
|
-
|
|
193
|
-
const drawSource: DrawSourceCallback = () => ({
|
|
194
|
-
worlds: [injectedWorld],
|
|
195
|
-
cameraOwner: 0,
|
|
196
|
-
resourceOwner: 0,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const { raf, caf, now, pump } = makeSyncScheduler();
|
|
200
|
-
const loop = createFrameLoopWithDrawSource({
|
|
201
|
-
world: loopWorld,
|
|
202
|
-
renderer: renderer as Renderer,
|
|
203
|
-
now,
|
|
204
|
-
raf,
|
|
205
|
-
caf,
|
|
206
|
-
drawSource,
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
// Reset per-frame counters immediately before the observed frame.
|
|
210
|
-
rhiNullDevice.totalDrawCount = 0;
|
|
211
|
-
rhiNullDevice.totalBindGroupCount = 0;
|
|
212
|
-
rhiNullDevice.framePassNames = [];
|
|
213
|
-
|
|
214
|
-
expect(loop.start().ok).toBe(true);
|
|
215
|
-
pump(1);
|
|
216
|
-
loop.stop();
|
|
217
|
-
|
|
218
|
-
// The full URP record path fired for the INJECTED world: a non-zero draw
|
|
219
|
-
// count is only reachable if drawSource was consumed AND the injected world
|
|
220
|
-
// was updated (camera + resolved Transform.world present for extract).
|
|
221
|
-
expect(rhiNullDevice.totalDrawCount).toBeGreaterThanOrEqual(1);
|
|
222
|
-
expect(rhiNullDevice.totalBindGroupCount).toBeGreaterThanOrEqual(1);
|
|
223
|
-
expect(renderer.perFramePassNames.length).toBeGreaterThan(0);
|
|
224
|
-
expect(renderer.perFramePassNames).toContain('main');
|
|
225
|
-
});
|
|
226
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
createEcsImportModule,
|
|
4
|
-
ECS_PUBLIC_SYMBOLS,
|
|
5
|
-
projectEcsPublicModule,
|
|
6
|
-
} from '../internal/ecs-import';
|
|
7
|
-
|
|
8
|
-
describe('host-owned ECS import projection', () => {
|
|
9
|
-
it('keeps the explicit six-symbol surface and drops implementation exports', () => {
|
|
10
|
-
expect(ECS_PUBLIC_SYMBOLS).toEqual([
|
|
11
|
-
'World',
|
|
12
|
-
'Entity',
|
|
13
|
-
'Update',
|
|
14
|
-
'FixedUpdate',
|
|
15
|
-
'Time',
|
|
16
|
-
'FixedTime',
|
|
17
|
-
]);
|
|
18
|
-
expect(
|
|
19
|
-
projectEcsPublicModule({
|
|
20
|
-
World: 'World',
|
|
21
|
-
Entity: 'Entity',
|
|
22
|
-
defineComponent: 'implementation-only',
|
|
23
|
-
}),
|
|
24
|
-
).toEqual({ World: 'World', Entity: 'Entity' });
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('projects only the ECS specifier and preserves other host modules', async () => {
|
|
28
|
-
const calls: string[] = [];
|
|
29
|
-
const importModule = createEcsImportModule(async (specifier) => {
|
|
30
|
-
calls.push(specifier);
|
|
31
|
-
return specifier === '@forgeax/engine-ecs'
|
|
32
|
-
? { World: 'World', defineComponent: 'implementation-only' }
|
|
33
|
-
: { value: 'other-module' };
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
await expect(importModule('@forgeax/engine-ecs')).resolves.toEqual({ World: 'World' });
|
|
37
|
-
await expect(importModule('virtual:host-module')).resolves.toEqual({ value: 'other-module' });
|
|
38
|
-
expect(calls).toEqual(['@forgeax/engine-ecs', 'virtual:host-module']);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { WorldExecutionHealth } from '@forgeax/engine-ecs/shared';
|
|
2
|
-
import { expectTypeOf, it } from 'vitest';
|
|
3
|
-
import type { ExecutionReport, ExecutionWorldHealth } from '../index';
|
|
4
|
-
|
|
5
|
-
it('projects the public App health type from the ECS World owner', () => {
|
|
6
|
-
expectTypeOf<ExecutionWorldHealth>().toEqualTypeOf<WorldExecutionHealth>();
|
|
7
|
-
expectTypeOf<WorldExecutionHealth>().toEqualTypeOf<ExecutionWorldHealth>();
|
|
8
|
-
expectTypeOf<ExecutionReport['world']['health']>().toEqualTypeOf<WorldExecutionHealth>();
|
|
9
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { resolveRemoteServeFlag } from '../internal/remote-serve-flag';
|
|
4
|
-
|
|
5
|
-
describe('resolveRemoteServeFlag', () => {
|
|
6
|
-
it('keeps the Node server disabled in a browser dev host', () => {
|
|
7
|
-
expect(resolveRemoteServeFlag(true, { FORGEAX_ENGINE_REMOTE_SERVE: '1' }, true)).toBe(false);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('starts in a Node dev host', () => {
|
|
11
|
-
expect(resolveRemoteServeFlag(true, undefined, false)).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('allows explicit headless opt-in outside dev mode', () => {
|
|
15
|
-
expect(resolveRemoteServeFlag(undefined, { FORGEAX_ENGINE_REMOTE_SERVE: '1' }, false)).toBe(
|
|
16
|
-
true,
|
|
17
|
-
);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('stays off for production and unconfigured headless hosts', () => {
|
|
21
|
-
expect(resolveRemoteServeFlag(false, undefined, false)).toBe(false);
|
|
22
|
-
expect(resolveRemoteServeFlag(undefined, undefined, false)).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import type { AssetDecoderLease, AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
2
|
-
import { defaultAssetDecoderContributions } from '@forgeax/engine-runtime';
|
|
3
|
-
import {
|
|
4
|
-
type AssetDecoderContributionRef,
|
|
5
|
-
type AssetKind,
|
|
6
|
-
err,
|
|
7
|
-
ok,
|
|
8
|
-
type Result,
|
|
9
|
-
} from '@forgeax/engine-types';
|
|
10
|
-
|
|
11
|
-
export interface AssetRuntimeAssemblyError {
|
|
12
|
-
readonly code: 'asset-assembly-failed';
|
|
13
|
-
readonly expected: string;
|
|
14
|
-
readonly hint: string;
|
|
15
|
-
readonly detail: { readonly kind: string; readonly cause: unknown };
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AssetRuntimeAssembly {
|
|
19
|
-
readonly registry: AssetRegistry;
|
|
20
|
-
readonly leases: readonly AssetDecoderLease[];
|
|
21
|
-
dispose(): void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Install owner decoder contributions into the realm's one asset registry. */
|
|
25
|
-
export function assembleAssetRuntime(
|
|
26
|
-
registry: AssetRegistry,
|
|
27
|
-
contributions: readonly AssetDecoderContributionRef[],
|
|
28
|
-
): Result<AssetRuntimeAssembly, AssetRuntimeAssemblyError> {
|
|
29
|
-
const leases: AssetDecoderLease[] = [];
|
|
30
|
-
const overrides = new Set(contributions.map((contribution) => contribution.kind.kind));
|
|
31
|
-
const defaults = defaultAssetDecoderContributions ?? [];
|
|
32
|
-
const assembledContributions = [
|
|
33
|
-
...defaults.filter((contribution) => !overrides.has(contribution.kind.kind)),
|
|
34
|
-
...contributions,
|
|
35
|
-
];
|
|
36
|
-
try {
|
|
37
|
-
const installedKinds = new Set<string>();
|
|
38
|
-
for (const contribution of assembledContributions) {
|
|
39
|
-
if (installedKinds.has(contribution.kind.kind)) {
|
|
40
|
-
throw new TypeError(`duplicate decoder kind "${contribution.kind.kind}"`);
|
|
41
|
-
}
|
|
42
|
-
installedKinds.add(contribution.kind.kind);
|
|
43
|
-
leases.push(
|
|
44
|
-
registry.installDecoder(
|
|
45
|
-
contribution.kind as AssetKind<unknown, string>,
|
|
46
|
-
contribution.decoder,
|
|
47
|
-
),
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
} catch (cause) {
|
|
51
|
-
for (const lease of leases) lease.dispose();
|
|
52
|
-
const failed = assembledContributions[leases.length];
|
|
53
|
-
return err({
|
|
54
|
-
code: 'asset-assembly-failed',
|
|
55
|
-
expected: 'each owner decoder contribution to install exactly once in the realm registry',
|
|
56
|
-
hint: 'remove the conflicting owner contribution and assemble the current realm again',
|
|
57
|
-
detail: { kind: failed?.kind.kind ?? 'unknown', cause },
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
let disposed = false;
|
|
61
|
-
return ok({
|
|
62
|
-
registry,
|
|
63
|
-
leases: Object.freeze([...leases]),
|
|
64
|
-
dispose() {
|
|
65
|
-
if (disposed) return;
|
|
66
|
-
disposed = true;
|
|
67
|
-
for (const lease of leases) lease.dispose();
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
}
|