@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
package/package.json
CHANGED
package/src/asset-pack.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { join, relative, resolve, sep } from 'node:path';
|
|
|
13
13
|
|
|
14
14
|
const SUPPORTED = {
|
|
15
15
|
png: 'image',
|
|
16
|
+
webp: 'image',
|
|
16
17
|
jpg: 'image',
|
|
17
18
|
jpeg: 'image',
|
|
18
19
|
wav: 'audio',
|
|
@@ -40,7 +41,7 @@ const SUPPORTED = {
|
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
const APPROVED_FORMATS = [
|
|
43
|
-
'.png', '.jpg', '.jpeg',
|
|
44
|
+
'.png', '.webp', '.jpg', '.jpeg',
|
|
44
45
|
'.wav', '.ogg', '.mp3', '.mp4',
|
|
45
46
|
'.gltf', '.glb',
|
|
46
47
|
'.ttf', '.otf',
|
|
@@ -276,6 +277,9 @@ function collectAssets(assetsRoot) {
|
|
|
276
277
|
function walkDir(dir, out) {
|
|
277
278
|
const items = readdirSync(dir).sort((a, b) => a.localeCompare(b));
|
|
278
279
|
for (const item of items) {
|
|
280
|
+
if (item === '.gitkeep' || item === '.DS_Store') {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
279
283
|
const full = join(dir, item);
|
|
280
284
|
const stat = statSync(full);
|
|
281
285
|
if (stat.isDirectory()) {
|
package/src/authored-runtime.mjs
CHANGED
|
@@ -46,6 +46,14 @@ export function renderUiFormsModule() {
|
|
|
46
46
|
export function renderAppStateModule() {
|
|
47
47
|
return `export const APP_STATE_SCHEMA = ${JSON.stringify(APP_STATE_SCHEMA)};
|
|
48
48
|
|
|
49
|
+
// Shared mutable app/session state lives here.
|
|
50
|
+
//
|
|
51
|
+
// Example scene usage:
|
|
52
|
+
// const runFlags = context.ensureSessionState('runFlags', { DID_START: false });
|
|
53
|
+
// if (!runFlags.DID_START) {
|
|
54
|
+
// runFlags.DID_START = true;
|
|
55
|
+
// }
|
|
56
|
+
|
|
49
57
|
function cloneAppStateValue(value) {
|
|
50
58
|
if (Array.isArray(value)) {
|
|
51
59
|
return value.map((entry) => cloneAppStateValue(entry));
|
|
@@ -69,7 +77,9 @@ export function createAppState({ projectTitle = 'AuraJS Game', template = 'blank
|
|
|
69
77
|
schema: APP_STATE_SCHEMA,
|
|
70
78
|
projectTitle,
|
|
71
79
|
template,
|
|
80
|
+
// Cross-scene gameplay and session state.
|
|
72
81
|
session: createStoreSection(),
|
|
82
|
+
// Cross-screen UI and presentation state.
|
|
73
83
|
ui: createStoreSection(),
|
|
74
84
|
runtime: {
|
|
75
85
|
startSceneId: null,
|
|
@@ -803,6 +813,7 @@ import { createSceneRegistry } from './scene-registry.js';
|
|
|
803
813
|
import { createSceneFlow } from './scene-flow.js';
|
|
804
814
|
import { createScreenShell } from './screen-shell.js';
|
|
805
815
|
import { assertRuntimeCapabilities } from './capabilities.js';
|
|
816
|
+
import { initSplash, updateSplash, drawSplash, isSplashActive } from './splash.js';
|
|
806
817
|
|
|
807
818
|
const PROJECT_STATE_SCHEMA = ${JSON.stringify(PROJECT_STATE_SCHEMA)};
|
|
808
819
|
const PROJECT_CONTINUITY_STATE_SCHEMA = 'aurajs.project-continuity-state.v1';
|
|
@@ -1813,12 +1824,14 @@ export function createApp() {
|
|
|
1813
1824
|
},
|
|
1814
1825
|
setup() {
|
|
1815
1826
|
assertRuntimeCapabilities();
|
|
1827
|
+
initSplash();
|
|
1816
1828
|
runtimeStarted = true;
|
|
1817
1829
|
paused = false;
|
|
1818
1830
|
activeScene()?.setup?.();
|
|
1819
1831
|
syncAppStateRuntime();
|
|
1820
1832
|
},
|
|
1821
1833
|
update(dt) {
|
|
1834
|
+
if (isSplashActive()) { updateSplash(dt); return; }
|
|
1822
1835
|
projectInspector.update();
|
|
1823
1836
|
if (!paused) {
|
|
1824
1837
|
activeScene()?.update?.(dt);
|
|
@@ -1826,6 +1839,7 @@ export function createApp() {
|
|
|
1826
1839
|
syncAppStateRuntime();
|
|
1827
1840
|
},
|
|
1828
1841
|
draw() {
|
|
1842
|
+
if (isSplashActive()) { drawSplash(); return; }
|
|
1829
1843
|
applySharedUiTheme(appState);
|
|
1830
1844
|
activeScene()?.draw?.();
|
|
1831
1845
|
const shellState = screenShell.getState();
|
package/src/bin-integrity.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
3
2
|
import { dirname, join, resolve } from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
@@ -28,14 +27,37 @@ function normalizeText(value) {
|
|
|
28
27
|
return String(value || '').replace(/\r\n?/g, '\n');
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
function sha256Text(value) {
|
|
32
|
-
return createHash('sha256').update(normalizeText(value)).digest('hex');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
30
|
function readJsonFile(path) {
|
|
36
31
|
return JSON.parse(readFileSync(path, 'utf8'));
|
|
37
32
|
}
|
|
38
33
|
|
|
34
|
+
const PLAY_WRAPPER_REQUIRED_MARKERS = [
|
|
35
|
+
'#!/usr/bin/env node',
|
|
36
|
+
'const fallbackAuraPackage =',
|
|
37
|
+
"const MINIMAL_COMMANDS = ['dev', 'join', 'play', 'fork', 'publish', 'session'];",
|
|
38
|
+
"const ALL_COMMANDS = ['dev', 'join', 'play', 'fork', 'publish', 'session', 'state', 'inspect', 'action'];",
|
|
39
|
+
"args: ['exec', '--yes', '--package', fallbackAuraPackage, '--', 'aura', ...commandArgs]",
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function assertPlayWrapperContract(relativePath, fileContent) {
|
|
43
|
+
const normalized = normalizeText(fileContent);
|
|
44
|
+
const missingMarkers = PLAY_WRAPPER_REQUIRED_MARKERS.filter((marker) => !normalized.includes(marker));
|
|
45
|
+
if (missingMarkers.length > 0) {
|
|
46
|
+
throw new ProjectBinIntegrityError(
|
|
47
|
+
'project_bin_wrapper_contract_invalid',
|
|
48
|
+
`Bin target "${relativePath}" does not satisfy the AuraJS play wrapper contract.`,
|
|
49
|
+
{
|
|
50
|
+
relativePath,
|
|
51
|
+
missingMarkers,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
markers: [...PLAY_WRAPPER_REQUIRED_MARKERS],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
39
61
|
function listRelativeFiles(root, current = root, acc = []) {
|
|
40
62
|
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
41
63
|
const fullPath = join(current, entry.name);
|
|
@@ -84,6 +106,7 @@ export function assertProjectBinIntegrity(
|
|
|
84
106
|
aurajsPackageRoot = DEFAULT_AURAJS_PACKAGE_ROOT,
|
|
85
107
|
expectedAurajsVersion = null,
|
|
86
108
|
enforceExactAurajsDependency = true,
|
|
109
|
+
enforceResolvedAurajsVersionMatch = false,
|
|
87
110
|
} = {},
|
|
88
111
|
) {
|
|
89
112
|
const resolvedProjectRoot = resolve(projectRoot || process.cwd());
|
|
@@ -146,7 +169,7 @@ export function assertProjectBinIntegrity(
|
|
|
146
169
|
|
|
147
170
|
const aurajsPackage = readJsonFile(aurajsPackageJsonPath);
|
|
148
171
|
const resolvedAurajsVersion = String(aurajsPackage?.version || '').trim();
|
|
149
|
-
if (expectedAurajsVersion && resolvedAurajsVersion !== expectedAurajsVersion) {
|
|
172
|
+
if (enforceResolvedAurajsVersionMatch && expectedAurajsVersion && resolvedAurajsVersion !== expectedAurajsVersion) {
|
|
150
173
|
throw new ProjectBinIntegrityError(
|
|
151
174
|
'project_aurajs_resolved_version_mismatch',
|
|
152
175
|
`Resolved @auraindustry/aurajs ${resolvedAurajsVersion || '<missing>'}, expected ${expectedAurajsVersion}.`,
|
|
@@ -167,9 +190,6 @@ export function assertProjectBinIntegrity(
|
|
|
167
190
|
);
|
|
168
191
|
}
|
|
169
192
|
|
|
170
|
-
const canonicalTemplate = normalizeText(readFileSync(templatePath, 'utf8'));
|
|
171
|
-
const canonicalTemplateHash = sha256Text(canonicalTemplate);
|
|
172
|
-
|
|
173
193
|
const binEntries = resolveProjectBinEntries(resolvedProjectPackage, resolvedPackageName);
|
|
174
194
|
if (binEntries.length === 0) {
|
|
175
195
|
throw new ProjectBinIntegrityError(
|
|
@@ -227,26 +247,13 @@ export function assertProjectBinIntegrity(
|
|
|
227
247
|
);
|
|
228
248
|
}
|
|
229
249
|
|
|
230
|
-
const fileContent =
|
|
231
|
-
const
|
|
232
|
-
if (fileContent !== canonicalTemplate) {
|
|
233
|
-
throw new ProjectBinIntegrityError(
|
|
234
|
-
'project_bin_template_modified',
|
|
235
|
-
`Bin target "${relativePath}" does not match the canonical AuraJS play.js template.`,
|
|
236
|
-
{
|
|
237
|
-
relativePath,
|
|
238
|
-
absolutePath,
|
|
239
|
-
expectedTemplatePath: templatePath,
|
|
240
|
-
expectedTemplateHash: canonicalTemplateHash,
|
|
241
|
-
actualHash: fileHash,
|
|
242
|
-
},
|
|
243
|
-
);
|
|
244
|
-
}
|
|
250
|
+
const fileContent = readFileSync(absolutePath, 'utf8');
|
|
251
|
+
const wrapperContract = assertPlayWrapperContract(relativePath, fileContent);
|
|
245
252
|
|
|
246
253
|
verifiedFiles.push({
|
|
247
254
|
relativePath,
|
|
248
255
|
absolutePath,
|
|
249
|
-
|
|
256
|
+
wrapperContract,
|
|
250
257
|
});
|
|
251
258
|
}
|
|
252
259
|
|
|
@@ -260,7 +267,7 @@ export function assertProjectBinIntegrity(
|
|
|
260
267
|
resolvedVersion: resolvedAurajsVersion,
|
|
261
268
|
packageRoot: aurajsPackageRoot,
|
|
262
269
|
templatePath,
|
|
263
|
-
|
|
270
|
+
wrapperContract: 'aurajs.play-wrapper.v1',
|
|
264
271
|
},
|
|
265
272
|
bin: {
|
|
266
273
|
entries: binEntries,
|
package/src/cli.mjs
CHANGED
|
@@ -4553,6 +4553,11 @@ function printForegroundSessionReady(commandLabel, record, {
|
|
|
4553
4553
|
reattached = false,
|
|
4554
4554
|
includeWorkflowHints = false,
|
|
4555
4555
|
} = {}) {
|
|
4556
|
+
if (commandLabel === 'aura run') {
|
|
4557
|
+
printRunLaunchBanner();
|
|
4558
|
+
return;
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4556
4561
|
const readiness = reattached ? 'dev session reattached' : 'dev session ready';
|
|
4557
4562
|
console.log(
|
|
4558
4563
|
` ${commandLabel}: ${readiness} `
|
|
@@ -4570,6 +4575,18 @@ function printForegroundSessionReady(commandLabel, record, {
|
|
|
4570
4575
|
console.log('');
|
|
4571
4576
|
}
|
|
4572
4577
|
|
|
4578
|
+
function printRunLaunchBanner() {
|
|
4579
|
+
console.log('');
|
|
4580
|
+
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ');
|
|
4581
|
+
console.log(' [ AURAJS ]');
|
|
4582
|
+
console.log(' Docs: https://www.aurajs.gg/docs');
|
|
4583
|
+
console.log('');
|
|
4584
|
+
console.log(' To create your own game:');
|
|
4585
|
+
console.log(' npm install -g auramaxx');
|
|
4586
|
+
console.log(' auramaxx create my-game');
|
|
4587
|
+
console.log('');
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4573
4590
|
function logForegroundSessionRegistryHygiene(commandLabel, report) {
|
|
4574
4591
|
if (!report || typeof report !== 'object') {
|
|
4575
4592
|
return;
|
|
@@ -4689,8 +4706,6 @@ async function cmdRun(args) {
|
|
|
4689
4706
|
const launchContract = built.buildManifestPath;
|
|
4690
4707
|
const launchExecutablePath = built.launchExecutablePath || built.executablePath;
|
|
4691
4708
|
|
|
4692
|
-
console.log('\n aura run: launching native host.\n');
|
|
4693
|
-
|
|
4694
4709
|
const launchEnv = {
|
|
4695
4710
|
...process.env,
|
|
4696
4711
|
AURA_MODE: 'release',
|
|
@@ -221,6 +221,8 @@ export async function cmdInit(args, { error } = {}) {
|
|
|
221
221
|
const dest = resolve(process.cwd(), name);
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
+
printBanner('CREATE');
|
|
225
|
+
printSection('AuraJS Create', `Scaffolding ${name}...`);
|
|
224
226
|
const result = scaffold(name, dest);
|
|
225
227
|
console.log(`\n Created "${name}" at ${dest}`);
|
|
226
228
|
console.log('');
|
|
@@ -232,6 +234,13 @@ export async function cmdInit(args, { error } = {}) {
|
|
|
232
234
|
console.log(` cd ${name}`);
|
|
233
235
|
console.log(' aura dev');
|
|
234
236
|
console.log('');
|
|
237
|
+
console.log(' Docs:');
|
|
238
|
+
console.log(' https://www.aurajs.gg/docs');
|
|
239
|
+
console.log('');
|
|
240
|
+
console.log(' Agent skills:');
|
|
241
|
+
console.log(' cd <your-codebase>');
|
|
242
|
+
console.log(' npx -y skills add Aura-Industry/auramaxx');
|
|
243
|
+
console.log('');
|
|
235
244
|
} catch (err) {
|
|
236
245
|
handleProjectAuthoringError(error, err);
|
|
237
246
|
}
|
|
@@ -251,6 +260,8 @@ export async function cmdCreate(args, { error } = {}) {
|
|
|
251
260
|
const dest = resolve(process.cwd(), name);
|
|
252
261
|
|
|
253
262
|
try {
|
|
263
|
+
printBanner('CREATE');
|
|
264
|
+
printSection('AuraJS Create', `Scaffolding ${name}...`);
|
|
254
265
|
const result = scaffoldGame({
|
|
255
266
|
name,
|
|
256
267
|
dest,
|
|
@@ -287,9 +298,18 @@ export async function cmdCreate(args, { error } = {}) {
|
|
|
287
298
|
console.log(' npm run build:gbc');
|
|
288
299
|
} else {
|
|
289
300
|
console.log(' npm run dev');
|
|
301
|
+
console.log('');
|
|
302
|
+
console.log(' Optional packaged local run:');
|
|
290
303
|
console.log(' npm run play');
|
|
291
304
|
}
|
|
292
305
|
console.log('');
|
|
306
|
+
console.log(' Docs:');
|
|
307
|
+
console.log(' https://www.aurajs.gg/docs');
|
|
308
|
+
console.log('');
|
|
309
|
+
console.log(' Agent skills:');
|
|
310
|
+
console.log(' cd <your-codebase>');
|
|
311
|
+
console.log(' npx -y skills add Aura-Industry/auramaxx');
|
|
312
|
+
console.log('');
|
|
293
313
|
} catch (err) {
|
|
294
314
|
handleProjectAuthoringError(error, err);
|
|
295
315
|
}
|
package/src/config.mjs
CHANGED
|
@@ -175,6 +175,7 @@ const SCHEMA = {
|
|
|
175
175
|
relay: { type: 'string', default: null, validate: nonEmptyString },
|
|
176
176
|
coordinatorUrl: { type: 'string', default: null, validate: nonEmptyString },
|
|
177
177
|
relayUrl: { type: 'string', default: null, validate: nonEmptyString },
|
|
178
|
+
launcherBaseUrl: { type: 'string', default: null, validate: absoluteHttpUrl },
|
|
178
179
|
showDiagnostics: { type: 'boolean', default: false },
|
|
179
180
|
chatEnabled: { type: 'boolean', default: false },
|
|
180
181
|
chatHistoryLimit: { type: 'number', default: 6, validate: positiveInteger },
|
|
@@ -234,6 +235,22 @@ function nonEmptyString(value, path) {
|
|
|
234
235
|
return null;
|
|
235
236
|
}
|
|
236
237
|
|
|
238
|
+
function absoluteHttpUrl(value, path) {
|
|
239
|
+
const normalized = String(value || '').trim();
|
|
240
|
+
if (normalized.length === 0) {
|
|
241
|
+
return `${path} must be a non-empty string`;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const parsed = new URL(normalized);
|
|
245
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
246
|
+
return `${path} must use http:// or https://, got: ${JSON.stringify(value)}`;
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
} catch {
|
|
250
|
+
return `${path} must be an absolute http:// or https:// URL, got: ${JSON.stringify(value)}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
237
254
|
function multiplayerRoomCode(value, path) {
|
|
238
255
|
const normalized = String(value || '').trim();
|
|
239
256
|
if (!/^[A-Za-z0-9]{4,8}$/.test(normalized)) {
|