@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
@@ -0,0 +1,174 @@
1
+ import { MOBILE_CAPABILITY_REPORT_SCHEMA } from '../../build-contract/constants.mjs';
2
+
3
+ export const MOBILE_BUILD_TARGETS = new Set(['android', 'ios', 'mobile']);
4
+
5
+ const MOBILE_UNSUPPORTED_API_RULES = Object.freeze([
6
+ Object.freeze({
7
+ api: 'aura.input.isKeyDown',
8
+ capability: 'keyboardInput',
9
+ reasonCode: 'mobile_keyboard_first_control_deferred',
10
+ }),
11
+ Object.freeze({
12
+ api: 'aura.input.isKeyPressed',
13
+ capability: 'keyboardInput',
14
+ reasonCode: 'mobile_keyboard_first_control_deferred',
15
+ }),
16
+ Object.freeze({
17
+ api: 'aura.input.isKeyReleased',
18
+ capability: 'keyboardInput',
19
+ reasonCode: 'mobile_keyboard_first_control_deferred',
20
+ }),
21
+ Object.freeze({
22
+ api: 'aura.input.pressed',
23
+ capability: 'keyboardInput',
24
+ reasonCode: 'mobile_keyboard_first_control_deferred',
25
+ }),
26
+ Object.freeze({
27
+ api: 'aura.input.justPressed',
28
+ capability: 'keyboardInput',
29
+ reasonCode: 'mobile_keyboard_first_control_deferred',
30
+ }),
31
+ Object.freeze({
32
+ api: 'aura.input.released',
33
+ capability: 'keyboardInput',
34
+ reasonCode: 'mobile_keyboard_first_control_deferred',
35
+ }),
36
+ Object.freeze({
37
+ api: 'aura.window.setCursorLocked',
38
+ capability: 'cursorLock',
39
+ reasonCode: 'mobile_cursor_lock_unsupported',
40
+ }),
41
+ Object.freeze({
42
+ api: 'aura.input.getMouseWheel',
43
+ capability: 'mouseWheel',
44
+ reasonCode: 'mobile_mouse_wheel_unsupported',
45
+ }),
46
+ Object.freeze({
47
+ api: 'aura.window.setSize',
48
+ capability: 'windowManagement',
49
+ reasonCode: 'mobile_window_management_unsupported',
50
+ }),
51
+ Object.freeze({
52
+ api: 'aura.window.setFullscreen',
53
+ capability: 'windowManagement',
54
+ reasonCode: 'mobile_window_management_unsupported',
55
+ }),
56
+ Object.freeze({
57
+ api: 'aura.window.minimize',
58
+ capability: 'windowManagement',
59
+ reasonCode: 'mobile_window_management_unsupported',
60
+ }),
61
+ Object.freeze({
62
+ api: 'aura.window.restore',
63
+ capability: 'windowManagement',
64
+ reasonCode: 'mobile_window_management_unsupported',
65
+ }),
66
+ Object.freeze({
67
+ api: 'aura.window.focus',
68
+ capability: 'windowManagement',
69
+ reasonCode: 'mobile_window_management_unsupported',
70
+ }),
71
+ Object.freeze({
72
+ api: 'aura.window.getSafeAreaInsets',
73
+ capability: 'safeArea',
74
+ reasonCode: 'mobile_safe_area_unavailable',
75
+ }),
76
+ Object.freeze({
77
+ api: 'aura.window.setOrientationLock',
78
+ capability: 'orientationControl',
79
+ reasonCode: 'mobile_orientation_control_unavailable',
80
+ }),
81
+ Object.freeze({
82
+ api: 'aura.input.gyro',
83
+ capability: 'sensor',
84
+ reasonCode: 'mobile_sensor_unavailable',
85
+ }),
86
+ Object.freeze({
87
+ api: 'aura.input.accelerometer',
88
+ capability: 'sensor',
89
+ reasonCode: 'mobile_sensor_unavailable',
90
+ }),
91
+ ]);
92
+
93
+ const MOBILE_DEFERRED_SURFACES = Object.freeze([
94
+ Object.freeze({
95
+ surface: 'keyboardFirstControls',
96
+ status: 'deferred',
97
+ reasonCode: 'mobile_keyboard_first_control_deferred',
98
+ }),
99
+ Object.freeze({
100
+ surface: 'multiTouchPublicContract',
101
+ status: 'deferred',
102
+ reasonCode: null,
103
+ }),
104
+ Object.freeze({
105
+ surface: 'orientationControl',
106
+ status: 'deferred',
107
+ reasonCode: 'mobile_orientation_control_unavailable',
108
+ }),
109
+ Object.freeze({
110
+ surface: 'safeArea',
111
+ status: 'deferred',
112
+ reasonCode: 'mobile_safe_area_unavailable',
113
+ }),
114
+ Object.freeze({
115
+ surface: 'sensors',
116
+ status: 'deferred',
117
+ reasonCode: 'mobile_sensor_unavailable',
118
+ }),
119
+ ]);
120
+
121
+ function normalizeRequiredApis(requiredApis) {
122
+ if (!Array.isArray(requiredApis)) {
123
+ return [];
124
+ }
125
+ return [...new Set(requiredApis
126
+ .filter((entry) => typeof entry === 'string')
127
+ .map((entry) => entry.trim())
128
+ .filter(Boolean))]
129
+ .sort((a, b) => a.localeCompare(b));
130
+ }
131
+
132
+ export function normalizeMobileBuildTarget(target) {
133
+ const normalized = typeof target === 'string' ? target.trim().toLowerCase() : '';
134
+ if (!MOBILE_BUILD_TARGETS.has(normalized)) {
135
+ throw new Error(`Unsupported mobile build target: ${target}`);
136
+ }
137
+ return normalized;
138
+ }
139
+
140
+ export function buildMobileCapabilityReport({ target, capabilityDeclaration } = {}) {
141
+ const normalizedTarget = normalizeMobileBuildTarget(target || 'mobile');
142
+ const requiredApis = normalizeRequiredApis(capabilityDeclaration?.requiredApis);
143
+ const unsupportedApis = MOBILE_UNSUPPORTED_API_RULES
144
+ .filter((rule) => requiredApis.includes(rule.api))
145
+ .map((rule) => ({
146
+ api: rule.api,
147
+ capability: rule.capability,
148
+ status: 'unsupported',
149
+ reasonCode: rule.reasonCode,
150
+ }));
151
+
152
+ return {
153
+ schema: MOBILE_CAPABILITY_REPORT_SCHEMA,
154
+ schemaVersion: '1.0.0',
155
+ buildTarget: normalizedTarget,
156
+ source: typeof capabilityDeclaration?.source === 'string' ? capabilityDeclaration.source : null,
157
+ reasonCode: unsupportedApis.length > 0
158
+ ? 'mobile_required_api_unsupported'
159
+ : 'mobile_capability_contract_ok',
160
+ ok: unsupportedApis.length === 0,
161
+ input: {
162
+ primaryPointer: {
163
+ status: 'supported',
164
+ reasonCode: null,
165
+ },
166
+ multiTouch: {
167
+ status: 'deferred',
168
+ reasonCode: null,
169
+ },
170
+ },
171
+ unsupportedApis,
172
+ deferredSurfaces: MOBILE_DEFERRED_SURFACES,
173
+ };
174
+ }
@@ -72,7 +72,7 @@ function readJsonFile(path) {
72
72
  }
