@auraindustry/aurajs 0.1.3 → 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 (108) 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/authored-project.mjs +498 -2
  6. package/src/build-contract/capabilities.mjs +87 -1
  7. package/src/build-contract/constants.mjs +1 -0
  8. package/src/build-contract.mjs +2 -0
  9. package/src/bundler.mjs +143 -13
  10. package/src/cli.mjs +681 -13
  11. package/src/commands/packs.mjs +741 -0
  12. package/src/commands/project-authoring.mjs +128 -1
  13. package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
  14. package/src/conformance/cases/core-runtime-cases.mjs +6 -2
  15. package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
  16. package/src/conformance/cases/systems-and-gameplay-cases.mjs +265 -4
  17. package/src/conformance-mobile.mjs +166 -0
  18. package/src/conformance.mjs +89 -30
  19. package/src/evidence-bundle.mjs +242 -0
  20. package/src/headless-test/runtime-coordinator.mjs +186 -33
  21. package/src/headless-test.mjs +2 -0
  22. package/src/helpers/2d/index.mjs +183 -0
  23. package/src/helpers/index.mjs +26 -0
  24. package/src/helpers/starter-utils/adventure-objectives.js +102 -0
  25. package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
  26. package/src/helpers/starter-utils/animation-2d.js +337 -0
  27. package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
  28. package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
  29. package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
  30. package/src/helpers/starter-utils/avatar-3d.js +404 -0
  31. package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
  32. package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
  33. package/src/helpers/starter-utils/core.js +150 -0
  34. package/src/helpers/starter-utils/dialogue-2d.js +351 -0
  35. package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
  36. package/src/helpers/starter-utils/index.js +26 -0
  37. package/src/helpers/starter-utils/inventory-2d.js +268 -0
  38. package/src/helpers/starter-utils/journal-2d.js +267 -0
  39. package/src/helpers/starter-utils/platformer-3d.js +132 -0
  40. package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
  41. package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
  42. package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
  43. package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
  44. package/src/helpers/starter-utils/triggers.js +662 -0
  45. package/src/helpers/starter-utils/tween-2d.js +615 -0
  46. package/src/helpers/starter-utils/wave-director.js +101 -0
  47. package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
  48. package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
  49. package/src/mobile/android/build.mjs +606 -0
  50. package/src/mobile/android/host-artifact.mjs +280 -0
  51. package/src/mobile/ios/build.mjs +1323 -0
  52. package/src/mobile/ios/host-artifact.mjs +819 -0
  53. package/src/mobile/shared/capabilities.mjs +174 -0
  54. package/src/packs/catalog.mjs +259 -0
  55. package/src/perf-benchmark-runner.mjs +17 -12
  56. package/src/perf-benchmark.mjs +408 -4
  57. package/src/publish-command.mjs +303 -6
  58. package/src/replay-runtime.mjs +257 -0
  59. package/src/scaffold/config.mjs +2 -0
  60. package/src/scaffold/fs.mjs +8 -1
  61. package/src/scaffold/project-docs.mjs +43 -1
  62. package/src/scaffold.mjs +4 -0
  63. package/src/session-runtime.mjs +4 -3
  64. package/src/web-conformance.mjs +0 -36
  65. package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
  66. package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
  67. package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
  68. package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
  69. package/templates/create/2d-adventure/docs/design/loop.md +4 -3
  70. package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
  71. package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
  72. package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
  73. package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
  74. package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
  75. package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
  76. package/templates/create/3d/scenes/gameplay.scene.js +30 -3
  77. package/templates/create/3d/src/runtime/capabilities.js +5 -0
  78. package/templates/create/3d/src/runtime/materials.js +10 -0
  79. package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
  80. package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
  81. package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
  82. package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
  83. package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
  84. package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
  85. package/templates/create/shared/src/runtime/ui-forms.js +552 -0
  86. package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
  87. package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
  88. package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
  89. package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
  90. package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
  91. package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
  92. package/templates/create/shared/src/starter-utils/index.js +15 -1
  93. package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
  94. package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
  95. package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
  96. package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
  97. package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
  98. package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
  99. package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
  100. package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
  101. package/templates/create-bin/play.js +36 -7
  102. package/templates/skills/auramaxx/SKILL.md +46 -0
  103. package/templates/skills/auramaxx/project-requirements.md +68 -0
  104. package/templates/skills/auramaxx/starter-recipes.md +104 -0
  105. package/templates/skills/auramaxx/validation-checklist.md +49 -0
  106. package/templates/skills/aurajs/SKILL.md +0 -96
  107. package/templates/skills/aurajs/api-contract-3d.md +0 -7
  108. package/templates/skills/aurajs/api-contract.md +0 -7
