@auraindustry/aurajs 0.1.1 → 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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/asset-pack.mjs +2 -1
  3. package/src/authored-runtime.mjs +14 -0
  4. package/src/bin-integrity.mjs +33 -26
  5. package/src/conformance/cases/systems-and-gameplay-cases.mjs +861 -6
  6. package/src/external-package-surface.mjs +1 -1
  7. package/src/package-integrity.mjs +18 -4
  8. package/src/publish-command.mjs +133 -13
  9. package/src/publish-validation.mjs +22 -11
  10. package/src/scaffold/project-docs.mjs +58 -40
  11. package/templates/create/2d/src/runtime/app.js +4 -0
  12. package/templates/create/2d-survivor/src/runtime/app.js +4 -0
  13. package/templates/create/3d/src/runtime/app.js +4 -0
  14. package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
  15. package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
  16. package/templates/create/blank/assets/splash/bg.webp +0 -0
  17. package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
  18. package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
  19. package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
  20. package/templates/create/blank/assets/splash/logoholo.webp +0 -0
  21. package/templates/create/blank/src/main.js +5 -1
  22. package/templates/create/blank/src/runtime/splash.js +305 -0
  23. package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
  24. package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
  25. package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
  26. package/templates/create/shared/assets/splash/bg.webp +0 -0
  27. package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
  28. package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
  29. package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
  30. package/templates/create/shared/assets/splash/logoholo.webp +0 -0
  31. package/templates/create/shared/src/runtime/splash.js +305 -0
  32. package/templates/create/video-cutscene/src/runtime/app.js +4 -0
  33. package/templates/create-bin/play.js +114 -2
  34. package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
  35. package/templates/starter/assets/splash/bg.webp +0 -0
  36. package/templates/starter/assets/splash/boot-loop.wav +0 -0
  37. package/templates/starter/assets/splash/boot-sting.wav +0 -0
  38. package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
  39. package/templates/starter/assets/splash/logoholo.webp +0 -0
  40. package/templates/starter/src/main.js +4 -0
  41. 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',
@@ -22,7 +22,7 @@ export function renderProjectReadme({ name, projectTitle, template, templateMeta
22
22
 
23
23
  return `# ${projectTitle}
24
24
 
25
- Scaffolded with \`aura create ${name} --template ${template}\`.
25
+ Scaffolded with \`auramaxx create ${name} --template ${template}\`.
26
26
 
27
27
  ## Quick Start
28
28
 
@@ -35,14 +35,14 @@ Optional commands:
35
35
 
36
36
  \`\`\`bash
37
37
  ${optionalCommands}
38
- npx aura explain
39
- npx aura check
38
+ auramaxx explain
39
+ auramaxx check
40
40
  \`\`\`
41
41
 
42
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'}
43
43
 
44
44
  If you are publishing outside the \`@aurajs\` scope, update \`package.json -> name\`
45
- before the first \`npm run publish\`.
45
+ before the first \`auramaxx publish\`.
46
46
 
47
47
  ${largeAssetSection}
48
48
  ${workflowSection}
@@ -112,7 +112,7 @@ If any are unavailable at runtime, fail fast and capture the reason code before
112
112
  1. \`npm run build\` to emit native/web artifacts.
113
113
  1. \`npm run play\` to sanity-check the packaged local wrapper path.
114
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.
115
- 1. \`npm run publish\` once metadata and binaries are ready.
115
+ 1. \`auramaxx publish\` once metadata and binaries are ready.
116
116
  1. If you are not publishing under \`@aurajs\`, update \`package.json -> name\` first.
117
117
 
118
118
  ${largeAssetSection}
@@ -137,7 +137,7 @@ function renderRetroProjectReadme({ name, projectTitle, template, templateMetada
137
137
  const workflowSection = renderTemplateWorkflowSection(templateMetadata);
138
138
  return `# ${projectTitle}
139
139
 
140
- Scaffolded with \`aura create ${name} --template ${template}\`.
140
+ Scaffolded with \`auramaxx create ${name} --template ${template}\`.
141
141
 
142
142
  ## Quick Start
143
143
 
@@ -156,8 +156,8 @@ npm run retro:explain
156
156
  \`\`\`
157
157
 
158
158
  Aura Retro projects build through the main AuraScript CLI, but they do not use
159
- the default desktop play/dev wrapper flow. Treat \`aura retro check\` and
160
- \`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.
161
161
 
162
162
  ${workflowSection}
163
163
  ## Template Summary
@@ -225,6 +225,15 @@ export function renderStateOwnershipSection() {
225
225
  - \`screenShell\` payloads are only the data passed into HUD, overlay, and modal screens.
226
226
  - \`config/\` is for defaults and tuning.
227
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
+ \`\`\`
228
237
  `;
229
238
  }
230
239
 
@@ -240,16 +249,16 @@ export function renderContinuityOwnershipSection() {
240
249
  - \`scenes/*.scene.js\` should keep \`sceneState\` JSON-safe when it must survive restore.
241
250
  - \`context.getCurrentScenePayload()\` is the read seam for payloads restored through \`sceneFlow\`.
242
251
  - saved slots live under \`.aura/state/slots/\` and checkpoints live under \`.aura/state/checkpoints/\`.
243
- - 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>\`.
244
253
  `;
245
254
  }
246
255
 
247
256
  export function renderLargeAssetSection() {
248
257
  return `## Publish and Large Assets
249
258
 
250
- - \`npm run publish\` delegates to the engine-owned \`aura publish\` lane.
259
+ - \`auramaxx publish\` is the public publish command inside an AuraJS project.
251
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\`.
252
- - 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>\`.
253
262
  - That command writes \`aura.external-assets.json\` in the project root and stages manifests plus upload records under \`.aura/external-assets/\`.
254
263
  - Packaged \`npx <game> play\` and \`join\` hydrate self-hosted assets into a local cache before launch.
255
264
  - Cloudflare R2 and S3 are examples only. AuraPM and \`publishv2\` remain preview-only.
@@ -261,16 +270,16 @@ export function renderGenerateFilesSection(template) {
261
270
  return `## Generate Files
262
271
 
263
272
  \`\`\`bash
264
- npx aura make scene MainMenu
265
- npx aura make ui-screen PauseMenu
266
- npx aura make config EnemyTable
267
- npx aura make content SpawnTable
268
- npx aura explain
269
- 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
270
279
  \`\`\`
271
280
 
272
281
  Blank now ships the same authored roots as the other starters. Use the seeded
273
- 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\`.
274
283
  `;
275
284
  }
276
285
 
@@ -278,12 +287,12 @@ files as the reference structure, then grow the project with \`npx aura make\`.
278
287
  return `## Generate Files
279
288
 
280
289
  \`\`\`bash
281
- npx aura make card StrikePlus
282
- npx aura make enemy JawWorm
283
- npx aura make relic BurningBlood
284
- npx aura make encounter Act1Hallway
285
- npx aura explain
286
- 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
287
296
  \`\`\`
288
297
  `;
289
298
  }
