@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
@@ -11,6 +11,13 @@ import {
11
11
  validateStarterContentRegistries,
12
12
  } from './starter-content-registry.mjs';
13
13
  import { loadMakeCatalog } from './make-catalog.mjs';
14
+ import { describeProjectAiGuidance, normalizeAiGuidance } from './ai-guidance.mjs';
15
+ import {
16
+ PROJECT_PACKS_MANIFEST_PATH,
17
+ findPackOrBundleEntry,
18
+ listPackRegistry,
19
+ normalizeProjectPackManifest,
20
+ } from './packs/catalog.mjs';
14
21
 
15
22
  export const PROJECT_REGISTRY_SCHEMA = 'aurajs.project-registry.v1';
16
23
  export const PROJECT_EXPLAIN_SCHEMA = 'aurajs.project-explain.v1';
@@ -29,6 +36,14 @@ const RUNTIME_BOOTSTRAP_FILES = [
29
36
  const SELF_WIRING_KINDS = new Set(['scene', 'ui-screen', 'prefab']);
30
37
  const CANONICAL_PREFAB_ROLE_SET = new Set(CANONICAL_PREFAB_ROLES);
31
38
  const POSIX_SEP = '/';
39
+ const AUTHORED_SOURCE_ROOT_DIRS = Object.freeze(['src', 'scenes', 'ui', 'prefabs', 'config', 'content', 'data']);
40
+ const STARTER_UTILS_ROOT_RELATIVE_PATH = 'src/starter-utils';
41
+ const PROJECT_PACKS_RELATIVE_PATH = PROJECT_PACKS_MANIFEST_PATH;
42
+ const SHARED_HELPER_PACKAGE_SPECIFIER = '@auraindustry/aurajs/helpers';
43
+ const SUPPORTED_HELPER_PACKAGE_SPECIFIERS = Object.freeze([
44
+ SHARED_HELPER_PACKAGE_SPECIFIER,
45
+ `${SHARED_HELPER_PACKAGE_SPECIFIER}/2d`,
46
+ ]);
32
47
 
33
48
  const BASE_PROJECT_CONFIG_FILES = ['config/gameplay/game.config.json'];
34
49
  const BASE_PROJECT_CONTENT_FILES = [];
@@ -57,6 +72,7 @@ const STARTER_PROJECT_REGISTRY_PRESETS = {
57
72
  ],
58
73
  screens: [
59
74
  { id: 'hud', path: 'ui/hud.screen.js', exportName: 'default' },
75
+ { id: 'journal', path: 'ui/journal.screen.js', exportName: 'default' },
60
76
  ],
61
77
  prefabs: [
62
78
  { id: 'player', role: 'player', path: 'prefabs/player.prefab.js', exportName: 'default' },
@@ -64,7 +80,7 @@ const STARTER_PROJECT_REGISTRY_PRESETS = {
64
80
  { id: 'world', role: 'world', path: 'prefabs/world.prefab.js', exportName: 'default' },
65
81
  ],
66
82
  configFiles: ['config/gameplay/adventure.config.js'],
67
- contentFiles: ['content/gameplay/world.js'],
83
+ contentFiles: ['content/gameplay/dialogue.js', 'content/gameplay/world.js', 'content/gameplay/world.tilemap.json'],
68
84
  },
69
85
  '2d-shooter': {
70
86
  startSceneId: 'gameplay',
@@ -555,6 +571,10 @@ export async function explainProject({ projectRoot, catalogRoot = null }) {
555
571
  owner: entry.owner,
556
572
  scope: entry.scope,
557
573
  }));
574
+ const sourceBoundary = describeSourceBoundary(projectRoot);
575
+ const starterUtils = describeStarterUtils(projectRoot);
576
+ const packs = describeProjectPacks(projectRoot);
577
+ const aiGuidance = describeProjectAiGuidance({ template, starterUtils });
558
578
 
559
579
  if (mode === 'bootstrap-only') {
560
580
  return {
@@ -577,6 +597,10 @@ export async function explainProject({ projectRoot, catalogRoot = null }) {
577
597
  continuity: createProjectContinuityModel(),
578
598
  starterContentRegistries,
579
599
  availableMakeKinds,
600
+ sourceBoundary,
601
+ starterUtils,
602
+ packs,
603
+ aiGuidance,
580
604
  };
581
605
  }
582
606
 
@@ -605,6 +629,10 @@ export async function explainProject({ projectRoot, catalogRoot = null }) {
605
629
  continuity: registry.continuity,
606
630
  starterContentRegistries,
607
631
  availableMakeKinds,
632
+ sourceBoundary,
633
+ starterUtils,
634
+ packs,
635
+ aiGuidance,
608
636
  };
609
637
  }