package/README.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  Write games in JavaScript and build native binaries for macOS, Linux, and Windows.
6
6
 
7
+ Current mobile build truth:
8
+
9
+ - `aura build --target android|ios|mobile` stages mobile package roots in v1
10
+ - `build/android/android-build-manifest.json` and `build/ios/ios-build-manifest.json` are the target-owned truth surfaces
11
+ - `aura run` remains desktop/web only; mobile targets hand off to the staged package lane
12
+ - shared mobile capability conformance reason-codes desktop-only assumptions instead of implying parity
13
+
7
14
  ## Create A New Game
8
15
 
9
16
  ```bash
@@ -41,6 +41,15 @@
41
41
  "maxP99FrameTimeMs": 48,
42
42
  "maxStutterBurstCount": 12
43
43
  },
44
+ "native_2d_flagship_stress": {
45
+ "maxAvgFrameTimeMs": 36,
46
+ "maxJitterMs": 14,
47
+ "minFps": 18,
48
+ "maxP50FrameTimeMs": 36,
49
+ "maxP95FrameTimeMs": 52,
50
+ "maxP99FrameTimeMs": 64,
51
+ "maxStutterBurstCount": 16
52
+ },
44
53
  "ui_composition_surface_stress": {
45
54
  "maxAvgFrameTimeMs": 26,
46
55
  "maxJitterMs": 10,
@@ -98,6 +107,15 @@
98
107
  "maxP99FrameTimeMs": 56,
99
108
  "maxStutterBurstCount": 3
100
109
  },
110
+ "native_2d_flagship_stress": {
111
+ "maxAvgFrameTimeMs": 44,
112
+ "maxJitterMs": 16,
113
+ "minFps": 16,
114
+ "maxP50FrameTimeMs": 44,
115
+ "maxP95FrameTimeMs": 60,
116
+ "maxP99FrameTimeMs": 72,
117
+ "maxStutterBurstCount": 4
118
+ },
101
119
  "ui_composition_surface_stress": {
102
120
  "maxAvgFrameTimeMs": 34,
103
121
  "maxJitterMs": 12,
@@ -156,6 +174,15 @@
156
174
  "maxP99FrameTimeMs": 40,
157
175
  "maxStutterBurstCount": 10
158
176
  },
177
+ "native_2d_flagship_stress": {
178
+ "maxAvgFrameTimeMs": 32,
179
+ "maxJitterMs": 10,
180
+ "minFps": 20,
181
+ "maxP50FrameTimeMs": 32,
182
+ "maxP95FrameTimeMs": 44,
183
+ "maxP99FrameTimeMs": 52,
184
+ "maxStutterBurstCount": 14
185
+ },
159
186
  "ui_composition_surface_stress": {
160
187
  "maxAvgFrameTimeMs": 24,
161
188
  "maxJitterMs": 8,
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@auraindustry/aurajs",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Write games in JavaScript, build native binaries.",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "./cutscene": "./src/cutscene.mjs",
8
+ "./helpers": "./src/helpers/index.mjs",
9
+ "./helpers/2d": "./src/helpers/2d/index.mjs",
10
+ "./helpers/starter-utils/*": "./src/helpers/starter-utils/*.js",
8
11
  "./scene-composition": "./src/scene-composition/index.mjs",
9
12
  "./prefabs": "./src/prefabs/index.mjs",
10
13
  "./shader-kits": "./src/shader-kits/index.mjs",
@@ -20,6 +23,8 @@
20
23
  },
21
24
  "files": [
22
25
  "src/",
26
+ "!src/mobile/ios/vendor/",
27
+ "!src/mobile/ios/vendor/**",
23
28
  "benchmarks/perf-thresholds.json",
24
29
  "templates/"
25
30
  ],
@@ -0,0 +1,302 @@
1
+ const SHARED_HELPER_PACKAGE_SPECIFIER = '@auraindustry/aurajs/helpers';
2
+ const SHARED_HELPER_PACKAGE_SPECIFIER_2D = '@auraindustry/aurajs/helpers/2d';
3
+
4
+ const MODULE_CATALOG = Object.freeze({
5
+ 'world-compositor-2d': {
6
+ id: 'world-compositor-2d',
7
+ title: 'Layered world compositor',
8
+ starterModulePath: 'src/starter-utils/world-compositor-2d.js',
9
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
10
+ importNames: ['createWorldCompositor2D', 'ensureWorldCompositorTargets2D', 'runWorldCompositor2D'],
11
+ useWhen: 'weather, lighting, fog, or any other multi-pass world FX',
12
+ },
13
+ 'tilemap-nav-2d': {
14
+ id: 'tilemap-nav-2d',
15
+ title: 'Tilemap navigation and occupancy',
16
+ starterModulePath: 'src/starter-utils/tilemap-nav-2d.js',
17
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
18
+ importNames: ['createTilemapNav2D', 'rebuildTilemapNav2D', 'findTilemapPath2D', 'resolveTilemapStep2D'],
19
+ useWhen: 'AI movement, lane routing, and mutable occupancy on grid worlds',
20
+ },
21
+ 'tilemap-world-2d': {
22
+ id: 'tilemap-world-2d',
23
+ title: 'Tilemap world loading and queries',
24
+ starterModulePath: 'src/starter-utils/tilemap-world-2d.js',
25
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
26
+ importNames: ['createTilemapWorld2D', 'ensureTilemapWorldLoaded2D', 'queryTilemapWorldPoint2D', 'moveActorThroughTilemapWorld2D'],
27
+ useWhen: '2D worlds driven by tilemap objects, blockers, and spawn points',
28
+ },
29
+ 'wave-director': {
30
+ id: 'wave-director',
31
+ title: 'Wave and pacing director',
32
+ starterModulePath: 'src/starter-utils/wave-director.js',
33
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
34
+ importNames: ['createWaveDirector', 'activeWave', 'stepWaveDirector'],
35
+ useWhen: 'escalating spawn pressure, round structure, or timed encounters',
36
+ },
37
+ 'enemy-archetypes-2d': {
38
+ id: 'enemy-archetypes-2d',
39
+ title: 'Enemy archetype catalog',
40
+ starterModulePath: 'src/starter-utils/enemy-archetypes-2d.js',
41
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
42
+ importNames: ['listEnemyArchetypeIds2D', 'getEnemyArchetype2D', 'pickEnemyArchetypeForWave2D', 'spawnEnemy2D'],
43
+ useWhen: 'authoring reusable enemy definitions instead of scene-local enemy branches',
44
+ },
45
+ 'combat-runtime-2d': {
46
+ id: 'combat-runtime-2d',
47
+ title: 'Combat runtime helpers',
48
+ starterModulePath: 'src/starter-utils/combat-runtime-2d.js',
49
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
50
+ importNames: ['applyShieldedDamage2D', 'createEncounterPressure2D', 'recordEncounterKill2D', 'advanceBossPhase2D'],
51
+ useWhen: 'damage, shield logic, encounter pressure, or boss phase transitions',
52
+ },
53
+ 'combat-feedback-2d': {
54
+ id: 'combat-feedback-2d',
55
+ title: 'Combat feedback and hit FX',
56
+ starterModulePath: 'src/starter-utils/combat-feedback-2d.js',
57
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
58
+ importNames: ['createFloatingTextLayer2D', 'spawnFloatingText2D', 'createHitSparkLayer2D', 'spawnHitSpark2D'],
59
+ useWhen: 'damage popups, sparks, flashes, and other readable combat feedback',
60
+ },
61
+ 'dialogue-2d': {
62
+ id: 'dialogue-2d',
63
+ title: 'Dialogue state helpers',
64
+ starterModulePath: 'src/starter-utils/dialogue-2d.js',
65
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
66
+ importNames: ['createDialogueState2D', 'registerDialogueConversation2D', 'startDialogueConversation2D', 'getDialogueView2D'],
67
+ useWhen: 'branching dialogue, NPC interaction state, or conversation HUDs',
68
+ },
69
+ 'journal-2d': {
70
+ id: 'journal-2d',
71
+ title: 'Journal and objective log',
72
+ starterModulePath: 'src/starter-utils/journal-2d.js',
73
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
74
+ importNames: ['createJournal2D', 'upsertJournalEntry2D', 'setJournalEntryStatus2D', 'getJournalView2D'],
75
+ useWhen: 'quest logs, codex views, or collectible notes',
76
+ },
77
+ 'inventory-2d': {
78
+ id: 'inventory-2d',
79
+ title: 'Inventory helpers',
80
+ starterModulePath: 'src/starter-utils/inventory-2d.js',
81
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
82
+ importNames: ['createInventory2D', 'addInventoryItem2D', 'setInventoryItemEquipped2D', 'getInventoryView2D'],
83
+ useWhen: 'loadouts, pickups, or retained player inventory state',
84
+ },
85
+ 'adventure-objectives': {
86
+ id: 'adventure-objectives',
87
+ title: 'Adventure objective helpers',
88
+ starterModulePath: 'src/starter-utils/adventure-objectives.js',
89
+ sharedPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
90
+ importNames: ['createAdventureObjectives', 'objectivesComplete', 'collectAdventureObjectives2D'],
91
+ useWhen: 'objective tracking, relic hunts, or checkpoint-style adventure progression',
92
+ },
93
+ });
94
+
95
+ const FEATURE_CATALOG = Object.freeze({
96
+ 'native-2d-action-world': {
97
+ id: 'native-2d-action-world',
98
+ title: '2D world FX and traversal',
99
+ moduleIds: ['world-compositor-2d', 'tilemap-world-2d', 'tilemap-nav-2d'],
100
+ recommendedStarters: ['2d-shooter', '2d-survivor'],
101
+ examples: ['examples/sector-surge-2d/src/main.js'],
102
+ },
103
+ 'native-2d-action-combat': {
104
+ id: 'native-2d-action-combat',
105
+ title: '2D action combat loop',
106
+ moduleIds: ['wave-director', 'enemy-archetypes-2d', 'combat-runtime-2d', 'combat-feedback-2d'],
107
+ recommendedStarters: ['2d-shooter', '2d-survivor'],
108
+ examples: ['examples/sector-surge-2d/src/main.js'],
109
+ },
110
+ 'native-2d-adventure-systems': {
111
+ id: 'native-2d-adventure-systems',
112
+ title: '2D adventure state, dialogue, and journal systems',
113
+ moduleIds: ['dialogue-2d', 'journal-2d', 'inventory-2d', 'adventure-objectives'],
114
+ recommendedStarters: ['2d-adventure'],
115
+ examples: ['src/cli/templates/create/2d/scenes/gameplay.scene.js'],
116
+ },
117
+ });
118
+
119
+ const TEMPLATE_LANES = Object.freeze({
120
+ blank: {
121
+ laneId: 'bootstrap-authored-project',
122
+ starterKind: 'blank',
123
+ summary: 'Use the blank scaffold when you need full control over project shape and are willing to wire gameplay systems yourself.',
124
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
125
+ recommendedModules: [],
126
+ featureIds: [],
127
+ firstExamplePaths: ['src/runtime/project-registry.js'],
128
+ },
129
+ '2d-shooter': {
130
+ laneId: 'native-2d-action',
131
+ starterKind: 'starter-2d-action',
132
+ summary: 'This is the strongest starter-owned lane for fast AI-assisted action combat work.',
133
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
134
+ recommendedModules: ['world-compositor-2d', 'tilemap-world-2d', 'tilemap-nav-2d', 'wave-director', 'enemy-archetypes-2d', 'combat-runtime-2d', 'combat-feedback-2d'],
135
+ featureIds: ['native-2d-action-world', 'native-2d-action-combat'],
136
+ firstExamplePaths: ['examples/sector-surge-2d/src/main.js'],
137
+ },
138
+ '2d-survivor': {
139
+ laneId: 'native-2d-action',
140
+ starterKind: 'starter-2d-action',
141
+ summary: 'This starter shares the same action-combat helper lane as the shooter template, biased toward swarm survival loops.',
142
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
143
+ recommendedModules: ['world-compositor-2d', 'tilemap-world-2d', 'tilemap-nav-2d', 'wave-director', 'enemy-archetypes-2d', 'combat-runtime-2d', 'combat-feedback-2d'],
144
+ featureIds: ['native-2d-action-world', 'native-2d-action-combat'],
145
+ firstExamplePaths: ['examples/sector-surge-2d/src/main.js'],
146
+ },
147
+ '2d-adventure': {
148
+ laneId: 'native-2d-adventure',
149
+ starterKind: 'starter-2d-adventure',
150
+ summary: 'Use this lane for dialogue-heavy worlds, objective flow, inventory, and journal-driven progression.',
151
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
152
+ recommendedModules: ['tilemap-world-2d', 'dialogue-2d', 'journal-2d', 'inventory-2d', 'adventure-objectives'],
153
+ featureIds: ['native-2d-adventure-systems'],
154
+ firstExamplePaths: ['src/cli/templates/create/2d/scenes/gameplay.scene.js'],
155
+ },
156
+ '3d-adventure': {
157
+ laneId: 'native-3d-adventure',
158
+ starterKind: 'starter-3d-adventure',
159
+ summary: 'This is the current paved 3D starter lane for lightweight authored scene flow and progression.',
160
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
161
+ recommendedModules: [],
162
+ featureIds: [],
163
+ firstExamplePaths: ['src/cli/templates/create/3d/scenes/gameplay.scene.js'],
164
+ },
165
+ '3d-platformer': {
166
+ laneId: 'native-3d-platformer',
167
+ starterKind: 'starter-3d-platformer',
168
+ summary: 'Use this lane when you need checkpoints, movement loops, and 3D traversal scaffolding.',
169
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
170
+ recommendedModules: [],
171
+ featureIds: [],
172
+ firstExamplePaths: ['src/cli/templates/create/3d/scenes/gameplay.scene.js'],
173
+ },
174
+ '3d-collectathon': {
175
+ laneId: 'native-3d-collectathon',
176
+ starterKind: 'starter-3d-collectathon',
177
+ summary: 'This starter is the paved road for lightweight 3D collectathon structure and checkpoint flow.',
178
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
179
+ recommendedModules: [],
180
+ featureIds: [],
181
+ firstExamplePaths: ['src/cli/templates/create/3d/scenes/gameplay.scene.js'],
182
+ },
183
+ 'local-multiplayer': {
184
+ laneId: 'local-multiplayer',
185
+ starterKind: 'starter-local-multiplayer',
186
+ summary: 'This lane is optimized for same-machine room-code multiplayer structure instead of single-player action helpers.',
187
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
188
+ recommendedModules: [],
189
+ featureIds: [],
190
+ firstExamplePaths: ['src/cli/templates/create/local-multiplayer/scenes/gameplay.scene.js'],
191
+ },
192
+ 'deckbuilder-2d': {
193
+ laneId: 'deckbuilder-2d',
194
+ starterKind: 'starter-deckbuilder-2d',
195
+ summary: 'This lane is the paved road for content-registry-driven 2D deckbuilder projects.',
196
+ preferredPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER_2D,
197
+ recommendedModules: [],
198
+ featureIds: [],
199
+ firstExamplePaths: ['src/cli/templates/create/deckbuilder-2d/scenes/gameplay.scene.js'],
200
+ },
201
+ });
202
+
203
+ export function describeProjectAiGuidance({ template, starterUtils }) {
204
+ const normalizedTemplate = String(template || '').trim() || 'blank';
205
+ const lane = TEMPLATE_LANES[normalizedTemplate] || TEMPLATE_LANES.blank;
206
+ const recommendedImportPath = starterUtils?.available === true
207
+ && typeof starterUtils?.recommendedImportPath === 'string'
208
+ && starterUtils.recommendedImportPath.trim().length > 0
209
+ ? starterUtils.recommendedImportPath.trim()
210
+ : null;
211
+
212
+ const recommendedModules = lane.recommendedModules
213
+ .map((moduleId) => MODULE_CATALOG[moduleId])
214
+ .filter(Boolean)
215
+ .map((entry) => ({
216
+ ...entry,
217
+ preferredImportSource: recommendedImportPath || entry.sharedPackageSpecifier || lane.preferredPackageSpecifier || null,
218
+ sourceMode: recommendedImportPath ? 'starter-utils' : 'package',
219
+ }));
220
+
221
+ const featureMap = lane.featureIds
222
+ .map((featureId) => FEATURE_CATALOG[featureId])
223
+ .filter(Boolean)
224
+ .map((feature) => ({
225
+ id: feature.id,
226
+ title: feature.title,
227
+ recommendedStarters: [...feature.recommendedStarters],
228
+ examples: [...feature.examples],
229
+ modules: feature.moduleIds
230
+ .map((moduleId) => MODULE_CATALOG[moduleId])
231
+ .filter(Boolean)
232
+ .map((entry) => ({
233
+ id: entry.id,
234
+ title: entry.title,
235
+ starterModulePath: entry.starterModulePath,
236
+ importNames: [...entry.importNames],
237
+ })),
238
+ }));
239
+
240
+ return {
241
+ laneId: lane.laneId,
242
+ starterKind: lane.starterKind,
243
+ summary: lane.summary,
244
+ recommendedImportPath,
245
+ preferredPackageSpecifier: lane.preferredPackageSpecifier || null,
246
+ recommendedModules,
247
+ featureMap,
248
+ firstExamplePaths: [...lane.firstExamplePaths],
249
+ };
250
+ }
251
+
252
+ export function normalizeAiGuidance(value) {
253
+ const safe = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
254
+ return {
255
+ laneId: typeof safe.laneId === 'string' && safe.laneId.trim().length > 0
256
+ ? safe.laneId.trim()
257
+ : 'bootstrap-authored-project',
258
+ starterKind: typeof safe.starterKind === 'string' && safe.starterKind.trim().length > 0
259
+ ? safe.starterKind.trim()
260
+ : 'blank',
261
+ summary: typeof safe.summary === 'string' && safe.summary.trim().length > 0
262
+ ? safe.summary.trim()
263
+ : '',
264
+ recommendedImportPath: typeof safe.recommendedImportPath === 'string' && safe.recommendedImportPath.trim().length > 0
265
+ ? safe.recommendedImportPath.trim()
266
+ : null,
267
+ preferredPackageSpecifier: typeof safe.preferredPackageSpecifier === 'string' && safe.preferredPackageSpecifier.trim().length > 0
268
+ ? safe.preferredPackageSpecifier.trim()
269
+ : null,
270
+ recommendedModules: Array.isArray(safe.recommendedModules)
271
+ ? safe.recommendedModules.map((entry) => ({
272
+ id: String(entry?.id || '').trim(),
273
+ title: typeof entry?.title === 'string' ? entry.title.trim() : '',
274
+ starterModulePath: typeof entry?.starterModulePath === 'string' ? entry.starterModulePath.trim() : null,
275
+ sharedPackageSpecifier: typeof entry?.sharedPackageSpecifier === 'string' ? entry.sharedPackageSpecifier.trim() : null,
276
+ preferredImportSource: typeof entry?.preferredImportSource === 'string' ? entry.preferredImportSource.trim() : null,
277
+ sourceMode: entry?.sourceMode === 'starter-utils' ? 'starter-utils' : 'package',
278
+ importNames: Array.isArray(entry?.importNames) ? entry.importNames.map((name) => String(name)) : [],
279
+ useWhen: typeof entry?.useWhen === 'string' ? entry.useWhen.trim() : '',
280
+ })).filter((entry) => entry.id.length > 0)
281
+ : [],
282
+ featureMap: Array.isArray(safe.featureMap)
283
+ ? safe.featureMap.map((entry) => ({
284
+ id: String(entry?.id || '').trim(),
285
+ title: typeof entry?.title === 'string' ? entry.title.trim() : '',
286
+ recommendedStarters: Array.isArray(entry?.recommendedStarters) ? entry.recommendedStarters.map((item) => String(item)) : [],
287
+ examples: Array.isArray(entry?.examples) ? entry.examples.map((item) => String(item)) : [],
288
+ modules: Array.isArray(entry?.modules)
289
+ ? entry.modules.map((moduleEntry) => ({
290
+ id: String(moduleEntry?.id || '').trim(),
291
+ title: typeof moduleEntry?.title === 'string' ? moduleEntry.title.trim() : '',
292
+ starterModulePath: typeof moduleEntry?.starterModulePath === 'string' ? moduleEntry.starterModulePath.trim() : null,
293
+ importNames: Array.isArray(moduleEntry?.importNames) ? moduleEntry.importNames.map((name) => String(name)) : [],
294
+ })).filter((moduleEntry) => moduleEntry.id.length > 0)
295
+ : [],
296
+ })).filter((entry) => entry.id.length > 0)
297
+ : [],
298
+ firstExamplePaths: Array.isArray(safe.firstExamplePaths)
299
+ ? safe.firstExamplePaths.map((entry) => String(entry)).filter(Boolean)
300
+ : [],
301
+ };
302
+ }