@volter/project 0.5.211 → 0.5.212
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/NOTICE +2 -2
- package/README.md +8 -15
- package/package.json +5 -26
- package/schemas/volter-project.schema.json +33 -145
- package/schemas/volter-settings.schema.json +11 -49
- package/src/adapter/adapter-module.ts +204 -465
- package/src/adapter/adapter-surface.ts +4 -32
- package/src/adapter/authoring-seam-contract.ts +7 -24
- package/src/adapter/authoring.ts +126 -265
- package/src/adapter/editor-looks.ts +96 -127
- package/src/adapter/finders/registry.ts +6 -21
- package/src/adapter/host-context.ts +17 -72
- package/src/adapter/index.ts +4 -77
- package/src/adapter/manifest-interpreter.ts +7 -50
- package/src/adapter/physics-adapter-2d.ts +4 -21
- package/src/adapter/root-adapter.ts +37 -111
- package/src/adapter/root-seam-contract.ts +3 -6
- package/src/adapter/root-stacking.ts +4 -46
- package/src/adapter/seam-evidence.ts +0 -16
- package/src/adapter/system-adapter.ts +24 -656
- package/src/adapter/system-seam-contract.ts +1 -91
- package/src/adapter/transform.ts +4 -3
- package/src/adapter/workspace-arrangement.ts +29 -49
- package/src/manifest/editor-port.ts +10 -36
- package/src/manifest/filename.ts +3 -42
- package/src/manifest/load-file.ts +9 -15
- package/src/manifest/load.ts +44 -179
- package/src/manifest/locate.ts +6 -29
- package/src/manifest/schema.ts +155 -427
- package/src/semver.ts +16 -0
- package/src/settings/keys.ts +9 -11
- package/src/settings/schema.ts +55 -44
- package/schemas/engine-api.json +0 -57
- package/schemas/engine-api.md +0 -22
- package/src/adapter/binding.ts +0 -445
- package/src/adapter/debug-registry-ref.ts +0 -19
- package/src/adapter/frame-capture.ts +0 -80
- package/src/adapter/ingest/contract-system-slots.ts +0 -28
- package/src/adapter/ingest/game-contract-seams.ts +0 -86
- package/src/adapter/ingest/game-contract.ts +0 -506
- package/src/adapter/ingest/mount-readiness.ts +0 -140
- package/src/adapter/loop-gate-report.ts +0 -53
- package/src/adapter/native-entry-surface.ts +0 -100
- package/src/adapter/render-audio.ts +0 -20
- package/src/adapter/render-memory.ts +0 -34
- package/src/adapter/renderer-config.ts +0 -84
- package/src/adapter/system-slot.ts +0 -301
- package/src/core/system-phase.ts +0 -52
- package/src/inspection.ts +0 -281
- package/src/manifest/configuration-kinds.ts +0 -241
- package/src/manifest/kind-modules.ts +0 -58
- package/src/manifest/runtime-environment.ts +0 -48
package/src/adapter/index.ts
CHANGED
|
@@ -1,32 +1,10 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Adapter interfaces — the seams the engine host and the editor DEPEND ON.
|
|
3
|
-
*
|
|
4
|
-
* host → RootAdapter ← { the editor's R3F root mounts, IngestRootAdapter, … }
|
|
5
|
-
* editor → AuthoringAdapter ← { ThreeAuthoringAdapter, ReactRootAuthoringAdapter, … }
|
|
6
|
-
* game → SystemAdapters (physics/networking/navigation/audio/camera/debug)
|
|
7
|
-
*
|
|
8
|
-
* The first-party Rapier/Colyseus stack is ONE implementer of these
|
|
9
|
-
* interfaces, not the engine's vocabulary.
|
|
10
|
-
*
|
|
11
|
-
* **This module is TYPE-ONLY, and must stay that way (P-6).** It used to
|
|
12
|
-
* value-export its own implementers — `createRapierPhysicsAdapter`,
|
|
13
|
-
* `createColyseusNetworkingAdapter`, `createNavigationAdapter` — so
|
|
14
|
-
* `import type { AuthoringAdapter } from './index'` was the only
|
|
15
|
-
* thing keeping the three/Rapier/Colyseus stack out of a Pixi or React
|
|
16
|
-
* consumer's module graph. A seam that ships its implementers is not a
|
|
17
|
-
* seam. Import an implementer from its OWN path instead:
|
|
18
|
-
*
|
|
19
|
-
* `@volter/threejs-runtime/adapter/rapier-physics-adapter` createRapierPhysicsAdapter
|
|
20
|
-
* `@volter/threejs-runtime/adapter/first-party-navigation-system` createNavigationAdapter
|
|
21
|
-
*/
|
|
1
|
+
/** Project adapter contracts and neutral contract helpers. Implementations live in the integrations that own their library objects. */
|
|
22
2
|
|
|
23
3
|
export type { AdapterSurface } from './adapter-surface';
|
|
24
4
|
export type {
|
|
25
5
|
AssetDropContext,
|
|
26
6
|
AssetDropProvider,
|
|
27
|
-
AssetSubjectProvider,
|
|
28
7
|
AuthoringAdapter,
|
|
29
|
-
AuthoringAssetSubject,
|
|
30
8
|
AuthoringCapabilities,
|
|
31
9
|
AuthoringProvenance,
|
|
32
10
|
AuthoringProviderKey,
|
|
@@ -86,38 +64,20 @@ export {
|
|
|
86
64
|
AUTHORING_PROVIDER_SHAPES,
|
|
87
65
|
HIERARCHY_PROVIDER_SHAPE,
|
|
88
66
|
} from './authoring-seam-contract';
|
|
89
|
-
export type {
|
|
90
|
-
EntryStaticSurface,
|
|
91
|
-
ObservationBinding,
|
|
92
|
-
ProjectBinding,
|
|
93
|
-
ProjectionBinding,
|
|
94
|
-
ProtocolFamily,
|
|
95
|
-
RootBinding,
|
|
96
|
-
RootBindingParts,
|
|
97
|
-
RootDeclaration,
|
|
98
|
-
SubstrateBinding,
|
|
99
|
-
TruthBinding,
|
|
100
|
-
} from './binding';
|
|
101
|
-
export { createRootBinding } from './binding';
|
|
102
67
|
export type {
|
|
103
68
|
CanvasHostContext,
|
|
104
69
|
DomHostContext,
|
|
105
|
-
HostContextBase,
|
|
106
70
|
HostContextFor,
|
|
107
71
|
HostSurface,
|
|
108
|
-
|
|
72
|
+
StageHostContext,
|
|
109
73
|
} from './host-context';
|
|
110
|
-
/** The offline-audio contract an `AudioAdapter.renderOffline` implements.
|
|
111
|
-
* Defined beside the render-mode harness that first needed it; re-exported
|
|
112
|
-
* here so a world binding the seam imports one name from one place. */
|
|
113
|
-
export type { OfflineAudioRenderer, RenderedAudio } from './render-audio';
|
|
114
74
|
export type {
|
|
115
75
|
MountedCanvasRoot,
|
|
116
76
|
MountedCanvasSubstrate,
|
|
117
77
|
MountedReactRoot,
|
|
118
78
|
MountedRoot,
|
|
119
79
|
MountedRootBase,
|
|
120
|
-
|
|
80
|
+
MountedStageRoot,
|
|
121
81
|
RootAdapter,
|
|
122
82
|
RootStateObserver,
|
|
123
83
|
SurfaceAdapter,
|
|
@@ -127,7 +87,7 @@ export {
|
|
|
127
87
|
MOUNTED_CANVAS_SURFACE_SHAPE,
|
|
128
88
|
MOUNTED_DOM_SURFACE_SHAPE,
|
|
129
89
|
MOUNTED_ROOT_BASE_SHAPE,
|
|
130
|
-
|
|
90
|
+
MOUNTED_STAGE_SURFACE_SHAPE,
|
|
131
91
|
ROOT_ADAPTER_SHAPE,
|
|
132
92
|
ROOT_STATE_OBSERVER_SHAPE,
|
|
133
93
|
} from './root-seam-contract';
|
|
@@ -150,44 +110,16 @@ export {
|
|
|
150
110
|
seamStageAtLeast,
|
|
151
111
|
} from './seam-evidence';
|
|
152
112
|
export type {
|
|
153
|
-
AudioAdapter,
|
|
154
|
-
AudioDebugEvent,
|
|
155
|
-
AudioGraphNode,
|
|
156
|
-
AudioMeterFrame,
|
|
157
|
-
AudioMeterHandle,
|
|
158
|
-
AudioRecordingHandle,
|
|
159
|
-
AudioTransportState,
|
|
160
113
|
CameraAdapter,
|
|
161
114
|
CameraRuntimeCamera,
|
|
162
115
|
CameraRuntimeSnapshot,
|
|
163
116
|
CameraRuntimeTransition,
|
|
164
|
-
ConnectionState,
|
|
165
|
-
DebugAdapter,
|
|
166
|
-
NavCrowdAgentState,
|
|
167
|
-
NavigationAdapter,
|
|
168
|
-
NavPoint,
|
|
169
|
-
NetConditioning,
|
|
170
|
-
NetMessageEvent,
|
|
171
|
-
NetPeer,
|
|
172
|
-
NetPlayerIdentity,
|
|
173
|
-
NetConditioningLimits,
|
|
174
|
-
NetRates,
|
|
175
|
-
NetServerClient,
|
|
176
|
-
NetServerInspection,
|
|
177
|
-
NetServerRoom,
|
|
178
|
-
NetTypeTraffic,
|
|
179
|
-
NetEntityTraffic,
|
|
180
|
-
NetServerConfig,
|
|
181
|
-
NetworkingAdapter,
|
|
182
117
|
PhysicsAdapter,
|
|
183
118
|
PhysicsCarrier,
|
|
184
119
|
PhysicsColliderShape,
|
|
185
120
|
PhysicsColliderSnapshot,
|
|
186
121
|
PhysicsJointSnapshot,
|
|
187
122
|
PhysicsJointType,
|
|
188
|
-
RenderDebugAdapter,
|
|
189
|
-
ReplicationStats,
|
|
190
|
-
RoomInfo,
|
|
191
123
|
SystemAdapters,
|
|
192
124
|
Unsubscribe,
|
|
193
125
|
} from './system-adapter';
|
|
@@ -195,14 +127,9 @@ export type {
|
|
|
195
127
|
* `PhysicsAdapter`'s comment for why it is tagged at all. */
|
|
196
128
|
export { displayKeyedPhysics, nodeKeyedPhysics, physicsOwnerOf } from './system-adapter';
|
|
197
129
|
export {
|
|
198
|
-
AUDIO_ADAPTER_SHAPE,
|
|
199
130
|
CAMERA_ADAPTER_SHAPE,
|
|
200
|
-
DEBUG_ADAPTER_SHAPE,
|
|
201
|
-
NAVIGATION_ADAPTER_SHAPE,
|
|
202
|
-
NETWORKING_ADAPTER_SHAPE,
|
|
203
131
|
PHYSICS_2D_ADAPTER_SHAPE,
|
|
204
132
|
PHYSICS_ADAPTER_SHAPE,
|
|
205
|
-
RENDER_DEBUG_ADAPTER_SHAPE,
|
|
206
133
|
SYSTEM_ADAPTERS_SHAPE,
|
|
207
134
|
SYSTEM_PROVIDER_SHAPES,
|
|
208
135
|
} from './system-seam-contract';
|
|
@@ -1,27 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* never by reading `manifest.roots` directly (the guard is
|
|
7
|
-
* `packages/engine/test/manifest-sole-interpreter.test.ts`).
|
|
2
|
+
* Pure manifest interpretation shared by host consumers. These functions
|
|
3
|
+
* turn loaded inventory into adapter bindings without evaluating project
|
|
4
|
+
* modules. Their structural input types accept resolved or lenient edit-mode
|
|
5
|
+
* manifest views.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* interpreter gets the project's answer for free; a path that read the raw
|
|
13
|
-
* field would silently keep the mechanical one.
|
|
14
|
-
*
|
|
15
|
-
* The module-free audiences named by the ruling (a bundled CLI's raw
|
|
16
|
-
* discovery over unvalidated files, Vite config time, synchronous
|
|
17
|
-
* dep-optimization, and the loader that CONSTRUCTS the resolution) remain
|
|
18
|
-
* direct readers by design; everything else asks here.
|
|
19
|
-
*
|
|
20
|
-
* These functions are pure and synchronous over an already-loaded manifest —
|
|
21
|
-
* interpreting inventory never evaluates project modules, so any host realm
|
|
22
|
-
* may import this. They are structurally generic so both the loader's
|
|
23
|
-
* `ResolvedGameManifest` and the editor's lenient `EditModeManifest` view
|
|
24
|
-
* flow through the same doorway.
|
|
7
|
+
* Raw discovery, config-time dependency optimization and the loader that
|
|
8
|
+
* constructs resolution read inventory directly; execution hosts use these
|
|
9
|
+
* derivations.
|
|
25
10
|
*/
|
|
26
11
|
|
|
27
12
|
/**
|
|
@@ -34,31 +19,3 @@ export function declaredRoots<Root>(manifest: {
|
|
|
34
19
|
}): readonly Root[] {
|
|
35
20
|
return manifest.roots;
|
|
36
21
|
}
|
|
37
|
-
|
|
38
|
-
/** The root declaring `id`, or undefined — never a silent first-wins pick. */
|
|
39
|
-
export function rootById<Root extends { readonly id: string }>(
|
|
40
|
-
manifest: { readonly roots: readonly Root[] },
|
|
41
|
-
id: string,
|
|
42
|
-
): Root | undefined {
|
|
43
|
-
return manifest.roots.find((root) => root.id === id);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** Whether any declared root mounts on `surface` (e.g. the editor's
|
|
47
|
-
* "does this project have a Three world" gates). The parameter is the closed
|
|
48
|
-
* surface union, not `string` — a typo'd literal must stay a compile error,
|
|
49
|
-
* not a silent forever-false. */
|
|
50
|
-
export function hasRootOnSurface(
|
|
51
|
-
manifest: { readonly roots: readonly { readonly surface: string }[] },
|
|
52
|
-
surface: 'three' | 'canvas' | 'dom',
|
|
53
|
-
): boolean {
|
|
54
|
-
return manifest.roots.some((root) => root.surface === surface);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** The declared ingest roots (`adapter.type === 'ingest'`), in mount order.
|
|
58
|
-
* Composite-manifest rules (exactly one, ≥2 is a loud named error) belong to
|
|
59
|
-
* the call sites that state them. */
|
|
60
|
-
export function ingestRoots<
|
|
61
|
-
Root extends { readonly adapter: { readonly type: string } },
|
|
62
|
-
>(manifest: { readonly roots: readonly Root[] }): Root[] {
|
|
63
|
-
return manifest.roots.filter((root) => root.adapter.type === 'ingest');
|
|
64
|
-
}
|
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* PhysicsAdapter2D — the Pixi analog of the 3D PhysicsAdapter (rapier-physics-adapter).
|
|
9
|
-
* Lets the editor coordinate editing of a Rapier-2D-driven entity WITHOUT owning the
|
|
10
|
-
* simulation: report transform ownership, then freeze → commit → unfreeze so a drag
|
|
11
|
-
* sticks instead of being stomped by the next physics step.
|
|
12
|
-
*
|
|
13
|
-
* KEYED BY THE DISPLAY OBJECT, and that is this surface's own vocabulary rather
|
|
14
|
-
* than a shortcut: the canvas gizmo's real caller
|
|
15
|
-
* (`editor/src/authoring/pixi-live-write-target.ts`) resolves node id → display
|
|
16
|
-
* itself before it asks, so there is no node-id hop on this lane at all.
|
|
17
|
-
*
|
|
18
|
-
* `keyedBy` is REQUIRED here and optional on the 3D {@link PhysicsAdapter} —
|
|
19
|
-
* see that interface's doc comment for why the union has to be tagged, and why
|
|
20
|
-
* the newcomer is the shape obliged to say so.
|
|
2
|
+
* Coordinate canvas physics edits through freeze, commit and unfreeze, keyed by the native
|
|
3
|
+
* display object. The canvas caller resolves node ids before entering this seam. The required
|
|
4
|
+
* display tag distinguishes it from the node-id physics contract in system-adapter.ts.
|
|
21
5
|
*/
|
|
22
6
|
export interface PhysicsAdapter2D<D = unknown> {
|
|
23
|
-
/** Discriminates this shape
|
|
24
|
-
* inside `SystemAdapters['physics']`. */
|
|
7
|
+
/** Discriminates this display-keyed shape inside SystemAdapters.physics. */
|
|
25
8
|
readonly keyedBy: 'display';
|
|
26
9
|
ownerOf(display: D): 'physics' | 'none';
|
|
27
10
|
freeze(display: D): void;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* RootAdapter — the host ⇄ runtime contract. A ROOT is *anything that mounts*.
|
|
3
3
|
*
|
|
4
4
|
* This is the interface the host depends on. First-party content and
|
|
5
|
-
* unmodified external
|
|
5
|
+
* unmodified external projects are peer root adapters. A project is the
|
|
6
6
|
* manifest's complete roots array, while each adapter owns exactly one
|
|
7
7
|
* root.
|
|
8
8
|
*/
|
|
@@ -10,131 +10,78 @@
|
|
|
10
10
|
import type { AdapterSurface } from './adapter-surface';
|
|
11
11
|
import type { AuthoringAdapter } from './authoring';
|
|
12
12
|
import type { HostContextFor } from './host-context';
|
|
13
|
-
import type { WorldRendererConfig } from './renderer-config';
|
|
14
13
|
import type { SystemAdapters } from './system-adapter';
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* shared state (that's D7's answer for first-party↔first-party flow).
|
|
21
|
-
* Optional on {@link MountedThreeRoot} —
|
|
22
|
-
* absence means the adapter has nothing genuinely observable to expose (the
|
|
23
|
-
* anti-shim rule: never fabricate state), and the world takes the honest
|
|
24
|
-
* "no state bridge" tier hit (see `Game.registerRoot` in `runtime/game.ts`)
|
|
25
|
-
* rather than silently offering an `undefined`-forever subscription.
|
|
16
|
+
* An adapter-provided observation interface for a mounted root’s state.
|
|
17
|
+
* Optional absence means the root exposes no state bridge. Consumers report
|
|
18
|
+
* that absence rather than fabricating state or an undefined-only subscription.
|
|
26
19
|
*/
|
|
27
20
|
export interface RootStateObserver {
|
|
28
21
|
/**
|
|
29
|
-
* Notified at most once per frame IF the adapter can hook the
|
|
30
|
-
* update (`loop: 'gated'` roots
|
|
22
|
+
* Notified at most once per frame IF the adapter can hook the project's own
|
|
23
|
+
* update (`loop: 'gated'` roots); self-driven roots (`loop:
|
|
31
24
|
* 'self-driven'`, raw-rAF) may notify on their OWN rAF cadence instead —
|
|
32
25
|
* consumers must not assume our frame timing. Returns an unsubscribe
|
|
33
26
|
* function.
|
|
34
27
|
*/
|
|
35
28
|
subscribe(onChange: () => void): () => void;
|
|
36
29
|
/**
|
|
37
|
-
* A cheap
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `GameStateBridge`'s frame-version cache). Shape is adapter-defined —
|
|
41
|
-
* not centrally schematized in v1.
|
|
30
|
+
* A cheap snapshot of the root’s observable state. Preserve its reference
|
|
31
|
+
* while unchanged so selectors can skip recomputation. The adapter owns
|
|
32
|
+
* the snapshot’s shape.
|
|
42
33
|
*/
|
|
43
34
|
snapshot(): unknown;
|
|
44
35
|
}
|
|
45
36
|
|
|
46
37
|
/**
|
|
47
38
|
* Everything a mounted world provides except its render surface. Tagged
|
|
48
|
-
* mounted-root variants carry their native surface fields: a
|
|
39
|
+
* mounted-root variants carry their native surface fields: a stage scene and
|
|
49
40
|
* camera, a declared canvas substrate root, or a DOM container.
|
|
50
41
|
*/
|
|
51
42
|
export interface MountedRootBase {
|
|
52
43
|
/**
|
|
53
|
-
* Loop
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* The host does NOT tick it. Pause/step control is a DECLARED CAPABILITY,
|
|
58
|
-
* not a promise: implement `setPaused`/`step` only where the game exposes
|
|
59
|
-
* a sanctioned pause/step mechanism. Gating a raw-rAF loop from outside
|
|
60
|
-
* was demonstrated and REJECTED (D5 — it halts the loop rather than
|
|
61
|
-
* pausing it). Where `setPaused` is absent the host reports the
|
|
62
|
-
* shortfall loudly, naming the mechanism that would close it (T7.6).
|
|
44
|
+
* Loop ownership: host-driven roots receive `update(dt)` from the host;
|
|
45
|
+
* self-driven roots own their rendering loop and are not ticked by the host.
|
|
46
|
+
* Expose pause/step only when the project supplies those operations. Missing
|
|
47
|
+
* pause support is reported as a capability shortfall.
|
|
63
48
|
*/
|
|
64
49
|
readonly drivesOwnLoop: boolean;
|
|
65
50
|
|
|
66
51
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* the callback runs once per consumed simulation substep. `display` is for
|
|
71
|
-
* a reconciler whose one native advance is inseparably BOTH its callbacks
|
|
72
|
-
* and its draw (React Three Fiber's `advance()` is the measured case). The
|
|
73
|
-
* real host then withholds it from fixed catch-up substeps and invokes it
|
|
74
|
-
* exactly once from the display pass. A direct/offline `runFrame` that does
|
|
75
|
-
* not skip render phases still invokes it once, preserving capture and
|
|
76
|
-
* explicit-tick semantics.
|
|
77
|
-
*
|
|
78
|
-
* Absent means `fixed`. This changes no foreign/ingest adapter unless that
|
|
79
|
-
* adapter opts in explicitly.
|
|
52
|
+
* Host clock for the mounted root’s `update(dt)` hook.
|
|
53
|
+
* `fixed` requests simulation substeps. `display` requests one update per
|
|
54
|
+
* display pass for reconcilers whose advance combines callbacks and drawing.
|
|
80
55
|
*/
|
|
81
56
|
readonly updateCadence?: 'fixed' | 'display';
|
|
82
57
|
|
|
83
58
|
update?(dt: number): void; // host-driven only
|
|
84
59
|
fixedUpdate?(dt: number): void;
|
|
85
|
-
setPaused?(paused: boolean): void; //
|
|
60
|
+
setPaused?(paused: boolean): void; // optional adapter-owned pause operation
|
|
86
61
|
step?(): void;
|
|
87
62
|
|
|
88
63
|
resize?(width: number, height: number): void;
|
|
89
|
-
/** Does this world claim a pointer at (x, y), in CSS pixels from its surface's top-left? The input
|
|
90
|
-
* router asks it of a world stacked above the bottom one (a 2D layer's interactive Pixi objects).
|
|
91
|
-
* Absent: such a world claims no point, and input falls through to the bottom world. */
|
|
92
|
-
hitTest?(x: number, y: number): boolean;
|
|
93
64
|
dispose(): void;
|
|
94
65
|
/** Resolves after an asynchronous native reconciler has run every component
|
|
95
66
|
* cleanup started by {@link dispose}. Omitted by synchronously-disposing
|
|
96
|
-
* roots.
|
|
67
|
+
* roots. */
|
|
97
68
|
readonly disposeComplete?: Promise<void>;
|
|
98
69
|
|
|
99
70
|
/** Optional capability providers — absence = "not supported", host degrades. */
|
|
100
71
|
readonly authoring?: AuthoringAdapter;
|
|
101
72
|
readonly systems?: SystemAdapters;
|
|
102
|
-
/**
|
|
103
|
-
readonly systemScope?: object;
|
|
104
|
-
/** Optional state-observation capability (T7.4 slice 2, §4) — absent means
|
|
105
|
-
* "no state bridge"; `Game.registerRoot` reports this loudly, once, for
|
|
106
|
-
* self-driven mount. A host-driven mount ticks inside the game's frame,
|
|
107
|
-
* where the editor reads it directly, so it needs none. */
|
|
73
|
+
/** Optional state-observation capability. Absence means no state bridge. */
|
|
108
74
|
readonly observe?: RootStateObserver;
|
|
109
75
|
}
|
|
110
76
|
|
|
111
|
-
/** A live, mounted
|
|
112
|
-
* and author it; they are opaque here, and
|
|
113
|
-
*
|
|
114
|
-
export interface
|
|
115
|
-
readonly kind: '
|
|
77
|
+
/** A live, mounted `stage` world. The host obtains `scene`/`camera` to render
|
|
78
|
+
* and author it; they are opaque here, and a runtime extends the root with its
|
|
79
|
+
* own types. */
|
|
80
|
+
export interface MountedStageRoot<Scene = unknown, Camera = unknown> extends MountedRootBase {
|
|
81
|
+
readonly kind: 'stage';
|
|
116
82
|
/** The live scene + camera the editor inspects/renders for authoring. */
|
|
117
83
|
readonly scene: Scene;
|
|
118
84
|
readonly camera: Camera;
|
|
119
|
-
/**
|
|
120
|
-
* The colour pipeline this world was authored for, REPORTED rather than
|
|
121
|
-
* applied — the adapter has already applied it to the renderer its own host
|
|
122
|
-
* handed it (`./renderer-config.ts`).
|
|
123
|
-
*
|
|
124
|
-
* It is here because the host that MOUNTS a world is not always the host
|
|
125
|
-
* that DRAWS it. The editor's design session mounts against a
|
|
126
|
-
* non-rasterizing renderer on purpose and then draws the mounted scene with
|
|
127
|
-
* the editor viewport's own `WebGLRenderer`, so the world's declaration
|
|
128
|
-
* lands on a surface with no pixels while the surface with pixels never
|
|
129
|
-
* hears it. Reporting the declaration is what lets that host apply the same
|
|
130
|
-
* config, through the same `applyWorldRendererConfig`, to the renderer that
|
|
131
|
-
* actually produces the frame.
|
|
132
|
-
*
|
|
133
|
-
* Absent means the world declared nothing and the drawing host should leave
|
|
134
|
-
* its own configuration alone — the same "absent = don't touch" rule the
|
|
135
|
-
* config's own fields follow.
|
|
136
|
-
*/
|
|
137
|
-
readonly rendererConfig?: WorldRendererConfig | undefined;
|
|
138
85
|
}
|
|
139
86
|
|
|
140
87
|
/**
|
|
@@ -145,10 +92,6 @@ export interface MountedThreeRoot<Scene = unknown, Camera = unknown> extends Mou
|
|
|
145
92
|
export interface MountedCanvasSubstrate<T = unknown> {
|
|
146
93
|
readonly name: string;
|
|
147
94
|
readonly root: T;
|
|
148
|
-
/** The substrate's own module namespace when authoring needs constructors
|
|
149
|
-
* (for example Babylon's native GizmoManager). Omitted when the projection
|
|
150
|
-
* only needs the root itself, as Pixi's does. */
|
|
151
|
-
readonly api?: unknown;
|
|
152
95
|
}
|
|
153
96
|
|
|
154
97
|
/** A live, mounted canvas world. The host owns the canvas and lifecycle; the
|
|
@@ -161,23 +104,20 @@ export interface MountedCanvasRoot extends MountedRootBase {
|
|
|
161
104
|
readonly substrate: MountedCanvasSubstrate;
|
|
162
105
|
}
|
|
163
106
|
|
|
164
|
-
/** A
|
|
165
|
-
* {@link MountedThreeRoot}. `container` is the DOM-root layer the host
|
|
166
|
-
* handed the adapter's `mount` (the SAME element `RootInstance.reactRoot()`
|
|
167
|
-
* returns) — a react world's tree renders into it via `createRoot`. */
|
|
107
|
+
/** A mounted DOM world. Its container is the same host-owned element supplied to mount. */
|
|
168
108
|
export interface MountedReactRoot extends MountedRootBase {
|
|
169
109
|
readonly kind: 'dom';
|
|
170
110
|
readonly container: HTMLElement;
|
|
171
111
|
}
|
|
172
112
|
|
|
173
113
|
/** Every kind of live, mounted world. */
|
|
174
|
-
export type MountedRoot =
|
|
114
|
+
export type MountedRoot = MountedStageRoot | MountedCanvasRoot | MountedReactRoot;
|
|
175
115
|
|
|
176
116
|
/** Map a {@link AdapterSurface} to its mounted-world shape — lets generic
|
|
177
117
|
* code over
|
|
178
118
|
* `K extends AdapterSurface` name the right surface without a manual union. */
|
|
179
|
-
export type MountedRootFor<K extends AdapterSurface> = K extends '
|
|
180
|
-
?
|
|
119
|
+
export type MountedRootFor<K extends AdapterSurface> = K extends 'stage'
|
|
120
|
+
? MountedStageRoot
|
|
181
121
|
: K extends 'canvas'
|
|
182
122
|
? MountedCanvasRoot
|
|
183
123
|
: K extends 'dom'
|
|
@@ -185,27 +125,13 @@ export type MountedRootFor<K extends AdapterSurface> = K extends 'three'
|
|
|
185
125
|
: never;
|
|
186
126
|
|
|
187
127
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* types. Bare `RootAdapter` denotes the first-party Three surface.
|
|
191
|
-
*
|
|
192
|
-
* `K` parameterizes both halves of `mount`: `HostContextFor<K>` selects the
|
|
193
|
-
* host context and `MountedRootFor<K>` selects the mounted result. Production
|
|
194
|
-
* adapters are genuine `RootAdapter<K>` subtypes —
|
|
195
|
-
* `Pixi2DRootAdapter extends RootAdapter<'canvas'>`, `ReactRootAdapter extends
|
|
196
|
-
* RootAdapter<'dom'>` (`runtime/create-runtime.ts`).
|
|
197
|
-
*/
|
|
198
|
-
/**
|
|
199
|
-
* The `= 'three'` default is type-level only: an author who writes bare
|
|
200
|
-
* `RootAdapter` for
|
|
201
|
-
* a pixi root gets a compile error on `mount`'s return type, because
|
|
202
|
-
* `MountedRootFor<'three'>` demands `scene`/`camera` a `MountedCanvasRoot` has
|
|
203
|
-
* not got.
|
|
128
|
+
* A root adapter's surface selects both its host context and mounted result. The type-level
|
|
129
|
+
* default is stage; a canvas or DOM adapter declares its own surface to receive that context.
|
|
204
130
|
*/
|
|
205
|
-
export interface RootAdapter<K extends AdapterSurface = '
|
|
131
|
+
export interface RootAdapter<K extends AdapterSurface = 'stage'> {
|
|
206
132
|
/** Stable id (telemetry/registry/conformance). */
|
|
207
133
|
readonly id: string;
|
|
208
|
-
/** Build/start the
|
|
134
|
+
/** Build/start the project against the host context for ITS surface; return the
|
|
209
135
|
* handle. */
|
|
210
136
|
mount(host: HostContextFor<K>): Promise<MountedRootFor<K>>;
|
|
211
137
|
}
|
|
@@ -215,11 +141,11 @@ export interface RootAdapter<K extends AdapterSurface = 'three'> {
|
|
|
215
141
|
*
|
|
216
142
|
* A resolver that turns a manifest root into a mountable
|
|
217
143
|
* adapter handles every surface in one function, so its return type has to
|
|
218
|
-
* span all three. Returning the bare union `RootAdapter<'
|
|
144
|
+
* span all three. Returning the bare union `RootAdapter<'stage'> |
|
|
219
145
|
* RootAdapter<'canvas'> | RootAdapter<'dom'>` does NOT work — a union of
|
|
220
146
|
* function types INTERSECTS its parameters, so `.mount(host)` becomes
|
|
221
147
|
* uncallable with any single host value (no one object is simultaneously a
|
|
222
|
-
* `
|
|
148
|
+
* `StageHostContext`, a `CanvasHostContext` and a `DomHostContext`).
|
|
223
149
|
*
|
|
224
150
|
* Carrying the tag ALONGSIDE the adapter fixes that structurally: this is a
|
|
225
151
|
* discriminated union on `surface`, so `switch (resolved.surface)` narrows
|
|
@@ -234,10 +160,10 @@ export type SurfaceAdapterFor<K extends AdapterSurface> = {
|
|
|
234
160
|
|
|
235
161
|
/** Every surface's {@link SurfaceAdapterFor}, as one discriminated union.
|
|
236
162
|
* Written out member-by-member (rather than as a distributive conditional)
|
|
237
|
-
* so
|
|
163
|
+
* so an exhaustive switch sees exactly three variants and a hypothetical 4th
|
|
238
164
|
* `AdapterSurface` member fails to compile HERE, at the vocabulary, rather
|
|
239
165
|
* than silently widening every consumer's exhaustiveness guard. */
|
|
240
166
|
export type SurfaceAdapter =
|
|
241
|
-
| SurfaceAdapterFor<'
|
|
167
|
+
| SurfaceAdapterFor<'stage'>
|
|
242
168
|
| SurfaceAdapterFor<'canvas'>
|
|
243
169
|
| SurfaceAdapterFor<'dom'>;
|
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
MountedCanvasRoot,
|
|
6
6
|
MountedReactRoot,
|
|
7
7
|
MountedRootBase,
|
|
8
|
-
|
|
8
|
+
MountedStageRoot,
|
|
9
9
|
RootAdapter,
|
|
10
10
|
RootStateObserver,
|
|
11
11
|
} from './root-adapter';
|
|
@@ -24,12 +24,10 @@ export const MOUNTED_ROOT_BASE_SHAPE = defineSeamShape<MountedRootBase>()({
|
|
|
24
24
|
setPaused: { optional: true, kind: 'function', required: 'effect' },
|
|
25
25
|
step: { optional: true, kind: 'function', required: 'effect' },
|
|
26
26
|
resize: { optional: true, kind: 'function', required: 'effect' },
|
|
27
|
-
hitTest: { optional: true, kind: 'function', required: 'operation' },
|
|
28
27
|
dispose: { optional: false, kind: 'function', required: 'effect' },
|
|
29
28
|
disposeComplete: { optional: true, kind: 'value', required: 'effect' },
|
|
30
29
|
authoring: { optional: true, kind: 'value', required: 'operation' },
|
|
31
30
|
systems: { optional: true, kind: 'value', required: 'operation' },
|
|
32
|
-
systemScope: { optional: true, kind: 'value', required: 'operation' },
|
|
33
31
|
observe: { optional: true, kind: 'value', required: 'effect' },
|
|
34
32
|
});
|
|
35
33
|
|
|
@@ -38,15 +36,14 @@ export const ROOT_STATE_OBSERVER_SHAPE = defineSeamShape<RootStateObserver>()({
|
|
|
38
36
|
snapshot: { optional: false, kind: 'function', required: 'operation' },
|
|
39
37
|
});
|
|
40
38
|
|
|
41
|
-
type
|
|
39
|
+
type StageSurface = Omit<MountedStageRoot<object, object>, keyof MountedRootBase>;
|
|
42
40
|
type CanvasSurface = Omit<MountedCanvasRoot, keyof MountedRootBase>;
|
|
43
41
|
type DomSurface = Omit<MountedReactRoot, keyof MountedRootBase>;
|
|
44
42
|
|
|
45
|
-
export const
|
|
43
|
+
export const MOUNTED_STAGE_SURFACE_SHAPE = defineSeamShape<StageSurface>()({
|
|
46
44
|
kind: { optional: false, kind: 'value', required: 'shape' },
|
|
47
45
|
scene: { optional: false, kind: 'value', required: 'effect' },
|
|
48
46
|
camera: { optional: false, kind: 'value', required: 'effect' },
|
|
49
|
-
rendererConfig: { optional: true, kind: 'value', required: 'shape' },
|
|
50
47
|
});
|
|
51
48
|
|
|
52
49
|
export const MOUNTED_CANVAS_SURFACE_SHAPE = defineSeamShape<CanvasSurface>()({
|
|
@@ -1,35 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Two callers with nothing else in common depend on it: the engine's
|
|
7
|
-
* `runtime/input-router.ts`, which wires it to real canvases and forwards
|
|
8
|
-
* claimed events; and the EDITOR, which asks the same question at design time
|
|
9
|
-
* with no game running (`authoring/layered-pick.ts` picks through stacked
|
|
10
|
-
* authoring layers, `authoring/design-time-layers.ts` orders the layers it
|
|
11
|
-
* mounts). That second caller is why the file moved here 2026-09-18
|
|
12
|
-
* (WORK.md §The open-source launch, phase 1 unit 7): the host may import the
|
|
13
|
-
* adapter CONTRACT and must not import the game runtime, and this was the one
|
|
14
|
-
* `runtime/` module left in its closure. No DOM, no canvas, no mount.
|
|
2
|
+
* Pure stacking and point-ownership rules for adapter roots. Runtime and
|
|
3
|
+
* design-time authoring use the same z-order decisions without depending on
|
|
4
|
+
* DOM, canvas or mounting implementations.
|
|
15
5
|
*/
|
|
16
6
|
|
|
17
7
|
/** The pure decision inputs — no DOM. */
|
|
18
8
|
export interface ClaimEntry {
|
|
19
9
|
readonly id: string;
|
|
20
10
|
readonly zOrder: number;
|
|
21
|
-
/**
|
|
22
|
-
* Optional claim predicate over a point RELATIVE TO THE CONTAINER. Absent
|
|
23
|
-
* means: "this world claims only if it is the bottom (lowest zOrder)
|
|
24
|
-
* world" — D5 §2a's stated default ("transparent upper roots do not
|
|
25
|
-
* claim, the bottom world claims everything").
|
|
26
|
-
*/
|
|
27
|
-
readonly hitTest?: ((x: number, y: number) => boolean) | undefined;
|
|
28
11
|
}
|
|
29
12
|
|
|
30
13
|
/**
|
|
31
14
|
* Bottom-to-top paint order: ascending `zOrder`, ties -> original array
|
|
32
|
-
* order — the SAME rule `manifest/load.ts`'s `
|
|
15
|
+
* order — the SAME rule `manifest/load.ts`'s `loadProjectManifest` sorts
|
|
33
16
|
* roots by, so a world's DOM stacking position always matches its
|
|
34
17
|
* manifest-resolved zOrder. The last element is the topmost (visually on
|
|
35
18
|
* top / highest z-index).
|
|
@@ -40,28 +23,3 @@ export function stackOrder<T extends ClaimEntry>(entries: readonly T[]): T[] {
|
|
|
40
23
|
.sort((a, b) => a.entry.zOrder - b.entry.zOrder || a.index - b.index)
|
|
41
24
|
.map(({ entry }) => entry);
|
|
42
25
|
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Resolve which world's id claims a container-relative point `(x, y)` — D5
|
|
46
|
-
* §2a: walk top-down (topmost first); the first world whose `hitTest`
|
|
47
|
-
* returns true wins ("topmost-claim wins"). A world with no `hitTest` never
|
|
48
|
-
* claims UNLESS it is the bottom-most world, which claims unconditionally
|
|
49
|
-
* (or via its own `hitTest`, if it declares one) — "no-claim falls to
|
|
50
|
-
* bottom". Returns `null` only when `entries` is empty.
|
|
51
|
-
*/
|
|
52
|
-
export function resolveClaimingRoot(
|
|
53
|
-
entries: readonly ClaimEntry[],
|
|
54
|
-
x: number,
|
|
55
|
-
y: number,
|
|
56
|
-
): string | null {
|
|
57
|
-
if (entries.length === 0) return null;
|
|
58
|
-
const order = stackOrder(entries); // bottom -> top
|
|
59
|
-
for (let i = order.length - 1; i >= 1; i--) {
|
|
60
|
-
const entry = order[i]!;
|
|
61
|
-
if (entry.hitTest?.(x, y)) return entry.id;
|
|
62
|
-
}
|
|
63
|
-
// No upper world claimed (or there is only one world) -> the bottom world
|
|
64
|
-
// claims — honoring its own hitTest if it declares one (rare: the bottom
|
|
65
|
-
// world usually claims everything unconditionally per D5 §2a's default).
|
|
66
|
-
return order[0]!.id;
|
|
67
|
-
}
|
|
@@ -178,22 +178,6 @@ export class SeamEvidenceLedger {
|
|
|
178
178
|
);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
verdict(options: {
|
|
182
|
-
readonly seam: string;
|
|
183
|
-
readonly subject: string;
|
|
184
|
-
readonly epoch: string;
|
|
185
|
-
readonly required: SeamProofStage;
|
|
186
|
-
readonly notApplicable?: string | undefined;
|
|
187
|
-
}): SeamEvidenceVerdict {
|
|
188
|
-
return gradeSeamEvidence({ ...options, receipts: this.#receipts });
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
clearSubject(subject: string): void {
|
|
192
|
-
for (let index = this.#receipts.length - 1; index >= 0; index -= 1) {
|
|
193
|
-
if (this.#receipts[index]?.subject === subject) this.#receipts.splice(index, 1);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
181
|
clear(): void {
|
|
198
182
|
this.#receipts.length = 0;
|
|
199
183
|
}
|