@auraindustry/aurajs 0.1.1 → 0.1.5

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.
Files changed (144) hide show
  1. package/README.md +7 -0
  2. package/benchmarks/perf-thresholds.json +27 -0
  3. package/package.json +6 -1
  4. package/src/ai-guidance.mjs +302 -0
  5. package/src/asset-pack.mjs +2 -1
  6. package/src/authored-project.mjs +498 -2
  7. package/src/authored-runtime.mjs +14 -0
  8. package/src/bin-integrity.mjs +33 -26
  9. package/src/build-contract/capabilities.mjs +87 -1
  10. package/src/build-contract/constants.mjs +1 -0
  11. package/src/build-contract.mjs +2 -0
  12. package/src/bundler.mjs +143 -13
  13. package/src/cli.mjs +681 -13
  14. package/src/commands/packs.mjs +741 -0
  15. package/src/commands/project-authoring.mjs +128 -1
  16. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  17. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  18. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  19. package/src/conformance/cases/systems-and-gameplay-cases.mjs +1126 -10
  20. package/src/conformance-mobile.mjs +166 -0
  21. package/src/conformance.mjs +89 -30
  22. package/src/evidence-bundle.mjs +242 -0
  23. package/src/external-package-surface.mjs +1 -1
  24. package/src/headless-test/runtime-coordinator.mjs +186 -33
  25. package/src/headless-test.mjs +2 -0
  26. package/src/helpers/2d/index.mjs +183 -0
  27. package/src/helpers/index.mjs +26 -0
  28. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  29. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  30. package/src/helpers/starter-utils/animation-2d.js +337 -0
  31. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  32. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  33. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  34. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  35. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  36. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  37. package/src/helpers/starter-utils/core.js +150 -0
  38. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  39. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  40. package/src/helpers/starter-utils/index.js +26 -0
  41. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  42. package/src/helpers/starter-utils/journal-2d.js +267 -0
  43. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  44. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  45. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  46. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  47. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  48. package/src/helpers/starter-utils/triggers.js +662 -0
  49. package/src/helpers/starter-utils/tween-2d.js +615 -0
  50. package/src/helpers/starter-utils/wave-director.js +101 -0
  51. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  52. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  53. package/src/mobile/android/build.mjs +606 -0
  54. package/src/mobile/android/host-artifact.mjs +280 -0
  55. package/src/mobile/ios/build.mjs +1323 -0
  56. package/src/mobile/ios/host-artifact.mjs +819 -0
  57. package/src/mobile/shared/capabilities.mjs +174 -0
  58. package/src/package-integrity.mjs +18 -4
  59. package/src/packs/catalog.mjs +259 -0
  60. package/src/perf-benchmark-runner.mjs +17 -12
  61. package/src/perf-benchmark.mjs +408 -4
  62. package/src/publish-command.mjs +434 -17
  63. package/src/publish-validation.mjs +22 -11
  64. package/src/replay-runtime.mjs +257 -0
  65. package/src/scaffold/config.mjs +2 -0
  66. package/src/scaffold/fs.mjs +8 -1
  67. package/src/scaffold/project-docs.mjs +101 -41
  68. package/src/scaffold.mjs +4 -0
  69. package/src/session-runtime.mjs +4 -3
  70. package/src/web-conformance.mjs +0 -36
  71. package/templates/create/2d/src/runtime/app.js +4 -0
  72. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  73. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  74. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  75. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  76. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  77. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  78. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  79. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  80. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  81. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  82. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  83. package/templates/create/2d-survivor/src/runtime/app.js +4 -0
  84. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  85. package/templates/create/3d/src/runtime/app.js +4 -0
  86. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  87. package/templates/create/3d/src/runtime/materials.js +10 -0
  88. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  89. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  90. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  91. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  92. package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
  93. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  94. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  95. package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
  96. package/templates/create/blank/assets/splash/bg.webp +0 -0
  97. package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
  98. package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
  99. package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
  100. package/templates/create/blank/assets/splash/logoholo.webp +0 -0
  101. package/templates/create/blank/src/main.js +5 -1
  102. package/templates/create/blank/src/runtime/splash.js +305 -0
  103. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
  104. package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
  105. package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
  106. package/templates/create/shared/assets/splash/bg.webp +0 -0
  107. package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
  108. package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
  109. package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
  110. package/templates/create/shared/assets/splash/logoholo.webp +0 -0
  111. package/templates/create/shared/src/runtime/splash.js +305 -0
  112. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  113. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  114. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  115. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  116. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  117. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  118. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  119. package/templates/create/shared/src/starter-utils/index.js +15 -1
  120. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  121. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  122. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  123. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  124. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  125. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  126. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  127. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  128. package/templates/create/video-cutscene/src/runtime/app.js +4 -0
  129. package/templates/create-bin/play.js +148 -7
  130. package/templates/skills/auramaxx/SKILL.md +46 -0
  131. package/templates/skills/auramaxx/project-requirements.md +68 -0
  132. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  133. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  134. package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
  135. package/templates/starter/assets/splash/bg.webp +0 -0
  136. package/templates/starter/assets/splash/boot-loop.wav +0 -0
  137. package/templates/starter/assets/splash/boot-sting.wav +0 -0
  138. package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
  139. package/templates/starter/assets/splash/logoholo.webp +0 -0
  140. package/templates/starter/src/main.js +4 -0
  141. package/templates/starter/src/runtime/splash.js +305 -0
  142. package/templates/skills/aurajs/SKILL.md +0 -96
  143. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  144. package/templates/skills/aurajs/api-contract.md +0 -7