73
73
  }
74
74
 
75
- function listHashedPackageFiles(root, current = root, acc = []) {
75
+ function listHashedPackageFiles(root, current = root, acc = [], includedPaths = null) {
76
76
  for (const entry of readdirSync(current, { withFileTypes: true })) {
77
77
  const fullPath = join(current, entry.name);
78
78
  const relativePath = normalizeRelativePath(relative(root, fullPath));
@@ -92,7 +92,7 @@ function listHashedPackageFiles(root, current = root, acc = []) {
92
92
  }
93
93
 
94
94
  if (entry.isDirectory()) {
95
- listHashedPackageFiles(root, fullPath, acc);
95
+ listHashedPackageFiles(root, fullPath, acc, includedPaths);
96
96
  continue;
97
97
  }
98
98
 
@@ -100,6 +100,10 @@ function listHashedPackageFiles(root, current = root, acc = []) {
100
100
  continue;
101
101
  }
102
102
 
103
+ if (includedPaths && !includedPaths.has(relativePath)) {
104
+ continue;
105
+ }
106
+
103
107
  const buffer = readFileSync(fullPath);
104
108
  acc.push({
105
109
  path: relativePath,
@@ -198,8 +202,16 @@ function normalizeBuildMetadata(buildMetadata = {}) {
198
202
  };
199
203
  }
200
204
 
201
- function buildManifestBody({ packageRoot, projectPackage, buildMetadata = null, signer }) {
205
+ function buildManifestBody({ packageRoot, projectPackage, buildMetadata = null, signer, includedRelativePaths = null }) {
202
206
  const resolvedPackage = projectPackage || readJsonFile(resolve(packageRoot, 'package.json'));
207
+ const normalizedIncludedPaths = Array.isArray(includedRelativePaths)
208
+ ? new Set(
209
+ includedRelativePaths
210
+ .map((entry) => normalizeRelativePath(entry))
211
+ .filter(Boolean)
212
+ .filter((entry) => entry !== PACKAGE_INTEGRITY_MANIFEST_PATH && entry !== PACKAGE_INTEGRITY_SIGNATURE_PATH),
213
+ )
214
+ : null;
203
215
  return {
204
216
  schema: PACKAGE_INTEGRITY_SCHEMA,
205
217
  package: {
@@ -219,7 +231,7 @@ function buildManifestBody({ packageRoot, projectPackage, buildMetadata = null,
219
231
  publicKeyPem: signer.publicKeyPem,
220
232
  fingerprint: signer.fingerprint,
221
233
  },
222
- files: listHashedPackageFiles(packageRoot),
234
+ files: listHashedPackageFiles(packageRoot, packageRoot, [], normalizedIncludedPaths),
223
235
  };
224
236
  }
225
237
 
@@ -512,6 +524,7 @@ export function writeSignedPackageIntegrityArtifacts({
512
524
  signerProjectRoot = packageRoot,
513
525
  buildMetadata = null,
514
526
  projectPackage = null,
527
+ includedRelativePaths = null,
515
528
  } = {}) {
516
529
  const resolvedPackageRoot = resolve(packageRoot || process.cwd());
517
530
  const signer = ensureSignerKeyPair(resolve(signerProjectRoot || resolvedPackageRoot));
@@ -520,6 +533,7 @@ export function writeSignedPackageIntegrityArtifacts({
520
533
  projectPackage,
521
534
  buildMetadata,
522
535
  signer,
536
+ includedRelativePaths,
523
537
  });
524
538
  const signature = sign(
525
539
  null,
@@ -0,0 +1,259 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, relative, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ export const PACK_CATALOG_SCHEMA = 'aurajs.pack-catalog.v1';
6
+ export const PROJECT_PACKS_SCHEMA = 'aurajs.project-packs.v1';
7
+ export const PROJECT_PACKS_MANIFEST_PATH = 'aura.packs.json';
8
+
9
+ const PACKS_DIR = dirname(fileURLToPath(import.meta.url));
10
+ const CLI_PACKAGE_ROOT = resolve(PACKS_DIR, '../..');
11
+ const REPO_ROOT = resolve(CLI_PACKAGE_ROOT, '../..');
12
+ const WORKSPACE_PACKAGES_ROOT = resolve(REPO_ROOT, 'packages');
13
+ const CLI_PACKAGE = JSON.parse(readFileSync(resolve(CLI_PACKAGE_ROOT, 'package.json'), 'utf8'));
14
+ const CLI_PACKAGE_VERSION = String(CLI_PACKAGE.version || '0.1.0');
15
+ const PUBLIC_PACK_GITHUB_REPOSITORY = 'Aura-Industry/auramaxx';
16
+
17
+ function createPublicPackGithub(packDirName) {
18
+ return {
19
+ repository: PUBLIC_PACK_GITHUB_REPOSITORY,
20
+ ref: 'main',
21
+ packPath: `packs/${packDirName}`,
22
+ };
23
+ }
24
+
25
+ const PACK_REGISTRY = [
26
+ {
27
+ id: 'ai-starters',
28
+ kind: 'starter-pack',
29
+ title: 'AI Starters',
30
+ description: 'AI-first recipes, prompts, and starter overlays for AuraJS projects.',
31
+ packageName: '@auraindustry/aurajs-pack-ai-starters',
32
+ workspaceDirName: 'aurajs-pack-ai-starters',
33
+ surfaces: ['recipes', 'skills', 'starter-overlays'],
34
+ recommendedFor: ['ai', '2d', 'starter-authoring'],
35
+ github: createPublicPackGithub('aurajs-pack-ai-starters'),
36
+ },
37
+ {
38
+ id: 'assets-2d-core',
39
+ kind: 'asset-pack',
40
+ title: '2D Core Assets',
41
+ description: 'Optional placeholder-ready 2D palette, HUD, and audio planning assets.',
42
+ packageName: '@auraindustry/aurajs-pack-assets-2d-core',
43
+ workspaceDirName: 'aurajs-pack-assets-2d-core',
44
+ surfaces: ['assets', 'palettes', 'audio-planning'],
45
+ recommendedFor: ['2d', 'prototype', 'placeholder-art'],
46
+ github: createPublicPackGithub('aurajs-pack-assets-2d-core'),
47
+ },
48
+ {
49
+ id: 'ui-hud-2d',
50
+ kind: 'ui-pack',
51
+ title: 'UI HUD 2D',
52
+ description: 'Reusable 2D HUD themes, layout payloads, and widget planning files.',
53
+ packageName: '@auraindustry/aurajs-pack-ui-hud-2d',
54
+ workspaceDirName: 'aurajs-pack-ui-hud-2d',
55
+ surfaces: ['themes', 'layouts', 'widgets'],
56
+ recommendedFor: ['2d', 'hud', 'ui'],
57
+ github: createPublicPackGithub('aurajs-pack-ui-hud-2d'),
58
+ },
59
+ {
60
+ id: 'audio-2d-core',
61
+ kind: 'audio-pack',
62
+ title: 'Audio 2D Core',
63
+ description: 'Structured 2D scene audio defaults, bus layout, stingers, and bark planning payloads.',
64
+ packageName: '@auraindustry/aurajs-pack-audio-2d-core',
65
+ workspaceDirName: 'aurajs-pack-audio-2d-core',
66
+ surfaces: ['audio-config', 'barks', 'stingers'],
67
+ recommendedFor: ['2d', 'audio', 'scene-audio'],
68
+ github: createPublicPackGithub('aurajs-pack-audio-2d-core'),
69
+ },
70
+ ];
71
+
72
+ const BUNDLE_REGISTRY = [
73
+ {
74
+ id: 'ai-2d-bundle',
75
+ kind: 'bundle',
76
+ title: 'AI 2D Bundle',
77
+ description: 'Recommended optional pack set for AI-assisted 2D AuraJS projects.',
78
+ packageName: '@auraindustry/aurajs-bundle-ai-2d',
79
+ workspaceDirName: 'aurajs-bundle-ai-2d',
80
+ memberPackIds: ['ai-starters', 'assets-2d-core', 'ui-hud-2d', 'audio-2d-core'],
81
+ recommendedFor: ['ai', '2d', 'flagship-prototype'],
82
+ github: createPublicPackGithub('aurajs-bundle-ai-2d'),
83
+ },
84
+ ];
85
+
86
+ function toPosixPath(value) {
87
+ return String(value || '').split(/[/\\]+/g).filter(Boolean).join('/');
88
+ }
89
+
90
+ function normalizeRelativeFileSpec(projectRoot, targetRoot) {
91
+ const relativePath = relative(projectRoot, targetRoot);
92
+ const normalized = toPosixPath(relativePath);
93
+ if (!normalized || normalized === '.') {
94
+ return 'file:.';
95
+ }
96
+ if (normalized.startsWith('.')) {
97
+ return `file:${normalized}`;
98
+ }
99
+ return `file:./${normalized}`;
100
+ }
101
+
102
+ function buildPackEntry(entry) {
103
+ const workspacePath = resolve(WORKSPACE_PACKAGES_ROOT, entry.workspaceDirName);
104
+ return {
105
+ ...entry,
106
+ workspacePath,
107
+ workspaceAvailable: existsSync(workspacePath),
108
+ versionRange: `^${CLI_PACKAGE_VERSION}`,
109
+ github: entry.github ? { ...entry.github } : null,
110
+ };
111
+ }
112
+
113
+ function buildBundleEntry(entry) {
114
+ const workspacePath = resolve(WORKSPACE_PACKAGES_ROOT, entry.workspaceDirName);
115
+ return {
116
+ ...entry,
117
+ workspacePath,
118
+ workspaceAvailable: existsSync(workspacePath),
119
+ versionRange: `^${CLI_PACKAGE_VERSION}`,
120
+ github: entry.github ? { ...entry.github } : null,
121
+ };
122
+ }
123
+
124
+ export function listPackRegistry() {
125
+ return PACK_REGISTRY.map(buildPackEntry);
126
+ }
127
+
128
+ export function listBundleRegistry() {
129
+ return BUNDLE_REGISTRY.map(buildBundleEntry);
130
+ }
131
+
132
+ export function findPackEntry(id) {
133
+ return listPackRegistry().find((entry) => entry.id === id) || null;
134
+ }
135
+
136
+ export function findBundleEntry(id) {
137
+ return listBundleRegistry().find((entry) => entry.id === id) || null;
138
+ }
139
+
140
+ export function findPackOrBundleEntry(id) {
141
+ return findPackEntry(id) || findBundleEntry(id);
142
+ }
143
+
144
+ export function expandPackSelection(id) {
145
+ const pack = findPackEntry(id);
146
+ if (pack) {
147
+ return {
148
+ requested: pack,
149
+ bundles: [],
150
+ packs: [pack],
151
+ };
152
+ }
153
+ const bundle = findBundleEntry(id);
154
+ if (!bundle) {
155
+ return null;
156
+ }
157
+ const packs = bundle.memberPackIds
158
+ .map((memberId) => findPackEntry(memberId))
159
+ .filter(Boolean);
160
+ return {
161
+ requested: bundle,
162
+ bundles: [bundle],
163
+ packs,
164
+ };
165
+ }
166
+
167
+ export function resolvePackInstallSpec(entry, { projectRoot, workspace = false } = {}) {
168
+ if (workspace && projectRoot && entry.workspaceAvailable) {
169
+ return {
170
+ mode: 'workspace',
171
+ spec: normalizeRelativeFileSpec(projectRoot, entry.workspacePath),
172
+ };
173
+ }
174
+ return {
175
+ mode: 'package',
176
+ spec: entry.versionRange,
177
+ };
178
+ }
179
+
180
+ export function createPackCatalogReport() {
181
+ return {
182
+ schema: PACK_CATALOG_SCHEMA,
183
+ corePackage: CLI_PACKAGE.name,
184
+ coreVersion: CLI_PACKAGE_VERSION,
185
+ packs: listPackRegistry().map((entry) => ({
186
+ id: entry.id,
187
+ kind: entry.kind,
188
+ title: entry.title,
189
+ description: entry.description,
190
+ packageName: entry.packageName,
191
+ versionRange: entry.versionRange,
192
+ workspaceAvailable: entry.workspaceAvailable,
193
+ github: entry.github ? { ...entry.github } : null,
194
+ recommendedFor: [...entry.recommendedFor],
195
+ surfaces: [...entry.surfaces],
196
+ })),
197
+ bundles: listBundleRegistry().map((entry) => ({
198
+ id: entry.id,
199
+ kind: entry.kind,
200
+ title: entry.title,
201
+ description: entry.description,
202
+ packageName: entry.packageName,
203
+ versionRange: entry.versionRange,
204
+ workspaceAvailable: entry.workspaceAvailable,
205
+ github: entry.github ? { ...entry.github } : null,
206
+ recommendedFor: [...entry.recommendedFor],
207
+ memberPackIds: [...entry.memberPackIds],
208
+ })),
209
+ };
210
+ }
211
+
212
+ export function normalizeProjectPackManifest(value) {
213
+ const safe = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
214
+ return {
215
+ schema: PROJECT_PACKS_SCHEMA,
216
+ selected: Array.isArray(safe.selected)
217
+ ? safe.selected
218
+ .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
219
+ .map((entry) => ({
220
+ id: typeof entry.id === 'string' ? entry.id : null,
221
+ kind: typeof entry.kind === 'string' ? entry.kind : 'starter-pack',
222
+ sourceMode: entry.sourceMode === 'github'
223
+ ? 'github'
224
+ : entry.sourceMode === 'workspace'
225
+ ? 'workspace'
226
+ : 'package',
227
+ }))
228
+ .filter((entry) => entry.id)
229
+ : [],
230
+ packs: Array.isArray(safe.packs)
231
+ ? safe.packs
232
+ .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
233
+ .map((entry) => ({
234
+ id: typeof entry.id === 'string' ? entry.id : null,
235
+ kind: typeof entry.kind === 'string' ? entry.kind : 'starter-pack',
236
+ packageName: typeof entry.packageName === 'string' ? entry.packageName : null,
237
+ spec: typeof entry.spec === 'string' ? entry.spec : null,
238
+ installMode: entry.installMode === 'workspace' ? 'workspace' : 'package',
239
+ sourceMode: entry.sourceMode === 'github'
240
+ ? 'github'
241
+ : entry.sourceMode === 'workspace'
242
+ ? 'workspace'
243
+ : 'package',
244
+ vendoredPaths: Array.isArray(entry.vendoredPaths) ? entry.vendoredPaths.map((item) => String(item)) : [],
245
+ }))
246
+ .filter((entry) => entry.id)
247
+ : [],
248
+ bundles: Array.isArray(safe.bundles)
249
+ ? safe.bundles
250
+ .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
251
+ .map((entry) => ({
252
+ id: typeof entry.id === 'string' ? entry.id : null,
253
+ packageName: typeof entry.packageName === 'string' ? entry.packageName : null,
254
+ memberPackIds: Array.isArray(entry.memberPackIds) ? entry.memberPackIds.map((item) => String(item)) : [],
255
+ }))
256
+ .filter((entry) => entry.id)
257
+ : [],
258
+ };
259
+ }
@@ -77,19 +77,24 @@ function parseCliArgs(argv) {
77
77
  }
78
78
 
79
79
  function mapApplicableScenes(report, sceneThresholds = {}) {
80
- const reportSceneIds = new Set((report?.scenes || []).map((scene) => scene.id));
80
+ const scenesById = new Map((report?.scenes || []).map((scene) => [scene.id, scene]));
81
81
  return Object.entries(sceneThresholds)
82
- .filter(([sceneId]) => reportSceneIds.has(sceneId))
83
- .map(([sceneId, limits]) => ({
84
- id: sceneId,
85
- maxAvgFrameTimeMs: limits.maxAvgFrameTimeMs,
86
- maxJitterMs: limits.maxJitterMs,
87
- minFps: limits.minFps,
88
- maxP50FrameTimeMs: limits.maxP50FrameTimeMs,
89
- maxP95FrameTimeMs: limits.maxP95FrameTimeMs,
90
- maxP99FrameTimeMs: limits.maxP99FrameTimeMs,
91
- maxStutterBurstCount: limits.maxStutterBurstCount,
92
- }));
82
+ .filter(([sceneId]) => scenesById.has(sceneId))
83
+ .map(([sceneId, limits]) => {
84
+ const scene = scenesById.get(sceneId) || {};
85
+ return {
86
+ id: sceneId,
87
+ lane: typeof scene.lane === 'string' ? scene.lane : null,
88
+ surfaceTags: Array.isArray(scene.surfaceTags) ? [...scene.surfaceTags] : [],
89
+ maxAvgFrameTimeMs: limits.maxAvgFrameTimeMs,
90
+ maxJitterMs: limits.maxJitterMs,
91
+ minFps: limits.minFps,
92
+ maxP50FrameTimeMs: limits.maxP50FrameTimeMs,
93
+ maxP95FrameTimeMs: limits.maxP95FrameTimeMs,
94
+ maxP99FrameTimeMs: limits.maxP99FrameTimeMs,
95
+ maxStutterBurstCount: limits.maxStutterBurstCount,
96
+ };
97
+ });
93
98
  }
94
99
 
95
100
  function bucketFailures(failures) {