@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auraindustry/aurajs",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Write games in JavaScript, build native binaries.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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()) {
@@ -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();
@@ -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 = normalizeText(readFileSync(absolutePath, 'utf8'));
231
- const fileHash = sha256Text(fileContent);
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
- hash: fileHash,
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
- templateHash: canonicalTemplateHash,
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)) {