@auraindustry/aurajs 0.1.0 → 0.1.3

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/asset-pack.mjs +5 -1
  3. package/src/authored-runtime.mjs +14 -0
  4. package/src/bin-integrity.mjs +33 -26
  5. package/src/cli.mjs +17 -2
  6. package/src/commands/project-authoring.mjs +20 -0
  7. package/src/config.mjs +17 -0
  8. package/src/conformance/cases/systems-and-gameplay-cases.mjs +861 -6
  9. package/src/external-package-surface.mjs +1 -1
  10. package/src/package-integrity.mjs +18 -4
  11. package/src/publish-command.mjs +133 -13
  12. package/src/publish-validation.mjs +22 -11
  13. package/src/scaffold/project-docs.mjs +60 -41
  14. package/src/web-conformance.mjs +4 -4
  15. package/templates/create/2d/src/runtime/app.js +4 -0
  16. package/templates/create/2d-survivor/src/runtime/app.js +4 -0
  17. package/templates/create/3d/src/runtime/app.js +4 -0
  18. package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
  19. package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
  20. package/templates/create/blank/assets/splash/bg.webp +0 -0
  21. package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
  22. package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
  23. package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
  24. package/templates/create/blank/assets/splash/logoholo.webp +0 -0
  25. package/templates/create/blank/src/main.js +5 -1
  26. package/templates/create/blank/src/runtime/splash.js +305 -0
  27. package/templates/create/local-multiplayer/aura.config.json +1 -0
  28. package/templates/create/local-multiplayer/docs/design/loop.md +3 -1
  29. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +216 -13
  30. package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
  31. package/templates/create/local-multiplayer/ui/hud.screen.js +12 -7
  32. package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
  33. package/templates/create/shared/assets/splash/bg.webp +0 -0
  34. package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
  35. package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
  36. package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
  37. package/templates/create/shared/assets/splash/logoholo.webp +0 -0
  38. package/templates/create/shared/src/runtime/splash.js +305 -0
  39. package/templates/create/video-cutscene/src/runtime/app.js +4 -0
  40. package/templates/create-bin/play.js +121 -4
  41. package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
  42. package/templates/starter/assets/splash/bg.webp +0 -0
  43. package/templates/starter/assets/splash/boot-loop.wav +0 -0
  44. package/templates/starter/assets/splash/boot-sting.wav +0 -0
  45. package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
  46. package/templates/starter/assets/splash/logoholo.webp +0 -0
  47. package/templates/starter/src/main.js +4 -0
  48. package/templates/starter/src/runtime/splash.js +305 -0
