@forgeax/engine-devkit 0.1.3 → 0.1.6
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/README.md +30 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/cli-output.unit.test.d.ts +2 -0
- package/dist/__tests__/cli-output.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts +2 -0
- package/dist/__tests__/gltf-source-key-diagnostics.integration.test.d.ts.map +1 -0
- package/dist/__tests__/preview-port.integration.test.d.ts +2 -0
- package/dist/__tests__/preview-port.integration.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts +2 -0
- package/dist/__tests__/rhi-debug-build-graph.integration.test.d.ts.map +1 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts +2 -0
- package/dist/__tests__/sdk-update.unit.test.d.ts.map +1 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/bootstrap-commands.d.ts.map +1 -1
- package/dist/cli-output.d.ts +4 -0
- package/dist/cli-output.d.ts.map +1 -0
- package/dist/cli.mjs +728 -641
- package/dist/cli.mjs.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts +2 -0
- package/dist/host/__tests__/bootstrap-root-owner.test-d.d.ts.map +1 -0
- package/dist/host/project-bootstrap.d.ts +4 -2
- package/dist/host/project-bootstrap.d.ts.map +1 -1
- package/dist/host/resource-bootstrap.d.ts +5 -3
- package/dist/host/resource-bootstrap.d.ts.map +1 -1
- package/dist/host.d.ts +2 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +662 -655
- package/dist/index.mjs.map +1 -1
- package/dist/init.d.ts +2 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/project.d.ts +0 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/rhi-debug/cli-context.d.ts.map +1 -1
- package/dist/sdk-bootstrap.d.ts +34 -0
- package/dist/sdk-bootstrap.d.ts.map +1 -0
- package/dist/sdk-cli.mjs +412 -66
- package/dist/sdk-cli.mjs.map +1 -1
- package/dist/sdk-install.d.ts.map +1 -1
- package/dist/sdk-update.d.ts +23 -0
- package/dist/sdk-update.d.ts.map +1 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts +2 -0
- package/dist/tools/benchmark/__tests__/benchmark-vocabulary-owner.test-d.d.ts.map +1 -0
- package/dist/tools/benchmark/harness.d.ts.map +1 -1
- package/dist/tools/browser-host.d.ts.map +1 -1
- package/dist/tools/native-preview.d.ts.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +29 -30
- package/dist/__tests__/tool-migration.test.d.ts +0 -2
- package/dist/__tests__/tool-migration.test.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { deflateRawSync } from 'zlib';
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
|
-
import { readFile, writeFile, copyFile, readdir,
|
|
3
|
+
import { readFile, writeFile, mkdir, copyFile, readdir, mkdtemp, cp, rename, rm, stat, symlink, realpath, unlink, lstat, readlink, rmdir, access } from 'fs/promises';
|
|
4
4
|
import { resolve, relative, sep, isAbsolute, extname, dirname, basename, join } from 'path';
|
|
5
|
-
import { existsSync
|
|
5
|
+
import { existsSync } from 'fs';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { audioImporter } from '@forgeax/engine-audio-webaudio/audio-importer';
|
|
@@ -10,8 +10,11 @@ import { fbxImporter } from '@forgeax/engine-fbx';
|
|
|
10
10
|
import { fontImporter } from '@forgeax/engine-font/font-importer';
|
|
11
11
|
import { gltfImporter } from '@forgeax/engine-gltf';
|
|
12
12
|
import { imageImporter } from '@forgeax/engine-image/image-importer';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { BUILTIN_MESH_ASSETS } from '@forgeax/engine-pack/builtin';
|
|
14
|
+
import { scanInventory } from '@forgeax/engine-pack/scanner';
|
|
15
|
+
import { validatePreviewArtifactManifest, createMaterialPreviewContribution, createMeshPreviewContribution, createVfxPreviewContribution, createTexturePreviewContribution, validateCanonicalKitReceipt, describeResourcePreviewFailure } from '@forgeax/engine-preview';
|
|
16
|
+
import { createMaterialPackCooker } from '@forgeax/engine-shader-compiler';
|
|
17
|
+
import { createStandaloneRuntimeAssetBinding, err, ok } from '@forgeax/engine-types';
|
|
15
18
|
import { createParticleCodeNativeCookerFromRoots } from '@forgeax/engine-vfx-compiler';
|
|
16
19
|
import { pluginPack, reloadAssetHost } from '@forgeax/engine-vite-plugin-pack';
|
|
17
20
|
import { forgeaxShader } from '@forgeax/engine-vite-plugin-shader';
|
|
@@ -20,10 +23,12 @@ import { runCliGltf } from '@forgeax/engine-gltf/cli-gltf';
|
|
|
20
23
|
import { scanEntries } from '@forgeax/engine-pack/cli-asset';
|
|
21
24
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
22
25
|
import { execFile } from 'child_process';
|
|
26
|
+
import { tmpdir } from 'os';
|
|
23
27
|
import { promisify } from 'util';
|
|
24
28
|
import { createRhiDebugError, decodeTape, openReplay, buildFrameModel } from '@forgeax/engine-rhi-debug';
|
|
25
|
-
import {
|
|
26
|
-
import { build,
|
|
29
|
+
import { rhi, createShaderModule } from '@forgeax/engine-rhi-null';
|
|
30
|
+
import { build, preview, createServer as createServer$2 } from 'vite';
|
|
31
|
+
import { createServer as createServer$1 } from 'net';
|
|
27
32
|
import { startVitest } from 'vitest/node';
|
|
28
33
|
import materialPreviewPlugin from '@forgeax/engine-preview/material';
|
|
29
34
|
import meshPreviewPlugin from '@forgeax/engine-preview/mesh';
|
|
@@ -33,9 +38,8 @@ import { validateArtifactManifest, defineTool, createToolRuntime, validateRealmB
|
|
|
33
38
|
export { createAuthenticatedLoopbackTransport, createCapabilityToken, createMigrationRecipe, createRealmCapabilityMatrix, createServiceCapability, probeMigrationTarget } from '@forgeax/engine-tool-runtime';
|
|
34
39
|
import { Context, isToolPlugin } from '@forgeax/engine-plugin';
|
|
35
40
|
import { installCatalogLoader, projectPluginEntries, bootstrapCatalogLoader } from '@forgeax/engine-plugin/loader';
|
|
36
|
-
import { createServer
|
|
41
|
+
import { createServer } from 'http';
|
|
37
42
|
import { createToolPreviewRecipe, createToolPreviewHost } from '@forgeax/engine-app';
|
|
38
|
-
import { createServer as createServer$2 } from 'net';
|
|
39
43
|
import { chromium } from 'playwright';
|
|
40
44
|
|
|
41
45
|
var __defProp = Object.defineProperty;
|
|
@@ -300,16 +304,17 @@ var init_dist = __esm({
|
|
|
300
304
|
"src/dist.ts"() {
|
|
301
305
|
}
|
|
302
306
|
});
|
|
307
|
+
|
|
308
|
+
// src/host.ts
|
|
309
|
+
var host_exports = {};
|
|
310
|
+
__export(host_exports, {
|
|
311
|
+
createViteConfig: () => createViteConfig,
|
|
312
|
+
devKitDdcRoots: () => devKitDdcRoots,
|
|
313
|
+
ignoreDevKitCatalogPath: () => ignoreDevKitCatalogPath
|
|
314
|
+
});
|
|
303
315
|
function ignoreDevKitCatalogPath(path) {
|
|
304
316
|
const normalized = path.replace(/\\/g, "/");
|
|
305
|
-
|
|
306
|
-
if (!normalized.endsWith(".meta.json")) return false;
|
|
307
|
-
try {
|
|
308
|
-
const meta = JSON.parse(readFileSync(path, "utf8"));
|
|
309
|
-
return meta.importer === "game-default-target-profile";
|
|
310
|
-
} catch {
|
|
311
|
-
return false;
|
|
312
|
-
}
|
|
317
|
+
return normalized.split("/").includes("shaders");
|
|
313
318
|
}
|
|
314
319
|
function devKitDdcRoots(projectRoot) {
|
|
315
320
|
return {
|
|
@@ -323,6 +328,42 @@ function isResourcePreviewIgnoredPath(path) {
|
|
|
323
328
|
function isProjectSourceIgnoredPath(path) {
|
|
324
329
|
return ignoreDevKitCatalogPath(path) || path.endsWith(".wgsl.meta.json");
|
|
325
330
|
}
|
|
331
|
+
async function prepareBuiltinPack(projectRoots, generated, ignorePath) {
|
|
332
|
+
const inventory = await scanInventory(projectRoots, { ignorePath });
|
|
333
|
+
if (!inventory.ok) return void 0;
|
|
334
|
+
const declared = /* @__PURE__ */ new Set();
|
|
335
|
+
for (const declaration of inventory.value.declarations.values()) {
|
|
336
|
+
if (declaration.format === "pack.json") {
|
|
337
|
+
for (const asset of declaration.value.assets) declared.add(asset.guid.toLowerCase());
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
for (const asset of declaration.value.subAssets) declared.add(asset.guid.toLowerCase());
|
|
341
|
+
}
|
|
342
|
+
const missing = BUILTIN_MESH_ASSETS.filter((asset) => !declared.has(asset.guid.toLowerCase()));
|
|
343
|
+
if (missing.length === 0) return void 0;
|
|
344
|
+
const packPath = resolve(generated, "engine-builtins.pack.json");
|
|
345
|
+
await writeFile(
|
|
346
|
+
packPath,
|
|
347
|
+
`${JSON.stringify(
|
|
348
|
+
{
|
|
349
|
+
schemaVersion: "2.0.0",
|
|
350
|
+
kind: "internal-text-package",
|
|
351
|
+
assets: missing.map((asset) => ({
|
|
352
|
+
guid: asset.guid,
|
|
353
|
+
kind: "mesh",
|
|
354
|
+
payload: { geometry: asset.geometry },
|
|
355
|
+
refs: [],
|
|
356
|
+
artifacts: {}
|
|
357
|
+
}))
|
|
358
|
+
},
|
|
359
|
+
null,
|
|
360
|
+
2
|
|
361
|
+
)}
|
|
362
|
+
`,
|
|
363
|
+
"utf8"
|
|
364
|
+
);
|
|
365
|
+
return packPath;
|
|
366
|
+
}
|
|
326
367
|
function findEngineWorkspaceRoot() {
|
|
327
368
|
let cursor = dirname(fileURLToPath(import.meta.url));
|
|
328
369
|
for (; ; ) {
|
|
@@ -338,6 +379,7 @@ async function engineWorkspacePackages() {
|
|
|
338
379
|
const workspaceRoot = findEngineWorkspaceRoot();
|
|
339
380
|
if (workspaceRoot === void 0) return /* @__PURE__ */ new Map();
|
|
340
381
|
const packageRoot = resolve(workspaceRoot, "packages");
|
|
382
|
+
if (!existsSync(packageRoot)) return /* @__PURE__ */ new Map();
|
|
341
383
|
const packages = /* @__PURE__ */ new Map();
|
|
342
384
|
for (const entry of await readdir(packageRoot, { withFileTypes: true })) {
|
|
343
385
|
if (!entry.isDirectory()) continue;
|
|
@@ -432,6 +474,18 @@ async function createEngineWorkspaceResolver() {
|
|
|
432
474
|
}
|
|
433
475
|
};
|
|
434
476
|
}
|
|
477
|
+
async function consumerEngineAliases(projectRoot) {
|
|
478
|
+
const root = resolve(projectRoot, "node_modules", ".pnpm", "node_modules", "@forgeax");
|
|
479
|
+
try {
|
|
480
|
+
const entries2 = await readdir(root, { withFileTypes: true });
|
|
481
|
+
return entries2.filter((entry) => entry.isDirectory() && entry.name.startsWith("engine-")).map((entry) => ({
|
|
482
|
+
find: `@forgeax/${entry.name}`,
|
|
483
|
+
replacement: resolve(root, entry.name)
|
|
484
|
+
})).sort((a, b) => a.find.localeCompare(b.find));
|
|
485
|
+
} catch {
|
|
486
|
+
return [];
|
|
487
|
+
}
|
|
488
|
+
}
|
|
435
489
|
async function resolveCanonicalKit() {
|
|
436
490
|
const packageJson = hostRequire.resolve("@forgeax/engine-preview/package.json");
|
|
437
491
|
const root = resolve(packageJson, "..", "assets/canonical-kit");
|
|
@@ -506,6 +560,7 @@ function hostSource(facts, bootstrapRoot, canonicalEnvironmentGuid) {
|
|
|
506
560
|
const plugins = bootstrapRoot === "resource-bootstrap" ? [] : [
|
|
507
561
|
`webAudioPlugin()`,
|
|
508
562
|
`audioPlugin()`,
|
|
563
|
+
`skinningPlugin()`,
|
|
509
564
|
...facts.physics === void 0 ? [] : [`physicsPlugin('${facts.physics === "2d" ? "rapier-2d" : "rapier-3d"}')`]
|
|
510
565
|
];
|
|
511
566
|
const bootstrapPlugin = facts.bootstrapEntry === void 0 ? "" : `const bootstrapModule = await import(${JSON.stringify(
|
|
@@ -523,20 +578,24 @@ await app.pluginContext.plugin({
|
|
|
523
578
|
},
|
|
524
579
|
});`;
|
|
525
580
|
return `import { forgeaxBundlerAdapter } from 'virtual:forgeax/bundler';
|
|
526
|
-
import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine
|
|
527
|
-
import { createCatalogSource } from '@forgeax/engine
|
|
528
|
-
import {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
import {
|
|
533
|
-
import {
|
|
534
|
-
import {
|
|
535
|
-
import {
|
|
536
|
-
import {
|
|
537
|
-
import {
|
|
538
|
-
import {
|
|
539
|
-
import {
|
|
581
|
+
import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine/app';
|
|
582
|
+
import { createCatalogSource } from '@forgeax/engine/assets-runtime';
|
|
583
|
+
import {
|
|
584
|
+
createRuntimeAssetImportTransport,
|
|
585
|
+
runtimeBinding,
|
|
586
|
+
} from 'virtual:forgeax/pack-runtime';
|
|
587
|
+
import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine/plugin/loader';
|
|
588
|
+
import { audioPlugin } from '@forgeax/engine/audio';
|
|
589
|
+
import { webAudioPlugin } from '@forgeax/engine/audio-webaudio';
|
|
590
|
+
import { physicsPlugin } from '@forgeax/engine/physics';
|
|
591
|
+
import { skinningPlugin } from '@forgeax/engine/skinning';
|
|
592
|
+
import { createPrimitiveMesh } from '@forgeax/engine/geometry';
|
|
593
|
+
import { mat4 } from '@forgeax/engine/math';
|
|
594
|
+
import { CAMERA_PROJECTION_ORTHOGRAPHIC, Camera, DirectionalLight, Materials, MeshFilter, MeshRenderer, Skylight, SkyboxBackground, TONEMAP_NONE, TONEMAP_REINHARD_EXTENDED } from '@forgeax/engine/render';
|
|
595
|
+
import { Transform } from '@forgeax/engine/scene';
|
|
596
|
+
import { type SceneAsset } from '@forgeax/engine/types';
|
|
597
|
+
import { ParticleEffectPlayer, vfxGpuEffectContribution } from '@forgeax/engine/vfx';
|
|
598
|
+
import { createVfxRuntimeHost } from '@forgeax/engine/vfx-render';
|
|
540
599
|
|
|
541
600
|
const pluginCatalog = new Map([
|
|
542
601
|
${catalogModules(facts).map(
|
|
@@ -635,16 +694,39 @@ const resizeCanvas = () => {
|
|
|
635
694
|
const resizeObserver = new ResizeObserver(resizeCanvas);
|
|
636
695
|
resizeObserver.observe(canvas);
|
|
637
696
|
resizeCanvas();
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
:
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
697
|
+
const runtimeScopeBinding = runtimeBinding;
|
|
698
|
+
if (runtimeScopeBinding === undefined) {
|
|
699
|
+
throw new Error('forgeax: Vite Pack runtime binding is required in the generated host');
|
|
700
|
+
}
|
|
701
|
+
const assetCatalog = createCatalogSource({
|
|
702
|
+
url: import.meta.env.DEV
|
|
703
|
+
? runtimeScopeBinding.catalogUrl
|
|
704
|
+
: new URL('pack-index.json', document.baseURI).href,
|
|
705
|
+
...(import.meta.env.DEV ? { expectedScope: runtimeScopeBinding } : {}),
|
|
706
|
+
});
|
|
644
707
|
const bundler = {
|
|
645
708
|
...forgeaxBundlerAdapter(),
|
|
646
|
-
...(
|
|
709
|
+
...(import.meta.env.DEV
|
|
710
|
+
? { importTransport: createRuntimeAssetImportTransport(runtimeScopeBinding) }
|
|
711
|
+
: {}),
|
|
647
712
|
};
|
|
713
|
+
const assetPreparation = new WeakMap();
|
|
714
|
+
|
|
715
|
+
function prepareAssetRegistry(assets) {
|
|
716
|
+
const existing = assetPreparation.get(assets);
|
|
717
|
+
if (existing !== undefined) return existing;
|
|
718
|
+
const pending = (async () => {
|
|
719
|
+
assets.configureRuntimeBinding(runtimeScopeBinding);
|
|
720
|
+
assets.setCatalogSource(assetCatalog);
|
|
721
|
+
if (vfxRuntimeHost !== undefined) {
|
|
722
|
+
assets.installDecoder(vfxGpuEffectContribution.kind, vfxGpuEffectContribution.decoder);
|
|
723
|
+
}
|
|
724
|
+
const catalog = await assets.enumerateCatalog();
|
|
725
|
+
if (!catalog.ok) throw catalog.error;
|
|
726
|
+
})();
|
|
727
|
+
assetPreparation.set(assets, pending);
|
|
728
|
+
return pending;
|
|
729
|
+
}
|
|
648
730
|
|
|
649
731
|
function previewStable(value) {
|
|
650
732
|
if (value instanceof ArrayBuffer) return 'ArrayBuffer:' + JSON.stringify(Array.from(new Uint8Array(value)));
|
|
@@ -705,13 +787,14 @@ async function previewOwnerFacts(assets, resource, payload) {
|
|
|
705
787
|
}
|
|
706
788
|
|
|
707
789
|
async function prepareProject(app) {
|
|
790
|
+
await prepareAssetRegistry(app.assets);
|
|
708
791
|
previewWorld = app.world;
|
|
709
792
|
const assets = app.assets;
|
|
710
793
|
let canonicalEnvironment;
|
|
711
794
|
if (resource !== undefined) {
|
|
712
795
|
if (resource.kind !== 'texture') {
|
|
713
796
|
if (canonicalEnvironmentGuid === null) throw new Error('canonical preview environment is unavailable');
|
|
714
|
-
const environment = await assets.
|
|
797
|
+
const environment = await assets.loadByGuid(assets.parseGuid(canonicalEnvironmentGuid));
|
|
715
798
|
if (!environment.ok) throw environment.error;
|
|
716
799
|
if (environment.value.kind !== 'equirect') {
|
|
717
800
|
throw new Error(\`canonical preview environment expected equirect, received \${environment.value.kind}\`);
|
|
@@ -726,7 +809,7 @@ if (resource !== undefined) {
|
|
|
726
809
|
const attached = await vfxRuntimeHost.attachWorld({ world: app.world, assets });
|
|
727
810
|
if (!attached.ok) throw attached.error;
|
|
728
811
|
}
|
|
729
|
-
const loaded = await assets.
|
|
812
|
+
const loaded = await assets.loadByGuid(assets.parseGuid(resource.guid));
|
|
730
813
|
if (!loaded.ok) throw loaded.error;
|
|
731
814
|
const payload = loaded.value;
|
|
732
815
|
if (resource.kind === 'vfx' && payload.kind !== 'particle-effect') {
|
|
@@ -739,7 +822,7 @@ if (resource !== undefined) {
|
|
|
739
822
|
const meshMaterialHandles = resource.kind === 'mesh' && payload.kind === 'mesh'
|
|
740
823
|
? await Promise.all(payload.materialSlots.map(async (slot) => {
|
|
741
824
|
if (slot.defaultMaterial === undefined) return 0;
|
|
742
|
-
const material = await assets.
|
|
825
|
+
const material = await assets.loadByGuid(assets.parseGuid(slot.defaultMaterial));
|
|
743
826
|
if (!material.ok) throw material.error;
|
|
744
827
|
if (material.value.kind !== 'material') {
|
|
745
828
|
throw new Error('mesh material slot resolved to a non-material asset');
|
|
@@ -869,15 +952,13 @@ let defaultScene;
|
|
|
869
952
|
let defaultSceneRoot;
|
|
870
953
|
const defaultSceneGuid = ${JSON.stringify(facts.defaultScene)};
|
|
871
954
|
if (defaultSceneGuid !== undefined) {
|
|
872
|
-
const loaded = await assets.
|
|
955
|
+
const loaded = await assets.loadByGuid<SceneAsset>(assets.parseGuid(defaultSceneGuid));
|
|
873
956
|
if (!loaded.ok) throw loaded.error;
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
const handle = app.world.allocSharedRef('SceneAsset', projected.value);
|
|
878
|
-
const instantiated = worldInstantiateScene(app.world, handle);
|
|
957
|
+
defaultScene = loaded.value;
|
|
958
|
+
const handle = app.world.allocSharedRef('SceneAsset', loaded.value);
|
|
959
|
+
const instantiated = assets.instantiate<SceneAsset>(handle, app.world);
|
|
879
960
|
if (!instantiated.ok) throw instantiated.error;
|
|
880
|
-
defaultSceneRoot = instantiated.value
|
|
961
|
+
defaultSceneRoot = instantiated.value;
|
|
881
962
|
}
|
|
882
963
|
const uiRoot = document.querySelector('#game-ui');
|
|
883
964
|
await app.pluginContext.plugin(gameHostPlugin({
|
|
@@ -916,8 +997,6 @@ if (query.has('forgeax-tool-replay')) {
|
|
|
916
997
|
...(resource === undefined ? {} : { resource }),
|
|
917
998
|
canvas,
|
|
918
999
|
app: {
|
|
919
|
-
...(assetCatalog === undefined ? {} : { assetCatalog }),
|
|
920
|
-
...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
|
|
921
1000
|
plugins: [${plugins.join(", ")}],
|
|
922
1001
|
...(vfxRuntimeHost === undefined ? {} : { features: [vfxRuntimeHost.feature] }),
|
|
923
1002
|
},
|
|
@@ -1022,12 +1101,16 @@ if (query.has('forgeax-tool-replay')) {
|
|
|
1022
1101
|
window.addEventListener('pagehide', () => void host.value.dispose(), { once: true });
|
|
1023
1102
|
}
|
|
1024
1103
|
} else {
|
|
1104
|
+
// WebDriver/headless browsers cannot satisfy the trusted-gesture requirement
|
|
1105
|
+
// for pointer lock. Keep the game running and let its keyboard fallback own
|
|
1106
|
+
// camera input; real browser users retain the normal click-to-lock path.
|
|
1107
|
+
const pointerLockAllowed =
|
|
1108
|
+
typeof navigator !== 'undefined' && navigator.webdriver === true ? () => false : undefined;
|
|
1025
1109
|
const result = await createApp(
|
|
1026
1110
|
canvas,
|
|
1027
1111
|
{
|
|
1028
|
-
...(assetCatalog === undefined ? {} : { assetCatalog }),
|
|
1029
|
-
...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
|
|
1030
1112
|
plugins: [${plugins.join(", ")}],
|
|
1113
|
+
...(pointerLockAllowed === undefined ? {} : { pointerLockAllowed }),
|
|
1031
1114
|
},
|
|
1032
1115
|
bundler,
|
|
1033
1116
|
);
|
|
@@ -1086,10 +1169,32 @@ function htmlSource(title) {
|
|
|
1086
1169
|
<div id="app-shell"><canvas id="app"></canvas><div id="game-ui"></div></div><div id="forgeax-fatal" role="alert"></div>
|
|
1087
1170
|
<script>
|
|
1088
1171
|
(() => {
|
|
1172
|
+
const formatStartupFailure = (reason) => {
|
|
1173
|
+
if (reason instanceof Error) return reason.message;
|
|
1174
|
+
if (reason !== null && typeof reason === 'object') {
|
|
1175
|
+
const record = reason;
|
|
1176
|
+
const lines = [];
|
|
1177
|
+
for (const key of ['code', 'expected', 'hint']) {
|
|
1178
|
+
if (typeof record[key] === 'string' && record[key].length > 0) {
|
|
1179
|
+
lines.push(key + ': ' + record[key]);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const detail = record.detail;
|
|
1183
|
+
if (detail !== null && typeof detail === 'object') {
|
|
1184
|
+
for (const key of ['reason', 'guid']) {
|
|
1185
|
+
if (typeof detail[key] === 'string' && detail[key].length > 0) {
|
|
1186
|
+
lines.push('detail.' + key + ': ' + detail[key]);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (lines.length > 0) return lines.join('\\n');
|
|
1191
|
+
}
|
|
1192
|
+
return String(reason ?? 'Unknown startup failure');
|
|
1193
|
+
};
|
|
1089
1194
|
const show = (reason) => {
|
|
1090
1195
|
const notice = document.querySelector('#forgeax-fatal');
|
|
1091
1196
|
if (!(notice instanceof HTMLElement)) return;
|
|
1092
|
-
const message =
|
|
1197
|
+
const message = formatStartupFailure(reason);
|
|
1093
1198
|
notice.textContent = 'ForgeaX game failed to start.\\n' + message;
|
|
1094
1199
|
notice.style.display = 'grid';
|
|
1095
1200
|
};
|
|
@@ -1104,29 +1209,32 @@ function htmlSource(title) {
|
|
|
1104
1209
|
}
|
|
1105
1210
|
async function createViteConfig(facts, command, base = "/", options = {}) {
|
|
1106
1211
|
const bootstrapRoot = options.bootstrapRoot ?? "project-bootstrap";
|
|
1107
|
-
const canonicalKit =
|
|
1212
|
+
const canonicalKit = await resolveCanonicalKit();
|
|
1108
1213
|
const generated = resolve(facts.root, ".forgeax", "generated");
|
|
1109
1214
|
await mkdir(generated, { recursive: true });
|
|
1215
|
+
const projectRoots = facts.assetRoots.map((root) => resolve(facts.root, root));
|
|
1216
|
+
const ignorePath = bootstrapRoot === "resource-bootstrap" ? isResourcePreviewIgnoredPath : isProjectSourceIgnoredPath;
|
|
1217
|
+
const builtinPack = await prepareBuiltinPack(projectRoots, generated, ignorePath);
|
|
1110
1218
|
await Promise.all([
|
|
1111
1219
|
writeFile(resolve(generated, "index.html"), htmlSource(facts.name)),
|
|
1112
1220
|
writeFile(resolve(generated, "main.ts"), hostSource(facts, bootstrapRoot, canonicalKit?.guid))
|
|
1113
1221
|
]);
|
|
1114
1222
|
const roots = [
|
|
1115
|
-
...
|
|
1223
|
+
...projectRoots,
|
|
1224
|
+
...builtinPack === void 0 ? [] : [builtinPack],
|
|
1116
1225
|
...canonicalKit === void 0 ? [] : [canonicalKit.root]
|
|
1117
1226
|
];
|
|
1118
1227
|
const importers = [...projectImporters(facts)];
|
|
1119
1228
|
const runtimeBinding = createStandaloneRuntimeAssetBinding(facts.id);
|
|
1120
1229
|
const engineWorkspaceResolver = await createEngineWorkspaceResolver();
|
|
1230
|
+
const consumerAliases = await consumerEngineAliases(facts.root);
|
|
1121
1231
|
const plugins = [
|
|
1122
1232
|
...engineWorkspaceResolver === void 0 ? [] : [engineWorkspaceResolver],
|
|
1123
1233
|
forgeaxShader(),
|
|
1124
1234
|
pluginPack({
|
|
1125
1235
|
roots,
|
|
1126
|
-
base,
|
|
1127
1236
|
runtimeBinding,
|
|
1128
1237
|
ddc: devKitDdcRoots(facts.root),
|
|
1129
|
-
scriptablePack: {},
|
|
1130
1238
|
refresh: command === "serve" ? reloadAssetHost() : void 0,
|
|
1131
1239
|
importers: [
|
|
1132
1240
|
audioImporter,
|
|
@@ -1136,8 +1244,8 @@ async function createViteConfig(facts, command, base = "/", options = {}) {
|
|
|
1136
1244
|
fontImporter,
|
|
1137
1245
|
...importers
|
|
1138
1246
|
],
|
|
1139
|
-
cookers: [createParticleCodeNativeCookerFromRoots(roots)],
|
|
1140
|
-
|
|
1247
|
+
cookers: [createMaterialPackCooker(roots), createParticleCodeNativeCookerFromRoots(roots)],
|
|
1248
|
+
ignorePath
|
|
1141
1249
|
})
|
|
1142
1250
|
];
|
|
1143
1251
|
return {
|
|
@@ -1146,8 +1254,11 @@ async function createViteConfig(facts, command, base = "/", options = {}) {
|
|
|
1146
1254
|
configFile: false,
|
|
1147
1255
|
publicDir: facts.assetPublicDir === void 0 ? false : resolve(facts.root, facts.assetPublicDir),
|
|
1148
1256
|
plugins,
|
|
1149
|
-
resolve: {
|
|
1150
|
-
|
|
1257
|
+
resolve: {
|
|
1258
|
+
alias: consumerAliases,
|
|
1259
|
+
dedupe: ["@forgeax/engine"]
|
|
1260
|
+
},
|
|
1261
|
+
server: { ...options.server, fs: { allow: [facts.root, ...roots] } },
|
|
1151
1262
|
build: {
|
|
1152
1263
|
target: "esnext",
|
|
1153
1264
|
outDir: options.outDir ?? resolve(facts.root, "dist"),
|
|
@@ -1171,7 +1282,7 @@ async function readJson(path) {
|
|
|
1171
1282
|
function firstUnsupportedStandaloneRealm(entries2, inheritedRealm = "engine") {
|
|
1172
1283
|
for (const entry of entries2) {
|
|
1173
1284
|
const realm = entry.realm ?? inheritedRealm ?? "engine";
|
|
1174
|
-
if (realm
|
|
1285
|
+
if (realm !== "engine") return { id: entry.id, realm };
|
|
1175
1286
|
if (entry.group === true) {
|
|
1176
1287
|
const unsupported = firstUnsupportedStandaloneRealm(
|
|
1177
1288
|
entry.config,
|
|
@@ -1289,13 +1400,10 @@ async function readProjectFacts(rootInput = process.cwd()) {
|
|
|
1289
1400
|
}
|
|
1290
1401
|
};
|
|
1291
1402
|
}
|
|
1292
|
-
function isCommandError(value) {
|
|
1293
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1294
|
-
const candidate = value;
|
|
1295
|
-
return typeof candidate.code === "string" && typeof candidate.expected === "string" && typeof candidate.hint === "string" && candidate.detail !== null && typeof candidate.detail === "object" && !Array.isArray(candidate.detail);
|
|
1296
|
-
}
|
|
1297
1403
|
function commandError(cause, fallbackCode) {
|
|
1298
|
-
if (
|
|
1404
|
+
if (cause !== null && typeof cause === "object" && "code" in cause && "expected" in cause && "hint" in cause && "detail" in cause) {
|
|
1405
|
+
return cause;
|
|
1406
|
+
}
|
|
1299
1407
|
return {
|
|
1300
1408
|
code: fallbackCode,
|
|
1301
1409
|
expected: "the ForgeaX command to complete",
|
|
@@ -1307,16 +1415,17 @@ var init_project = __esm({
|
|
|
1307
1415
|
"src/project.ts"() {
|
|
1308
1416
|
}
|
|
1309
1417
|
});
|
|
1310
|
-
|
|
1311
|
-
|
|
1418
|
+
|
|
1419
|
+
// src/types.ts
|
|
1420
|
+
function resolveProjectPort(port) {
|
|
1421
|
+
return { port: port ?? 5173, strictPort: port !== 0 };
|
|
1312
1422
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
const parsed = JSON.parse(stderr.at(-1) ?? "");
|
|
1316
|
-
if (isCommandError(parsed)) return failure(parsed);
|
|
1317
|
-
} catch {
|
|
1423
|
+
var init_types = __esm({
|
|
1424
|
+
"src/types.ts"() {
|
|
1318
1425
|
}
|
|
1319
|
-
|
|
1426
|
+
});
|
|
1427
|
+
function failure(error) {
|
|
1428
|
+
return { ok: false, error };
|
|
1320
1429
|
}
|
|
1321
1430
|
async function sourcesAt(path) {
|
|
1322
1431
|
const info = await stat(path);
|
|
@@ -1401,15 +1510,16 @@ async function addGltf(sourcePath, dryRun) {
|
|
|
1401
1510
|
stderrWrite: (line) => stderr.push(line)
|
|
1402
1511
|
});
|
|
1403
1512
|
if (exitCode !== 0) {
|
|
1404
|
-
|
|
1405
|
-
stderr
|
|
1406
|
-
|
|
1513
|
+
try {
|
|
1514
|
+
return failure(JSON.parse(stderr.at(-1) ?? ""));
|
|
1515
|
+
} catch {
|
|
1516
|
+
return failure({
|
|
1407
1517
|
code: "asset-add-failed",
|
|
1408
1518
|
expected: "the glTF producer to create or reuse a valid sidecar",
|
|
1409
|
-
hint: "Inspect the glTF source and its external references."
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1519
|
+
hint: "Inspect the glTF source and its external references.",
|
|
1520
|
+
detail: { source: sourcePath, diagnostic: stderr.join("\n") }
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1413
1523
|
}
|
|
1414
1524
|
return { ok: true, value: { source: sourcePath, metaPath: `${sourcePath}.meta.json` } };
|
|
1415
1525
|
}
|
|
@@ -1452,15 +1562,16 @@ async function entries(options) {
|
|
|
1452
1562
|
{ stdoutWrite: (line) => stdout.push(line), stderrWrite: (line) => stderr.push(line) }
|
|
1453
1563
|
);
|
|
1454
1564
|
if (!result.ok) {
|
|
1455
|
-
|
|
1456
|
-
stderr
|
|
1457
|
-
|
|
1565
|
+
try {
|
|
1566
|
+
return failure(JSON.parse(stderr.at(-1) ?? ""));
|
|
1567
|
+
} catch {
|
|
1568
|
+
return failure({
|
|
1458
1569
|
code: "asset-authority-invalid",
|
|
1459
1570
|
expected: "all asset roots and sidecars to pass the pack scanner",
|
|
1460
|
-
hint: "Repair the first invalid asset authority reported by the scanner."
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1571
|
+
hint: "Repair the first invalid asset authority reported by the scanner.",
|
|
1572
|
+
detail: { diagnostic: stderr.join("\n") }
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1464
1575
|
}
|
|
1465
1576
|
return { ok: true, value: { facts: facts.value, entries: result.value } };
|
|
1466
1577
|
}
|
|
@@ -1545,7 +1656,6 @@ var init_package = __esm({
|
|
|
1545
1656
|
"@forgeax/engine-audio-webaudio": "workspace:*",
|
|
1546
1657
|
"@forgeax/engine-fbx": "workspace:*",
|
|
1547
1658
|
"@forgeax/engine-font": "workspace:*",
|
|
1548
|
-
"@forgeax/engine-geometry": "workspace:*",
|
|
1549
1659
|
"@forgeax/engine-gltf": "workspace:*",
|
|
1550
1660
|
"@forgeax/engine-image": "workspace:*",
|
|
1551
1661
|
"@forgeax/engine-input": "workspace:*",
|
|
@@ -1555,6 +1665,7 @@ var init_package = __esm({
|
|
|
1555
1665
|
"@forgeax/engine-preview": "workspace:*",
|
|
1556
1666
|
"@forgeax/engine-project": "workspace:*",
|
|
1557
1667
|
"@forgeax/engine-rhi-debug": "workspace:*",
|
|
1668
|
+
"@forgeax/engine-rhi-null": "workspace:*",
|
|
1558
1669
|
"@forgeax/engine-render": "workspace:*",
|
|
1559
1670
|
"@forgeax/engine-render-graph": "workspace:*",
|
|
1560
1671
|
"@forgeax/engine-runtime": "workspace:*",
|
|
@@ -1572,7 +1683,6 @@ var init_package = __esm({
|
|
|
1572
1683
|
vitest: "4.1.5"
|
|
1573
1684
|
},
|
|
1574
1685
|
devDependencies: {
|
|
1575
|
-
"@forgeax/engine-rhi-null": "workspace:*",
|
|
1576
1686
|
"@types/node": "^20.14.0"
|
|
1577
1687
|
},
|
|
1578
1688
|
forgeax: {
|
|
@@ -1698,6 +1808,7 @@ function createInitPlan(facts, sdk) {
|
|
|
1698
1808
|
value: {
|
|
1699
1809
|
root: facts.root,
|
|
1700
1810
|
version: sdk?.sdkVersion ?? devkitVersion,
|
|
1811
|
+
...sdk === void 0 ? {} : { pnpmVersion: sdk.requirements.pnpm },
|
|
1701
1812
|
archiveBacked: sdk !== void 0,
|
|
1702
1813
|
dependencyChanges,
|
|
1703
1814
|
scriptChanges
|
|
@@ -1718,23 +1829,8 @@ async function applyInitPlan(facts, plan, options) {
|
|
|
1718
1829
|
for (const change of plan.scriptChanges) scripts[change.name] = change.to;
|
|
1719
1830
|
manifest.scripts = scripts;
|
|
1720
1831
|
if (plan.archiveBacked) {
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const pnpm = pnpmValue !== null && typeof pnpmValue === "object" ? pnpmValue : {};
|
|
1724
|
-
manifest.pnpm = {
|
|
1725
|
-
...pnpm,
|
|
1726
|
-
onlyBuiltDependencies: [
|
|
1727
|
-
"@forgeax/engine-codec",
|
|
1728
|
-
"@forgeax/engine-fbx",
|
|
1729
|
-
"@forgeax/engine-wgpu-wasm",
|
|
1730
|
-
"esbuild"
|
|
1731
|
-
],
|
|
1732
|
-
supportedArchitectures: {
|
|
1733
|
-
os: ["darwin", "linux", "win32"],
|
|
1734
|
-
cpu: ["x64", "arm64"],
|
|
1735
|
-
libc: ["glibc", "musl"]
|
|
1736
|
-
}
|
|
1737
|
-
};
|
|
1832
|
+
if (plan.pnpmVersion === void 0) throw new Error("sdk-pnpm-version-missing");
|
|
1833
|
+
manifest.packageManager = `pnpm@${plan.pnpmVersion}`;
|
|
1738
1834
|
}
|
|
1739
1835
|
await writeFile(resolve(facts.root, "package.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1740
1836
|
`);
|
|
@@ -1800,6 +1896,270 @@ var init_sdk = __esm({
|
|
|
1800
1896
|
"src/sdk.ts"() {
|
|
1801
1897
|
}
|
|
1802
1898
|
});
|
|
1899
|
+
function agentOnboarding(sdk, projectRoot) {
|
|
1900
|
+
const root = projectRoot ?? sdk.root;
|
|
1901
|
+
return {
|
|
1902
|
+
read: [
|
|
1903
|
+
resolve(root, "AGENTS.md"),
|
|
1904
|
+
resolve(root, "skills", "forgeax-engine-sdk", "SKILL.md"),
|
|
1905
|
+
resolve(root, "skills", "forgeax-engine-sdk", "references", "feature-catalog.md")
|
|
1906
|
+
],
|
|
1907
|
+
next: projectRoot === void 0 ? {
|
|
1908
|
+
cwd: root,
|
|
1909
|
+
argv: ["node", "./bin/forgeax.mjs", "new", "../my-game"]
|
|
1910
|
+
} : { cwd: root, argv: ["pnpm", "exec", "forgeax", "list", "--json"] }
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
function sdkInitPath(sdk) {
|
|
1914
|
+
return resolve(sdk.root, ...SDK_INIT_PATH);
|
|
1915
|
+
}
|
|
1916
|
+
function commandFailure(code, expected, hint, detail = {}) {
|
|
1917
|
+
return { ok: false, error: { code, expected, hint, detail } };
|
|
1918
|
+
}
|
|
1919
|
+
function sdkProjectInstallArgs(store) {
|
|
1920
|
+
const common = [
|
|
1921
|
+
"install",
|
|
1922
|
+
"--frozen-lockfile",
|
|
1923
|
+
"--ignore-scripts",
|
|
1924
|
+
"--side-effects-cache=true",
|
|
1925
|
+
"--child-concurrency=1"
|
|
1926
|
+
];
|
|
1927
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
1928
|
+
}
|
|
1929
|
+
function sdkBootstrapInstallArgs(store) {
|
|
1930
|
+
const common = [
|
|
1931
|
+
"install",
|
|
1932
|
+
"--frozen-lockfile",
|
|
1933
|
+
"--child-concurrency=1",
|
|
1934
|
+
"--side-effects-cache=true"
|
|
1935
|
+
];
|
|
1936
|
+
return store === void 0 ? common : [...common, "--offline", "--config.trust-lockfile=true", "--store-dir", store];
|
|
1937
|
+
}
|
|
1938
|
+
function supportedPnpm(version) {
|
|
1939
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
1940
|
+
return major === 11 && minor >= 7;
|
|
1941
|
+
}
|
|
1942
|
+
async function currentPnpm() {
|
|
1943
|
+
try {
|
|
1944
|
+
const result = await execFileAsync("pnpm", ["--version"], { maxBuffer: 1024 * 1024 });
|
|
1945
|
+
const version = result.stdout.trim();
|
|
1946
|
+
if (version.length === 0) throw new Error("pnpm returned an empty version");
|
|
1947
|
+
return { ok: true, value: version };
|
|
1948
|
+
} catch (cause) {
|
|
1949
|
+
return commandFailure(
|
|
1950
|
+
"pnpm-unavailable",
|
|
1951
|
+
"pnpm 11.7.0 or newer in the pnpm 11 line to be available on PATH",
|
|
1952
|
+
"Install or activate pnpm 11, then rerun forgeax init from the SDK root.",
|
|
1953
|
+
{ reason: cause instanceof Error ? cause.message : String(cause) }
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
function matchesState(sdk, state) {
|
|
1958
|
+
if (state === null || typeof state !== "object" || Array.isArray(state)) return false;
|
|
1959
|
+
const value = state;
|
|
1960
|
+
return value.schemaVersion === SDK_INIT_SCHEMA_VERSION && value.sdkVersion === sdk.manifest.sdkVersion && value.engineCommit === sdk.manifest.engineCommit && typeof value.pnpm === "string" && supportedPnpm(value.pnpm) && value.node === process.versions.node && value.platform === process.platform && value.arch === process.arch;
|
|
1961
|
+
}
|
|
1962
|
+
async function readSdkInitState(sdk) {
|
|
1963
|
+
try {
|
|
1964
|
+
const value = JSON.parse(await readFile(sdkInitPath(sdk), "utf8"));
|
|
1965
|
+
return matchesState(sdk, value) ? value : void 0;
|
|
1966
|
+
} catch {
|
|
1967
|
+
return void 0;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
async function requireSdkInitialization(sdk) {
|
|
1971
|
+
const state = await readSdkInitState(sdk);
|
|
1972
|
+
if (state === void 0) {
|
|
1973
|
+
return commandFailure(
|
|
1974
|
+
"sdk-not-initialized",
|
|
1975
|
+
"the downloaded SDK to be initialized for this Node/pnpm/platform tuple",
|
|
1976
|
+
"Run ./bin/forgeax init from the SDK root once, then run forgeax new outside the SDK.",
|
|
1977
|
+
{
|
|
1978
|
+
sdkRoot: sdk.root,
|
|
1979
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
1980
|
+
pnpm: sdk.manifest.requirements.pnpm,
|
|
1981
|
+
node: process.versions.node,
|
|
1982
|
+
platform: process.platform,
|
|
1983
|
+
arch: process.arch
|
|
1984
|
+
}
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
return { ok: true, value: state };
|
|
1988
|
+
}
|
|
1989
|
+
async function copyBootstrapInputs(sdk, root) {
|
|
1990
|
+
const template = sdk.templates.get(sdk.defaultTemplate);
|
|
1991
|
+
if (template === void 0) throw new Error("sdk-default-template-missing");
|
|
1992
|
+
for (const name of ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"]) {
|
|
1993
|
+
await cp(resolve(template, name), resolve(root, name));
|
|
1994
|
+
}
|
|
1995
|
+
try {
|
|
1996
|
+
await cp(resolve(template, ".npmrc"), resolve(root, ".npmrc"));
|
|
1997
|
+
} catch (cause) {
|
|
1998
|
+
if (cause === null || typeof cause !== "object" || !("code" in cause) || cause.code !== "ENOENT") {
|
|
1999
|
+
throw cause;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
async function sdkInitCommand(sdk, options = {}) {
|
|
2004
|
+
const pnpmResult = await currentPnpm();
|
|
2005
|
+
if (!pnpmResult.ok) return pnpmResult;
|
|
2006
|
+
if (!supportedPnpm(pnpmResult.value)) {
|
|
2007
|
+
return commandFailure(
|
|
2008
|
+
"pnpm-version-unsupported",
|
|
2009
|
+
"pnpm >=11.7.0 <12",
|
|
2010
|
+
"Activate pnpm 11.7.0 or newer in the pnpm 11 line, then rerun forgeax init.",
|
|
2011
|
+
{ actual: pnpmResult.value, expected: sdk.manifest.requirements.pnpm }
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
const state = {
|
|
2015
|
+
schemaVersion: SDK_INIT_SCHEMA_VERSION,
|
|
2016
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
2017
|
+
engineCommit: sdk.manifest.engineCommit,
|
|
2018
|
+
pnpm: pnpmResult.value,
|
|
2019
|
+
node: process.versions.node,
|
|
2020
|
+
platform: process.platform,
|
|
2021
|
+
arch: process.arch
|
|
2022
|
+
};
|
|
2023
|
+
const report = {
|
|
2024
|
+
...state,
|
|
2025
|
+
root: sdk.root,
|
|
2026
|
+
store: sdk.store === void 0 ? "registry" : "offline",
|
|
2027
|
+
onboarding: agentOnboarding(sdk)
|
|
2028
|
+
};
|
|
2029
|
+
if (options.dryRun === true || options.install === false) return { ok: true, value: report };
|
|
2030
|
+
let staging;
|
|
2031
|
+
try {
|
|
2032
|
+
staging = await mkdtemp(resolve(tmpdir(), "forgeax-sdk-init-"));
|
|
2033
|
+
await copyBootstrapInputs(sdk, staging);
|
|
2034
|
+
await execFileAsync("pnpm", sdkBootstrapInstallArgs(sdk.store), {
|
|
2035
|
+
cwd: staging,
|
|
2036
|
+
env: { ...process.env, CI: "true" },
|
|
2037
|
+
maxBuffer: 16 * 1024 * 1024
|
|
2038
|
+
});
|
|
2039
|
+
await mkdir(resolve(sdk.root, ".forgeax"), { recursive: true });
|
|
2040
|
+
await writeFile(sdkInitPath(sdk), `${JSON.stringify(state, null, 2)}
|
|
2041
|
+
`);
|
|
2042
|
+
return { ok: true, value: report };
|
|
2043
|
+
} catch (cause) {
|
|
2044
|
+
return commandFailure(
|
|
2045
|
+
"sdk-init-failed",
|
|
2046
|
+
"the SDK dependency closure to install and build native packages successfully",
|
|
2047
|
+
"Inspect the pnpm output, repair Node/pnpm or platform permissions, then rerun forgeax init from the SDK root.",
|
|
2048
|
+
{ reason: cause instanceof Error ? cause.message : String(cause), sdkRoot: sdk.root }
|
|
2049
|
+
);
|
|
2050
|
+
} finally {
|
|
2051
|
+
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
var execFileAsync, SDK_INIT_PATH, SDK_INIT_SCHEMA_VERSION;
|
|
2055
|
+
var init_sdk_bootstrap = __esm({
|
|
2056
|
+
"src/sdk-bootstrap.ts"() {
|
|
2057
|
+
execFileAsync = promisify(execFile);
|
|
2058
|
+
SDK_INIT_PATH = [".forgeax", "sdk-init.json"];
|
|
2059
|
+
SDK_INIT_SCHEMA_VERSION = "1.0.0";
|
|
2060
|
+
}
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
// src/sdk-update.ts
|
|
2064
|
+
function parseVersion(version) {
|
|
2065
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
|
|
2066
|
+
version
|
|
2067
|
+
);
|
|
2068
|
+
if (match === null) return void 0;
|
|
2069
|
+
const major = match[1];
|
|
2070
|
+
const minor = match[2];
|
|
2071
|
+
const patch = match[3];
|
|
2072
|
+
if (major === void 0 || minor === void 0 || patch === void 0) return void 0;
|
|
2073
|
+
const prerelease = match[4]?.split(".");
|
|
2074
|
+
if (prerelease?.some(
|
|
2075
|
+
(identifier) => identifier.length === 0 || /^\d+$/.test(identifier) && /^0\d+/.test(identifier)
|
|
2076
|
+
)) {
|
|
2077
|
+
return void 0;
|
|
2078
|
+
}
|
|
2079
|
+
return {
|
|
2080
|
+
core: [major, minor, patch],
|
|
2081
|
+
...prerelease === void 0 ? {} : { prerelease }
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
function compareNumericIdentifier(current, latest) {
|
|
2085
|
+
if (current.length !== latest.length) return latest.length > current.length ? 1 : -1;
|
|
2086
|
+
if (current === latest) return 0;
|
|
2087
|
+
return latest > current ? 1 : -1;
|
|
2088
|
+
}
|
|
2089
|
+
function comparePrerelease(current, latest) {
|
|
2090
|
+
const length = Math.max(current.length, latest.length);
|
|
2091
|
+
for (let index = 0; index < length; index += 1) {
|
|
2092
|
+
const currentIdentifier = current[index];
|
|
2093
|
+
const latestIdentifier = latest[index];
|
|
2094
|
+
if (currentIdentifier === void 0) return 1;
|
|
2095
|
+
if (latestIdentifier === void 0) return -1;
|
|
2096
|
+
if (currentIdentifier === latestIdentifier) continue;
|
|
2097
|
+
const currentNumeric = /^\d+$/.test(currentIdentifier);
|
|
2098
|
+
const latestNumeric = /^\d+$/.test(latestIdentifier);
|
|
2099
|
+
if (currentNumeric && latestNumeric) {
|
|
2100
|
+
return compareNumericIdentifier(currentIdentifier, latestIdentifier);
|
|
2101
|
+
}
|
|
2102
|
+
if (currentNumeric !== latestNumeric) return latestNumeric ? -1 : 1;
|
|
2103
|
+
return latestIdentifier > currentIdentifier ? 1 : -1;
|
|
2104
|
+
}
|
|
2105
|
+
return 0;
|
|
2106
|
+
}
|
|
2107
|
+
function newerSdkVersion(currentVersion, latestVersion) {
|
|
2108
|
+
const current = parseVersion(currentVersion);
|
|
2109
|
+
const latest = parseVersion(latestVersion);
|
|
2110
|
+
if (current === void 0 || latest === void 0) return false;
|
|
2111
|
+
for (let index = 0; index < current.core.length; index += 1) {
|
|
2112
|
+
const precedence = compareNumericIdentifier(
|
|
2113
|
+
current.core[index] ?? "0",
|
|
2114
|
+
latest.core[index] ?? "0"
|
|
2115
|
+
);
|
|
2116
|
+
if (precedence !== 0) return precedence > 0;
|
|
2117
|
+
}
|
|
2118
|
+
if (current.prerelease !== void 0 && latest.prerelease === void 0) return true;
|
|
2119
|
+
if (current.prerelease === void 0 || latest.prerelease === void 0) return false;
|
|
2120
|
+
return comparePrerelease(current.prerelease, latest.prerelease) > 0;
|
|
2121
|
+
}
|
|
2122
|
+
function offline() {
|
|
2123
|
+
return ["1", "true"].includes((process.env.npm_config_offline ?? "").toLowerCase());
|
|
2124
|
+
}
|
|
2125
|
+
async function checkSdkUpdate(currentVersion) {
|
|
2126
|
+
if (process.env.FORGEAX_DISABLE_UPDATE_CHECK === "1") {
|
|
2127
|
+
return { status: "skipped", currentVersion, reason: "disabled" };
|
|
2128
|
+
}
|
|
2129
|
+
if (offline()) return { status: "skipped", currentVersion, reason: "offline" };
|
|
2130
|
+
try {
|
|
2131
|
+
const registry = process.env.npm_config_registry ?? "https://registry.npmjs.org/";
|
|
2132
|
+
const response = await fetch(
|
|
2133
|
+
new URL("@forgeax%2Fengine-sdk", `${registry.replace(/\/$/, "")}/`),
|
|
2134
|
+
{
|
|
2135
|
+
headers: { accept: "application/vnd.npm.install-v1+json" },
|
|
2136
|
+
signal: AbortSignal.timeout(2e3)
|
|
2137
|
+
}
|
|
2138
|
+
);
|
|
2139
|
+
if (!response.ok) throw new Error(`registry-http-${response.status}`);
|
|
2140
|
+
const metadata = await response.json();
|
|
2141
|
+
const latestVersion = metadata["dist-tags"]?.latest;
|
|
2142
|
+
if (typeof latestVersion !== "string") throw new Error("registry-latest-version-missing");
|
|
2143
|
+
return newerSdkVersion(currentVersion, latestVersion) ? {
|
|
2144
|
+
status: "available",
|
|
2145
|
+
currentVersion,
|
|
2146
|
+
latestVersion,
|
|
2147
|
+
migrationRisk: UPDATE_MIGRATION_RISK
|
|
2148
|
+
} : { status: "current", currentVersion, latestVersion };
|
|
2149
|
+
} catch (cause) {
|
|
2150
|
+
return {
|
|
2151
|
+
status: "unavailable",
|
|
2152
|
+
currentVersion,
|
|
2153
|
+
reason: cause instanceof Error ? cause.message : String(cause)
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
var UPDATE_MIGRATION_RISK;
|
|
2158
|
+
var init_sdk_update = __esm({
|
|
2159
|
+
"src/sdk-update.ts"() {
|
|
2160
|
+
UPDATE_MIGRATION_RISK = "Install a newer SDK in a separate directory. Existing games remain pinned and are not migrated automatically; review release notes and migrate and test each game before changing its Engine version.";
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
1803
2163
|
function slash(path) {
|
|
1804
2164
|
return path.split(sep).join("/");
|
|
1805
2165
|
}
|
|
@@ -2165,6 +2525,20 @@ async function canonicalProspectivePath(path) {
|
|
|
2165
2525
|
}
|
|
2166
2526
|
}
|
|
2167
2527
|
}
|
|
2528
|
+
async function configureSdkStore(root, store) {
|
|
2529
|
+
if (store === void 0) return;
|
|
2530
|
+
const path = resolve(root, ".npmrc");
|
|
2531
|
+
let content = "";
|
|
2532
|
+
try {
|
|
2533
|
+
content = await readFile(path, "utf8");
|
|
2534
|
+
} catch (cause) {
|
|
2535
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
2536
|
+
}
|
|
2537
|
+
const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0 && !/^\s*store-dir\s*=/.test(line));
|
|
2538
|
+
lines.push(`store-dir=${store}`);
|
|
2539
|
+
await writeFile(path, `${lines.join("\n")}
|
|
2540
|
+
`);
|
|
2541
|
+
}
|
|
2168
2542
|
function containsOrEquals(parent, child) {
|
|
2169
2543
|
const path = relative(parent, child);
|
|
2170
2544
|
return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
@@ -2175,25 +2549,14 @@ function nodeSupported() {
|
|
|
2175
2549
|
}
|
|
2176
2550
|
function pnpmSupported(version) {
|
|
2177
2551
|
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
2178
|
-
return major ===
|
|
2179
|
-
}
|
|
2180
|
-
function sdkInstallArgs(store) {
|
|
2181
|
-
if (store === void 0) return ["install", "--frozen-lockfile"];
|
|
2182
|
-
return [
|
|
2183
|
-
"install",
|
|
2184
|
-
"--offline",
|
|
2185
|
-
"--frozen-lockfile",
|
|
2186
|
-
"--side-effects-cache=false",
|
|
2187
|
-
"--store-dir",
|
|
2188
|
-
store
|
|
2189
|
-
];
|
|
2552
|
+
return major === 11 && minor >= 7;
|
|
2190
2553
|
}
|
|
2191
2554
|
async function doctorCommand(options = {}) {
|
|
2192
2555
|
const facts = await readProjectFacts(options.root);
|
|
2193
2556
|
if (!facts.ok) return facts;
|
|
2194
2557
|
let pnpm;
|
|
2195
2558
|
try {
|
|
2196
|
-
pnpm = (await
|
|
2559
|
+
pnpm = (await execFileAsync2("pnpm", ["--version"], { cwd: facts.value.root })).stdout.trim();
|
|
2197
2560
|
} catch (cause) {
|
|
2198
2561
|
return {
|
|
2199
2562
|
ok: false,
|
|
@@ -2221,7 +2584,7 @@ async function doctorCommand(options = {}) {
|
|
|
2221
2584
|
ok: false,
|
|
2222
2585
|
error: {
|
|
2223
2586
|
code: "pnpm-version-unsupported",
|
|
2224
|
-
expected: "pnpm >=
|
|
2587
|
+
expected: "pnpm >=11.7.0 <12",
|
|
2225
2588
|
hint: "Enable the packageManager-declared pnpm version with Corepack and retry.",
|
|
2226
2589
|
detail: { actual: pnpm }
|
|
2227
2590
|
}
|
|
@@ -2260,12 +2623,20 @@ async function doctorCommand(options = {}) {
|
|
|
2260
2623
|
};
|
|
2261
2624
|
}
|
|
2262
2625
|
async function initCommand(options = {}) {
|
|
2263
|
-
const facts = await readProjectFacts(options.root);
|
|
2264
|
-
if (!facts.ok) return facts;
|
|
2265
2626
|
try {
|
|
2266
2627
|
const sdk = await findSdkContext();
|
|
2628
|
+
const root = await canonicalProspectivePath(options.root ?? process.cwd());
|
|
2629
|
+
if (sdk !== void 0 && root === await canonicalProspectivePath(sdk.root)) {
|
|
2630
|
+
return sdkInitCommand(sdk, options);
|
|
2631
|
+
}
|
|
2632
|
+
const facts = await readProjectFacts(root);
|
|
2633
|
+
if (!facts.ok) return facts;
|
|
2267
2634
|
const plan = createInitPlan(facts.value, sdk?.manifest);
|
|
2268
2635
|
if (!plan.ok) return plan;
|
|
2636
|
+
if (sdk !== void 0 && options.dryRun !== true) {
|
|
2637
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
2638
|
+
if (!initialized.ok) return initialized;
|
|
2639
|
+
}
|
|
2269
2640
|
const applied = await applyInitPlan(facts.value, plan.value, options);
|
|
2270
2641
|
if (!applied.ok || options.dryRun === true) return applied;
|
|
2271
2642
|
if (sdk !== void 0) {
|
|
@@ -2275,12 +2646,31 @@ async function initCommand(options = {}) {
|
|
|
2275
2646
|
resolve(template, "pnpm-lock.yaml"),
|
|
2276
2647
|
resolve(facts.value.root, "pnpm-lock.yaml")
|
|
2277
2648
|
);
|
|
2649
|
+
await copyFile(
|
|
2650
|
+
resolve(template, "pnpm-workspace.yaml"),
|
|
2651
|
+
resolve(facts.value.root, "pnpm-workspace.yaml")
|
|
2652
|
+
);
|
|
2653
|
+
try {
|
|
2654
|
+
await readFile(resolve(facts.value.root, ".npmrc"), "utf8");
|
|
2655
|
+
} catch (cause) {
|
|
2656
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
2657
|
+
try {
|
|
2658
|
+
await copyFile(resolve(template, ".npmrc"), resolve(facts.value.root, ".npmrc"));
|
|
2659
|
+
} catch (templateCause) {
|
|
2660
|
+
if (!isMissingPathError(templateCause)) throw templateCause;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2278
2663
|
await copySdkSkills(sdk, facts.value.root);
|
|
2279
2664
|
await installProjectSkills(facts.value.root, sdk.manifest);
|
|
2665
|
+
await configureSdkStore(
|
|
2666
|
+
facts.value.root,
|
|
2667
|
+
sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store)
|
|
2668
|
+
);
|
|
2280
2669
|
}
|
|
2281
2670
|
if (options.install === false) return applied;
|
|
2282
|
-
const
|
|
2283
|
-
|
|
2671
|
+
const store = sdk === void 0 || sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
2672
|
+
const installArgs = sdk === void 0 ? ["install", "--frozen-lockfile=false"] : sdkProjectInstallArgs(store);
|
|
2673
|
+
await execFileAsync2("pnpm", installArgs, {
|
|
2284
2674
|
cwd: facts.value.root,
|
|
2285
2675
|
env: { ...process.env, CI: "true" },
|
|
2286
2676
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -2358,12 +2748,15 @@ async function newCommand(options = {}) {
|
|
|
2358
2748
|
value: { root, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
|
|
2359
2749
|
};
|
|
2360
2750
|
}
|
|
2751
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
2752
|
+
if (!initialized.ok) return initialized;
|
|
2361
2753
|
const parent = dirname(root);
|
|
2362
2754
|
await mkdir(parent, { recursive: true });
|
|
2363
2755
|
let staging = await mkdtemp(
|
|
2364
2756
|
resolve(parent, `.${basename(root)}.forgeax-staging-`)
|
|
2365
2757
|
);
|
|
2366
2758
|
const committedNames = [];
|
|
2759
|
+
let committed = false;
|
|
2367
2760
|
try {
|
|
2368
2761
|
for (const name of await readdir(template)) {
|
|
2369
2762
|
await cp(resolve(template, name), resolve(staging, name), {
|
|
@@ -2374,14 +2767,12 @@ async function newCommand(options = {}) {
|
|
|
2374
2767
|
}
|
|
2375
2768
|
await copySdkSkills(sdk, staging);
|
|
2376
2769
|
await installProjectSkills(staging, sdk.manifest);
|
|
2377
|
-
await
|
|
2378
|
-
|
|
2379
|
-
env: { ...process.env, CI: "true" },
|
|
2380
|
-
maxBuffer: 16 * 1024 * 1024
|
|
2381
|
-
});
|
|
2770
|
+
const store = sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
2771
|
+
await configureSdkStore(staging, store);
|
|
2382
2772
|
if (!targetExists) {
|
|
2383
2773
|
await rename(staging, root);
|
|
2384
2774
|
staging = void 0;
|
|
2775
|
+
committedNames.push(...await readdir(root));
|
|
2385
2776
|
} else {
|
|
2386
2777
|
for (const name of await readdir(staging)) {
|
|
2387
2778
|
await rename(resolve(staging, name), resolve(root, name));
|
|
@@ -2390,29 +2781,57 @@ async function newCommand(options = {}) {
|
|
|
2390
2781
|
await rm(staging, { recursive: true, force: true });
|
|
2391
2782
|
staging = void 0;
|
|
2392
2783
|
}
|
|
2784
|
+
committed = true;
|
|
2785
|
+
await execFileAsync2("pnpm", sdkProjectInstallArgs(store), {
|
|
2786
|
+
cwd: root,
|
|
2787
|
+
env: { ...process.env, CI: "true" },
|
|
2788
|
+
maxBuffer: 16 * 1024 * 1024
|
|
2789
|
+
});
|
|
2790
|
+
const sdkUpdate = await checkSdkUpdate(sdk.manifest.sdkVersion);
|
|
2393
2791
|
return {
|
|
2394
2792
|
ok: true,
|
|
2395
|
-
value: {
|
|
2793
|
+
value: {
|
|
2794
|
+
root,
|
|
2795
|
+
template: templateId,
|
|
2796
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
2797
|
+
onboarding: agentOnboarding(sdk, root),
|
|
2798
|
+
sdkUpdate
|
|
2799
|
+
}
|
|
2396
2800
|
};
|
|
2397
2801
|
} catch (cause) {
|
|
2398
2802
|
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2803
|
+
if (committed) {
|
|
2804
|
+
if (!targetExists) {
|
|
2805
|
+
await rm(root, { recursive: true, force: true });
|
|
2806
|
+
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2807
|
+
}
|
|
2808
|
+
const cleanupNames = /* @__PURE__ */ new Set([...committedNames, "node_modules"]);
|
|
2809
|
+
await Promise.all(
|
|
2810
|
+
[...cleanupNames].map(
|
|
2811
|
+
(name) => rm(resolve(root, name), { recursive: true, force: true })
|
|
2812
|
+
)
|
|
2813
|
+
);
|
|
2814
|
+
} else {
|
|
2815
|
+
await Promise.all(
|
|
2816
|
+
committedNames.map((name) => rm(resolve(root, name), { recursive: true, force: true }))
|
|
2817
|
+
);
|
|
2818
|
+
}
|
|
2402
2819
|
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2403
2820
|
}
|
|
2404
2821
|
} catch (cause) {
|
|
2405
2822
|
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2406
2823
|
}
|
|
2407
2824
|
}
|
|
2408
|
-
var
|
|
2825
|
+
var execFileAsync2;
|
|
2409
2826
|
var init_bootstrap_commands = __esm({
|
|
2410
2827
|
"src/bootstrap-commands.ts"() {
|
|
2411
2828
|
init_init();
|
|
2412
2829
|
init_project();
|
|
2413
2830
|
init_sdk();
|
|
2831
|
+
init_sdk_bootstrap();
|
|
2832
|
+
init_sdk_update();
|
|
2414
2833
|
init_skill_install();
|
|
2415
|
-
|
|
2834
|
+
execFileAsync2 = promisify(execFile);
|
|
2416
2835
|
}
|
|
2417
2836
|
});
|
|
2418
2837
|
|
|
@@ -2471,7 +2890,7 @@ function withoutEntry(entries2, id) {
|
|
|
2471
2890
|
}
|
|
2472
2891
|
async function mutateDependency(root, action, dependency) {
|
|
2473
2892
|
if (dependency === void 0) return;
|
|
2474
|
-
await
|
|
2893
|
+
await execFileAsync3("pnpm", [action, dependency], { cwd: root, maxBuffer: 16 * 1024 * 1024 });
|
|
2475
2894
|
}
|
|
2476
2895
|
async function pluginInstallCommand(options) {
|
|
2477
2896
|
const root = resolve(options.root ?? process.cwd());
|
|
@@ -2573,489 +2992,10 @@ async function pluginUninstallCommand(options) {
|
|
|
2573
2992
|
};
|
|
2574
2993
|
}
|
|
2575
2994
|
}
|
|
2576
|
-
var
|
|
2995
|
+
var execFileAsync3;
|
|
2577
2996
|
var init_plugin_authoring = __esm({
|
|
2578
2997
|
"src/plugin-authoring.ts"() {
|
|
2579
|
-
|
|
2580
|
-
}
|
|
2581
|
-
});
|
|
2582
|
-
var RhiError;
|
|
2583
|
-
var init_dist2 = __esm({
|
|
2584
|
-
"../rhi/dist/index.mjs"() {
|
|
2585
|
-
RhiError = class extends Error {
|
|
2586
|
-
code;
|
|
2587
|
-
expected;
|
|
2588
|
-
hint;
|
|
2589
|
-
detail;
|
|
2590
|
-
constructor(args) {
|
|
2591
|
-
super(`[RhiError ${args.code}] expected: ${args.expected}; hint: ${args.hint}`);
|
|
2592
|
-
this.name = "RhiError";
|
|
2593
|
-
this.code = args.code;
|
|
2594
|
-
this.expected = args.expected;
|
|
2595
|
-
this.hint = args.hint;
|
|
2596
|
-
this.detail = args.detail;
|
|
2597
|
-
}
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
});
|
|
2601
|
-
function readPassLabel(desc) {
|
|
2602
|
-
if (desc && typeof desc.label === "string" && desc.label.length > 0) {
|
|
2603
|
-
return desc.label;
|
|
2604
|
-
}
|
|
2605
|
-
return "<unnamed>";
|
|
2606
|
-
}
|
|
2607
|
-
function readRecord(handle) {
|
|
2608
|
-
if (handle === null || typeof handle !== "object") return void 0;
|
|
2609
|
-
const tagged = handle;
|
|
2610
|
-
return tagged[BOOKKEEPING_KEY];
|
|
2611
|
-
}
|
|
2612
|
-
function isHandleDestroyed(handle) {
|
|
2613
|
-
if (readRecord(handle)?.destroyed === true) {
|
|
2614
|
-
return err(
|
|
2615
|
-
new RhiError({
|
|
2616
|
-
code: "destroy-after-destroy",
|
|
2617
|
-
expected: "GPU buffer/texture handle has not been destroyed yet",
|
|
2618
|
-
hint: "object already destroyed; track lifecycle in caller or check isDestroyed before re-destroy"
|
|
2619
|
-
})
|
|
2620
|
-
);
|
|
2621
|
-
}
|
|
2622
|
-
return ok(void 0);
|
|
2623
|
-
}
|
|
2624
|
-
function acquireCanvasContext(_canvas) {
|
|
2625
|
-
return ok(new RhiNullCanvasContext());
|
|
2626
|
-
}
|
|
2627
|
-
function createShaderModule(_device, _desc) {
|
|
2628
|
-
return Promise.resolve(ok({}));
|
|
2629
|
-
}
|
|
2630
|
-
function requestAdapter(_opts, _compatibleSurface) {
|
|
2631
|
-
return Promise.resolve(ok(new RhiNullAdapter()));
|
|
2632
|
-
}
|
|
2633
|
-
var RhiNullRenderPassEncoder, RhiNullComputePassEncoder, DeviceCounter, RhiNullCommandEncoder, BOOKKEEPING_KEY, Bookkeeper, nextDeviceId, RhiNullDevice, EMPTY_FEATURES, EMPTY_LIMITS, NEVER, RhiNullQueue, RhiNullAdapter, RhiNullCanvasContext, rhi;
|
|
2634
|
-
var init_dist3 = __esm({
|
|
2635
|
-
"../rhi-null/dist/index.mjs"() {
|
|
2636
|
-
init_dist2();
|
|
2637
|
-
RhiNullRenderPassEncoder = class {
|
|
2638
|
-
/** Number of draw* calls issued on this pass (AC-06 readback). */
|
|
2639
|
-
drawCount = 0;
|
|
2640
|
-
bindGroupCount = 0;
|
|
2641
|
-
/** Most recent setVertexBuffer / setBindGroup ownership validation; ok unless
|
|
2642
|
-
* a foreign handle was passed (AC-09 readback). */
|
|
2643
|
-
lastValidation = ok(void 0);
|
|
2644
|
-
bookkeeper;
|
|
2645
|
-
counter;
|
|
2646
|
-
passName;
|
|
2647
|
-
constructor(bookkeeper, counter, passName) {
|
|
2648
|
-
this.bookkeeper = bookkeeper;
|
|
2649
|
-
this.counter = counter;
|
|
2650
|
-
this.passName = passName;
|
|
2651
|
-
}
|
|
2652
|
-
setPipeline(_pipeline) {
|
|
2653
|
-
}
|
|
2654
|
-
setVertexBuffer(_slot, buffer, _offset, _size) {
|
|
2655
|
-
this.lastValidation = this.bookkeeper.validateOwnership(buffer);
|
|
2656
|
-
}
|
|
2657
|
-
setIndexBuffer(_buffer, _format, _offset, _size) {
|
|
2658
|
-
}
|
|
2659
|
-
setBindGroup(_index, bindGroup, _dynamicOffsetsData, _dynamicOffsetsDataStart, _dynamicOffsetsDataLength) {
|
|
2660
|
-
this.bindGroupCount++;
|
|
2661
|
-
this.counter?.recordBindGroup();
|
|
2662
|
-
this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
|
|
2663
|
-
}
|
|
2664
|
-
draw(_vertexCount, _instanceCount, _firstVertex, _firstInstance) {
|
|
2665
|
-
this.drawCount++;
|
|
2666
|
-
this.counter?.recordDraw();
|
|
2667
|
-
}
|
|
2668
|
-
drawIndexed(_indexCount, _instanceCount, _firstIndex, _baseVertex, _firstInstance) {
|
|
2669
|
-
this.drawCount++;
|
|
2670
|
-
this.counter?.recordDraw();
|
|
2671
|
-
}
|
|
2672
|
-
end() {
|
|
2673
|
-
this.counter?.recordPassName(this.passName);
|
|
2674
|
-
}
|
|
2675
|
-
setViewport(_x, _y, _w, _h, _minDepth, _maxDepth) {
|
|
2676
|
-
}
|
|
2677
|
-
setScissorRect(_x, _y, _w, _h) {
|
|
2678
|
-
}
|
|
2679
|
-
setBlendConstant(_color) {
|
|
2680
|
-
}
|
|
2681
|
-
setStencilReference(_reference) {
|
|
2682
|
-
}
|
|
2683
|
-
drawIndirect(_indirectBuffer, _indirectOffset) {
|
|
2684
|
-
this.drawCount++;
|
|
2685
|
-
this.counter?.recordDraw();
|
|
2686
|
-
}
|
|
2687
|
-
drawIndexedIndirect(_indirectBuffer, _indirectOffset) {
|
|
2688
|
-
this.drawCount++;
|
|
2689
|
-
this.counter?.recordDraw();
|
|
2690
|
-
}
|
|
2691
|
-
pushDebugGroup(_groupLabel) {
|
|
2692
|
-
}
|
|
2693
|
-
popDebugGroup() {
|
|
2694
|
-
}
|
|
2695
|
-
insertDebugMarker(_markerLabel) {
|
|
2696
|
-
}
|
|
2697
|
-
executeBundles(_bundles) {
|
|
2698
|
-
return ok(void 0);
|
|
2699
|
-
}
|
|
2700
|
-
beginOcclusionQuery(_queryIndex) {
|
|
2701
|
-
return ok(void 0);
|
|
2702
|
-
}
|
|
2703
|
-
endOcclusionQuery() {
|
|
2704
|
-
return ok(void 0);
|
|
2705
|
-
}
|
|
2706
|
-
};
|
|
2707
|
-
RhiNullComputePassEncoder = class {
|
|
2708
|
-
/** Number of dispatchWorkgroups calls issued on this pass (readback). */
|
|
2709
|
-
dispatchCount = 0;
|
|
2710
|
-
/** Most recent setBindGroup ownership validation (AC-09 readback). */
|
|
2711
|
-
lastValidation = ok(void 0);
|
|
2712
|
-
bookkeeper;
|
|
2713
|
-
counter;
|
|
2714
|
-
passName;
|
|
2715
|
-
constructor(bookkeeper, counter, passName) {
|
|
2716
|
-
this.bookkeeper = bookkeeper;
|
|
2717
|
-
this.counter = counter;
|
|
2718
|
-
this.passName = passName;
|
|
2719
|
-
}
|
|
2720
|
-
setPipeline(_pipeline) {
|
|
2721
|
-
}
|
|
2722
|
-
setBindGroup(_index, bindGroup, _dynamicOffsets) {
|
|
2723
|
-
this.lastValidation = this.bookkeeper.validateOwnership(bindGroup);
|
|
2724
|
-
}
|
|
2725
|
-
dispatchWorkgroups(_x, _y, _z) {
|
|
2726
|
-
this.dispatchCount++;
|
|
2727
|
-
this.counter?.recordDispatch();
|
|
2728
|
-
}
|
|
2729
|
-
dispatchWorkgroupsIndirect(indirectBuffer, _indirectOffset) {
|
|
2730
|
-
this.lastValidation = this.bookkeeper.validateOwnership(indirectBuffer);
|
|
2731
|
-
this.dispatchCount++;
|
|
2732
|
-
this.counter?.recordDispatch();
|
|
2733
|
-
}
|
|
2734
|
-
end() {
|
|
2735
|
-
this.counter?.recordPassName(this.passName);
|
|
2736
|
-
}
|
|
2737
|
-
};
|
|
2738
|
-
DeviceCounter = class {
|
|
2739
|
-
constructor(device) {
|
|
2740
|
-
this.device = device;
|
|
2741
|
-
}
|
|
2742
|
-
device;
|
|
2743
|
-
recordDraw() {
|
|
2744
|
-
this.device.totalDrawCount++;
|
|
2745
|
-
}
|
|
2746
|
-
recordDispatch() {
|
|
2747
|
-
this.device.totalDispatchCount++;
|
|
2748
|
-
}
|
|
2749
|
-
recordBindGroup() {
|
|
2750
|
-
this.device.totalBindGroupCount++;
|
|
2751
|
-
}
|
|
2752
|
-
recordPassName(name) {
|
|
2753
|
-
this.device.framePassNames.push(name);
|
|
2754
|
-
}
|
|
2755
|
-
};
|
|
2756
|
-
RhiNullCommandEncoder = class {
|
|
2757
|
-
bookkeeper;
|
|
2758
|
-
counter;
|
|
2759
|
-
constructor(bookkeeper, device) {
|
|
2760
|
-
this.bookkeeper = bookkeeper;
|
|
2761
|
-
this.counter = new DeviceCounter(device);
|
|
2762
|
-
}
|
|
2763
|
-
beginRenderPass(desc) {
|
|
2764
|
-
const label = readPassLabel(desc);
|
|
2765
|
-
return new RhiNullRenderPassEncoder(this.bookkeeper, this.counter, label);
|
|
2766
|
-
}
|
|
2767
|
-
beginComputePass(desc) {
|
|
2768
|
-
const label = readPassLabel(desc);
|
|
2769
|
-
return new RhiNullComputePassEncoder(this.bookkeeper, this.counter, label);
|
|
2770
|
-
}
|
|
2771
|
-
copyBufferToBuffer(_source, _sourceOffsetOrDestination, _destinationOrSize, _destinationOffset, _size) {
|
|
2772
|
-
}
|
|
2773
|
-
copyBufferToTexture(_source, _destination, _copySize) {
|
|
2774
|
-
}
|
|
2775
|
-
copyTextureToBuffer(_source, _destination, _copySize) {
|
|
2776
|
-
}
|
|
2777
|
-
copyTextureToTexture(_source, _destination, _copySize) {
|
|
2778
|
-
}
|
|
2779
|
-
clearBuffer(_buffer, _offset, _size) {
|
|
2780
|
-
}
|
|
2781
|
-
resolveQuerySet(_querySet, _firstQuery, _queryCount, _destination, _destinationOffset) {
|
|
2782
|
-
return ok(void 0);
|
|
2783
|
-
}
|
|
2784
|
-
writeTimestamp(_querySet, _queryIndex) {
|
|
2785
|
-
}
|
|
2786
|
-
pushDebugGroup(_groupLabel) {
|
|
2787
|
-
}
|
|
2788
|
-
popDebugGroup() {
|
|
2789
|
-
}
|
|
2790
|
-
insertDebugMarker(_markerLabel) {
|
|
2791
|
-
}
|
|
2792
|
-
finish() {
|
|
2793
|
-
return ok(this.bookkeeper.register("CommandBuffer"));
|
|
2794
|
-
}
|
|
2795
|
-
};
|
|
2796
|
-
BOOKKEEPING_KEY = /* @__PURE__ */ Symbol("forgeax-rhi-null-bookkeeping");
|
|
2797
|
-
Bookkeeper = class {
|
|
2798
|
-
deviceId;
|
|
2799
|
-
nextHandleId = 0;
|
|
2800
|
-
records = /* @__PURE__ */ new Map();
|
|
2801
|
-
constructor(deviceId) {
|
|
2802
|
-
this.deviceId = deviceId;
|
|
2803
|
-
}
|
|
2804
|
-
/**
|
|
2805
|
-
* Register a freshly-minted handle of the given kind, returning a plain
|
|
2806
|
-
* object that carries its ledger row. The caller casts the return value to
|
|
2807
|
-
* the concrete brand (`as unknown as Buffer` etc.).
|
|
2808
|
-
*/
|
|
2809
|
-
register(kind) {
|
|
2810
|
-
const id = this.nextHandleId++;
|
|
2811
|
-
const record = {
|
|
2812
|
-
id,
|
|
2813
|
-
kind,
|
|
2814
|
-
destroyed: false,
|
|
2815
|
-
sourceDeviceId: this.deviceId
|
|
2816
|
-
};
|
|
2817
|
-
this.records.set(id, record);
|
|
2818
|
-
return { [BOOKKEEPING_KEY]: record };
|
|
2819
|
-
}
|
|
2820
|
-
/**
|
|
2821
|
-
* Mark a handle destroyed. Fail-fasts on a second destroy
|
|
2822
|
-
* ('destroy-after-destroy') or on a handle issued by a different device
|
|
2823
|
-
* ('rhi-not-available'); otherwise flips the destroyed flag and returns ok.
|
|
2824
|
-
*/
|
|
2825
|
-
destroy(handle) {
|
|
2826
|
-
const ownership = this.validateOwnership(handle);
|
|
2827
|
-
if (!ownership.ok) return ownership;
|
|
2828
|
-
const destroyedCheck = isHandleDestroyed(handle);
|
|
2829
|
-
if (!destroyedCheck.ok) return destroyedCheck;
|
|
2830
|
-
ownership.value.destroyed = true;
|
|
2831
|
-
return ok(void 0);
|
|
2832
|
-
}
|
|
2833
|
-
/**
|
|
2834
|
-
* Report whether a handle has already been destroyed (true once destroy()
|
|
2835
|
-
* has flipped its flag). Used by command-stream methods that must not
|
|
2836
|
-
* consume a stale handle.
|
|
2837
|
-
*/
|
|
2838
|
-
isDestroyed(handle) {
|
|
2839
|
-
return readRecord(handle)?.destroyed === true;
|
|
2840
|
-
}
|
|
2841
|
-
/**
|
|
2842
|
-
* Validate that a handle was issued by THIS device. Returns the ledger row on
|
|
2843
|
-
* success so callers can mutate it (e.g. flip destroyed); returns
|
|
2844
|
-
* 'rhi-not-available' for a foreign handle (AC-09 — no silent pass).
|
|
2845
|
-
*/
|
|
2846
|
-
validateOwnership(handle) {
|
|
2847
|
-
const record = readRecord(handle);
|
|
2848
|
-
if (record === void 0 || record.sourceDeviceId !== this.deviceId) {
|
|
2849
|
-
return err(
|
|
2850
|
-
new RhiError({
|
|
2851
|
-
code: "rhi-not-available",
|
|
2852
|
-
expected: "handle was issued by this RhiNull device",
|
|
2853
|
-
hint: "do not pass a handle created on a different RhiNull device into this device; create resources on the device they are used with"
|
|
2854
|
-
})
|
|
2855
|
-
);
|
|
2856
|
-
}
|
|
2857
|
-
return ok(record);
|
|
2858
|
-
}
|
|
2859
|
-
/**
|
|
2860
|
-
* Return all ledger rows as a readonly array. M3 unit tests (w17) read this
|
|
2861
|
-
* to assert create/destroy pairing, BGL/PSO shape counts, and resource
|
|
2862
|
-
* lifecycle coverage (AC-05/06/07). Returns a snapshot of the current Map
|
|
2863
|
-
* so callers can iterate without a stale reference after further mutations.
|
|
2864
|
-
*/
|
|
2865
|
-
allRecords() {
|
|
2866
|
-
return [...this.records.values()];
|
|
2867
|
-
}
|
|
2868
|
-
/** Report the total number of records in the ledger. */
|
|
2869
|
-
recordCount() {
|
|
2870
|
-
return this.records.size;
|
|
2871
|
-
}
|
|
2872
|
-
};
|
|
2873
|
-
nextDeviceId = 0;
|
|
2874
|
-
RhiNullDevice = class {
|
|
2875
|
-
internalBookkeeper;
|
|
2876
|
-
nullQueue;
|
|
2877
|
-
encoderFactory;
|
|
2878
|
-
/** Per-frame total draw count across all pass encoders executed this frame
|
|
2879
|
-
* (aggregated by the command encoder on finish, then reset). M3 unit tests
|
|
2880
|
-
* (w17) read this to assert draw count >= 1 (AC-06). */
|
|
2881
|
-
totalDrawCount = 0;
|
|
2882
|
-
/** Per-frame total direct and indirect compute dispatch count. */
|
|
2883
|
-
totalDispatchCount = 0;
|
|
2884
|
-
/** Per-frame total bind group set count (AC-06 / AC-05 readback). */
|
|
2885
|
-
totalBindGroupCount = 0;
|
|
2886
|
-
/** Per-frame pass names executed this frame, in schedule order (AC-04). */
|
|
2887
|
-
framePassNames = [];
|
|
2888
|
-
/** The per-device handle ledger — exposed so M3 tests can assert create/destroy
|
|
2889
|
-
* pairing and BGL/PSO shape counts (AC-05/06/07). */
|
|
2890
|
-
get bookkeeper() {
|
|
2891
|
-
return this.internalBookkeeper;
|
|
2892
|
-
}
|
|
2893
|
-
constructor(queue, encoderFactory) {
|
|
2894
|
-
this.internalBookkeeper = new Bookkeeper(nextDeviceId++);
|
|
2895
|
-
this.nullQueue = queue;
|
|
2896
|
-
this.encoderFactory = encoderFactory;
|
|
2897
|
-
}
|
|
2898
|
-
get caps() {
|
|
2899
|
-
return {
|
|
2900
|
-
backendKind: "null",
|
|
2901
|
-
compute: true,
|
|
2902
|
-
timestampQuery: false,
|
|
2903
|
-
timestampPeriodNanoseconds: null,
|
|
2904
|
-
indirectDrawing: true,
|
|
2905
|
-
textureCompressionBc: false,
|
|
2906
|
-
textureCompressionEtc2: false,
|
|
2907
|
-
textureCompressionAstc: false,
|
|
2908
|
-
// 3 wgpu-native-only reserved flags stay false on non-native backends
|
|
2909
|
-
// (D-5); the headless backend is not a native runtime.
|
|
2910
|
-
multiDrawIndirect: false,
|
|
2911
|
-
pushConstants: false,
|
|
2912
|
-
textureBindingArray: false,
|
|
2913
|
-
samplerAliasing: true,
|
|
2914
|
-
firstInstanceIndirect: true,
|
|
2915
|
-
storageBuffer: true,
|
|
2916
|
-
storageTexture: true,
|
|
2917
|
-
rgba16floatRenderable: true,
|
|
2918
|
-
rg11b10ufloatRenderable: true,
|
|
2919
|
-
float32Filterable: true,
|
|
2920
|
-
maxColorAttachments: 8
|
|
2921
|
-
};
|
|
2922
|
-
}
|
|
2923
|
-
get features() {
|
|
2924
|
-
return EMPTY_FEATURES;
|
|
2925
|
-
}
|
|
2926
|
-
get limits() {
|
|
2927
|
-
return EMPTY_LIMITS;
|
|
2928
|
-
}
|
|
2929
|
-
get queue() {
|
|
2930
|
-
return this.nullQueue;
|
|
2931
|
-
}
|
|
2932
|
-
// forgeax-async-whitelist: dom-native — spec `GPUDevice.lost` Promise
|
|
2933
|
-
// passthrough. The headless backend never loses a device (no GPU), so the
|
|
2934
|
-
// Promise stays unsettled for the lifetime of the device, mirroring a live
|
|
2935
|
-
// device that never transitions to the lost state.
|
|
2936
|
-
get lost() {
|
|
2937
|
-
return NEVER;
|
|
2938
|
-
}
|
|
2939
|
-
createBuffer(_desc) {
|
|
2940
|
-
return ok(this.internalBookkeeper.register("Buffer"));
|
|
2941
|
-
}
|
|
2942
|
-
createTexture(_desc) {
|
|
2943
|
-
return ok(this.internalBookkeeper.register("Texture"));
|
|
2944
|
-
}
|
|
2945
|
-
destroyBuffer(buf) {
|
|
2946
|
-
return this.internalBookkeeper.destroy(buf);
|
|
2947
|
-
}
|
|
2948
|
-
destroyQuerySet(querySet) {
|
|
2949
|
-
return this.internalBookkeeper.destroy(querySet);
|
|
2950
|
-
}
|
|
2951
|
-
destroyTexture(tex) {
|
|
2952
|
-
return this.internalBookkeeper.destroy(tex);
|
|
2953
|
-
}
|
|
2954
|
-
createTextureView(_texture, _desc) {
|
|
2955
|
-
return ok(this.internalBookkeeper.register("TextureView"));
|
|
2956
|
-
}
|
|
2957
|
-
createSampler(_desc) {
|
|
2958
|
-
return ok(this.internalBookkeeper.register("Sampler"));
|
|
2959
|
-
}
|
|
2960
|
-
createBindGroupLayout(_desc) {
|
|
2961
|
-
return ok(this.internalBookkeeper.register("BindGroupLayout"));
|
|
2962
|
-
}
|
|
2963
|
-
createBindGroup(_desc) {
|
|
2964
|
-
return ok(this.internalBookkeeper.register("BindGroup"));
|
|
2965
|
-
}
|
|
2966
|
-
createPipelineLayout(_desc) {
|
|
2967
|
-
return ok(this.internalBookkeeper.register("PipelineLayout"));
|
|
2968
|
-
}
|
|
2969
|
-
createRenderPipeline(_desc) {
|
|
2970
|
-
return ok(this.makePipeline("RenderPipeline"));
|
|
2971
|
-
}
|
|
2972
|
-
createComputePipeline(_desc) {
|
|
2973
|
-
return ok(this.makePipeline("ComputePipeline"));
|
|
2974
|
-
}
|
|
2975
|
-
createQuerySet(desc) {
|
|
2976
|
-
if (desc.type === "timestamp") {
|
|
2977
|
-
return err(
|
|
2978
|
-
new RhiError({
|
|
2979
|
-
code: "feature-not-enabled",
|
|
2980
|
-
expected: "caps.timestampQuery === true (timestamp-query feature)",
|
|
2981
|
-
hint: "RhiNull is structural-only and cannot produce GPU timestamp ticks"
|
|
2982
|
-
})
|
|
2983
|
-
);
|
|
2984
|
-
}
|
|
2985
|
-
return ok(this.internalBookkeeper.register("QuerySet"));
|
|
2986
|
-
}
|
|
2987
|
-
createCommandEncoder(_desc) {
|
|
2988
|
-
return ok(this.encoderFactory(this.internalBookkeeper, this));
|
|
2989
|
-
}
|
|
2990
|
-
/**
|
|
2991
|
-
* Mint a pipeline handle whose object also carries the no-op
|
|
2992
|
-
* `getBindGroupLayout(index)` ops method (D-2). The prod auto-layout path
|
|
2993
|
-
* (debug-draw.ts) and the existing mock unit tests both call
|
|
2994
|
-
* `pipeline.getBindGroupLayout(n)`; returning a legal BindGroupLayout brand
|
|
2995
|
-
* (recorded in the ledger) keeps those consumers from crashing on a missing
|
|
2996
|
-
* method.
|
|
2997
|
-
*/
|
|
2998
|
-
makePipeline(kind) {
|
|
2999
|
-
const handle = this.internalBookkeeper.register(kind);
|
|
3000
|
-
const getBindGroupLayout = (_index) => this.internalBookkeeper.register("BindGroupLayout");
|
|
3001
|
-
return Object.assign(handle, { getBindGroupLayout });
|
|
3002
|
-
}
|
|
3003
|
-
};
|
|
3004
|
-
EMPTY_FEATURES = /* @__PURE__ */ new Set();
|
|
3005
|
-
EMPTY_LIMITS = {};
|
|
3006
|
-
NEVER = new Promise(() => {
|
|
3007
|
-
});
|
|
3008
|
-
RhiNullQueue = class {
|
|
3009
|
-
writeBuffer(_buffer, _bufferOffset, _data, _dataOffset, _size) {
|
|
3010
|
-
return ok(void 0);
|
|
3011
|
-
}
|
|
3012
|
-
writeTexture(_destination, _data, _dataLayout, _size) {
|
|
3013
|
-
return ok(void 0);
|
|
3014
|
-
}
|
|
3015
|
-
copyExternalImageToTexture(_source, _destination, _copySize) {
|
|
3016
|
-
return ok(void 0);
|
|
3017
|
-
}
|
|
3018
|
-
submit(_commandBuffers) {
|
|
3019
|
-
return ok(void 0);
|
|
3020
|
-
}
|
|
3021
|
-
// forgeax-async-whitelist: dom-native — spec `GPUQueue.onSubmittedWorkDone`
|
|
3022
|
-
// never rejects. The headless backend has no pending GPU work, so it resolves
|
|
3023
|
-
// immediately (AC-12: read-back idioms must not hang).
|
|
3024
|
-
onSubmittedWorkDone() {
|
|
3025
|
-
return Promise.resolve(void 0);
|
|
3026
|
-
}
|
|
3027
|
-
};
|
|
3028
|
-
RhiNullAdapter = class {
|
|
3029
|
-
features = /* @__PURE__ */ new Set();
|
|
3030
|
-
limits = {};
|
|
3031
|
-
// forgeax-async-whitelist is not needed: this returns Promise<Result<...>>
|
|
3032
|
-
// per the spec contract; never rejects.
|
|
3033
|
-
requestDevice(_opts) {
|
|
3034
|
-
const device = new RhiNullDevice(
|
|
3035
|
-
new RhiNullQueue(),
|
|
3036
|
-
(bookkeeper, dev) => new RhiNullCommandEncoder(bookkeeper, dev)
|
|
3037
|
-
);
|
|
3038
|
-
return Promise.resolve(ok(device));
|
|
3039
|
-
}
|
|
3040
|
-
};
|
|
3041
|
-
RhiNullCanvasContext = class {
|
|
3042
|
-
configure(_desc) {
|
|
3043
|
-
return ok(void 0);
|
|
3044
|
-
}
|
|
3045
|
-
unconfigure() {
|
|
3046
|
-
}
|
|
3047
|
-
getConfiguration() {
|
|
3048
|
-
return void 0;
|
|
3049
|
-
}
|
|
3050
|
-
getCurrentTexture() {
|
|
3051
|
-
return ok({});
|
|
3052
|
-
}
|
|
3053
|
-
};
|
|
3054
|
-
rhi = {
|
|
3055
|
-
requestAdapter,
|
|
3056
|
-
acquireCanvasContext,
|
|
3057
|
-
createShaderModule
|
|
3058
|
-
};
|
|
2998
|
+
execFileAsync3 = promisify(execFile);
|
|
3059
2999
|
}
|
|
3060
3000
|
});
|
|
3061
3001
|
function createCliRhiDebugOperationContext() {
|
|
@@ -3063,7 +3003,7 @@ function createCliRhiDebugOperationContext() {
|
|
|
3063
3003
|
captureFrame: async () => err(
|
|
3064
3004
|
createRhiDebugError("capture-unavailable", {
|
|
3065
3005
|
stage: "capture",
|
|
3066
|
-
cause: "the standalone CLI has no live App capture provider"
|
|
3006
|
+
cause: "the standalone CLI has no live App capture provider; start a recorder-enabled live host and invoke its rhiCapture root"
|
|
3067
3007
|
})
|
|
3068
3008
|
),
|
|
3069
3009
|
async readArtifact(artifact) {
|
|
@@ -3126,7 +3066,6 @@ function createCliRhiDebugOperationContext() {
|
|
|
3126
3066
|
}
|
|
3127
3067
|
var init_cli_context = __esm({
|
|
3128
3068
|
"src/rhi-debug/cli-context.ts"() {
|
|
3129
|
-
init_dist3();
|
|
3130
3069
|
}
|
|
3131
3070
|
});
|
|
3132
3071
|
function createRhiDebugOperationContext(host) {
|
|
@@ -3431,7 +3370,7 @@ async function sdkInstallCommand(options) {
|
|
|
3431
3370
|
try {
|
|
3432
3371
|
staging = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-staging-`));
|
|
3433
3372
|
download = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-download-`));
|
|
3434
|
-
await
|
|
3373
|
+
await execFileAsync4(
|
|
3435
3374
|
process.env.FORGEAX_NPM_CLIENT ?? "npm",
|
|
3436
3375
|
[
|
|
3437
3376
|
"install",
|
|
@@ -3464,7 +3403,15 @@ async function sdkInstallCommand(options) {
|
|
|
3464
3403
|
await rename(resolve(staging, name), resolve(root, name));
|
|
3465
3404
|
}
|
|
3466
3405
|
}
|
|
3467
|
-
return {
|
|
3406
|
+
return {
|
|
3407
|
+
ok: true,
|
|
3408
|
+
value: {
|
|
3409
|
+
root,
|
|
3410
|
+
sdkVersion: version,
|
|
3411
|
+
source: "@forgeax/engine-sdk",
|
|
3412
|
+
next: { cwd: root, argv: ["node", "./bin/forgeax.mjs", "init"] }
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3468
3415
|
} catch (cause) {
|
|
3469
3416
|
return { ok: false, error: commandError(cause, "sdk-install-failed") };
|
|
3470
3417
|
} finally {
|
|
@@ -3472,11 +3419,11 @@ async function sdkInstallCommand(options) {
|
|
|
3472
3419
|
if (download !== void 0) await rm(download, { recursive: true, force: true });
|
|
3473
3420
|
}
|
|
3474
3421
|
}
|
|
3475
|
-
var
|
|
3422
|
+
var execFileAsync4;
|
|
3476
3423
|
var init_sdk_install = __esm({
|
|
3477
3424
|
"src/sdk-install.ts"() {
|
|
3478
3425
|
init_project();
|
|
3479
|
-
|
|
3426
|
+
execFileAsync4 = promisify(execFile);
|
|
3480
3427
|
}
|
|
3481
3428
|
});
|
|
3482
3429
|
async function shaderCheckCommand(options = {}) {
|
|
@@ -3547,6 +3494,21 @@ __export(commands_exports, {
|
|
|
3547
3494
|
function runRhiDebugCommand(name, input, context) {
|
|
3548
3495
|
return runRhiDebugOperation(name, input, context);
|
|
3549
3496
|
}
|
|
3497
|
+
async function materializeViteDevPort(port) {
|
|
3498
|
+
if (port !== 0) return port;
|
|
3499
|
+
const reservation = createServer$1();
|
|
3500
|
+
await new Promise((resolveListen, rejectListen) => {
|
|
3501
|
+
reservation.once("error", rejectListen);
|
|
3502
|
+
reservation.listen(0, "127.0.0.1", resolveListen);
|
|
3503
|
+
});
|
|
3504
|
+
const address = reservation.address();
|
|
3505
|
+
const assigned = typeof address === "object" && address !== null ? address.port : 0;
|
|
3506
|
+
await new Promise((resolveClose, rejectClose) => {
|
|
3507
|
+
reservation.close((error) => error === void 0 ? resolveClose() : rejectClose(error));
|
|
3508
|
+
});
|
|
3509
|
+
if (assigned === 0) throw new Error("OS did not assign an ephemeral preview port");
|
|
3510
|
+
return assigned;
|
|
3511
|
+
}
|
|
3550
3512
|
async function buildCommand(options = {}) {
|
|
3551
3513
|
const facts = await readProjectFacts(options.root);
|
|
3552
3514
|
if (!facts.ok) return facts;
|
|
@@ -3649,13 +3611,38 @@ async function devCommand(options = {}) {
|
|
|
3649
3611
|
const facts = await readProjectFacts(options.root);
|
|
3650
3612
|
if (!facts.ok) return facts;
|
|
3651
3613
|
const previous = process.cwd();
|
|
3614
|
+
let server;
|
|
3652
3615
|
try {
|
|
3616
|
+
const { createServer: createServer5 } = await import('vite');
|
|
3617
|
+
const { createViteConfig: createViteConfig2 } = await Promise.resolve().then(() => (init_host(), host_exports));
|
|
3618
|
+
const port = resolveProjectPort(options.port);
|
|
3619
|
+
const vitePort = { ...port, port: await materializeViteDevPort(port.port) };
|
|
3653
3620
|
process.chdir(facts.value.root);
|
|
3654
|
-
|
|
3655
|
-
|
|
3621
|
+
server = await createServer5(
|
|
3622
|
+
await createViteConfig2(facts.value, "serve", "/", {
|
|
3623
|
+
server: vitePort
|
|
3624
|
+
})
|
|
3625
|
+
);
|
|
3626
|
+
await server.listen(vitePort.port);
|
|
3656
3627
|
if (options.json !== true) server.printUrls();
|
|
3657
|
-
return {
|
|
3628
|
+
return {
|
|
3629
|
+
ok: true,
|
|
3630
|
+
value: {
|
|
3631
|
+
root: facts.value.root,
|
|
3632
|
+
urls: server.resolvedUrls,
|
|
3633
|
+
mode: "dev",
|
|
3634
|
+
serves: "source",
|
|
3635
|
+
capabilities: {
|
|
3636
|
+
"rhi.capture": {
|
|
3637
|
+
available: false,
|
|
3638
|
+
realm: "host",
|
|
3639
|
+
reason: "standalone-dev-server-has-no-live-app-cli-attachment"
|
|
3640
|
+
}
|
|
3641
|
+
}
|
|
3642
|
+
}
|
|
3643
|
+
};
|
|
3658
3644
|
} catch (cause) {
|
|
3645
|
+
await server?.close();
|
|
3659
3646
|
process.chdir(previous);
|
|
3660
3647
|
return { ok: false, error: commandError(cause, "dev-server-failed") };
|
|
3661
3648
|
}
|
|
@@ -3665,15 +3652,31 @@ async function previewCommand(options = {}) {
|
|
|
3665
3652
|
const verified = await verifyDist(resolve(root, "dist"));
|
|
3666
3653
|
if (!verified.ok) return verified;
|
|
3667
3654
|
try {
|
|
3655
|
+
const port = resolveProjectPort(options.port);
|
|
3668
3656
|
const server = await preview({
|
|
3669
3657
|
root,
|
|
3670
3658
|
configFile: false,
|
|
3671
3659
|
base: verified.value.base,
|
|
3672
|
-
preview: { open: false, port
|
|
3660
|
+
preview: { open: false, ...port },
|
|
3673
3661
|
build: { outDir: resolve(root, "dist") }
|
|
3674
3662
|
});
|
|
3675
3663
|
if (options.json !== true) server.printUrls();
|
|
3676
|
-
return {
|
|
3664
|
+
return {
|
|
3665
|
+
ok: true,
|
|
3666
|
+
value: {
|
|
3667
|
+
root,
|
|
3668
|
+
urls: server.resolvedUrls,
|
|
3669
|
+
mode: "preview",
|
|
3670
|
+
serves: "dist",
|
|
3671
|
+
capabilities: {
|
|
3672
|
+
"rhi.capture": {
|
|
3673
|
+
available: false,
|
|
3674
|
+
realm: "host",
|
|
3675
|
+
reason: "static-dist-host-does-not-install-dev-capture"
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
};
|
|
3677
3680
|
} catch (cause) {
|
|
3678
3681
|
return { ok: false, error: commandError(cause, "preview-server-failed") };
|
|
3679
3682
|
}
|
|
@@ -3723,6 +3726,7 @@ var init_commands = __esm({
|
|
|
3723
3726
|
init_dist();
|
|
3724
3727
|
init_host();
|
|
3725
3728
|
init_project();
|
|
3729
|
+
init_types();
|
|
3726
3730
|
init_assets();
|
|
3727
3731
|
init_bootstrap_commands();
|
|
3728
3732
|
init_plugin_authoring();
|
|
@@ -4117,7 +4121,7 @@ function hasStaticToolDeclarations(entry) {
|
|
|
4117
4121
|
return Array.isArray(Reflect.get(entry.config, "tools"));
|
|
4118
4122
|
}
|
|
4119
4123
|
async function createViteModuleLoader(root) {
|
|
4120
|
-
const server = await createServer({
|
|
4124
|
+
const server = await createServer$2({
|
|
4121
4125
|
root,
|
|
4122
4126
|
appType: "custom",
|
|
4123
4127
|
configFile: false,
|
|
@@ -4319,7 +4323,7 @@ __export(contributions_exports, {
|
|
|
4319
4323
|
createBuildContribution: () => createBuildContribution,
|
|
4320
4324
|
createDefaultContributions: () => createDefaultContributions
|
|
4321
4325
|
});
|
|
4322
|
-
function
|
|
4326
|
+
function commandFailure2(error) {
|
|
4323
4327
|
return { ok: false, error: { ...error, detail: error.detail } };
|
|
4324
4328
|
}
|
|
4325
4329
|
function createBuildContribution(projectRoot = process.cwd()) {
|
|
@@ -4328,7 +4332,7 @@ function createBuildContribution(projectRoot = process.cwd()) {
|
|
|
4328
4332
|
async (options) => {
|
|
4329
4333
|
const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
4330
4334
|
const result = await buildCommand2({ ...options, root: options.root ?? projectRoot });
|
|
4331
|
-
return result.ok ? result.value :
|
|
4335
|
+
return result.ok ? result.value : commandFailure2(result.error);
|
|
4332
4336
|
}
|
|
4333
4337
|
);
|
|
4334
4338
|
}
|
|
@@ -4338,7 +4342,7 @@ function createAuthorContribution(projectRoot = process.cwd()) {
|
|
|
4338
4342
|
async (options) => {
|
|
4339
4343
|
const { pluginInstallCommand: pluginInstallCommand2 } = await Promise.resolve().then(() => (init_plugin_authoring(), plugin_authoring_exports));
|
|
4340
4344
|
const result = await pluginInstallCommand2({ ...options, root: options.root ?? projectRoot });
|
|
4341
|
-
return result.ok ? result.value :
|
|
4345
|
+
return result.ok ? result.value : commandFailure2(result.error);
|
|
4342
4346
|
}
|
|
4343
4347
|
);
|
|
4344
4348
|
}
|
|
@@ -4499,9 +4503,11 @@ function createAdmissionReport(recipe, samples, thresholds = DEFAULT_ADMISSION_T
|
|
|
4499
4503
|
async function runBenchmarkAdmission(options) {
|
|
4500
4504
|
const sampleCount = options.thresholds?.sampleCountPerPhase ?? 30;
|
|
4501
4505
|
const samples = [];
|
|
4502
|
-
|
|
4506
|
+
const phases = ["cold", "warm"];
|
|
4507
|
+
const modes = ["private", "service"];
|
|
4508
|
+
for (const phase of phases) {
|
|
4503
4509
|
for (let index = 0; index < sampleCount; index += 1) {
|
|
4504
|
-
for (const mode of
|
|
4510
|
+
for (const mode of modes) {
|
|
4505
4511
|
const measurement = await options.measure(mode, phase, index);
|
|
4506
4512
|
samples.push({
|
|
4507
4513
|
mode,
|
|
@@ -4608,7 +4614,7 @@ function reply(response, status, payload) {
|
|
|
4608
4614
|
async function createCarrierProviderService(options) {
|
|
4609
4615
|
const host = options.host ?? "127.0.0.1";
|
|
4610
4616
|
const bearerToken = options.machine.offer.bearerToken;
|
|
4611
|
-
const server = createServer
|
|
4617
|
+
const server = createServer(async (request, response) => {
|
|
4612
4618
|
if (request.method !== "POST" || !request.url?.startsWith("/carrier/")) {
|
|
4613
4619
|
reply(
|
|
4614
4620
|
response,
|
|
@@ -4727,14 +4733,14 @@ async function createCarrierProviderService(options) {
|
|
|
4727
4733
|
);
|
|
4728
4734
|
}
|
|
4729
4735
|
});
|
|
4730
|
-
await new Promise((
|
|
4736
|
+
await new Promise((resolve16, reject) => {
|
|
4731
4737
|
const onError = (error) => {
|
|
4732
4738
|
server.off("listening", onListening);
|
|
4733
4739
|
reject(error);
|
|
4734
4740
|
};
|
|
4735
4741
|
const onListening = () => {
|
|
4736
4742
|
server.off("error", onError);
|
|
4737
|
-
|
|
4743
|
+
resolve16();
|
|
4738
4744
|
};
|
|
4739
4745
|
server.once("error", onError);
|
|
4740
4746
|
server.once("listening", onListening);
|
|
@@ -4752,7 +4758,7 @@ async function createCarrierProviderService(options) {
|
|
|
4752
4758
|
if (closed) return;
|
|
4753
4759
|
closed = true;
|
|
4754
4760
|
await new Promise(
|
|
4755
|
-
(
|
|
4761
|
+
(resolve16, reject) => server.close((error) => error === void 0 ? resolve16() : reject(error))
|
|
4756
4762
|
);
|
|
4757
4763
|
}
|
|
4758
4764
|
};
|
|
@@ -5044,16 +5050,16 @@ function projectUri(projectRoot, path) {
|
|
|
5044
5050
|
return relative(projectRoot, path).split(sep).join("/");
|
|
5045
5051
|
}
|
|
5046
5052
|
async function allocateLoopbackPort() {
|
|
5047
|
-
const probe = createServer$
|
|
5053
|
+
const probe = createServer$1();
|
|
5048
5054
|
try {
|
|
5049
|
-
await new Promise((
|
|
5055
|
+
await new Promise((resolve16, reject) => {
|
|
5050
5056
|
const onError = (error) => {
|
|
5051
5057
|
probe.off("listening", onListening);
|
|
5052
5058
|
reject(error);
|
|
5053
5059
|
};
|
|
5054
5060
|
const onListening = () => {
|
|
5055
5061
|
probe.off("error", onError);
|
|
5056
|
-
|
|
5062
|
+
resolve16();
|
|
5057
5063
|
};
|
|
5058
5064
|
probe.once("error", onError);
|
|
5059
5065
|
probe.once("listening", onListening);
|
|
@@ -5066,7 +5072,7 @@ async function allocateLoopbackPort() {
|
|
|
5066
5072
|
return address.port;
|
|
5067
5073
|
} finally {
|
|
5068
5074
|
if (probe.listening) {
|
|
5069
|
-
await new Promise((
|
|
5075
|
+
await new Promise((resolve16) => probe.close(() => resolve16()));
|
|
5070
5076
|
}
|
|
5071
5077
|
}
|
|
5072
5078
|
}
|
|
@@ -5223,7 +5229,7 @@ async function runBrowserPreviewHost(projectRoot, recipe, snapshot, runId, signa
|
|
|
5223
5229
|
let server;
|
|
5224
5230
|
try {
|
|
5225
5231
|
const port = await allocateLoopbackPort();
|
|
5226
|
-
server = await createServer({
|
|
5232
|
+
server = await createServer$2({
|
|
5227
5233
|
...config,
|
|
5228
5234
|
cacheDir,
|
|
5229
5235
|
logLevel: "silent",
|
|
@@ -5345,9 +5351,7 @@ async function runBrowserPreviewHost(projectRoot, recipe, snapshot, runId, signa
|
|
|
5345
5351
|
);
|
|
5346
5352
|
});
|
|
5347
5353
|
await Promise.all(responseDiagnostics);
|
|
5348
|
-
if (!captured.ok)
|
|
5349
|
-
return browserFailure("capture-run", captured.error, pageErrors);
|
|
5350
|
-
}
|
|
5354
|
+
if (!captured.ok) return browserFailure("capture-run", captured.error, pageErrors);
|
|
5351
5355
|
if (pageErrors.length > 0)
|
|
5352
5356
|
return browserFailure("capture-page-runtime", pageErrors[0], pageErrors);
|
|
5353
5357
|
const capturePng = await page.screenshot({ type: "png" });
|
|
@@ -5688,7 +5692,7 @@ function isAuthorized(request, bearerToken) {
|
|
|
5688
5692
|
async function createAuthenticatedLoopbackService(options) {
|
|
5689
5693
|
if (options.bearerToken.length < 8) throw new TypeError("service bearer token is too short");
|
|
5690
5694
|
const host = options.host ?? "127.0.0.1";
|
|
5691
|
-
const server = createServer
|
|
5695
|
+
const server = createServer(async (request, response) => {
|
|
5692
5696
|
if (request.method !== "POST" || request.url !== "/run") {
|
|
5693
5697
|
reply2(response, 404, { error: "service route not found" });
|
|
5694
5698
|
return;
|
|
@@ -5713,14 +5717,14 @@ async function createAuthenticatedLoopbackService(options) {
|
|
|
5713
5717
|
reply2(response, 400, { error: message });
|
|
5714
5718
|
}
|
|
5715
5719
|
});
|
|
5716
|
-
await new Promise((
|
|
5720
|
+
await new Promise((resolve16, reject) => {
|
|
5717
5721
|
const onError = (error) => {
|
|
5718
5722
|
server.off("listening", onListening);
|
|
5719
5723
|
reject(error);
|
|
5720
5724
|
};
|
|
5721
5725
|
const onListening = () => {
|
|
5722
5726
|
server.off("error", onError);
|
|
5723
|
-
|
|
5727
|
+
resolve16();
|
|
5724
5728
|
};
|
|
5725
5729
|
server.once("error", onError);
|
|
5726
5730
|
server.once("listening", onListening);
|
|
@@ -5728,7 +5732,7 @@ async function createAuthenticatedLoopbackService(options) {
|
|
|
5728
5732
|
});
|
|
5729
5733
|
const address = server.address();
|
|
5730
5734
|
if (address === null || typeof address === "string") {
|
|
5731
|
-
await new Promise((
|
|
5735
|
+
await new Promise((resolve16) => server.close(() => resolve16()));
|
|
5732
5736
|
throw new Error("loopback service did not expose a TCP address");
|
|
5733
5737
|
}
|
|
5734
5738
|
const endpoint = `http://${host}:${address.port}/run`;
|
|
@@ -5743,14 +5747,17 @@ async function createAuthenticatedLoopbackService(options) {
|
|
|
5743
5747
|
async close() {
|
|
5744
5748
|
if (closed) return;
|
|
5745
5749
|
closed = true;
|
|
5746
|
-
await new Promise((
|
|
5747
|
-
server.close((error) => error === void 0 ?
|
|
5750
|
+
await new Promise((resolve16, reject) => {
|
|
5751
|
+
server.close((error) => error === void 0 ? resolve16() : reject(error));
|
|
5748
5752
|
});
|
|
5749
5753
|
transport.close();
|
|
5750
5754
|
}
|
|
5751
5755
|
};
|
|
5752
5756
|
}
|
|
5753
5757
|
|
|
5754
|
-
|
|
5758
|
+
// src/index.ts
|
|
5759
|
+
init_types();
|
|
5760
|
+
|
|
5761
|
+
export { DEFAULT_ADMISSION_THRESHOLDS, RHI_DEBUG_OPERATION_MANIFEST, analyzePreviewArtifacts, assetAddCommand, assetInspectCommand, assetListCommand, assetVerifyCommand, bootstrapRealm, buildCommand, createAdmissionReport, createAuthenticatedLoopbackService, createAuthorContribution, createBuildContribution, createCarrierProvider, createCarrierProviderService, createCarrierRendezvous, createCliRhiDebugOperationContext, createDefaultContributions, createDevkitToolRuntime, createDomainPreviewContributions, createInitPlan, createMigrationRoster, createOfflineAnalysisContribution, createPreviewContribution, createPreviewContributions, createPreviewToolRuntime, createResourceProbe, createRhiDebugOperationContext, createServiceCache, createServiceExecutor, createToolClient, describeTool, devCommand, discoverRhiDebugOperations, doctorCommand, initCommand, listTools, materializeToolCatalog, newCommand, packageCommand, pluginInstallCommand, pluginUninstallCommand, previewCommand, readProjectFacts, rebuildToolCatalog, recoverRhiDebugError, renderRhiDebugHelp, resolveMigration, resolveProjectPort, resolveRealmCapability, runBenchmarkAdmission, runCarrierPreviewRoute, runGenericTool, runLibraryTool, runNamedTool, runPreviewHost, runPrivateTool, runRhiDebugCommand, runRhiDebugOperation, sdkInstallCommand, shaderCheckCommand, skillInstallCommand, skillVerifyCommand, summarizeBenchmarkSamples, testCommand, verifyDist, writeDistManifest };
|
|
5755
5762
|
//# sourceMappingURL=index.mjs.map
|
|
5756
5763
|
//# sourceMappingURL=index.mjs.map
|