@game_engine/runtime-headless 0.1.0-alpha

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.
@@ -0,0 +1,68 @@
1
+ import { SystemRegistry, InputState, InputButton, World, ComponentValue } from '@game_engine/core';
2
+ export { InputButton, InputState, createInputState } from '@game_engine/core';
3
+ import { Diagnostic, SceneDocument } from '@game_engine/scene';
4
+
5
+ type InputTimelineAction = "press" | "release" | "clear";
6
+ interface InputTimelineEvent {
7
+ frame: number;
8
+ action: InputTimelineAction;
9
+ button?: InputButton;
10
+ }
11
+ interface InputTimeline {
12
+ events: InputTimelineEvent[];
13
+ }
14
+ interface HeadlessRunnerOptions {
15
+ fixedDeltaSeconds?: number;
16
+ systems?: SystemRegistry;
17
+ input?: InputState;
18
+ }
19
+ interface RunSceneOptions extends HeadlessRunnerOptions {
20
+ cwd?: string;
21
+ }
22
+ interface RuntimeResult {
23
+ ok: boolean;
24
+ scenePath: string;
25
+ frames: number;
26
+ fixedDeltaSeconds: number;
27
+ elapsedSeconds: number;
28
+ input: InputState;
29
+ world?: World;
30
+ diagnostics: Diagnostic[];
31
+ }
32
+ interface WorldDump {
33
+ frame: number;
34
+ fixedDeltaSeconds: number;
35
+ elapsedSeconds: number;
36
+ entities: Array<{
37
+ id: string;
38
+ components: Record<string, ComponentValue>;
39
+ }>;
40
+ }
41
+ interface PlayInputTimelineOptions {
42
+ frames: number;
43
+ dt?: number;
44
+ fixedDeltaSeconds?: number;
45
+ systems?: SystemRegistry;
46
+ }
47
+ interface PlayInputTimelineResult {
48
+ world: World;
49
+ frames: number;
50
+ fixedDeltaSeconds: number;
51
+ elapsedSeconds: number;
52
+ input: InputState;
53
+ snapshot: WorldDump;
54
+ }
55
+ declare function runScene(scenePath: string, frames: number, options?: RunSceneOptions): Promise<RuntimeResult>;
56
+ declare function runSceneDocument(scene: SceneDocument, options: HeadlessRunnerOptions & {
57
+ scenePath?: string;
58
+ frames: number;
59
+ }): RuntimeResult;
60
+ declare function sceneToWorld(scene: SceneDocument, input?: InputState): World;
61
+ declare function playInputTimeline(world: World, timeline: InputTimeline, options: PlayInputTimelineOptions): PlayInputTimelineResult;
62
+ declare function dumpWorld(world: World, frame: number, fixedDeltaSeconds?: number): WorldDump;
63
+ declare function dumpSceneWorldToFile(scenePath: string, frame: number, outPath: string, options?: RunSceneOptions): Promise<RuntimeResult & {
64
+ outPath: string;
65
+ dump?: WorldDump;
66
+ }>;
67
+
68
+ export { type HeadlessRunnerOptions, type InputTimeline, type InputTimelineAction, type InputTimelineEvent, type PlayInputTimelineOptions, type PlayInputTimelineResult, type RunSceneOptions, type RuntimeResult, type WorldDump, dumpSceneWorldToFile, dumpWorld, playInputTimeline, runScene, runSceneDocument, sceneToWorld };
package/dist/index.js ADDED
@@ -0,0 +1,231 @@
1
+ // src/index.ts
2
+ import { mkdir, writeFile } from "fs/promises";
3
+ import path from "path";
4
+ import {
5
+ createBuiltInSystemRegistry,
6
+ createInputState,
7
+ World
8
+ } from "@game_engine/core";
9
+ import { hasErrors, loadExpandedSceneFile } from "@game_engine/scene";
10
+ import { createInputState as createInputState2, InputState as InputState2 } from "@game_engine/core";
11
+ var defaultFixedDeltaSeconds = 1 / 60;
12
+ async function runScene(scenePath, frames, options = {}) {
13
+ const resolvedScenePath = path.resolve(options.cwd ?? process.cwd(), scenePath);
14
+ const diagnostics = validateFrameCount(frames, resolvedScenePath);
15
+ if (hasErrors(diagnostics)) {
16
+ return {
17
+ ok: false,
18
+ scenePath: resolvedScenePath,
19
+ frames,
20
+ fixedDeltaSeconds: options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds,
21
+ elapsedSeconds: 0,
22
+ input: options.input ?? createInputState(),
23
+ diagnostics
24
+ };
25
+ }
26
+ const sceneResult = await loadExpandedSceneFile(resolvedScenePath);
27
+ diagnostics.push(...sceneResult.diagnostics);
28
+ if (!sceneResult.document || hasErrors(diagnostics)) {
29
+ return {
30
+ ok: false,
31
+ scenePath: resolvedScenePath,
32
+ frames,
33
+ fixedDeltaSeconds: options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds,
34
+ elapsedSeconds: 0,
35
+ input: options.input ?? createInputState(),
36
+ diagnostics
37
+ };
38
+ }
39
+ return runSceneDocument(sceneResult.document, {
40
+ scenePath: resolvedScenePath,
41
+ frames,
42
+ fixedDeltaSeconds: options.fixedDeltaSeconds,
43
+ systems: options.systems,
44
+ input: options.input
45
+ });
46
+ }
47
+ function runSceneDocument(scene, options) {
48
+ const scenePath = options.scenePath ?? "<scene>";
49
+ const diagnostics = validateFrameCount(options.frames, scenePath);
50
+ const fixedDeltaSeconds = options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds;
51
+ const input = options.input ?? createInputState();
52
+ if (hasErrors(diagnostics)) {
53
+ return {
54
+ ok: false,
55
+ scenePath,
56
+ frames: options.frames,
57
+ fixedDeltaSeconds,
58
+ elapsedSeconds: 0,
59
+ input,
60
+ diagnostics
61
+ };
62
+ }
63
+ const world = sceneToWorld(scene, input);
64
+ const systems = options.systems ?? createBuiltInSystemRegistry();
65
+ for (let frame = 1; frame <= options.frames; frame += 1) {
66
+ systems.runFrame(world, {
67
+ deltaSeconds: fixedDeltaSeconds,
68
+ frame,
69
+ input
70
+ });
71
+ }
72
+ return {
73
+ ok: true,
74
+ scenePath,
75
+ frames: options.frames,
76
+ fixedDeltaSeconds,
77
+ elapsedSeconds: roundDeterministic(options.frames * fixedDeltaSeconds),
78
+ input,
79
+ world,
80
+ diagnostics
81
+ };
82
+ }
83
+ function sceneToWorld(scene, input = createInputState()) {
84
+ const world = new World(input);
85
+ for (const entity of scene.entities) {
86
+ world.createEntity(entity.id, entity.components ?? {});
87
+ }
88
+ return world;
89
+ }
90
+ function playInputTimeline(world, timeline, options) {
91
+ assertFrameCount(options.frames, "<input timeline>");
92
+ const fixedDeltaSeconds = options.dt ?? options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds;
93
+ const systems = options.systems ?? createBuiltInSystemRegistry();
94
+ const eventsByFrame = bucketTimelineEvents(timeline, options.frames);
95
+ applyTimelineEvents(world.input, eventsByFrame.get(0) ?? []);
96
+ for (let frame = 1; frame <= options.frames; frame += 1) {
97
+ applyTimelineEvents(world.input, eventsByFrame.get(frame) ?? []);
98
+ systems.runFrame(world, {
99
+ deltaSeconds: fixedDeltaSeconds,
100
+ frame,
101
+ input: world.input
102
+ });
103
+ }
104
+ applyTimelineEvents(world.input, eventsByFrame.get(options.frames + 1) ?? []);
105
+ return {
106
+ world,
107
+ frames: options.frames,
108
+ fixedDeltaSeconds,
109
+ elapsedSeconds: roundDeterministic(options.frames * fixedDeltaSeconds),
110
+ input: world.input,
111
+ snapshot: dumpWorld(world, options.frames, fixedDeltaSeconds)
112
+ };
113
+ }
114
+ function dumpWorld(world, frame, fixedDeltaSeconds = defaultFixedDeltaSeconds) {
115
+ return {
116
+ frame,
117
+ fixedDeltaSeconds,
118
+ elapsedSeconds: roundDeterministic(frame * fixedDeltaSeconds),
119
+ entities: world.listEntities().map((entity) => stableEntityDump(entity)).sort((left, right) => left.id.localeCompare(right.id))
120
+ };
121
+ }
122
+ async function dumpSceneWorldToFile(scenePath, frame, outPath, options = {}) {
123
+ const result = await runScene(scenePath, frame, options);
124
+ const resolvedOutPath = path.resolve(options.cwd ?? process.cwd(), outPath);
125
+ if (!result.world || !result.ok) {
126
+ return {
127
+ ...result,
128
+ outPath: resolvedOutPath
129
+ };
130
+ }
131
+ const dump = dumpWorld(result.world, frame, result.fixedDeltaSeconds);
132
+ await mkdir(path.dirname(resolvedOutPath), { recursive: true });
133
+ await writeFile(resolvedOutPath, `${JSON.stringify(dump, null, 2)}
134
+ `, "utf8");
135
+ return {
136
+ ...result,
137
+ outPath: resolvedOutPath,
138
+ dump
139
+ };
140
+ }
141
+ function stableEntityDump(entity) {
142
+ return {
143
+ id: entity.id,
144
+ components: stableObject(entity.components)
145
+ };
146
+ }
147
+ function stableObject(value) {
148
+ if (Array.isArray(value)) {
149
+ return value.map((item) => stableObject(item));
150
+ }
151
+ if (typeof value !== "object" || value === null) {
152
+ return value;
153
+ }
154
+ return Object.fromEntries(
155
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, stableObject(nested)])
156
+ );
157
+ }
158
+ function validateFrameCount(frames, file) {
159
+ if (Number.isInteger(frames) && frames >= 0) {
160
+ return [];
161
+ }
162
+ return [
163
+ {
164
+ severity: "error",
165
+ code: "RUNTIME_INVALID_FRAME_COUNT",
166
+ file,
167
+ path: "$.frames",
168
+ message: `Frame count must be a non-negative integer. Received '${frames}'.`,
169
+ suggestion: "Pass an integer frame count such as 60."
170
+ }
171
+ ];
172
+ }
173
+ function assertFrameCount(frames, file) {
174
+ const diagnostics = validateFrameCount(frames, file);
175
+ if (diagnostics.length > 0) {
176
+ throw new Error(diagnostics[0].message);
177
+ }
178
+ }
179
+ function roundDeterministic(value) {
180
+ return Number(value.toFixed(12));
181
+ }
182
+ function bucketTimelineEvents(timeline, frames) {
183
+ if (!timeline || !Array.isArray(timeline.events)) {
184
+ throw new Error("Input timeline must contain an events array.");
185
+ }
186
+ const eventsByFrame = /* @__PURE__ */ new Map();
187
+ for (const event of timeline.events) {
188
+ validateTimelineEvent(event, frames);
189
+ const events = eventsByFrame.get(event.frame) ?? [];
190
+ events.push(event);
191
+ eventsByFrame.set(event.frame, events);
192
+ }
193
+ return eventsByFrame;
194
+ }
195
+ function validateTimelineEvent(event, frames) {
196
+ if (!Number.isInteger(event.frame) || event.frame < 0 || event.frame > frames + 1) {
197
+ throw new Error(
198
+ `Input timeline event frame must be an integer from 0 to ${frames + 1}. Received '${event.frame}'.`
199
+ );
200
+ }
201
+ if (!["press", "release", "clear"].includes(event.action)) {
202
+ throw new Error(`Unknown input timeline action '${String(event.action)}'.`);
203
+ }
204
+ if ((event.action === "press" || event.action === "release") && typeof event.button !== "string") {
205
+ throw new Error(`Input timeline action '${event.action}' requires a button string.`);
206
+ }
207
+ }
208
+ function applyTimelineEvents(input, events) {
209
+ for (const event of events) {
210
+ if (event.action === "clear") {
211
+ input.clear();
212
+ continue;
213
+ }
214
+ if (event.action === "press") {
215
+ input.press(event.button);
216
+ continue;
217
+ }
218
+ input.release(event.button);
219
+ }
220
+ }
221
+ export {
222
+ InputState2 as InputState,
223
+ createInputState2 as createInputState,
224
+ dumpSceneWorldToFile,
225
+ dumpWorld,
226
+ playInputTimeline,
227
+ runScene,
228
+ runSceneDocument,
229
+ sceneToWorld
230
+ };
231
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n createBuiltInSystemRegistry,\n createInputState,\n type ComponentValue,\n type EntityRecord,\n InputState,\n type InputButton,\n type SystemRegistry,\n World\n} from \"@game_engine/core\";\nimport { hasErrors, loadExpandedSceneFile, type Diagnostic, type SceneDocument } from \"@game_engine/scene\";\n\nexport { createInputState, InputState, type InputButton } from \"@game_engine/core\";\n\nexport type InputTimelineAction = \"press\" | \"release\" | \"clear\";\n\nexport interface InputTimelineEvent {\n frame: number;\n action: InputTimelineAction;\n button?: InputButton;\n}\n\nexport interface InputTimeline {\n events: InputTimelineEvent[];\n}\n\nexport interface HeadlessRunnerOptions {\n fixedDeltaSeconds?: number;\n systems?: SystemRegistry;\n input?: InputState;\n}\n\nexport interface RunSceneOptions extends HeadlessRunnerOptions {\n cwd?: string;\n}\n\nexport interface RuntimeResult {\n ok: boolean;\n scenePath: string;\n frames: number;\n fixedDeltaSeconds: number;\n elapsedSeconds: number;\n input: InputState;\n world?: World;\n diagnostics: Diagnostic[];\n}\n\nexport interface WorldDump {\n frame: number;\n fixedDeltaSeconds: number;\n elapsedSeconds: number;\n entities: Array<{\n id: string;\n components: Record<string, ComponentValue>;\n }>;\n}\n\nexport interface PlayInputTimelineOptions {\n frames: number;\n dt?: number;\n fixedDeltaSeconds?: number;\n systems?: SystemRegistry;\n}\n\nexport interface PlayInputTimelineResult {\n world: World;\n frames: number;\n fixedDeltaSeconds: number;\n elapsedSeconds: number;\n input: InputState;\n snapshot: WorldDump;\n}\n\nconst defaultFixedDeltaSeconds = 1 / 60;\n\nexport async function runScene(\n scenePath: string,\n frames: number,\n options: RunSceneOptions = {}\n): Promise<RuntimeResult> {\n const resolvedScenePath = path.resolve(options.cwd ?? process.cwd(), scenePath);\n const diagnostics = validateFrameCount(frames, resolvedScenePath);\n if (hasErrors(diagnostics)) {\n return {\n ok: false,\n scenePath: resolvedScenePath,\n frames,\n fixedDeltaSeconds: options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds,\n elapsedSeconds: 0,\n input: options.input ?? createInputState(),\n diagnostics\n };\n }\n\n const sceneResult = await loadExpandedSceneFile(resolvedScenePath);\n diagnostics.push(...sceneResult.diagnostics);\n\n if (!sceneResult.document || hasErrors(diagnostics)) {\n return {\n ok: false,\n scenePath: resolvedScenePath,\n frames,\n fixedDeltaSeconds: options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds,\n elapsedSeconds: 0,\n input: options.input ?? createInputState(),\n diagnostics\n };\n }\n\n return runSceneDocument(sceneResult.document, {\n scenePath: resolvedScenePath,\n frames,\n fixedDeltaSeconds: options.fixedDeltaSeconds,\n systems: options.systems,\n input: options.input\n });\n}\n\nexport function runSceneDocument(\n scene: SceneDocument,\n options: HeadlessRunnerOptions & { scenePath?: string; frames: number }\n): RuntimeResult {\n const scenePath = options.scenePath ?? \"<scene>\";\n const diagnostics = validateFrameCount(options.frames, scenePath);\n const fixedDeltaSeconds = options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds;\n const input = options.input ?? createInputState();\n\n if (hasErrors(diagnostics)) {\n return {\n ok: false,\n scenePath,\n frames: options.frames,\n fixedDeltaSeconds,\n elapsedSeconds: 0,\n input,\n diagnostics\n };\n }\n\n const world = sceneToWorld(scene, input);\n const systems = options.systems ?? createBuiltInSystemRegistry();\n\n for (let frame = 1; frame <= options.frames; frame += 1) {\n systems.runFrame(world, {\n deltaSeconds: fixedDeltaSeconds,\n frame,\n input\n });\n }\n\n return {\n ok: true,\n scenePath,\n frames: options.frames,\n fixedDeltaSeconds,\n elapsedSeconds: roundDeterministic(options.frames * fixedDeltaSeconds),\n input,\n world,\n diagnostics\n };\n}\n\nexport function sceneToWorld(scene: SceneDocument, input: InputState = createInputState()): World {\n const world = new World(input);\n for (const entity of scene.entities) {\n world.createEntity(entity.id, entity.components ?? {});\n }\n return world;\n}\n\nexport function playInputTimeline(\n world: World,\n timeline: InputTimeline,\n options: PlayInputTimelineOptions\n): PlayInputTimelineResult {\n assertFrameCount(options.frames, \"<input timeline>\");\n const fixedDeltaSeconds = options.dt ?? options.fixedDeltaSeconds ?? defaultFixedDeltaSeconds;\n const systems = options.systems ?? createBuiltInSystemRegistry();\n const eventsByFrame = bucketTimelineEvents(timeline, options.frames);\n\n applyTimelineEvents(world.input, eventsByFrame.get(0) ?? []);\n\n for (let frame = 1; frame <= options.frames; frame += 1) {\n applyTimelineEvents(world.input, eventsByFrame.get(frame) ?? []);\n systems.runFrame(world, {\n deltaSeconds: fixedDeltaSeconds,\n frame,\n input: world.input\n });\n }\n\n applyTimelineEvents(world.input, eventsByFrame.get(options.frames + 1) ?? []);\n\n return {\n world,\n frames: options.frames,\n fixedDeltaSeconds,\n elapsedSeconds: roundDeterministic(options.frames * fixedDeltaSeconds),\n input: world.input,\n snapshot: dumpWorld(world, options.frames, fixedDeltaSeconds)\n };\n}\n\nexport function dumpWorld(world: World, frame: number, fixedDeltaSeconds = defaultFixedDeltaSeconds): WorldDump {\n return {\n frame,\n fixedDeltaSeconds,\n elapsedSeconds: roundDeterministic(frame * fixedDeltaSeconds),\n entities: world\n .listEntities()\n .map((entity) => stableEntityDump(entity))\n .sort((left, right) => left.id.localeCompare(right.id))\n };\n}\n\nexport async function dumpSceneWorldToFile(\n scenePath: string,\n frame: number,\n outPath: string,\n options: RunSceneOptions = {}\n): Promise<RuntimeResult & { outPath: string; dump?: WorldDump }> {\n const result = await runScene(scenePath, frame, options);\n const resolvedOutPath = path.resolve(options.cwd ?? process.cwd(), outPath);\n\n if (!result.world || !result.ok) {\n return {\n ...result,\n outPath: resolvedOutPath\n };\n }\n\n const dump = dumpWorld(result.world, frame, result.fixedDeltaSeconds);\n await mkdir(path.dirname(resolvedOutPath), { recursive: true });\n await writeFile(resolvedOutPath, `${JSON.stringify(dump, null, 2)}\\n`, \"utf8\");\n\n return {\n ...result,\n outPath: resolvedOutPath,\n dump\n };\n}\n\nfunction stableEntityDump(entity: EntityRecord): { id: string; components: Record<string, ComponentValue> } {\n return {\n id: entity.id,\n components: stableObject(entity.components) as Record<string, ComponentValue>\n };\n}\n\nfunction stableObject(value: ComponentValue): ComponentValue {\n if (Array.isArray(value)) {\n return value.map((item) => stableObject(item));\n }\n\n if (typeof value !== \"object\" || value === null) {\n return value;\n }\n\n return Object.fromEntries(\n Object.entries(value as Record<string, ComponentValue>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, stableObject(nested)])\n );\n}\n\nfunction validateFrameCount(frames: number, file: string): Diagnostic[] {\n if (Number.isInteger(frames) && frames >= 0) {\n return [];\n }\n\n return [\n {\n severity: \"error\",\n code: \"RUNTIME_INVALID_FRAME_COUNT\",\n file,\n path: \"$.frames\",\n message: `Frame count must be a non-negative integer. Received '${frames}'.`,\n suggestion: \"Pass an integer frame count such as 60.\"\n }\n ];\n}\n\nfunction assertFrameCount(frames: number, file: string): void {\n const diagnostics = validateFrameCount(frames, file);\n if (diagnostics.length > 0) {\n throw new Error(diagnostics[0]!.message);\n }\n}\n\nfunction roundDeterministic(value: number): number {\n return Number(value.toFixed(12));\n}\n\nfunction bucketTimelineEvents(timeline: InputTimeline, frames: number): Map<number, InputTimelineEvent[]> {\n if (!timeline || !Array.isArray(timeline.events)) {\n throw new Error(\"Input timeline must contain an events array.\");\n }\n\n const eventsByFrame = new Map<number, InputTimelineEvent[]>();\n\n for (const event of timeline.events) {\n validateTimelineEvent(event, frames);\n const events = eventsByFrame.get(event.frame) ?? [];\n events.push(event);\n eventsByFrame.set(event.frame, events);\n }\n\n return eventsByFrame;\n}\n\nfunction validateTimelineEvent(event: InputTimelineEvent, frames: number): void {\n if (!Number.isInteger(event.frame) || event.frame < 0 || event.frame > frames + 1) {\n throw new Error(\n `Input timeline event frame must be an integer from 0 to ${frames + 1}. Received '${event.frame}'.`\n );\n }\n\n if (![\"press\", \"release\", \"clear\"].includes(event.action)) {\n throw new Error(`Unknown input timeline action '${String(event.action)}'.`);\n }\n\n if ((event.action === \"press\" || event.action === \"release\") && typeof event.button !== \"string\") {\n throw new Error(`Input timeline action '${event.action}' requires a button string.`);\n }\n}\n\nfunction applyTimelineEvents(input: InputState, events: InputTimelineEvent[]): void {\n for (const event of events) {\n if (event.action === \"clear\") {\n input.clear();\n continue;\n }\n\n if (event.action === \"press\") {\n input.press(event.button!);\n continue;\n }\n\n input.release(event.button!);\n }\n}\n"],"mappings":"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","names":["createInputState","InputState"]}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@game_engine/runtime-headless",
3
+ "version": "0.1.0-alpha",
4
+ "description": "Deterministic headless scene loading, simulation, snapshots, traces, and world dumps for Agent Engine.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/rktkdduq01/game_engine.git",
9
+ "directory": "packages/runtime-headless"
10
+ },
11
+ "keywords": [
12
+ "agent-engine",
13
+ "game-engine",
14
+ "ai-agent",
15
+ "headless",
16
+ "simulation"
17
+ ],
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "scripts": {
34
+ "build": "tsup src/index.ts --format esm --dts --sourcemap --clean"
35
+ },
36
+ "dependencies": {
37
+ "@game_engine/core": "workspace:*",
38
+ "@game_engine/scene": "workspace:*"
39
+ },
40
+ "devDependencies": {
41
+ "tsup": "^8.3.5",
42
+ "typescript": "^5.7.2"
43
+ }
44
+ }