610
638
 
@@ -622,9 +650,17 @@ export async function checkProject({ projectRoot, catalogRoot = null }) {
622
650
  reasonCode: 'project_root_missing',
623
651
  errors: [{ reasonCode: 'project_root_missing', message: 'Missing aura.config.json in the current directory.' }],
624
652
  warnings,
653
+ sourceBoundary: null,
654
+ starterUtils: null,
655
+ aiGuidance: null,
625
656
  });
626
657
  }
627
658
 
659
+ const sourceBoundary = describeSourceBoundary(projectRoot);
660
+ const starterUtils = describeStarterUtils(projectRoot);
661
+ const packs = describeProjectPacks(projectRoot);
662
+ const aiGuidance = describeProjectAiGuidance({ template, starterUtils });
663
+
628
664
  if (!existsSync(registryPath)) {
629
665
  if (template === 'blank' && !hasAuthoredProjectFiles(projectRoot)) {
630
666
  return buildProjectCheckResult({
@@ -633,6 +669,10 @@ export async function checkProject({ projectRoot, catalogRoot = null }) {
633
669
  reasonCode: 'bootstrap_only_project_ok',
634
670
  errors,
635
671
  warnings,
672
+ sourceBoundary,
673
+ starterUtils,
674
+ packs,
675
+ aiGuidance,
636
676
  });
637
677
  }
638
678
  return buildProjectCheckResult({
@@ -641,6 +681,10 @@ export async function checkProject({ projectRoot, catalogRoot = null }) {
641
681
  reasonCode: 'project_registry_missing',
642
682
  errors: [{ reasonCode: 'project_registry_missing', message: 'Missing src/runtime/project-registry.js.' }],
643
683
  warnings,
684
+ sourceBoundary,
685
+ starterUtils,
686
+ packs,
687
+ aiGuidance,
644
688
  });
645
689
  }
646
690
 
@@ -658,6 +702,10 @@ export async function checkProject({ projectRoot, catalogRoot = null }) {
658
702
  path: PROJECT_REGISTRY_RELATIVE_PATH,
659
703
  }],
660
704
  warnings,
705
+ sourceBoundary,
706
+ starterUtils,
707
+ packs,
708
+ aiGuidance,
661
709
  });
662
710
  }
663
711
 
@@ -705,6 +753,10 @@ export async function checkProject({ projectRoot, catalogRoot = null }) {
705
753
  errors,
706
754
  warnings,
707
755
  starterContentRegistries: starterContentValidation.registries,
756
+ sourceBoundary,
757
+ starterUtils,
758
+ packs,
759
+ aiGuidance,
708
760
  });
709
761
  }
710
762
 
@@ -954,7 +1006,22 @@ function devRestoreOrFallback(value) {
954
1006
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
955
1007
  }
956
1008
 
