create-aura3d 1.0.5 → 1.0.7

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/dist/cli.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ export function createA3DProject(options) {
28
28
  const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
29
29
  packageJson.dependencies = {
30
30
  ...(packageJson.dependencies ?? {}),
31
- "@aura3d/engine": options.packageVersion ?? "1.0.5"
31
+ "@aura3d/engine": options.packageVersion ?? "1.0.6"
32
32
  };
33
33
  writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`);
34
34
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-aura3d",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-aura3d": "dist/cli.js"
@@ -52,4 +52,4 @@
52
52
  "url": "https://github.com/auraoneai/aura3d/issues"
53
53
  },
54
54
  "license": "MIT"
55
- }
55
+ }
@@ -2,9 +2,9 @@
2
2
 
3
3
  This template demonstrates Aura3D's public game runtime APIs:
4
4
 
5
- - `createAuraApp(...)` for one mounted Aura app.
5
+ - `createGameApp(...)` for one mounted Aura game app with lifecycle evidence.
6
6
  - `.runtime(game.runtimeNode("id"))` for mutable fighter and camera nodes.
7
- - `app.input(...)` for app-owned keyboard/gamepad input.
7
+ - `gameApp.input` / `gameApp.inputController(...)` for runtime-owned keyboard/gamepad input.
8
8
  - `game.inputReplay(...)` and `game.inputReplayDriver(...)` for deterministic replay smoke paths.
9
9
  - `game.kinematicBody(...)` and `game.jumpAssist(...)` for 2.5D movement, gravity, coyote time, jump buffering, dash, and knockback.
10
10
  - `game.combatWorld(...)` for hitboxes, hurtboxes, guard, hit-stop, stun, recovery, and typed combat events.
@@ -43,8 +43,9 @@ Controls:
43
43
  Add fighter assets:
44
44
 
45
45
  ```bash
46
- npx @aura3d/cli@latest assets add ./assets/player-fighter.glb --name playerFighter
47
- npx @aura3d/cli@latest assets add ./assets/rival-fighter.glb --name rivalFighter
46
+ npx @aura3d/cli@latest assets search "animated humanoid fighting character" --profile fighting-character --json
47
+ npx @aura3d/cli@latest assets resolve "animated humanoid fighting character" --name playerFighter --profile fighting-character
48
+ npx @aura3d/cli@latest assets resolve "animated humanoid fighting character" --name rivalFighter --profile fighting-character
48
49
  ```
49
50
 
50
51
  The CLI writes typed definitions to `src/aura-assets.ts`. The starter imports
@@ -55,9 +56,14 @@ or `unsafeModelUrl(...)`.
55
56
  Before claiming the route is asset-ready, run:
56
57
 
57
58
  ```bash
58
- npx @aura3d/cli@latest assets validate-game
59
+ npx @aura3d/cli@latest assets validate-game --profile fighting-character --json
59
60
  ```
60
61
 
62
+ If the catalog returns no production-ready fighting-character candidate, keep
63
+ the route in placeholder mode and do not invent a GLB URL. The refusal is a
64
+ valid safety result; production proof requires two distinct typed fighter
65
+ assets that pass the profile gate.
66
+
61
67
  The source-level readiness declaration in `src/game/stage.ts` is not launch
62
68
  evidence. Before claiming build, package, visual, accessibility, or launch
63
69
  readiness, archive the matching command output, runtime evidence JSON,
@@ -11,7 +11,7 @@
11
11
  "test": "playwright test tests/route-health.spec.ts tests/gameplay-smoke.spec.ts --workers=1"
12
12
  },
13
13
  "dependencies": {
14
- "@aura3d/engine": "1.0.5"
14
+ "@aura3d/engine": "1.0.6"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@playwright/test": "^1.52.0",
@@ -2,11 +2,11 @@ import { defineConfig } from "@playwright/test";
2
2
 
3
3
  export default defineConfig({
4
4
  webServer: {
5
- command: "npm run dev",
6
- url: "http://127.0.0.1:5173",
7
- reuseExistingServer: !process.env.CI
5
+ command: "npm run dev -- --port 5186",
6
+ url: "http://127.0.0.1:5186",
7
+ reuseExistingServer: false
8
8
  },
9
9
  use: {
10
- baseURL: "http://127.0.0.1:5173"
10
+ baseURL: "http://127.0.0.1:5186"
11
11
  }
12
12
  });
@@ -34,9 +34,10 @@ export const REQUIRED_FIGHTER_CLIPS: readonly FighterClip[] = [
34
34
  ];
35
35
 
36
36
  export const publicAssetInstructions = [
37
- "npx @aura3d/cli@latest assets add ./assets/player-fighter.glb --name playerFighter",
38
- "npx @aura3d/cli@latest assets add ./assets/rival-fighter.glb --name rivalFighter",
39
- "npx @aura3d/cli@latest assets validate-game"
37
+ "npx @aura3d/cli@latest assets search \"animated humanoid fighting character\" --profile fighting-character --json",
38
+ "npx @aura3d/cli@latest assets resolve \"animated humanoid fighting character\" --name playerFighter --profile fighting-character",
39
+ "npx @aura3d/cli@latest assets resolve \"animated humanoid fighting character\" --name rivalFighter --profile fighting-character",
40
+ "npx @aura3d/cli@latest assets validate-game --profile fighting-character --json"
40
41
  ] as const;
41
42
 
42
43
  export const fighterAnimationClips: readonly AuraNamedAnimationClipDefinition<FighterClip>[] = REQUIRED_FIGHTER_CLIPS.map((id) => ({
@@ -82,7 +83,7 @@ export function createFighterAnimationController(
82
83
  ): AnimationController<FighterClip> {
83
84
  return new AnimationController<FighterClip>({
84
85
  id: `${id}-animation-controller`,
85
- ...(asset ? { clipRegistry: asset } : {}),
86
+ ...(asset ? { clipRegistry: animationClipRegistry(asset) } : {}),
86
87
  clips: fighterAnimationClips,
87
88
  requiredClips: REQUIRED_FIGHTER_CLIPS,
88
89
  suppressRootMotion: true,
@@ -93,6 +94,17 @@ export function createFighterAnimationController(
93
94
  });
94
95
  }
95
96
 
97
+ function animationClipRegistry(asset: AuraAssetRef<"model">) {
98
+ const metadata: Record<string, unknown> = { ...(asset.metadata ?? {}) };
99
+ return {
100
+ id: asset.id,
101
+ name: asset.id,
102
+ url: asset.url,
103
+ hash: asset.hash,
104
+ metadata
105
+ };
106
+ }
107
+
96
108
  export function createFighterNode(
97
109
  id: FighterId,
98
110
  assetKey: FighterAssetKey,
@@ -30,12 +30,12 @@ export const fightingRouteReadiness = {
30
30
  entry: "src/main.ts",
31
31
  sourceFiles: ["src/main.ts", "src/aura-assets.ts", "src/game/fighters.ts", "src/game/moves.ts", "src/game/stage.ts"],
32
32
  publicEngineApis: [
33
- "createAuraApp",
33
+ "createGameApp",
34
34
  "scene",
35
35
  "model(typed AuraAssetRef)",
36
36
  "game.runtimeNode",
37
- "app.onFrame",
38
- "app.input",
37
+ "gameApp.onFrame",
38
+ "gameApp.input",
39
39
  "game.evidence",
40
40
  "game.kinematicBody",
41
41
  "game.jumpAssist",
@@ -57,7 +57,7 @@ export const fightingRouteReadiness = {
57
57
  gameplaySmokeSpec: "tests/gameplay-smoke.spec.ts",
58
58
  localCommandsNotRunByTemplate: ["npm run typecheck", "npm run build", "npm run test"],
59
59
  evidenceRequiredBeforeLaunchReady: [
60
- "npx @aura3d/cli@latest assets validate-game output",
60
+ "npx @aura3d/cli@latest assets validate-game --profile fighting-character --json output",
61
61
  "npm run test scaffold output proving route load and replay hit",
62
62
  "browser screenshot showing a typed GLB fighter moved by runtime node mutation",
63
63
  "archived collectGameRuntimeEvidence/app.evidence JSON under tests/reports/game-runtime/",
@@ -1,4 +1,4 @@
1
- import { camera, createAuraApp, effects, game, lights, scene, ui } from "@aura3d/engine";
1
+ import { camera, createGameApp, effects, game, lights, scene, ui } from "@aura3d/engine";
2
2
  import { assets } from "./aura-assets";
3
3
  import {
4
4
  animationLayer,
@@ -25,6 +25,7 @@ import "./styles.css";
25
25
  type Aura3DGameWindow = Window & {
26
26
  __AURA3D_GAME_DEBUG__?: unknown;
27
27
  __AURA3D_GAME_EVIDENCE__?: unknown;
28
+ __AURA3D_GAME_RUNTIME__?: unknown;
28
29
  __AURA3D_GAME_REPLAY__?: unknown;
29
30
  __AURA3D_GAME_SOURCE__?: unknown;
30
31
  };
@@ -112,6 +113,18 @@ const accessibilitySources = [
112
113
  })
113
114
  ];
114
115
 
116
+ const inputOptions = {
117
+ actions: {
118
+ ...fightingControls.actions,
119
+ guard: ["KeyQ", "GamepadLB"],
120
+ heavy: ["KeyK", "GamepadY"],
121
+ pause: ["Escape", "GamepadStart"]
122
+ },
123
+ axes: fightingControls.axes,
124
+ bufferMs: 150,
125
+ gamepad: true
126
+ } as const;
127
+
115
128
  const arena = scene()
116
129
  .background("#10071c")
117
130
  .addMany(fightingStage.nodes)
@@ -120,29 +133,20 @@ const arena = scene()
120
133
  .addMany([
121
134
  effects.bloom({ intensity: 0.32 }),
122
135
  lights.studio({ intensity: 1.15 }),
123
- lights.directional({ name: "rim light", color: "#80ffd4", intensity: 0.7 }).position(0, 4, 3),
124
- camera
125
- .perspective({ position: [0, 1.75, 5.8], target: [0, 0.85, 0], fov: 42 })
126
- ]);
136
+ lights.directional({ name: "rim light", color: "#80ffd4", intensity: 0.7 }).position(0, 4, 3)
137
+ ])
138
+ .camera(camera.perspective({ position: [0, 1.75, 5.8], target: [0, 0.85, 0], fov: 42 }));
127
139
 
128
- const app = createAuraApp("#app", {
140
+ const gameApp = createGameApp("#app", {
129
141
  diagnostics: { overlay: true, performancePanel: true },
142
+ input: inputOptions,
143
+ loop: { fixedDt: 1 / 60 },
130
144
  scene: arena
131
145
  });
132
146
 
133
- const inputOptions = {
134
- actions: {
135
- ...fightingControls.actions,
136
- guard: ["KeyQ", "GamepadLB"],
137
- heavy: ["KeyK", "GamepadY"],
138
- pause: ["Escape", "GamepadStart"]
139
- },
140
- axes: fightingControls.axes,
141
- bufferMs: 150,
142
- gamepad: true
143
- } as const;
144
-
145
- const input = app.input(inputOptions);
147
+ const app = gameApp.app;
148
+ const input = gameApp.input;
149
+ if (!input) throw new Error("create-aura3d fighting-game template failed to create runtime-owned input.");
146
150
  const replayInput = game.input({ ...inputOptions, autoListen: false, gamepad: false });
147
151
  const openingReplay = game.inputReplay(
148
152
  [
@@ -196,6 +200,11 @@ gameWindow.__AURA3D_GAME_SOURCE__ = {
196
200
  template: "fighting-game",
197
201
  package: "create-aura3d",
198
202
  publicEngineApi: true,
203
+ lifecycle: {
204
+ kind: gameApp.kind,
205
+ usesCreateGameApp: true,
206
+ runtimeEvidenceGlobal: "__AURA3D_GAME_RUNTIME__"
207
+ },
199
208
  typedAssetPattern: "src/aura-assets.ts",
200
209
  typedAssetKeys: REQUIRED_FIGHTER_ASSETS,
201
210
  missingAssets: missingFighterAssets,
@@ -223,7 +232,7 @@ ui.onClick(replayButton, () => {
223
232
 
224
233
  ui.onClick(pauseButton, () => setPaused(!paused));
225
234
 
226
- app.onFrame(({ dt }) => {
235
+ gameApp.onFrame(({ dt }) => {
227
236
  const activeInput = replayActive ? replayInput : input;
228
237
  if (replayActive) {
229
238
  replayDriver.step(dt);
@@ -360,6 +369,7 @@ app.onFrame(({ dt }) => {
360
369
  totalHitCount,
361
370
  liveInputEvents: input.recorded()
362
371
  };
372
+ gameWindow.__AURA3D_GAME_RUNTIME__ = gameApp.evidence;
363
373
  gameWindow.__AURA3D_GAME_DEBUG__ = game.debug.overlay({
364
374
  runtime: app.runtime,
365
375
  input: activeInput,
@@ -397,8 +407,8 @@ function setPaused(next: boolean): void {
397
407
  paused = next;
398
408
  ui.setPressed(pauseButton, paused);
399
409
  ui.setText(pauseButton, paused ? "Resume" : "Pause");
400
- if (paused) app.pause();
401
- else app.resume();
410
+ if (paused) gameApp.pause();
411
+ else gameApp.resume();
402
412
  }
403
413
 
404
414
  function syncFighterAnimation(controller: ReturnType<typeof createFighterAnimationController>, clip: FighterClip, dt: number): void {
@@ -4,11 +4,23 @@ test("input replay produces runtime evidence and a hit declaration", async ({ pa
4
4
  await page.goto("/");
5
5
  await page.click("#hud-replay-button");
6
6
  await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_EVIDENCE__?.systems?.inputPlan));
7
+ await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_RUNTIME__?.kind === "aura-game-app-runtime-evidence"));
7
8
  await page.waitForFunction(() => ((window as any).__AURA3D_GAME_REPLAY__?.hitCount ?? 0) > 0);
8
9
  const evidence = await page.evaluate(() => (window as any).__AURA3D_GAME_EVIDENCE__);
10
+ const runtime = await page.evaluate(() => (window as any).__AURA3D_GAME_RUNTIME__);
9
11
  const replay = await page.evaluate(() => (window as any).__AURA3D_GAME_REPLAY__);
10
12
  const source = await page.evaluate(() => (window as any).__AURA3D_GAME_SOURCE__);
11
13
 
14
+ expect(runtime).toMatchObject({
15
+ status: "running",
16
+ running: true,
17
+ started: true,
18
+ startCount: 1,
19
+ inputControllers: 1,
20
+ activeInputControllers: 1
21
+ });
22
+ expect(runtime.frame).toBeGreaterThan(0);
23
+ expect(runtime.loop.frame).toBeGreaterThan(0);
12
24
  expect(evidence.systems.mutableNodes).toBeTruthy();
13
25
  expect(evidence.systems.inputPlan).toBeTruthy();
14
26
  expect(evidence.systems.physicsPlan).toBeTruthy();
@@ -7,5 +7,10 @@ test("fighting-game route loads", async ({ page }) => {
7
7
  await page.waitForFunction(() => Boolean((window as any).__AURA3D_GAME_SOURCE__?.readiness));
8
8
  const source = await page.evaluate(() => (window as any).__AURA3D_GAME_SOURCE__);
9
9
  expect(source.readiness.route).toBe("/");
10
+ expect(source.lifecycle).toMatchObject({
11
+ kind: "aura-game-app-runtime",
12
+ usesCreateGameApp: true,
13
+ runtimeEvidenceGlobal: "__AURA3D_GAME_RUNTIME__"
14
+ });
10
15
  expect(source.readiness.buildDeclarations.routeHealthSpec).toBe("tests/route-health.spec.ts");
11
16
  });