@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.
- package/package.json +1 -1
- package/src/asset-pack.mjs +5 -1
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/cli.mjs +17 -2
- package/src/commands/project-authoring.mjs +20 -0
- package/src/config.mjs +17 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +861 -6
- package/src/external-package-surface.mjs +1 -1
- package/src/package-integrity.mjs +18 -4
- package/src/publish-command.mjs +133 -13
- package/src/publish-validation.mjs +22 -11
- package/src/scaffold/project-docs.mjs +60 -41
- package/src/web-conformance.mjs +4 -4
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
- package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/blank/assets/splash/bg.webp +0 -0
- package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
- package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
- package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/blank/assets/splash/logoholo.webp +0 -0
- package/templates/create/blank/src/main.js +5 -1
- package/templates/create/blank/src/runtime/splash.js +305 -0
- package/templates/create/local-multiplayer/aura.config.json +1 -0
- package/templates/create/local-multiplayer/docs/design/loop.md +3 -1
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +216 -13
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/local-multiplayer/ui/hud.screen.js +12 -7
- package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/shared/assets/splash/bg.webp +0 -0
- package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
- package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
- package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/shared/assets/splash/logoholo.webp +0 -0
- package/templates/create/shared/src/runtime/splash.js +305 -0
- package/templates/create/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +121 -4
- package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/starter/assets/splash/bg.webp +0 -0
- package/templates/starter/assets/splash/boot-loop.wav +0 -0
- package/templates/starter/assets/splash/boot-sting.wav +0 -0
- package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/starter/assets/splash/logoholo.webp +0 -0
- package/templates/starter/src/main.js +4 -0
- 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,
|
package/src/publish-command.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import {
|
|
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
|
|
50
|
-
|
|
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:
|
|
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}
|
|
247
|
+
stdout?.write(` Asset payload: ${formatBytes(assetBytes)} (${assetBytes} bytes)\n`);
|
|
248
|
+
stdout?.write(` Threshold: ${formatBytes(thresholdBytes)} (${thresholdBytes} bytes)\n`);
|
|
138
249
|
}
|
|
139
|
-
stdout?.write('
|
|
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 =
|
|
150
|
-
const
|
|
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
|
|
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
|
-
|
|
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 \`
|
|
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
|
-
|
|
38
|
-
|
|
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 \`
|
|
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
|
|
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. \`
|
|
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 \`
|
|
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 \`
|
|
159
|
-
\`
|
|
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 \`
|
|
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
|
-
- \`
|
|
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 \`
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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 \`
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
- \`
|
|
312
|
-
- \`
|
|
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 \`
|
|
358
|
-
1. Use \`
|
|
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 \`
|
|
366
|
-
1. Run \`
|
|
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 \`
|
|
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 \`
|
|
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\`
|
package/src/web-conformance.mjs
CHANGED
|
@@ -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-
|
|
29
|
+
id: 'web-partial-auramon-example',
|
|
30
30
|
bucket: 'partial',
|
|
31
31
|
expectation: 'partial',
|
|
32
|
-
title: '
|
|
32
|
+
title: 'auramon runs on the current browser-backed subset',
|
|
33
33
|
file: DEFAULT_TEST_FILE,
|
|
34
|
-
testName: 'web loader browser smoke:
|
|
35
|
-
examples: ['examples/
|
|
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
|
},
|