@@ -36,7 +36,7 @@ function shouldCopyPath(sourcePath, {
36
36
  }
37
37
 
38
38
  const topLevel = relativePath.split('/')[0];
39
- if (topLevel === '.aura' || topLevel === '.git' || topLevel === 'node_modules') {
39
+ if (topLevel === '.aura' || topLevel === '.git' || topLevel === '.logs' || topLevel === 'node_modules') {
40
40
  return false;
41
41
  }
42
42
 
@@ -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,
@@ -1,6 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { resolve } from 'node:path';
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join, resolve } from 'node:path';
4
5
 
5
6
  import { preparePublishPackageSurface } from './external-package-surface.mjs';
6
7
  import {
@@ -9,6 +10,7 @@ import {
9
10
  } from './package-integrity.mjs';
10
11
  import { resolvePublishEnvExampleSurface } from './publish-env-example.mjs';
11
12
  import { validatePublishProject } from './publish-validation.mjs';
13
+ import { formatBytes } from './external-asset-policy.mjs';
12
14
 
13
15
  export function isNpmPublishLifecycleInvocation(env = process.env) {
14
16
  return env.npm_lifecycle_event === 'publish' && env.npm_command === 'publish';
@@ -18,6 +20,116 @@ export function hasOption(commandArgs, optionName) {
18
20
  return commandArgs.some((arg) => arg === optionName || arg.startsWith(`${optionName}=`));
19
21
  }
20
22
 
23
+ function stripOption(commandArgs, optionName) {
24
+ const nextArgs = [];
25
+ for (let index = 0; index < commandArgs.length; index += 1) {
26
+ const current = commandArgs[index];
27
+ if (current === optionName) {
28
+ index += 1;
29
+ continue;
30
+ }
31
+ if (current.startsWith(`${optionName}=`)) {
32
+ continue;
33
+ }
34
+ nextArgs.push(current);
35
+ }
36
+ return nextArgs;
37
+ }
38
+
39
+ export function extractPublishToken(commandArgs, env = process.env) {
40
+ let explicitToken = null;
41
+
42
+ for (let index = 0; index < commandArgs.length; index += 1) {
43
+ const current = commandArgs[index];
44
+ if (current === '--token') {
45
+ explicitToken = commandArgs[index + 1] || '';
46
+ break;
47
+ }
48
+ if (current.startsWith('--token=')) {
49
+ explicitToken = current.slice('--token='.length);
50
+ break;
51
+ }
52
+ }
53
+
54
+ const envToken = typeof env.NODE_AUTH_TOKEN === 'string' && env.NODE_AUTH_TOKEN.trim().length > 0
55
+ ? env.NODE_AUTH_TOKEN.trim()
56
+ : null;
57
+ const normalizedToken = typeof explicitToken === 'string' && explicitToken.trim().length > 0
58
+ ? explicitToken.trim()
59
+ : envToken;
60
+
61
+ return {
62
+ publishArgs: stripOption(commandArgs, '--token'),
63
+ publishEnv: normalizedToken
64
+ ? {
65
+ ...env,
66
+ NODE_AUTH_TOKEN: normalizedToken,
67
+ }
68
+ : env,
69
+ publishToken: normalizedToken,
70
+ };
71
+ }
72
+
73
+ function normalizeRegistryUrl(registryValue) {
74
+ const normalizedValue = String(registryValue || '').trim();
75
+ if (!normalizedValue) {
76
+ return 'https://registry.npmjs.org/';
77
+ }
78
+
79
+ try {
80
+ const registryUrl = new URL(normalizedValue);
81
+ if (!registryUrl.pathname.endsWith('/')) {
82
+ registryUrl.pathname = `${registryUrl.pathname}/`;
83
+ }
84
+ return registryUrl.toString();
85
+ } catch {
86
+ return 'https://registry.npmjs.org/';
87
+ }
88
+ }
89
+
90
+ function buildRegistryAuthLine(token, registryUrl) {
91
+ const parsedUrl = new URL(registryUrl);
92
+ const registryPath = parsedUrl.pathname === '/' ? '' : parsedUrl.pathname.replace(/\/$/, '');
93
+ return `//${parsedUrl.host}${registryPath}/:_authToken=${token}`;
94
+ }
95
+
96
+ function createPublishTokenEnv(token, env = process.env) {
97
+ if (!token) {
98
+ return {
99
+ env,
100
+ cleanup() {},
101
+ };
102
+ }
103
+
104
+ const registryUrl = normalizeRegistryUrl(env.npm_config_registry || env.NPM_CONFIG_REGISTRY);
105
+ const userConfigDir = mkdtempSync(join(tmpdir(), 'aurajs-npm-auth-'));
106
+ const userConfigPath = join(userConfigDir, '.npmrc');
107
+ writeFileSync(
108
+ userConfigPath,
109
+ [
110
+ `registry=${registryUrl}`,
111
+ 'always-auth=true',
112
+ buildRegistryAuthLine(token, registryUrl),
113
+ '',
114
+ ].join('\n'),
115
+ 'utf8',
116
+ );
117
+
118
+ return {
119
+ env: {
120
+ ...env,
121
+ NODE_AUTH_TOKEN: token,
122
+ NPM_CONFIG_USERCONFIG: userConfigPath,
123
+ npm_config_userconfig: userConfigPath,
124
+ NPM_CONFIG_REGISTRY: registryUrl,
125
+ npm_config_registry: registryUrl,
126
+ },
127
+ cleanup() {
128
+ rmSync(userConfigDir, { recursive: true, force: true });
129
+ },
130
+ };
131
+ }
132
+
21
133
  export function readProjectPackage(projectRoot = process.cwd()) {
22
134
  const packagePath = resolve(projectRoot, 'package.json');
23
135
  if (!existsSync(packagePath)) {
@@ -46,8 +158,9 @@ export function readProjectPackage(projectRoot = process.cwd()) {
46
158
  }
47
159
 
48
160
  export function buildPublishArgs(commandArgs, { packageName } = {}) {
49
- const publishArgs = ['publish', ...commandArgs];
50
- if (String(packageName || '').startsWith('@') && !hasOption(commandArgs, '--access')) {
161
+ const sanitizedArgs = stripOption(commandArgs, '--token');
162
+ const publishArgs = ['publish', ...sanitizedArgs];
163
+ if (String(packageName || '').startsWith('@') && !hasOption(sanitizedArgs, '--access')) {
51
164
  publishArgs.push('--access', 'public');
52
165
  }
53
166
  return publishArgs;
@@ -68,13 +181,10 @@ function spawnPublish(commandArgs, { projectRoot, env, stdout, stderr }) {
68
181
  return new Promise((resolveRun, rejectRun) => {
69
182
  const child = spawn(resolveNpmCommand(), commandArgs, {
70
183
  cwd: projectRoot,
71
- stdio: ['ignore', 'pipe', 'pipe'],
184
+ stdio: 'inherit',
72
185
  env,
73
186
  });
74
187
 
75
- pipeChildStream(child.stdout, stdout);
76
- pipeChildStream(child.stderr, stderr);
77
-
78
188
  child.on('error', (error) => {
79
189
  rejectRun(error);
80
190
  });
@@ -134,9 +244,13 @@ export async function runPublishCommand(
134
244
  const assetBytes = Number.isFinite(error?.details?.assetBytes) ? error.details.assetBytes : null;
135
245
  const thresholdBytes = Number.isFinite(error?.details?.thresholdBytes) ? error.details.thresholdBytes : null;
136
246
  if (assetBytes !== null && thresholdBytes !== null) {
137
- stdout?.write(` Asset payload: ${assetBytes} bytes (threshold ${thresholdBytes})\n`);
247
+ stdout?.write(` Asset payload: ${formatBytes(assetBytes)} (${assetBytes} bytes)\n`);
248
+ stdout?.write(` Threshold: ${formatBytes(thresholdBytes)} (${thresholdBytes} bytes)\n`);
138
249
  }
139
- stdout?.write(' Next step: upload the heavy assets to a public HTTPS host like Cloudflare R2 or S3, generate the external asset config, then retry publish.\n');
250
+ stdout?.write(' This package is too large for npm-first publish.\n');
251
+ stdout?.write(' Self-host for now: upload the heavy assets to your own public HTTPS host, then generate the external asset config.\n');
252
+ stdout?.write(' Command: auramaxx external-assets generate --public-base-url https://cdn.example.com/my-game\n');
253
+ stdout?.write(' Docs: https://www.aurajs.gg/docs/publishing-and-large-assets\n');
140
254
  }
141
255
  if (error?.reportPath) {
142
256
  stdout?.write(` Validation report: ${error.reportPath}\n\n`);
@@ -146,8 +260,10 @@ export async function runPublishCommand(
146
260
 
147
261
  const packageName = validation.packageName;
148
262
  const { projectPackage } = readProjectPackage(projectRoot);
149
- const publishArgs = buildPublishArgs(commandArgs, { packageName });
150
- const dryRun = hasOption(commandArgs, '--dry-run');
263
+ const { publishArgs: sanitizedCommandArgs, publishEnv, publishToken } = extractPublishToken(commandArgs, env);
264
+ const publishArgs = buildPublishArgs(sanitizedCommandArgs, { packageName });
265
+ const publishTokenEnv = createPublishTokenEnv(publishToken, publishEnv);
266
+ const dryRun = hasOption(sanitizedCommandArgs, '--dry-run');
151
267
  const envExampleSurface = resolvePublishEnvExampleSurface({ projectRoot });
152
268
  const packageSurface = preparePublishPackageSurface({
153
269
  projectRoot,
@@ -162,6 +278,9 @@ export async function runPublishCommand(
162
278
  packageRoot: packageSurface.publishRoot,
163
279
  signerProjectRoot: projectRoot,
164
280
  buildMetadata: validation?.report?.validation?.build || null,
281
+ includedRelativePaths: Array.isArray(validation?.report?.validation?.pack?.files)
282
+ ? validation.report.validation.pack.files.map((entry) => entry?.path).filter(Boolean)
283
+ : null,
165
284
  });
166
285
  stdout?.write(`\n aura publish: ${dryRun ? 'dry-run' : 'npm-first publish'}\n`);
167
286
  stdout?.write(` Package: ${packageName}\n`);
@@ -176,11 +295,12 @@ export async function runPublishCommand(
176
295
  try {
177
296
  await spawnPublish(publishArgs, {
178
297
  projectRoot: packageSurface.publishRoot,
179
- env,
298
+ env: publishTokenEnv.env,
180
299
  stdout,
181
300
  stderr,
182
301
  });
183
302
  } finally {
303
+ publishTokenEnv.cleanup();
184
304
  packageSurface.cleanup();
185
305
  }
186
306
 
@@ -401,12 +401,16 @@ export async function validatePublishProject(
401
401
  );
402
402
  }
403
403
 
404
+ const expectedAurajsVersion = typeof projectPackage?.dependencies?.['@auraindustry/aurajs'] === 'string'
405
+ ? projectPackage.dependencies['@auraindustry/aurajs'].trim()
406
+ : null;
407
+
404
408
  const binIntegrity = assertProjectBinIntegrity({
405
409
  projectRoot,
406
410
  projectPackage,
407
411
  packageName,
408
412
  aurajsPackageRoot: DEFAULT_AURAJS_PACKAGE_ROOT,
409
- expectedAurajsVersion: AURAJS_PACKAGE_VERSION,
413
+ expectedAurajsVersion,
410
414
  enforceExactAurajsDependency: true,
411
415
  });
412
416
  report.validation.binIntegrity = {
@@ -566,20 +570,11 @@ export async function validatePublishProject(
566
570
  );
567
571
  }
568
572
 
569
- const packageIntegrity = writeSignedPackageIntegrityArtifacts({
573
+ let packageIntegrity = writeSignedPackageIntegrityArtifacts({
570
574
  packageRoot: packageSurface.publishRoot,
571
575
  signerProjectRoot: projectRoot,
572
576
  buildMetadata,
573
577
  });
574
- report.validation.packageIntegrity = {
575
- reasonCode: 'publish_package_integrity_ok',
576
- manifestPath: packageIntegrity.manifestPath,
577
- signaturePath: packageIntegrity.signaturePath,
578
- schema: packageIntegrity.schema,
579
- fileCount: packageIntegrity.fileCount,
580
- signerFingerprint: packageIntegrity.signerFingerprint,
581
- publishedMetadata: packageIntegrity.publishedMetadata,
582
- };
583
578
 
584
579
  assetThresholdRecord.packageSurfaceMode = packageSurface.mode;
585
580
  assetThresholdRecord.assetPackaging = packageSurface.mode === SELF_HOSTED_PACKAGE_SURFACE_MODE
@@ -664,6 +659,22 @@ export async function validatePublishProject(
664
659
  files: packFiles,
665
660
  };
666
661
 
662
+ packageIntegrity = writeSignedPackageIntegrityArtifacts({
663
+ packageRoot: packageSurface.publishRoot,
664
+ signerProjectRoot: projectRoot,
665
+ buildMetadata,
666
+ includedRelativePaths: packFiles.map((entry) => entry.path),
667
+ });
668
+ report.validation.packageIntegrity = {
669
+ reasonCode: 'publish_package_integrity_ok',
670
+ manifestPath: packageIntegrity.manifestPath,
671
+ signaturePath: packageIntegrity.signaturePath,
672
+ schema: packageIntegrity.schema,
673
+ fileCount: packageIntegrity.fileCount,
674
+ signerFingerprint: packageIntegrity.signerFingerprint,
675
+ publishedMetadata: packageIntegrity.publishedMetadata,
676
+ };
677
+
667
678
  report.summary = {
668
679
  pass: true,
669
680
  reasonCode: 'publish_validation_ok',
@@ -13,6 +13,7 @@ export function renderProjectReadme({ name, projectTitle, template, templateMeta
13
13
 
14
14
  const optionalCommands = [
15
15
  'npm run build',
16
+ '# packaged local launch sanity check',
16
17
  'npm run play',
17
18
  templateMetadata?.optionalModules?.multiplayer === true ? 'npm run join -- AURA2P' : null,
18
19
  'npm run publish',
@@ -21,7 +22,7 @@ export function renderProjectReadme({ name, projectTitle, template, templateMeta
21
22
 
22
23
  return `# ${projectTitle}
23
24
 
24
- Scaffolded with \`aura create ${name} --template ${template}\`.
25
+ Scaffolded with \`auramaxx create ${name} --template ${template}\`.
25
26
 
26
27
  ## Quick Start
27
28
 
@@ -34,14 +35,14 @@ Optional commands:
34
35
 
35
36
  \`\`\`bash
36
37
  ${optionalCommands}
37
- npx aura explain
38
- npx aura check
38
+ auramaxx explain
39
+ auramaxx check
39
40
  \`\`\`
40
41
 
41
42
  ${template === 'blank' ? '' : 'Press `F1` while the starter is running to toggle the development-only playtest HUD. Use the top scene strip to jump between scenes and the toolbar to record, capture screenshots, restart, pause, or minimize the HUD.\n\n'}
42
43
 
43
44
  If you are publishing outside the \`@aurajs\` scope, update \`package.json -> name\`
44
- before the first \`npm run publish\`.
45
+ before the first \`auramaxx publish\`.
45
46
 
46
47
  ${largeAssetSection}
47
48
  ${workflowSection}
@@ -109,9 +110,9 @@ If any are unavailable at runtime, fail fast and capture the reason code before
109
110
  ## Build + Share
110
111
 
111
112
  1. \`npm run build\` to emit native/web artifacts.
112
- 1. \`npm run play\` to launch through the generated game CLI wrapper.
113
+ 1. \`npm run play\` to sanity-check the packaged local wrapper path.
113
114
  ${templateMetadata?.optionalModules?.multiplayer === true ? '1. `npm run join -- AURA2P` to join a room-code multiplayer session through the generated wrapper.\n' : ''}1. \`npm run session -- start\` to open a persistent local developer session.
114
- 1. \`npm run publish\` once metadata and binaries are ready.
115
+ 1. \`auramaxx publish\` once metadata and binaries are ready.
115
116
  1. If you are not publishing under \`@aurajs\`, update \`package.json -> name\` first.
116
117
 
117
118
  ${largeAssetSection}
@@ -136,7 +137,7 @@ function renderRetroProjectReadme({ name, projectTitle, template, templateMetada
136
137
  const workflowSection = renderTemplateWorkflowSection(templateMetadata);
137
138
  return `# ${projectTitle}
138
139
 
139
- Scaffolded with \`aura create ${name} --template ${template}\`.
140
+ Scaffolded with \`auramaxx create ${name} --template ${template}\`.
140
141
 
141
142
  ## Quick Start
142
143
 
@@ -155,8 +156,8 @@ npm run retro:explain
155
156
  \`\`\`
156
157
 
157
158
  Aura Retro projects build through the main AuraScript CLI, but they do not use
158
- the default desktop play/dev wrapper flow. Treat \`aura retro check\` and
159
- \`aura build --target <retro-target>\` as the primary development loop.
159
+ the default desktop play/dev wrapper flow. Treat \`npm run retro:check\` and
160
+ \`auramaxx build --target <retro-target>\` as the primary development loop.
160
161
 
161
162
  ${workflowSection}
162
163
  ## Template Summary
@@ -224,6 +225,15 @@ export function renderStateOwnershipSection() {
224
225
  - \`screenShell\` payloads are only the data passed into HUD, overlay, and modal screens.
225
226
  - \`config/\` is for defaults and tuning.
226
227
  - \`content/\` is for authored definitions and registries.
228
+
229
+ Example shared session state:
230
+
231
+ \`\`\`js
232
+ const runFlags = context.ensureSessionState('runFlags', { DID_START: false });
233
+ if (!runFlags.DID_START) {
234
+ runFlags.DID_START = true;
235
+ }
236
+ \`\`\`
227
237
  `;
228
238
  }
229
239
 
@@ -239,16 +249,16 @@ export function renderContinuityOwnershipSection() {
239
249
  - \`scenes/*.scene.js\` should keep \`sceneState\` JSON-safe when it must survive restore.
240
250
  - \`context.getCurrentScenePayload()\` is the read seam for payloads restored through \`sceneFlow\`.
241
251
  - saved slots live under \`.aura/state/slots/\` and checkpoints live under \`.aura/state/checkpoints/\`.
242
- - native dev restore hooks use \`aura dev --restore-slot <name>\` or \`aura dev --restore-checkpoint <name>\`.
252
+ - native dev restore hooks use \`auramaxx dev --restore-slot <name>\` or \`auramaxx dev --restore-checkpoint <name>\`.
243
253
  `;
244
254
  }
245
255
 
246
256
  export function renderLargeAssetSection() {
247
257
  return `## Publish and Large Assets
248
258
 
249
- - \`npm run publish\` delegates to the engine-owned \`aura publish\` lane.
259
+ - \`auramaxx publish\` is the public publish command inside an AuraJS project.
250
260
  - AuraJS measures built asset payload size before publish. The default npm-first threshold is 50 MiB; operators can override it with \`AURA_PUBLISH_ASSET_THRESHOLD_BYTES\`.
251
- - If the payload is too large, use your own HTTPS host for the heavy assets and run \`aura external-assets generate --public-base-url <url>\`.
261
+ - If the payload is too large, use your own HTTPS host for the heavy assets and run \`auramaxx external-assets generate --public-base-url <url>\`.
252
262
  - That command writes \`aura.external-assets.json\` in the project root and stages manifests plus upload records under \`.aura/external-assets/\`.
253
263
  - Packaged \`npx <game> play\` and \`join\` hydrate self-hosted assets into a local cache before launch.
254
264
  - Cloudflare R2 and S3 are examples only. AuraPM and \`publishv2\` remain preview-only.
@@ -260,16 +270,16 @@ export function renderGenerateFilesSection(template) {
260
270
  return `## Generate Files
261
271
 
262
272
  \`\`\`bash
263
- npx aura make scene MainMenu
264
- npx aura make ui-screen PauseMenu
265
- npx aura make config EnemyTable
266
- npx aura make content SpawnTable
267
- npx aura explain
268
- npx aura check
273
+ auramaxx make scene MainMenu
274
+ auramaxx make ui-screen PauseMenu
275
+ auramaxx make config EnemyTable
276
+ auramaxx make content SpawnTable
277
+ auramaxx explain
278
+ auramaxx check
269
279
  \`\`\`
270
280
 
271
281
  Blank now ships the same authored roots as the other starters. Use the seeded
272
- files as the reference structure, then grow the project with \`npx aura make\`.
282
+ files as the reference structure, then grow the project with \`auramaxx make\`.
273
283
  `;
274
284
  }
275
285
 
@@ -277,12 +287,12 @@ files as the reference structure, then grow the project with \`npx aura make\`.
277
287
  return `## Generate Files
278
288
 
279
289
  \`\`\`bash
280
- npx aura make card StrikePlus
281
- npx aura make enemy JawWorm
282
- npx aura make relic BurningBlood
283
- npx aura make encounter Act1Hallway
284
- npx aura explain
285
- npx aura check
290
+ auramaxx make card StrikePlus
291
+ auramaxx make enemy JawWorm
292
+ auramaxx make relic BurningBlood
293
+ auramaxx make encounter Act1Hallway
294
+ auramaxx explain
295
+ auramaxx check
286
296
  \`\`\`
287
297
  `;
288
298
  }
@@ -290,12 +300,12 @@ npx aura check
290
300
  return `## Generate Files
291
301
 
292
302
  \`\`\`bash
293
- npx aura make scene MainMenu
294
- npx aura make ui-screen PauseMenu
295
- npx aura make config EnemyTable
296
- npx aura make content SpawnTable
297
- npx aura explain
298
- npx aura check
303
+ auramaxx make scene MainMenu
304
+ auramaxx make ui-screen PauseMenu
305
+ auramaxx make config EnemyTable
306
+ auramaxx make content SpawnTable
307
+ auramaxx explain
308
+ auramaxx check
299
309
  \`\`\`
300
310
  `;
301
311
  }
@@ -308,8 +318,8 @@ export function renderProjectMapSection(template) {
308
318
  - \`aura.config.json\` - identity/window/build/modules.
309
319
  - \`aura.capabilities.json\` - canonical runtime API declaration for this scaffold.
310
320
  - \`RUNBOOK.md\` - first-hour implementation and triage checklist.
311
- - \`npx aura explain\` - inspect how the current project is wired.
312
- - \`npx aura check\` - validate authored wiring before runtime.
321
+ - \`auramaxx explain\` - inspect how the current project is wired.
322
+ - \`auramaxx check\` - validate authored wiring before runtime.
313
323
  - \`src/runtime/\` - runtime/bootstrap helpers plus the project and scene registries.
314
324
  - \`src/runtime/project-registry.js\` - authored source of truth for scenes, screens, prefabs, \`configFiles\`, and \`contentFiles\`.
315
325
  - \`src/runtime/scene-flow.js\` - active-scene continuity, stack state, and route payload handoff.
@@ -323,13 +333,13 @@ export function renderProjectMapSection(template) {
323
333
  - \`config/\` - defaults and tunables.
324
334
  - \`content/\` - authored game definitions, progression payloads, and starter-owned registries.
325
335
  - \`content/registries/\` - starter-owned registries and future generator-owned indexes.
326
- - \`src/runtime/app-state.js\` - shared mutable \`appState\` split into \`session\`, \`ui\`, and \`runtime\` buckets plus helper-backed access from scenes.`;
336
+ - \`src/runtime/app-state.js\` - shared mutable \`appState\` split into \`session\`, \`ui\`, and \`runtime\` buckets plus helper-backed access from scenes. Starter example: \`context.ensureSessionState('runFlags', { DID_START: false })\`.`;
327
337
  }
328
338
 
329
339
  return `- \`src/main.js\` - stable bootstrap seam into the authored project layout.
330
340
  - \`src/runtime/\` - runtime/bootstrap helpers plus the project and scene registries.
331
341
  - \`src/runtime/project-registry.js\` - authored source of truth for scenes, screens, prefabs, \`configFiles\`, and \`contentFiles\`.
332
- - \`src/runtime/app-state.js\` - shared mutable \`appState\` split into \`session\`, \`ui\`, and \`runtime\` buckets plus helper-backed access from scenes.
342
+ - \`src/runtime/app-state.js\` - shared mutable \`appState\` split into \`session\`, \`ui\`, and \`runtime\` buckets plus helper-backed access from scenes. Starter example: \`context.ensureSessionState('runFlags', { DID_START: false })\`.
333
343
  - \`src/runtime/scene-flow.js\` - active-scene continuity, scene-stack restore ownership, and route payload handoff.
334
344
  - \`src/runtime/screen-shell.js\` - HUD, overlay, and modal payload continuity ownership.
335
345
  - \`src/runtime/ui-theme.js\` - shared theme presets plus \`appState.ui.preferences\` apply/reset helpers.
@@ -354,16 +364,16 @@ export function renderFirstThirtyMinutesSteps(template) {
354
364
  return `1. Run \`npm install\` then \`npm run dev\` and confirm the starter loop is playable.
355
365
  1. Read \`src/main.js\`, \`src/runtime/app.js\`, \`src/runtime/app-state.js\`, \`scenes/\`, \`config/\`, \`content/\`, and \`docs/design/\` once before adding new structure.
356
366
  1. Decide whether your next change belongs in \`appState.session\`, \`appState.ui\`, \`appState.runtime\`, \`sceneState\`, \`config/\`, or \`content/\`, and whether it must survive save or restart continuity.
357
- 1. Run \`npx aura explain\` once so the bootstrap stays obvious before the project grows.
358
- 1. Use \`npx aura make\` when you want more authored files without inventing paths.`;
367
+ 1. Run \`auramaxx explain\` once so the bootstrap stays obvious before the project grows.
368
+ 1. Use \`auramaxx make\` when you want more authored files without inventing paths.`;
359
369
  }
360
370
 
361
371
  if (template === 'deckbuilder-2d') {
362
372
  return `1. Run \`npm install\` then \`npm run dev\` and finish one starter battle end to end.
363
373
  1. Read \`content/registries/\`, \`content/cards/\`, \`content/enemies/\`, and \`content/encounters/\` before changing scene flow.
364
374
  1. Open \`src/runtime/ui-theme.js\`, \`src/runtime/ui-settings.js\`, and \`src/runtime/ui-forms.js\` before adding pause/settings or form glue of your own.
365
- 1. Generate one extra card or encounter with \`npx aura make\` instead of hand-creating files.
366
- 1. Run \`npx aura explain\` or \`npx aura check\` once so the starter-owned registries stay obvious as the project grows.
375
+ 1. Generate one extra card or encounter with \`auramaxx make\` instead of hand-creating files.
376
+ 1. Run \`auramaxx explain\` or \`auramaxx check\` once so the starter-owned registries stay obvious as the project grows.
367
377
  1. Only then move into battle polish, rewards, or meta-progression.`;
368
378
  }
369
379
 
@@ -372,9 +382,9 @@ export function renderFirstThirtyMinutesSteps(template) {
372
382
  1. Open \`src/runtime/ui-theme.js\`, \`src/runtime/ui-settings.js\`, and \`src/runtime/ui-forms.js\` before inventing starter-local theme, settings, or form plumbing.
373
383
  1. Review \`assets/starter/\`, \`config/gameplay/\`, \`content/gameplay/\`, and \`docs/design/\` and rewrite the seed content so it matches your game's nouns, tuning, and milestone goals.
374
384
  1. Keep shared state in \`appState.session\` / \`appState.ui\`, keep route payloads in \`sceneFlow\`, and keep scene-only state inside \`sceneState\`.
375
- 1. Run \`npx aura explain\` or \`npx aura check\` when you want a fast map of the authored project wiring.
385
+ 1. Run \`auramaxx explain\` or \`auramaxx check\` when you want a fast map of the authored project wiring.
376
386
  1. Press \`F1\` in the running starter to open the playtest HUD, jump between authored scenes, and use the compact capture/restart toolbar.
377
- 1. Use \`npx aura make scene MainMenu\` or \`npx aura make ui-screen PauseMenu\` instead of hand-making new authored file paths.`;
387
+ 1. Use \`auramaxx make scene MainMenu\` or \`auramaxx make ui-screen PauseMenu\` instead of hand-making new authored file paths.`;
378
388
  }
379
389
 
380
390
  export function renderContentMapDoc({ template }) {
@@ -402,6 +412,15 @@ Template: \`${template}\`
402
412
  - \`src/runtime/project-inspector.js\` exposes the live F1 playtest HUD.
403
413
  - authored scenes read shared state with \`ensureSessionState\`, \`ensureUiState\`, and \`getCurrentScenePayload()\`.
404
414
 
415
+ ## Shared State Example
416
+
417
+ \`\`\`js
418
+ const runFlags = context.ensureSessionState('runFlags', { DID_START: false });
419
+ if (!runFlags.DID_START) {
420
+ runFlags.DID_START = true;
421
+ }
422
+ \`\`\`
423
+
405
424
  ## Current Starter Inventory
406
425
 
407
426
  - \`scenes/boot.scene.js\`
@@ -26,13 +26,13 @@ export const DEFAULT_WEB_SMOKE_CASES = Object.freeze([
26
26
  examples: ['examples/web-render-2d-proof'],
27
27
  }),
28
28
  Object.freeze({
29
- id: 'web-partial-pokemon-style-example',
29
+ id: 'web-partial-auramon-example',
30
30
  bucket: 'partial',
31
31
  expectation: 'partial',
32
- title: 'pokemon-style runs on the current browser-backed subset',
32
+ title: 'auramon runs on the current browser-backed subset',
33
33
  file: DEFAULT_TEST_FILE,
34
- testName: 'web loader browser smoke: pokemon-style runs on the supported 2d subset with input and resize evidence',
35
- examples: ['examples/pokemon-style'],
34
+ testName: 'web loader browser smoke: auramon runs on the supported 2d subset with input and resize evidence',
35
+ examples: ['examples/auramon'],
36
36
  }),
37
37
  Object.freeze({
38
38
  id: 'web-supported-starter-3d-example',
@@ -1,6 +1,7 @@
1
1
  import { createSceneRegistry } from './scene-registry.js';
2
2
  import { createProjectInspector } from './project-inspector.js';
3
3
  import { assertRuntimeCapabilities } from './capabilities.js';
4
+ import { initSplash, updateSplash, drawSplash, isSplashActive } from './splash.js';
4
5
 
5
6
  export function createApp() {
6
7
  const sceneRegistry = createSceneRegistry({
@@ -26,13 +27,16 @@ export function createApp() {
26
27
  },
27
28
  setup() {
28
29
  assertRuntimeCapabilities();
30
+ initSplash();
29
31
  activeScene()?.setup?.();
30
32
  },
31
33
  update(dt) {
34
+ if (isSplashActive()) { updateSplash(dt); return; }
32
35
  projectInspector.syncInput(globalThis.aura?.input || null);
33
36
  activeScene()?.update?.(dt);
34
37
  },
35
38
  draw() {
39
+ if (isSplashActive()) { drawSplash(); return; }
36
40
  activeScene()?.draw?.();
37
41
  projectInspector.draw({ activeSceneId });
38
42
  },
@@ -1,6 +1,7 @@
1
1
  import { createSceneRegistry } from './scene-registry.js';
2
2
  import { createProjectInspector } from './project-inspector.js';
3
3
  import { assertRuntimeCapabilities } from './capabilities.js';
4
+ import { initSplash, updateSplash, drawSplash, isSplashActive } from './splash.js';
4
5
 
5
6
  export function createApp() {
6
7
  const sceneRegistry = createSceneRegistry({
@@ -26,13 +27,16 @@ export function createApp() {
26
27
  },
27
28
  setup() {
28
29
  assertRuntimeCapabilities();
30
+ initSplash();
29
31
  activeScene()?.setup?.();
30
32
  },
31
33
  update(dt) {
34
+ if (isSplashActive()) { updateSplash(dt); return; }
32
35
  projectInspector.syncInput(globalThis.aura?.input || null);
33
36
  activeScene()?.update?.(dt);
34
37
  },
35
38
  draw() {
39
+ if (isSplashActive()) { drawSplash(); return; }
36
40
  activeScene()?.draw?.();
37
41
  projectInspector.draw({ activeSceneId });
38
42
  },