957
- function buildProjectCheckResult({ mode, ok, reasonCode, errors, warnings, starterContentRegistries = [] }) {
1009
+ function buildProjectCheckResult({
1010
+ mode,
1011
+ ok,
1012
+ reasonCode,
1013
+ errors,
1014
+ warnings,
1015
+ starterContentRegistries = [],
1016
+ sourceBoundary = null,
1017
+ starterUtils = null,
1018
+ packs = null,
1019
+ aiGuidance = null,
1020
+ }) {
1021
+ const normalizedSourceBoundary = normalizeSourceBoundary(sourceBoundary);
1022
+ const normalizedStarterUtils = normalizeStarterUtils(starterUtils);
1023
+ const normalizedPacks = normalizeProjectPacks(packs);
1024
+ const normalizedAiGuidance = normalizeAiGuidance(aiGuidance);
958
1025
  return {
959
1026
  schema: PROJECT_CHECK_SCHEMA,
960
1027
  ok,
@@ -963,6 +1030,16 @@ function buildProjectCheckResult({ mode, ok, reasonCode, errors, warnings, start
963
1030
  errors,
964
1031
  warnings,
965
1032
  starterContentRegistries,
1033
+ sourceBoundary: normalizedSourceBoundary,
1034
+ starterUtils: normalizedStarterUtils,
1035
+ packs: normalizedPacks,
1036
+ aiGuidance: normalizedAiGuidance,
1037
+ surfaceDrift: describeProjectSurfaceDrift({
1038
+ mode,
1039
+ sourceBoundary: normalizedSourceBoundary,
1040
+ starterUtils: normalizedStarterUtils,
1041
+ aiGuidance: normalizedAiGuidance,
1042
+ }),
966
1043
  };
967
1044
  }
968
1045
 
@@ -998,6 +1075,43 @@ function formatExplainReport(report) {
998
1075
  }
999
1076
  }
1000
1077
 
1078
+ lines.push('');
1079
+ lines.push('Authoring boundary:');
1080
+ if (!Array.isArray(report.sourceBoundary?.authoredRoots) || report.sourceBoundary.authoredRoots.length === 0) {
1081
+ lines.push(' authored roots: (none detected)');
1082
+ } else {
1083
+ lines.push(` authored roots: ${report.sourceBoundary.authoredRoots.join(', ')}`);
1084
+ }
1085
+ if (!Array.isArray(report.sourceBoundary?.supportedPackageImports) || report.sourceBoundary.supportedPackageImports.length === 0) {
1086
+ lines.push(' supported shared imports: (none)');
1087
+ } else {
1088
+ lines.push(` supported shared imports: ${report.sourceBoundary.supportedPackageImports.join(', ')}`);
1089
+ }
1090
+
1091
+ lines.push('');
1092
+ lines.push('Starter helper lane:');
1093
+ if (report.starterUtils?.available !== true) {
1094
+ lines.push(' (no scaffolded starter-utils copy detected)');
1095
+ } else {
1096
+ lines.push(` root: ${report.starterUtils.rootPath}`);
1097
+ lines.push(` recommended import: ${report.starterUtils.recommendedImportPath || '(none)'}`);
1098
+ lines.push(` default package: ${report.starterUtils.defaultPackageSpecifier || SHARED_HELPER_PACKAGE_SPECIFIER}`);
1099
+ lines.push(` modules: ${(report.starterUtils.modules || []).map((entry) => `${entry.path} [${entry.mode}]`).join(', ') || '(none)'}`);
1100
+ }
1101
+
1102
+ lines.push('');
1103
+ lines.push('AI paved road:');
1104
+ lines.push(` lane: ${report.aiGuidance?.laneId || 'bootstrap-authored-project'}`);
1105
+ if (report.aiGuidance?.summary) {
1106
+ lines.push(` summary: ${report.aiGuidance.summary}`);
1107
+ }
1108
+ lines.push(` preferred import: ${report.aiGuidance?.recommendedImportPath || report.aiGuidance?.preferredPackageSpecifier || '(none)'}`);
1109
+ if (!Array.isArray(report.aiGuidance?.recommendedModules) || report.aiGuidance.recommendedModules.length === 0) {
1110
+ lines.push(' recommended modules: (none)');
1111
+ } else {
1112
+ lines.push(` recommended modules: ${report.aiGuidance.recommendedModules.map((entry) => `${entry.id} -> ${entry.preferredImportSource}`).join(', ')}`);
1113
+ }
1114
+
1001
1115
  lines.push('');
1002
1116
  lines.push('Scenes:');
1003
1117
  if ((report.scenes || []).length === 0) {
@@ -1088,6 +1202,22 @@ function formatExplainReport(report) {
1088
1202
  }
1089
1203
  }
1090
1204
 
1205
+ lines.push('');
1206
+ lines.push('Optional packs:');
1207
+ if (!Array.isArray(report.packs?.selected) || report.packs.selected.length === 0) {
1208
+ lines.push(' (none selected)');
1209
+ } else {
1210
+ for (const entry of report.packs.selected) {
1211
+ lines.push(` ${entry.id} -> ${entry.packageName || '(unknown package)'}${entry.spec ? ` [${entry.spec}]` : ''} (${entry.sourceMode || 'package'})`);
1212
+ if (Array.isArray(entry.vendoredPaths) && entry.vendoredPaths.length > 0) {
1213
+ lines.push(` vendored: ${entry.vendoredPaths.join(', ')}`);
1214
+ }
1215
+ }
1216
+ for (const entry of report.packs?.bundles || []) {
1217
+ lines.push(` bundle ${entry.id} -> ${(entry.memberPackIds || []).join(', ') || '(none)'}`);
1218
+ }
1219
+ }
1220
+
1091
1221
  return `${lines.join('\n')}\n`;
1092
1222
  }
1093
1223
 
@@ -1098,6 +1228,15 @@ function formatCheckReport(report) {
1098
1228
  `Reason code: ${report.reasonCode}`,
1099
1229
  ];
1100
1230
 
1231
+ lines.push('');
1232
+ lines.push(`Authoring boundary: ${(report.sourceBoundary?.authoredRoots || []).join(', ') || '(none detected)'}`);
1233
+ lines.push(`Starter helper lane: ${report.starterUtils?.available === true ? report.starterUtils.recommendedImportPath || report.starterUtils.rootPath : '(not scaffolded)'}`);
1234
+ lines.push(`Optional packs: ${Array.isArray(report.packs?.selected) && report.packs.selected.length > 0 ? report.packs.selected.map((entry) => entry.id).join(', ') : '(none selected)'}`);
1235
+ lines.push(`AI paved road: ${report.aiGuidance?.laneId || 'bootstrap-authored-project'}`);
1236
+ if (report.surfaceDrift) {
1237
+ lines.push(`Surface status: boundary=${report.surfaceDrift.sourceBoundary.reasonCode}, helpers=${report.surfaceDrift.starterUtils.reasonCode}, lane=${report.surfaceDrift.starterLane.reasonCode}`);
1238
+ }
1239
+
1101
1240
  if ((report.errors || []).length > 0) {
1102
1241
  lines.push('');
1103
1242
  lines.push('Errors:');
@@ -1349,6 +1488,354 @@ function normalizeProjectRelativePath(value) {
1349
1488
  return String(value || '').split(/[/\\]+/g).filter(Boolean).join(POSIX_SEP);
1350
1489
  }
1351
1490
 
1491
+ function describeSourceBoundary(projectRoot) {
1492
+ return {
1493
+ authoredRoots: AUTHORED_SOURCE_ROOT_DIRS
1494
+ .filter((relativePath) => existsSync(resolve(projectRoot, relativePath)))
1495
+ .map((relativePath) => normalizeProjectRelativePath(relativePath)),
1496
+ supportedPackageImports: [...SUPPORTED_HELPER_PACKAGE_SPECIFIERS],
1497
+ };
1498
+ }
1499
+
1500
+ function describeStarterUtils(projectRoot) {
1501
+ const starterUtilsRoot = resolve(projectRoot, STARTER_UTILS_ROOT_RELATIVE_PATH);
1502
+ if (!existsSync(starterUtilsRoot)) {
1503
+ return {
1504
+ available: false,
1505
+ rootPath: STARTER_UTILS_ROOT_RELATIVE_PATH,
1506
+ recommendedImportPath: null,
1507
+ defaultPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
1508
+ supportedPackageSpecifiers: [...SUPPORTED_HELPER_PACKAGE_SPECIFIERS],
1509
+ modules: [],
1510
+ };
1511
+ }
1512
+
1513
+ const modules = readdirSync(starterUtilsRoot)
1514
+ .filter((entry) => /\.(?:js|mjs)$/.test(entry))
1515
+ .sort((left, right) => left.localeCompare(right))
1516
+ .map((entry) => {
1517
+ const absolutePath = resolve(starterUtilsRoot, entry);
1518
+ const source = readFileSync(absolutePath, 'utf8');
1519
+ const packageSpecifier = detectStarterUtilsPackageSpecifier(source);
1520
+ return {
1521
+ path: normalizeProjectRelativePath(`${STARTER_UTILS_ROOT_RELATIVE_PATH}/${entry}`),
1522
+ mode: packageSpecifier ? 'package-facade' : 'authored-local',
1523
+ packageSpecifier: packageSpecifier || null,
1524
+ exports: listNamedExportsFromSource(source),
1525
+ };
1526
+ });
1527
+
1528
+ const indexModule = modules.find((entry) => entry.path === `${STARTER_UTILS_ROOT_RELATIVE_PATH}/index.js`) || null;
1529
+ return {
1530
+ available: true,
1531
+ rootPath: STARTER_UTILS_ROOT_RELATIVE_PATH,
1532
+ recommendedImportPath: indexModule ? './src/starter-utils/index.js' : null,
1533
+ defaultPackageSpecifier: SHARED_HELPER_PACKAGE_SPECIFIER,
1534
+ supportedPackageSpecifiers: [...SUPPORTED_HELPER_PACKAGE_SPECIFIERS],
1535
+ modules,
1536
+ };
1537
+ }
1538
+
1539
+ function describeProjectPacks(projectRoot) {
1540
+ const manifestPath = resolve(projectRoot, PROJECT_PACKS_RELATIVE_PATH);
1541
+ const packageJsonPath = resolve(projectRoot, 'package.json');
1542
+ const packageJson = existsSync(packageJsonPath)
1543
+ ? JSON.parse(readFileSync(packageJsonPath, 'utf8'))
1544
+ : {};
1545
+ const dependencies = packageJson.dependencies && typeof packageJson.dependencies === 'object'
1546
+ ? packageJson.dependencies
1547
+ : {};
1548
+ const manifest = existsSync(manifestPath)
1549
+ ? normalizeProjectPackManifest(JSON.parse(readFileSync(manifestPath, 'utf8')))
1550
+ : normalizeProjectPackManifest(null);
1551
+
1552
+ const selectedPacks = manifest.packs.length > 0
1553
+ ? manifest.packs.map((entry) => {
1554
+ const catalog = findPackOrBundleEntry(entry.id);
1555
+ return {
1556
+ id: entry.id,
1557
+ kind: entry.kind,
1558
+ packageName: entry.packageName || catalog?.packageName || null,
1559
+ spec: entry.spec || (entry.packageName && typeof dependencies[entry.packageName] === 'string' ? dependencies[entry.packageName] : null),
1560
+ installMode: entry.installMode === 'workspace' ? 'workspace' : 'package',
1561
+ sourceMode: entry.sourceMode === 'github'
1562
+ ? 'github'
1563
+ : entry.sourceMode === 'workspace'
1564
+ ? 'workspace'
1565
+ : 'package',
1566
+ vendoredPaths: Array.isArray(entry.vendoredPaths) ? entry.vendoredPaths.map((item) => String(item)) : [],
1567
+ title: catalog?.title || null,
1568
+ };
1569
+ })
1570
+ : listPackRegistry()
1571
+ .filter((entry) => typeof dependencies[entry.packageName] === 'string')
1572
+ .map((entry) => ({
1573
+ id: entry.id,
1574
+ kind: entry.kind,
1575
+ packageName: entry.packageName,
1576
+ spec: dependencies[entry.packageName],
1577
+ installMode: String(dependencies[entry.packageName]).startsWith('file:') ? 'workspace' : 'package',
1578
+ sourceMode: String(dependencies[entry.packageName]).startsWith('file:') ? 'workspace' : 'package',
1579
+ vendoredPaths: [],
1580
+ title: entry.title,
1581
+ }));
1582
+
1583
+ return {
1584
+ available: selectedPacks.length > 0 || manifest.bundles.length > 0 || existsSync(manifestPath),
1585
+ manifestPath: existsSync(manifestPath) ? PROJECT_PACKS_RELATIVE_PATH : null,
1586
+ selected: selectedPacks,
1587
+ bundles: manifest.bundles.map((entry) => {
1588
+ const catalog = findPackOrBundleEntry(entry.id);
1589
+ return {
1590
+ id: entry.id,
1591
+ packageName: entry.packageName || catalog?.packageName || null,
1592
+ memberPackIds: Array.isArray(entry.memberPackIds) ? entry.memberPackIds.map((item) => String(item)) : [],
1593
+ github: catalog?.github ? { ...catalog.github } : null,
1594
+ title: catalog?.title || null,
1595
+ };
1596
+ }),
1597
+ };
1598
+ }
1599
+
1600
+ function detectStarterUtilsPackageSpecifier(source) {
1601
+ const match = String(source || '').match(/from ['"](@auraindustry\/aurajs\/helpers(?:\/2d)?)['"]/);
1602
+ return match ? match[1] : null;
1603
+ }
1604
+
1605
+ function listNamedExportsFromSource(source) {
1606
+ const names = [];
1607
+ const seen = new Set();
1608
+ const lines = String(source || '').split(/\r?\n/);
1609
+
1610
+ const pushName = (value) => {
1611
+ if (!value || seen.has(value)) {
1612
+ return;
1613
+ }
1614
+ seen.add(value);
1615
+ names.push(value);
1616
+ };
1617
+
1618
+ for (let index = 0; index < lines.length; index += 1) {
1619
+ const trimmed = lines[index].trim();
1620
+ if (!trimmed.startsWith('export ')) {
1621
+ continue;
1622
+ }
1623
+
1624
+ const functionExport = trimmed.match(/^export\s+function\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/);
1625
+ if (functionExport) {
1626
+ pushName(functionExport[1]);
1627
+ continue;
1628
+ }
1629
+
1630
+ const classExport = trimmed.match(/^export\s+class\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/);
1631
+ if (classExport) {
1632
+ pushName(classExport[1]);
1633
+ continue;
1634
+ }
1635
+
1636
+ const variableExport = trimmed.match(/^export\s+(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\b/);
1637
+ if (variableExport) {
1638
+ pushName(variableExport[1]);
1639
+ continue;
1640
+ }
1641
+
1642
+ if (trimmed.startsWith('export {')) {
1643
+ let statement = trimmed;
1644
+ while (!statement.includes('}') && index + 1 < lines.length) {
1645
+ index += 1;
1646
+ statement += ` ${lines[index].trim()}`;
1647
+ }
1648
+ const match = statement.match(/^export\s*\{([^}]+)\}/);
1649
+ if (!match) {
1650
+ continue;
1651
+ }
1652
+ for (const entry of match[1].split(',')) {
1653
+ const spec = entry.trim();
1654
+ if (!spec) continue;
1655
+ const alias = spec.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\s+as\s+([A-Za-z_$][A-Za-z0-9_$]*)$/);
1656
+ pushName(alias ? alias[2] : spec);
1657
+ }
1658
+ }
1659
+ }
1660
+
1661
+ return names;
1662
+ }
1663
+
1664
+ function normalizeSourceBoundary(boundary) {
1665
+ const safe = boundary && typeof boundary === 'object' && !Array.isArray(boundary) ? boundary : {};
1666
+ return {
1667
+ authoredRoots: Array.isArray(safe.authoredRoots) ? safe.authoredRoots.map((entry) => normalizeProjectRelativePath(entry)) : [],
1668
+ supportedPackageImports: Array.isArray(safe.supportedPackageImports) ? safe.supportedPackageImports.map((entry) => String(entry)) : [],
1669
+ };
1670
+ }
1671
+
1672
+ function normalizeStarterUtils(starterUtils) {
1673
+ const safe = starterUtils && typeof starterUtils === 'object' && !Array.isArray(starterUtils) ? starterUtils : {};
1674
+ return {
1675
+ available: safe.available === true,
1676
+ rootPath: safe.rootPath ? normalizeProjectRelativePath(safe.rootPath) : STARTER_UTILS_ROOT_RELATIVE_PATH,
1677
+ recommendedImportPath: typeof safe.recommendedImportPath === 'string' && safe.recommendedImportPath.trim().length > 0
1678
+ ? safe.recommendedImportPath.trim()
1679
+ : null,
1680
+ defaultPackageSpecifier: typeof safe.defaultPackageSpecifier === 'string' && safe.defaultPackageSpecifier.trim().length > 0
1681
+ ? safe.defaultPackageSpecifier.trim()
1682
+ : SHARED_HELPER_PACKAGE_SPECIFIER,
1683
+ supportedPackageSpecifiers: Array.isArray(safe.supportedPackageSpecifiers)
1684
+ ? safe.supportedPackageSpecifiers.map((entry) => String(entry))
1685
+ : [...SUPPORTED_HELPER_PACKAGE_SPECIFIERS],
1686
+ modules: Array.isArray(safe.modules)
1687
+ ? safe.modules.map((entry) => ({
1688
+ path: normalizeProjectRelativePath(entry.path),
1689
+ mode: entry.mode === 'authored-local' ? 'authored-local' : 'package-facade',
1690
+ packageSpecifier: typeof entry.packageSpecifier === 'string' && entry.packageSpecifier.trim().length > 0
1691
+ ? entry.packageSpecifier.trim()
1692
+ : null,
1693
+ exports: Array.isArray(entry.exports) ? entry.exports.map((name) => String(name)) : [],
1694
+ }))
1695
+ : [],
1696
+ };
1697
+ }
1698
+
1699
+ function normalizeProjectPacks(packs) {
1700
+ const safe = packs && typeof packs === 'object' && !Array.isArray(packs) ? packs : {};
1701
+ return {
1702
+ available: safe.available === true,
1703
+ manifestPath: typeof safe.manifestPath === 'string' && safe.manifestPath.trim().length > 0
1704
+ ? normalizeProjectRelativePath(safe.manifestPath)
1705
+ : null,
1706
+ selected: Array.isArray(safe.selected)
1707
+ ? safe.selected.map((entry) => ({
1708
+ id: typeof entry.id === 'string' ? entry.id : null,
1709
+ kind: typeof entry.kind === 'string' ? entry.kind : 'starter-pack',
1710
+ packageName: typeof entry.packageName === 'string' ? entry.packageName : null,
1711
+ spec: typeof entry.spec === 'string' ? entry.spec : null,
1712
+ installMode: entry.installMode === 'workspace' ? 'workspace' : 'package',
1713
+ sourceMode: entry.sourceMode === 'github'
1714
+ ? 'github'
1715
+ : entry.sourceMode === 'workspace'
1716
+ ? 'workspace'
1717
+ : 'package',
1718
+ vendoredPaths: Array.isArray(entry.vendoredPaths) ? entry.vendoredPaths.map((item) => String(item)) : [],
1719
+ title: typeof entry.title === 'string' ? entry.title : null,
1720
+ })).filter((entry) => entry.id)
1721
+ : [],
1722
+ bundles: Array.isArray(safe.bundles)
1723
+ ? safe.bundles.map((entry) => ({
1724
+ id: typeof entry.id === 'string' ? entry.id : null,
1725
+ packageName: typeof entry.packageName === 'string' ? entry.packageName : null,
1726
+ memberPackIds: Array.isArray(entry.memberPackIds) ? entry.memberPackIds.map((item) => String(item)) : [],
1727
+ github: entry.github && typeof entry.github === 'object' && !Array.isArray(entry.github)
1728
+ ? {
1729
+ repository: typeof entry.github.repository === 'string' ? entry.github.repository : null,
1730
+ ref: typeof entry.github.ref === 'string' ? entry.github.ref : null,
1731
+ }
1732
+ : null,
1733
+ title: typeof entry.title === 'string' ? entry.title : null,
1734
+ })).filter((entry) => entry.id)
1735
+ : [],
1736
+ };
1737
+ }
1738
+
1739
+ function normalizeSurfaceDrift(value) {
1740
+ const safe = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
1741
+ const normalizeEntry = (entry, fallbackReasonCode) => {
1742
+ const source = entry && typeof entry === 'object' && !Array.isArray(entry) ? entry : {};
1743
+ return {
1744
+ status: typeof source.status === 'string' && source.status.trim().length > 0
1745
+ ? source.status.trim()
1746
+ : 'info',
1747
+ reasonCode: typeof source.reasonCode === 'string' && source.reasonCode.trim().length > 0
1748
+ ? source.reasonCode.trim()
1749
+ : fallbackReasonCode,
1750
+ message: typeof source.message === 'string' && source.message.trim().length > 0
1751
+ ? source.message.trim()
1752
+ : null,
1753
+ };
1754
+ };
1755
+
1756
+ return {
1757
+ sourceBoundary: normalizeEntry(safe.sourceBoundary, 'source_boundary_unknown'),
1758
+ starterUtils: normalizeEntry(safe.starterUtils, 'starter_utils_unknown'),
1759
+ starterLane: normalizeEntry(safe.starterLane, 'starter_lane_unknown'),
1760
+ };
1761
+ }
1762
+
1763
+ function describeProjectSurfaceDrift({ mode, sourceBoundary, starterUtils, aiGuidance }) {
1764
+ const authoredRoots = Array.isArray(sourceBoundary?.authoredRoots) ? sourceBoundary.authoredRoots : [];
1765
+ const hasStarterUtils = starterUtils?.available === true;
1766
+ const hasStarterImport = typeof starterUtils?.recommendedImportPath === 'string' && starterUtils.recommendedImportPath.trim().length > 0;
1767
+ const laneId = typeof aiGuidance?.laneId === 'string' && aiGuidance.laneId.trim().length > 0
1768
+ ? aiGuidance.laneId.trim()
1769
+ : 'bootstrap-authored-project';
1770
+ const preferredPackageSpecifier = typeof aiGuidance?.preferredPackageSpecifier === 'string' && aiGuidance.preferredPackageSpecifier.trim().length > 0
1771
+ ? aiGuidance.preferredPackageSpecifier.trim()
1772
+ : null;
1773
+
1774
+ const sourceBoundaryStatus = authoredRoots.length > 0
1775
+ ? {
1776
+ status: 'pass',
1777
+ reasonCode: 'source_boundary_detected',
1778
+ message: `Authored roots detected: ${authoredRoots.join(', ')}.`,
1779
+ }
1780
+ : {
1781
+ status: 'warn',
1782
+ reasonCode: 'source_boundary_empty',
1783
+ message: 'No authored roots were detected under the canonical project surface.',
1784
+ };
1785
+
1786
+ let starterUtilsStatus = {
1787
+ status: 'info',
1788
+ reasonCode: 'starter_utils_not_scaffolded',
1789
+ message: 'No local starter-utils lane is present in this project.',
1790
+ };
1791
+ if (hasStarterUtils && hasStarterImport) {
1792
+ starterUtilsStatus = {
1793
+ status: 'pass',
1794
+ reasonCode: 'starter_utils_detected',
1795
+ message: `Starter helper lane is available at ${starterUtils.recommendedImportPath}.`,
1796
+ };
1797
+ } else if (hasStarterUtils) {
1798
+ starterUtilsStatus = {
1799
+ status: 'warn',
1800
+ reasonCode: 'starter_utils_entry_missing',
1801
+ message: 'Starter helper files exist, but src/starter-utils/index.js is missing.',
1802
+ };
1803
+ }
1804
+
1805
+ let starterLaneStatus = {
1806
+ status: 'info',
1807
+ reasonCode: 'starter_lane_bootstrap',
1808
+ message: 'This project is currently on the bootstrap-authored lane.',
1809
+ };
1810
+ if (mode === 'registry' && laneId !== 'bootstrap-authored-project') {
1811
+ if (hasStarterUtils && hasStarterImport) {
1812
+ starterLaneStatus = {
1813
+ status: 'pass',
1814
+ reasonCode: 'starter_lane_local_helpers',
1815
+ message: `AI paved road ${laneId} is aligned with local starter-utils imports.`,
1816
+ };
1817
+ } else if (preferredPackageSpecifier) {
1818
+ starterLaneStatus = {
1819
+ status: 'info',
1820
+ reasonCode: 'starter_lane_package_surface',
1821
+ message: `AI paved road ${laneId} currently resolves through ${preferredPackageSpecifier}.`,
1822
+ };
1823
+ } else {
1824
+ starterLaneStatus = {
1825
+ status: 'warn',
1826
+ reasonCode: 'starter_lane_unresolved',
1827
+ message: `AI paved road ${laneId} does not have a local helper lane or preferred package surface.`,
1828
+ };
1829
+ }
1830
+ }
1831
+
1832
+ return {
1833
+ sourceBoundary: sourceBoundaryStatus,
1834
+ starterUtils: starterUtilsStatus,
1835
+ starterLane: starterLaneStatus,
1836
+ };
1837
+ }
1838
+
1352
1839
  function toImportPath(relativePath) {
1353
1840
  const normalized = normalizeProjectRelativePath(relativePath);
1354
1841
  if (normalized.startsWith('.')) return normalized;
@@ -1420,6 +1907,10 @@ function normalizeExplainPayload(explained) {
1420
1907
  scope: entry.scope || 'core',
1421
1908
  }))
1422
1909
  : [],
1910
+ sourceBoundary: normalizeSourceBoundary(explained.sourceBoundary),
1911
+ starterUtils: normalizeStarterUtils(explained.starterUtils),
1912
+ packs: normalizeProjectPacks(explained.packs),
1913
+ aiGuidance: normalizeAiGuidance(explained.aiGuidance),
1423
1914
  };
1424
1915
  }
1425
1916
 
@@ -1433,6 +1924,11 @@ function normalizeCheckPayload({ projectRoot, checked }) {
1433
1924
  errors: Array.isArray(checked.errors) ? checked.errors : [],
1434
1925
  warnings: Array.isArray(checked.warnings) ? checked.warnings : [],
1435
1926
  starterContentRegistries: Array.isArray(checked.starterContentRegistries) ? checked.starterContentRegistries : [],
1927
+ sourceBoundary: normalizeSourceBoundary(checked.sourceBoundary),
1928
+ starterUtils: normalizeStarterUtils(checked.starterUtils),
1929
+ packs: normalizeProjectPacks(checked.packs),
1930
+ aiGuidance: normalizeAiGuidance(checked.aiGuidance),
1931
+ surfaceDrift: normalizeSurfaceDrift(checked.surfaceDrift),
1436
1932
  };
1437
1933
  }
1438
1934