@@ -291,12 +300,12 @@ npx aura check
291
300
  return `## Generate Files
292
301
 
293
302
  \`\`\`bash
294
- npx aura make scene MainMenu
295
- npx aura make ui-screen PauseMenu
296
- npx aura make config EnemyTable
297
- npx aura make content SpawnTable
298
- npx aura explain
299
- 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
300
309
  \`\`\`
301
310
  `;
302
311
  }
@@ -309,8 +318,8 @@ export function renderProjectMapSection(template) {
309
318
  - \`aura.config.json\` - identity/window/build/modules.
310
319
  - \`aura.capabilities.json\` - canonical runtime API declaration for this scaffold.
311
320
  - \`RUNBOOK.md\` - first-hour implementation and triage checklist.
312
- - \`npx aura explain\` - inspect how the current project is wired.
313
- - \`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.
314
323
  - \`src/runtime/\` - runtime/bootstrap helpers plus the project and scene registries.
315
324
  - \`src/runtime/project-registry.js\` - authored source of truth for scenes, screens, prefabs, \`configFiles\`, and \`contentFiles\`.
316
325
  - \`src/runtime/scene-flow.js\` - active-scene continuity, stack state, and route payload handoff.
@@ -324,13 +333,13 @@ export function renderProjectMapSection(template) {
324
333
  - \`config/\` - defaults and tunables.
325
334
  - \`content/\` - authored game definitions, progression payloads, and starter-owned registries.
326
335
  - \`content/registries/\` - starter-owned registries and future generator-owned indexes.
327
- - \`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 })\`.`;
328
337
  }
329
338
 
330
339
  return `- \`src/main.js\` - stable bootstrap seam into the authored project layout.
331
340
  - \`src/runtime/\` - runtime/bootstrap helpers plus the project and scene registries.
332
341
  - \`src/runtime/project-registry.js\` - authored source of truth for scenes, screens, prefabs, \`configFiles\`, and \`contentFiles\`.
333
- - \`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 })\`.
334
343
  - \`src/runtime/scene-flow.js\` - active-scene continuity, scene-stack restore ownership, and route payload handoff.
335
344
  - \`src/runtime/screen-shell.js\` - HUD, overlay, and modal payload continuity ownership.
336
345
  - \`src/runtime/ui-theme.js\` - shared theme presets plus \`appState.ui.preferences\` apply/reset helpers.
@@ -355,16 +364,16 @@ export function renderFirstThirtyMinutesSteps(template) {
355
364
  return `1. Run \`npm install\` then \`npm run dev\` and confirm the starter loop is playable.
356
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.
357
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.
358
- 1. Run \`npx aura explain\` once so the bootstrap stays obvious before the project grows.
359
- 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.`;
360
369
  }
361
370
 
362
371
  if (template === 'deckbuilder-2d') {
363
372
  return `1. Run \`npm install\` then \`npm run dev\` and finish one starter battle end to end.
364
373
  1. Read \`content/registries/\`, \`content/cards/\`, \`content/enemies/\`, and \`content/encounters/\` before changing scene flow.
365
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.
366
- 1. Generate one extra card or encounter with \`npx aura make\` instead of hand-creating files.
367
- 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.
368
377
  1. Only then move into battle polish, rewards, or meta-progression.`;
369
378
  }
370
379
 
@@ -373,9 +382,9 @@ export function renderFirstThirtyMinutesSteps(template) {
373
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.
374
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.
375
384
  1. Keep shared state in \`appState.session\` / \`appState.ui\`, keep route payloads in \`sceneFlow\`, and keep scene-only state inside \`sceneState\`.
376
- 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.
377
386
  1. Press \`F1\` in the running starter to open the playtest HUD, jump between authored scenes, and use the compact capture/restart toolbar.
378
- 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.`;
379
388
  }
380
389
 
381
390
  export function renderContentMapDoc({ template }) {
@@ -403,6 +412,15 @@ Template: \`${template}\`
403
412
  - \`src/runtime/project-inspector.js\` exposes the live F1 playtest HUD.
404
413
  - authored scenes read shared state with \`ensureSessionState\`, \`ensureUiState\`, and \`getCurrentScenePayload()\`.
405
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
+
406
424
  ## Current Starter Inventory
407
425
 
408
426
  - \`scenes/boot.scene.js\`
@@ -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
  },
@@ -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
  },