@@ -217,6 +217,399 @@ export const DEFAULT_PERF_SCENES = [
217
217
  };
218
218
  `,
219
219
  },
220
+ {
221
+ id: 'native_2d_flagship_stress',
222
+ lane: 'stage183-native-2d',
223
+ surfaceTags: [
224
+ 'tilemap-mutation',
225
+ 'tilemap-query',
226
+ 'render-targets',
227
+ 'draw2d-compositor',
228
+ 'particles',
229
+ 'runtime-inspector',
230
+ ],
231
+ frames: 360,
232
+ source: `
233
+ let image = null;
234
+ let mapId = 0;
235
+ let worldTarget = null;
236
+ let lightTarget = null;
237
+ let finalTarget = null;
238
+ let elapsed = 0;
239
+ let frameIndex = 0;
240
+ let canUseParticles = false;
241
+ let lastStats = null;
242
+ const agents = [];
243
+ const coverCells = [];
244
+ const fallbackParticles = [];
245
+ const brokenCover = new Set();
246
+
247
+ function keyForCell(x, y) {
248
+ return x + ':' + y;
249
+ }
250
+
251
+ function buildMap() {
252
+ const width = 48;
253
+ const height = 32;
254
+ const tileCount = width * height;
255
+ const ground = Array.from({ length: tileCount }, () => 1);
256
+ const cover = Array.from({ length: tileCount }, () => 0);
257
+ const hazards = Array.from({ length: tileCount }, () => 0);
258
+
259
+ for (let y = 4; y < height - 4; y += 4) {
260
+ for (let x = 6; x < width - 6; x += 6) {
261
+ const coverIndex = (y * width) + x;
262
+ cover[coverIndex] = 1;
263
+ coverCells.push({ x, y });
264
+
265
+ const neighborX = Math.min(width - 1, x + 1);
266
+ const neighborIndex = (y * width) + neighborX;
267
+ cover[neighborIndex] = 1;
268
+ coverCells.push({ x: neighborX, y });
269
+ }
270
+ }
271
+
272
+ for (let x = 3; x < width - 3; x += 5) {
273
+ const y = 14 + ((Math.floor(x / 5)) % 5);
274
+ const index = (y * width) + x;
275
+ hazards[index] = 1;
276
+ }
277
+
278
+ return aura.tilemap.import({
279
+ width,
280
+ height,
281
+ tilewidth: 16,
282
+ tileheight: 16,
283
+ solidLayerNames: ['cover', 'hazards'],
284
+ layers: [
285
+ { name: 'ground', type: 'tilelayer', width, height, data: ground },
286
+ { name: 'cover', type: 'tilelayer', width, height, data: cover, properties: { solid: true } },
287
+ { name: 'hazards', type: 'tilelayer', width, height, data: hazards, properties: { solid: true } },
288
+ ],
289
+ tilesets: [
290
+ { firstgid: 1, image: 'player.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 },
291
+ ],
292
+ });
293
+ }
294
+
295
+ function seedAgents() {
296
+ for (let i = 0; i < 96; i += 1) {
297
+ agents.push({
298
+ x: 72 + ((i % 16) * 34),
299
+ y: 56 + (Math.floor(i / 16) * 28),
300
+ heading: (i * 0.37) % (Math.PI * 2),
301
+ speed: 26 + ((i % 7) * 4),
302
+ phase: i * 0.21,
303
+ radius: 6 + (i % 4),
304
+ hue: (i % 5) / 5,
305
+ });
306
+ }
307
+ }
308
+
309
+ function seedParticles() {
310
+ canUseParticles = !!(
311
+ aura.particles
312
+ && typeof aura.particles.emit === 'function'
313
+ && typeof aura.particles.update === 'function'
314
+ && typeof aura.particles.draw === 'function'
315
+ );
316
+
317
+ if (canUseParticles) {
318
+ for (let i = 0; i < 6; i += 1) {
319
+ const emitter = aura.particles.emit({
320
+ x: 92 + (i * 96),
321
+ y: 86 + ((i % 3) * 54),
322
+ count: 18,
323
+ life: 0.9,
324
+ size: 2,
325
+ speedMin: 16,
326
+ speedMax: 28,
327
+ direction: i * 0.31,
328
+ spread: 0.9,
329
+ loop: true,
330
+ rate: 24,
331
+ });
332
+ if (emitter?.emitterId && typeof aura.particles.setLayer === 'function') {
333
+ aura.particles.setLayer(emitter.emitterId, 2);
334
+ }
335
+ if (emitter?.emitterId && typeof aura.particles.setBlend === 'function') {
336
+ aura.particles.setBlend(emitter.emitterId, 'add');
337
+ }
338
+ }
339
+ return;
340
+ }
341
+
342
+ for (let i = 0; i < 96; i += 1) {
343
+ fallbackParticles.push({
344
+ originX: 84 + ((i % 12) * 44),
345
+ originY: 80 + (Math.floor(i / 12) * 22),
346
+ phase: i * 0.17,
347
+ radius: 4 + ((i % 5) * 2),
348
+ speed: 0.8 + ((i % 7) * 0.12),
349
+ });
350
+ }
351
+ }
352
+
353
+ function mutateTilemap() {
354
+ if (!mapId || !aura.tilemap || typeof aura.tilemap.setTile !== 'function') {
355
+ return;
356
+ }
357
+
358
+ const baseIndex = (frameIndex * 5) % coverCells.length;
359
+ for (let i = 0; i < 4; i += 1) {
360
+ const cell = coverCells[(baseIndex + (i * 11)) % coverCells.length];
361
+ const cellKey = keyForCell(cell.x, cell.y);
362
+ const shouldBreak = ((Math.floor(frameIndex / 18) + i) % 2) === 0;
363
+ if (shouldBreak && !brokenCover.has(cellKey)) {
364
+ aura.tilemap.setTile(mapId, 'cover', { x: cell.x, y: cell.y }, 0);
365
+ if (typeof aura.tilemap.setTileCollision === 'function') {
366
+ aura.tilemap.setTileCollision(mapId, 'cover', { x: cell.x, y: cell.y }, false);
367
+ }
368
+ brokenCover.add(cellKey);
369
+ continue;
370
+ }
371
+ if (!shouldBreak && brokenCover.has(cellKey)) {
372
+ aura.tilemap.setTile(mapId, 'cover', { x: cell.x, y: cell.y }, 1);
373
+ if (typeof aura.tilemap.setTileCollision === 'function') {
374
+ aura.tilemap.setTileCollision(mapId, 'cover', { x: cell.x, y: cell.y }, true);
375
+ }
376
+ brokenCover.delete(cellKey);
377
+ }
378
+ }
379
+
380
+ if ((frameIndex % 72) === 0 && typeof aura.tilemap.removeRegion === 'function') {
381
+ const column = 8 + (Math.floor(frameIndex / 72) % 8) * 4;
382
+ aura.tilemap.removeRegion(mapId, 'hazards', {
383
+ x: column,
384
+ y: 14,
385
+ w: 2,
386
+ h: 2,
387
+ });
388
+ }
389
+ }
390
+
391
+ function worldCamera() {
392
+ return {
393
+ x: 72 + ((Math.sin(elapsed * 0.6) + 1) * 76),
394
+ y: 42 + ((Math.cos(elapsed * 0.4) + 1) * 54),
395
+ width: 320,
396
+ height: 208,
397
+ };
398
+ }
399
+
400
+ function drawAgent(agent, options = {}) {
401
+ const width = 12 + (agent.radius * 2);
402
+ const height = 12 + (agent.radius * 2);
403
+ const tint = options.tint || aura.rgba(
404
+ 0.35 + (agent.hue * 0.45),
405
+ 0.75,
406
+ 1,
407
+ options.alpha ?? 0.92,
408
+ );
409
+
410
+ if (image) {
411
+ aura.draw2d.image(image, agent.x, agent.y, {
412
+ width,
413
+ height,
414
+ originX: 0.5,
415
+ originY: 0.5,
416
+ rotation: agent.heading,
417
+ tint,
418
+ alpha: options.alpha ?? 0.92,
419
+ });
420
+ return;
421
+ }
422
+
423
+ aura.draw2d.circle(agent.x, agent.y, agent.radius, tint);
424
+ }
425
+
426
+ function drawFallbackParticles() {
427
+ for (let i = 0; i < fallbackParticles.length; i += 1) {
428
+ const particle = fallbackParticles[i];
429
+ const theta = (elapsed * particle.speed * 2.2) + particle.phase;
430
+ aura.draw2d.circle(
431
+ particle.originX + Math.cos(theta) * particle.radius,
432
+ particle.originY + Math.sin(theta * 1.4) * particle.radius,
433
+ 2,
434
+ aura.rgba(0.78, 0.9, 1, 0.72),
435
+ );
436
+ }
437
+ }
438
+
439
+ function drawWorldStage() {
440
+ aura.draw2d.clear(2, 4, 8, 1);
441
+ aura.tilemap.draw(mapId, { camera: worldCamera() });
442
+
443
+ for (let i = 0; i < agents.length; i += 1) {
444
+ drawAgent(agents[i]);
445
+ }
446
+
447
+ if (canUseParticles) {
448
+ aura.particles.draw({ maxParticles: 768 });
449
+ } else {
450
+ drawFallbackParticles();
451
+ }
452
+ }
453
+
454
+ function drawLightStage(source) {
455
+ aura.draw2d.clear(0, 0, 0, 0);
456
+ aura.draw2d.sprite(source, 0, 0, { width: 384, height: 256 });
457
+ for (let i = 0; i < agents.length; i += 8) {
458
+ const agent = agents[i];
459
+ aura.draw2d.circleFill(
460
+ agent.x,
461
+ agent.y,
462
+ 18 + (Math.sin(elapsed * 3 + agent.phase) * 3),
463
+ aura.rgba(0.2, 0.85, 1, 0.12),
464
+ );
465
+ }
466
+ aura.draw2d.tileSprite(source, 0, 0, 384, 256, {
467
+ tileScaleX: 1.2,
468
+ tileScaleY: 1.2,
469
+ tileOffsetX: elapsed * 38,
470
+ tileOffsetY: elapsed * 22,
471
+ tint: aura.rgba(0.06, 0.12, 0.18, 0.24),
472
+ });
473
+ }
474
+
475
+ function drawFinalStage(worldSource, lightSource) {
476
+ aura.draw2d.clear(0, 0, 0, 0);
477
+ aura.draw2d.sprite(worldSource, 0, 0, { width: 384, height: 256 });
478
+ aura.draw2d.sprite(lightSource, 0, 0, { width: 384, height: 256, alpha: 0.94 });
479
+ aura.draw2d.rectFill(10, 10, 196, 28, aura.rgba(0.04, 0.07, 0.12, 0.84));
480
+ const particles = Number(lastStats?.phase2?.particles?.active || 0);
481
+ const tilemaps = Number(lastStats?.phase2?.tilemap?.active || 0);
482
+ const compositor = Number(lastStats?.draw2dRuntime?.compositor?.knownGraphCount || 0);
483
+ aura.draw2d.text(
484
+ '2D FLAGSHIP ' + particles + 'P ' + tilemaps + 'T ' + compositor + 'G',
485
+ 18,
486
+ 20,
487
+ { size: 12, color: aura.colors.white },
488
+ );
489
+ aura.draw2d.text(
490
+ 'frame ' + frameIndex + ' cover ' + brokenCover.size,
491
+ 18,
492
+ 34,
493
+ { size: 10, color: aura.colors.white },
494
+ );
495
+ }
496
+
497
+ aura.setup = function () {
498
+ image = aura.assets.image('player.png');
499
+ mapId = buildMap();
500
+ worldTarget = aura.draw2d.createRenderTarget(384, 256);
501
+ lightTarget = aura.draw2d.createRenderTarget(384, 256);
502
+ finalTarget = aura.draw2d.createRenderTarget(384, 256);
503
+ seedAgents();
504
+ seedParticles();
505
+ };
506
+
507
+ aura.update = function (dt) {
508
+ elapsed += dt;
509
+ frameIndex += 1;
510
+ mutateTilemap();
511
+ if (canUseParticles) {
512
+ aura.particles.update(dt);
513
+ }
514
+ lastStats = typeof aura.debug?.inspectorStats === 'function'
515
+ ? aura.debug.inspectorStats()
516
+ : null;
517
+
518
+ for (let i = 0; i < agents.length; i += 1) {
519
+ const agent = agents[i];
520
+ const theta = (elapsed * (0.9 + ((i % 5) * 0.14))) + agent.phase;
521
+ const dx = Math.cos(theta) * agent.speed * dt;
522
+ const dy = Math.sin(theta * 1.2) * agent.speed * dt;
523
+ const nextX = agent.x + dx;
524
+ const nextY = agent.y + dy;
525
+ const ahead = aura.tilemap.queryPoint(mapId, { x: nextX, y: nextY });
526
+ const sweep = aura.tilemap.queryAABB(mapId, {
527
+ x: nextX - agent.radius,
528
+ y: nextY - agent.radius,
529
+ w: agent.radius * 2,
530
+ h: agent.radius * 2,
531
+ });
532
+ const ray = aura.tilemap.queryRay(mapId, {
533
+ x: agent.x,
534
+ y: agent.y,
535
+ dx: Math.cos(agent.heading),
536
+ dy: Math.sin(agent.heading),
537
+ maxDistance: 22,
538
+ });
539
+
540
+ if (ahead?.hit || sweep?.hit || ray?.hit) {
541
+ agent.heading += 1.2 + ((i % 3) * 0.3);
542
+ } else {
543
+ agent.x = nextX;
544
+ agent.y = nextY;
545
+ agent.heading = theta;
546
+ }
547
+
548
+ if (agent.x < 24) agent.x = 24;
549
+ if (agent.y < 24) agent.y = 24;
550
+ if (agent.x > 740) agent.x = 740;
551
+ if (agent.y > 500) agent.y = 500;
552
+ }
553
+ };
554
+
555
+ aura.draw = function () {
556
+ aura.draw2d.clear(3, 5, 9);
557
+
558
+ let composed = finalTarget;
559
+
560
+ if (typeof aura.draw2d.runCompositorGraph === 'function') {
561
+ const graph = aura.draw2d.runCompositorGraph('perf-native-2d-flagship', [
562
+ { id: 'world', target: worldTarget, draw: drawWorldStage },
563
+ {
564
+ id: 'light',
565
+ target: lightTarget,
566
+ draw: () => drawLightStage({
567
+ type: 'compositorStage',
568
+ graph: 'perf-native-2d-flagship',
569
+ stage: 'world',
570
+ }),
571
+ },
572
+ {
573
+ id: 'final',
574
+ target: finalTarget,
575
+ draw: () => drawFinalStage(
576
+ {
577
+ type: 'compositorStage',
578
+ graph: 'perf-native-2d-flagship',
579
+ stage: 'world',
580
+ },
581
+ {
582
+ type: 'compositorStage',
583
+ graph: 'perf-native-2d-flagship',
584
+ stage: 'light',
585
+ },
586
+ ),
587
+ },
588
+ ]);
589
+ if (graph?.ok === true && Array.isArray(graph.stages) && graph.stages.length === 3) {
590
+ composed = graph.stages[2];
591
+ }
592
+ } else {
593
+ drawWorldStage();
594
+ return;
595
+ }
596
+
597
+ aura.draw2d.sprite(composed, 16, 16, {
598
+ width: 704,
599
+ height: 440,
600
+ });
601
+ aura.draw2d.sprite(composed, 744, 56, {
602
+ width: 256,
603
+ height: 160,
604
+ alpha: 0.88,
605
+ });
606
+ aura.draw2d.text('native_2d_flagship_stress', 748, 228, {
607
+ size: 11,
608
+ color: aura.colors.white,
609
+ });
610
+ };
611
+ `,
612
+ },
220
613
  {
221
614
  id: 'ui_composition_surface_stress',
222
615
  frames: 360,
@@ -910,16 +1303,24 @@ export async function runPerfBenchmarks(options = {}) {
910
1303
  const sceneReports = [];
911
1304
 
912
1305
  for (const scene of scenes) {
913
- const entryRelative = `src/${scene.id}.js`;
914
- const entryAbsolute = resolve(workspaceRoot, entryRelative);
915
- writeFileSync(entryAbsolute, scene.source.trimStart(), 'utf8');
1306
+ const hasInlineSource = typeof scene.source === 'string' && scene.source.trim().length > 0;
1307
+ const entryRelative = typeof scene.entryFile === 'string' && scene.entryFile.trim().length > 0
1308
+ ? scene.entryFile.trim()
1309
+ : `src/${scene.id}.js`;
1310
+ const sceneProjectRoot = hasInlineSource
1311
+ ? workspaceRoot
1312
+ : resolve(scene.projectRoot || projectRoot);
1313
+ if (hasInlineSource) {
1314
+ const entryAbsolute = resolve(workspaceRoot, entryRelative);
1315
+ writeFileSync(entryAbsolute, scene.source.trimStart(), 'utf8');
1316
+ }
916
1317
 
917
1318
  const samplesMs = [];
918
1319
 
919
1320
  for (let run = 0; run < repeats; run += 1) {
920
1321
  const started = process.hrtime.bigint();
921
1322
  await runHeadlessTest({
922
- projectRoot: workspaceRoot,
1323
+ projectRoot: sceneProjectRoot,
923
1324
  file: entryRelative,
924
1325
  frames: scene.frames,
925
1326
  width: scene.width ?? 1280,
@@ -945,6 +1346,9 @@ export async function runPerfBenchmarks(options = {}) {
945
1346
  id: scene.id,
946
1347
  scenario: scene.id,
947
1348
  assetMode,
1349
+ lane: typeof scene.lane === 'string' && scene.lane.trim().length > 0 ? scene.lane.trim() : null,
1350
+ surfaceTags: Array.isArray(scene.surfaceTags) ? [...scene.surfaceTags] : [],
1351
+ sourceMode: hasInlineSource ? 'synthetic-source' : 'authored-entry',
948
1352
  frames: scene.frames,
949
1353
  repeats,
950
1354
  frameTimeSamplesMs: frameTimes,