@forgeax/engine-devkit 0.1.3 → 0.1.4
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 +11 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/bootstrap-commands.d.ts.map +1 -1
- package/dist/cli.mjs +358 -84
- package/dist/cli.mjs.map +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/index.mjs +369 -95
- package/dist/index.mjs.map +1 -1
- package/dist/init.d.ts +2 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/sdk-bootstrap.d.ts +25 -0
- package/dist/sdk-bootstrap.d.ts.map +1 -0
- package/dist/sdk-cli.mjs +225 -45
- package/dist/sdk-cli.mjs.map +1 -1
- package/package.json +30 -30
package/dist/cli.mjs
CHANGED
|
@@ -11,7 +11,10 @@ import { fbxImporter } from '@forgeax/engine-fbx';
|
|
|
11
11
|
import { fontImporter } from '@forgeax/engine-font/font-importer';
|
|
12
12
|
import { gltfImporter } from '@forgeax/engine-gltf';
|
|
13
13
|
import { imageImporter } from '@forgeax/engine-image/image-importer';
|
|
14
|
+
import { BUILTIN_MESH_ASSETS } from '@forgeax/engine-pack/builtin';
|
|
15
|
+
import { scanInventory } from '@forgeax/engine-pack/scanner';
|
|
14
16
|
import { describeResourcePreviewFailure, validateCanonicalKitReceipt, createMaterialPreviewContribution, createMeshPreviewContribution, createVfxPreviewContribution, createTexturePreviewContribution, validatePreviewArtifactManifest } from '@forgeax/engine-preview';
|
|
17
|
+
import { createMaterialPackCooker } from '@forgeax/engine-shader-compiler';
|
|
15
18
|
import { createStandaloneRuntimeAssetBinding, ok, err } from '@forgeax/engine-types';
|
|
16
19
|
import { createParticleCodeNativeCookerFromRoots } from '@forgeax/engine-vfx-compiler';
|
|
17
20
|
import { pluginPack, reloadAssetHost } from '@forgeax/engine-vite-plugin-pack';
|
|
@@ -21,9 +24,9 @@ import { runCliGltf } from '@forgeax/engine-gltf/cli-gltf';
|
|
|
21
24
|
import { scanEntries } from '@forgeax/engine-pack/cli-asset';
|
|
22
25
|
import { AssetGuid } from '@forgeax/engine-pack/guid';
|
|
23
26
|
import { execFile } from 'child_process';
|
|
27
|
+
import { tmpdir } from 'os';
|
|
24
28
|
import { promisify } from 'util';
|
|
25
29
|
import { decodeTape, openReplay, buildFrameModel, createRhiDebugError } from '@forgeax/engine-rhi-debug';
|
|
26
|
-
import { tmpdir } from 'os';
|
|
27
30
|
import { build, createServer, preview } from 'vite';
|
|
28
31
|
import { startVitest } from 'vitest/node';
|
|
29
32
|
import materialPreviewPlugin from '@forgeax/engine-preview/material';
|
|
@@ -319,6 +322,42 @@ function isResourcePreviewIgnoredPath(path) {
|
|
|
319
322
|
function isProjectSourceIgnoredPath(path) {
|
|
320
323
|
return ignoreDevKitCatalogPath(path) || path.endsWith(".wgsl.meta.json");
|
|
321
324
|
}
|
|
325
|
+
async function prepareBuiltinPack(projectRoots, generated, ignorePath) {
|
|
326
|
+
const inventory = await scanInventory(projectRoots, { ignorePath });
|
|
327
|
+
if (!inventory.ok) return void 0;
|
|
328
|
+
const declared = /* @__PURE__ */ new Set();
|
|
329
|
+
for (const declaration of inventory.value.declarations.values()) {
|
|
330
|
+
if (declaration.format === "pack.json") {
|
|
331
|
+
for (const asset of declaration.value.assets) declared.add(asset.guid.toLowerCase());
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
for (const asset of declaration.value.subAssets) declared.add(asset.guid.toLowerCase());
|
|
335
|
+
}
|
|
336
|
+
const missing = BUILTIN_MESH_ASSETS.filter((asset) => !declared.has(asset.guid.toLowerCase()));
|
|
337
|
+
if (missing.length === 0) return void 0;
|
|
338
|
+
const packPath = resolve(generated, "engine-builtins.pack.json");
|
|
339
|
+
await writeFile(
|
|
340
|
+
packPath,
|
|
341
|
+
`${JSON.stringify(
|
|
342
|
+
{
|
|
343
|
+
schemaVersion: "2.0.0",
|
|
344
|
+
kind: "internal-text-package",
|
|
345
|
+
assets: missing.map((asset) => ({
|
|
346
|
+
guid: asset.guid,
|
|
347
|
+
kind: "mesh",
|
|
348
|
+
payload: { geometry: asset.geometry },
|
|
349
|
+
refs: [],
|
|
350
|
+
artifacts: {}
|
|
351
|
+
}))
|
|
352
|
+
},
|
|
353
|
+
null,
|
|
354
|
+
2
|
|
355
|
+
)}
|
|
356
|
+
`,
|
|
357
|
+
"utf8"
|
|
358
|
+
);
|
|
359
|
+
return packPath;
|
|
360
|
+
}
|
|
322
361
|
function findEngineWorkspaceRoot() {
|
|
323
362
|
let cursor = dirname(fileURLToPath(import.meta.url));
|
|
324
363
|
for (; ; ) {
|
|
@@ -334,6 +373,7 @@ async function engineWorkspacePackages() {
|
|
|
334
373
|
const workspaceRoot = findEngineWorkspaceRoot();
|
|
335
374
|
if (workspaceRoot === void 0) return /* @__PURE__ */ new Map();
|
|
336
375
|
const packageRoot = resolve(workspaceRoot, "packages");
|
|
376
|
+
if (!existsSync(packageRoot)) return /* @__PURE__ */ new Map();
|
|
337
377
|
const packages = /* @__PURE__ */ new Map();
|
|
338
378
|
for (const entry of await readdir(packageRoot, { withFileTypes: true })) {
|
|
339
379
|
if (!entry.isDirectory()) continue;
|
|
@@ -428,6 +468,18 @@ async function createEngineWorkspaceResolver() {
|
|
|
428
468
|
}
|
|
429
469
|
};
|
|
430
470
|
}
|
|
471
|
+
async function consumerEngineAliases(projectRoot2) {
|
|
472
|
+
const root = resolve(projectRoot2, "node_modules", ".pnpm", "node_modules", "@forgeax");
|
|
473
|
+
try {
|
|
474
|
+
const entries2 = await readdir(root, { withFileTypes: true });
|
|
475
|
+
return entries2.filter((entry) => entry.isDirectory() && entry.name.startsWith("engine-")).map((entry) => ({
|
|
476
|
+
find: `@forgeax/${entry.name}`,
|
|
477
|
+
replacement: resolve(root, entry.name)
|
|
478
|
+
})).sort((a, b) => a.find.localeCompare(b.find));
|
|
479
|
+
} catch {
|
|
480
|
+
return [];
|
|
481
|
+
}
|
|
482
|
+
}
|
|
431
483
|
async function resolveCanonicalKit() {
|
|
432
484
|
const packageJson = hostRequire.resolve("@forgeax/engine-preview/package.json");
|
|
433
485
|
const root = resolve(packageJson, "..", "assets/canonical-kit");
|
|
@@ -502,6 +554,7 @@ function hostSource(facts, bootstrapRoot, canonicalEnvironmentGuid) {
|
|
|
502
554
|
const plugins = bootstrapRoot === "resource-bootstrap" ? [] : [
|
|
503
555
|
`webAudioPlugin()`,
|
|
504
556
|
`audioPlugin()`,
|
|
557
|
+
`skinningPlugin()`,
|
|
505
558
|
...facts.physics === void 0 ? [] : [`physicsPlugin('${facts.physics === "2d" ? "rapier-2d" : "rapier-3d"}')`]
|
|
506
559
|
];
|
|
507
560
|
const bootstrapPlugin = facts.bootstrapEntry === void 0 ? "" : `const bootstrapModule = await import(${JSON.stringify(
|
|
@@ -519,20 +572,21 @@ await app.pluginContext.plugin({
|
|
|
519
572
|
},
|
|
520
573
|
});`;
|
|
521
574
|
return `import { forgeaxBundlerAdapter } from 'virtual:forgeax/bundler';
|
|
522
|
-
import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine
|
|
523
|
-
import { createCatalogSource } from '@forgeax/engine
|
|
524
|
-
import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine
|
|
525
|
-
import { audioPlugin } from '@forgeax/engine
|
|
526
|
-
import { webAudioPlugin } from '@forgeax/engine
|
|
527
|
-
import { physicsPlugin } from '@forgeax/engine
|
|
528
|
-
import {
|
|
529
|
-
import {
|
|
530
|
-
import {
|
|
531
|
-
import {
|
|
532
|
-
import {
|
|
533
|
-
import {
|
|
534
|
-
import {
|
|
535
|
-
import {
|
|
575
|
+
import { createApp, createToolPreviewHost, createToolPreviewRecipe, fitToolPreviewCameraToAabb, gameHostPlugin, replayToolPreviewCapture } from '@forgeax/engine/app';
|
|
576
|
+
import { createCatalogSource } from '@forgeax/engine/assets-runtime';
|
|
577
|
+
import { installCatalogLoader, projectPluginEntries } from '@forgeax/engine/plugin/loader';
|
|
578
|
+
import { audioPlugin } from '@forgeax/engine/audio';
|
|
579
|
+
import { webAudioPlugin } from '@forgeax/engine/audio-webaudio';
|
|
580
|
+
import { physicsPlugin } from '@forgeax/engine/physics';
|
|
581
|
+
import { skinningPlugin } from '@forgeax/engine/skinning';
|
|
582
|
+
import { createPrimitiveMesh } from '@forgeax/engine/geometry';
|
|
583
|
+
import { createDevImportTransport } from '@forgeax/engine/runtime';
|
|
584
|
+
import { mat4 } from '@forgeax/engine/math';
|
|
585
|
+
import { CAMERA_PROJECTION_ORTHOGRAPHIC, Camera, DirectionalLight, Materials, MeshFilter, MeshRenderer, Skylight, SkyboxBackground, TONEMAP_NONE, TONEMAP_REINHARD_EXTENDED } from '@forgeax/engine/render';
|
|
586
|
+
import { projectSceneAsset, Transform, worldInstantiateScene } from '@forgeax/engine/scene';
|
|
587
|
+
import { createStandaloneRuntimeAssetBinding } from '@forgeax/engine/types';
|
|
588
|
+
import { ParticleEffectPlayer, vfxGpuEffectContribution } from '@forgeax/engine/vfx';
|
|
589
|
+
import { createVfxRuntimeHost } from '@forgeax/engine/vfx-render';
|
|
536
590
|
|
|
537
591
|
const pluginCatalog = new Map([
|
|
538
592
|
${catalogModules(facts).map(
|
|
@@ -631,15 +685,16 @@ const resizeCanvas = () => {
|
|
|
631
685
|
const resizeObserver = new ResizeObserver(resizeCanvas);
|
|
632
686
|
resizeObserver.observe(canvas);
|
|
633
687
|
resizeCanvas();
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
:
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
:
|
|
688
|
+
const runtimeScopeBinding = createStandaloneRuntimeAssetBinding(${JSON.stringify(facts.id)});
|
|
689
|
+
const assetCatalog = createCatalogSource({
|
|
690
|
+
url: import.meta.env.DEV
|
|
691
|
+
? runtimeScopeBinding.catalogUrl
|
|
692
|
+
: new URL('pack-index.json', document.baseURI).href,
|
|
693
|
+
expectedScope: runtimeScopeBinding,
|
|
694
|
+
});
|
|
640
695
|
const bundler = {
|
|
641
696
|
...forgeaxBundlerAdapter(),
|
|
642
|
-
...(
|
|
697
|
+
...(import.meta.env.DEV ? { importTransport: createDevImportTransport(runtimeScopeBinding) } : {}),
|
|
643
698
|
};
|
|
644
699
|
|
|
645
700
|
function previewStable(value) {
|
|
@@ -1018,12 +1073,18 @@ if (query.has('forgeax-tool-replay')) {
|
|
|
1018
1073
|
window.addEventListener('pagehide', () => void host.value.dispose(), { once: true });
|
|
1019
1074
|
}
|
|
1020
1075
|
} else {
|
|
1076
|
+
// WebDriver/headless browsers cannot satisfy the trusted-gesture requirement
|
|
1077
|
+
// for pointer lock. Keep the game running and let its keyboard fallback own
|
|
1078
|
+
// camera input; real browser users retain the normal click-to-lock path.
|
|
1079
|
+
const pointerLockAllowed =
|
|
1080
|
+
typeof navigator !== 'undefined' && navigator.webdriver === true ? () => false : undefined;
|
|
1021
1081
|
const result = await createApp(
|
|
1022
1082
|
canvas,
|
|
1023
1083
|
{
|
|
1024
1084
|
...(assetCatalog === undefined ? {} : { assetCatalog }),
|
|
1025
1085
|
...(vfxRuntimeHost === undefined ? {} : { assetDecoders: [vfxGpuEffectContribution] }),
|
|
1026
1086
|
plugins: [${plugins.join(", ")}],
|
|
1087
|
+
...(pointerLockAllowed === undefined ? {} : { pointerLockAllowed }),
|
|
1027
1088
|
},
|
|
1028
1089
|
bundler,
|
|
1029
1090
|
);
|
|
@@ -1082,10 +1143,32 @@ function htmlSource(title) {
|
|
|
1082
1143
|
<div id="app-shell"><canvas id="app"></canvas><div id="game-ui"></div></div><div id="forgeax-fatal" role="alert"></div>
|
|
1083
1144
|
<script>
|
|
1084
1145
|
(() => {
|
|
1146
|
+
const formatStartupFailure = (reason) => {
|
|
1147
|
+
if (reason instanceof Error) return reason.message;
|
|
1148
|
+
if (reason !== null && typeof reason === 'object') {
|
|
1149
|
+
const record = reason;
|
|
1150
|
+
const lines = [];
|
|
1151
|
+
for (const key of ['code', 'expected', 'hint']) {
|
|
1152
|
+
if (typeof record[key] === 'string' && record[key].length > 0) {
|
|
1153
|
+
lines.push(key + ': ' + record[key]);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
const detail = record.detail;
|
|
1157
|
+
if (detail !== null && typeof detail === 'object') {
|
|
1158
|
+
for (const key of ['reason', 'guid']) {
|
|
1159
|
+
if (typeof detail[key] === 'string' && detail[key].length > 0) {
|
|
1160
|
+
lines.push('detail.' + key + ': ' + detail[key]);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (lines.length > 0) return lines.join('\\n');
|
|
1165
|
+
}
|
|
1166
|
+
return String(reason ?? 'Unknown startup failure');
|
|
1167
|
+
};
|
|
1085
1168
|
const show = (reason) => {
|
|
1086
1169
|
const notice = document.querySelector('#forgeax-fatal');
|
|
1087
1170
|
if (!(notice instanceof HTMLElement)) return;
|
|
1088
|
-
const message =
|
|
1171
|
+
const message = formatStartupFailure(reason);
|
|
1089
1172
|
notice.textContent = 'ForgeaX game failed to start.\\n' + message;
|
|
1090
1173
|
notice.style.display = 'grid';
|
|
1091
1174
|
};
|
|
@@ -1100,29 +1183,32 @@ function htmlSource(title) {
|
|
|
1100
1183
|
}
|
|
1101
1184
|
async function createViteConfig(facts, command2, base = "/", options = {}) {
|
|
1102
1185
|
const bootstrapRoot = options.bootstrapRoot ?? "project-bootstrap";
|
|
1103
|
-
const canonicalKit =
|
|
1186
|
+
const canonicalKit = await resolveCanonicalKit();
|
|
1104
1187
|
const generated = resolve(facts.root, ".forgeax", "generated");
|
|
1105
1188
|
await mkdir(generated, { recursive: true });
|
|
1189
|
+
const projectRoots = facts.assetRoots.map((root) => resolve(facts.root, root));
|
|
1190
|
+
const ignorePath = bootstrapRoot === "resource-bootstrap" ? isResourcePreviewIgnoredPath : isProjectSourceIgnoredPath;
|
|
1191
|
+
const builtinPack = await prepareBuiltinPack(projectRoots, generated, ignorePath);
|
|
1106
1192
|
await Promise.all([
|
|
1107
1193
|
writeFile(resolve(generated, "index.html"), htmlSource(facts.name)),
|
|
1108
1194
|
writeFile(resolve(generated, "main.ts"), hostSource(facts, bootstrapRoot, canonicalKit?.guid))
|
|
1109
1195
|
]);
|
|
1110
1196
|
const roots = [
|
|
1111
|
-
...
|
|
1197
|
+
...projectRoots,
|
|
1198
|
+
...builtinPack === void 0 ? [] : [builtinPack],
|
|
1112
1199
|
...canonicalKit === void 0 ? [] : [canonicalKit.root]
|
|
1113
1200
|
];
|
|
1114
1201
|
const importers = [...projectImporters(facts)];
|
|
1115
1202
|
const runtimeBinding = createStandaloneRuntimeAssetBinding(facts.id);
|
|
1116
1203
|
const engineWorkspaceResolver = await createEngineWorkspaceResolver();
|
|
1204
|
+
const consumerAliases = await consumerEngineAliases(facts.root);
|
|
1117
1205
|
const plugins = [
|
|
1118
1206
|
...engineWorkspaceResolver === void 0 ? [] : [engineWorkspaceResolver],
|
|
1119
1207
|
forgeaxShader(),
|
|
1120
1208
|
pluginPack({
|
|
1121
1209
|
roots,
|
|
1122
|
-
base,
|
|
1123
1210
|
runtimeBinding,
|
|
1124
1211
|
ddc: devKitDdcRoots(facts.root),
|
|
1125
|
-
scriptablePack: {},
|
|
1126
1212
|
refresh: command2 === "serve" ? reloadAssetHost() : void 0,
|
|
1127
1213
|
importers: [
|
|
1128
1214
|
audioImporter,
|
|
@@ -1132,8 +1218,8 @@ async function createViteConfig(facts, command2, base = "/", options = {}) {
|
|
|
1132
1218
|
fontImporter,
|
|
1133
1219
|
...importers
|
|
1134
1220
|
],
|
|
1135
|
-
cookers: [createParticleCodeNativeCookerFromRoots(roots)],
|
|
1136
|
-
|
|
1221
|
+
cookers: [createMaterialPackCooker(roots), createParticleCodeNativeCookerFromRoots(roots)],
|
|
1222
|
+
ignorePath
|
|
1137
1223
|
})
|
|
1138
1224
|
];
|
|
1139
1225
|
return {
|
|
@@ -1142,7 +1228,10 @@ async function createViteConfig(facts, command2, base = "/", options = {}) {
|
|
|
1142
1228
|
configFile: false,
|
|
1143
1229
|
publicDir: facts.assetPublicDir === void 0 ? false : resolve(facts.root, facts.assetPublicDir),
|
|
1144
1230
|
plugins,
|
|
1145
|
-
resolve: {
|
|
1231
|
+
resolve: {
|
|
1232
|
+
alias: consumerAliases,
|
|
1233
|
+
dedupe: ["@forgeax/engine-app", "@forgeax/engine-ecs", "@forgeax/engine-runtime"]
|
|
1234
|
+
},
|
|
1146
1235
|
server: { fs: { allow: [facts.root, ...roots] } },
|
|
1147
1236
|
build: {
|
|
1148
1237
|
target: "esnext",
|
|
@@ -1694,6 +1783,7 @@ function createInitPlan(facts, sdk) {
|
|
|
1694
1783
|
value: {
|
|
1695
1784
|
root: facts.root,
|
|
1696
1785
|
version: sdk?.sdkVersion ?? devkitVersion,
|
|
1786
|
+
...sdk === void 0 ? {} : { pnpmVersion: sdk.requirements.pnpm },
|
|
1697
1787
|
archiveBacked: sdk !== void 0,
|
|
1698
1788
|
dependencyChanges,
|
|
1699
1789
|
scriptChanges
|
|
@@ -1714,23 +1804,8 @@ async function applyInitPlan(facts, plan, options) {
|
|
|
1714
1804
|
for (const change of plan.scriptChanges) scripts[change.name] = change.to;
|
|
1715
1805
|
manifest.scripts = scripts;
|
|
1716
1806
|
if (plan.archiveBacked) {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const pnpm = pnpmValue !== null && typeof pnpmValue === "object" ? pnpmValue : {};
|
|
1720
|
-
manifest.pnpm = {
|
|
1721
|
-
...pnpm,
|
|
1722
|
-
onlyBuiltDependencies: [
|
|
1723
|
-
"@forgeax/engine-codec",
|
|
1724
|
-
"@forgeax/engine-fbx",
|
|
1725
|
-
"@forgeax/engine-wgpu-wasm",
|
|
1726
|
-
"esbuild"
|
|
1727
|
-
],
|
|
1728
|
-
supportedArchitectures: {
|
|
1729
|
-
os: ["darwin", "linux", "win32"],
|
|
1730
|
-
cpu: ["x64", "arm64"],
|
|
1731
|
-
libc: ["glibc", "musl"]
|
|
1732
|
-
}
|
|
1733
|
-
};
|
|
1807
|
+
if (plan.pnpmVersion === void 0) throw new Error("sdk-pnpm-version-missing");
|
|
1808
|
+
manifest.packageManager = `pnpm@${plan.pnpmVersion}`;
|
|
1734
1809
|
}
|
|
1735
1810
|
await writeFile(resolve(facts.root, "package.json"), `${JSON.stringify(manifest, null, 2)}
|
|
1736
1811
|
`);
|
|
@@ -1796,6 +1871,154 @@ var init_sdk = __esm({
|
|
|
1796
1871
|
"src/sdk.ts"() {
|
|
1797
1872
|
}
|
|
1798
1873
|
});
|
|
1874
|
+
function sdkInitPath(sdk) {
|
|
1875
|
+
return resolve(sdk.root, ...SDK_INIT_PATH);
|
|
1876
|
+
}
|
|
1877
|
+
function commandFailure(code, expected, hint, detail = {}) {
|
|
1878
|
+
return { ok: false, error: { code, expected, hint, detail } };
|
|
1879
|
+
}
|
|
1880
|
+
function sdkProjectInstallArgs(store) {
|
|
1881
|
+
const common = [
|
|
1882
|
+
"install",
|
|
1883
|
+
"--frozen-lockfile",
|
|
1884
|
+
"--ignore-scripts",
|
|
1885
|
+
"--side-effects-cache=true",
|
|
1886
|
+
"--child-concurrency=1"
|
|
1887
|
+
];
|
|
1888
|
+
return store === void 0 ? common : [...common, "--offline", "--trust-lockfile", "--store-dir", store];
|
|
1889
|
+
}
|
|
1890
|
+
function sdkBootstrapInstallArgs(store) {
|
|
1891
|
+
const common = [
|
|
1892
|
+
"install",
|
|
1893
|
+
"--frozen-lockfile",
|
|
1894
|
+
"--child-concurrency=1",
|
|
1895
|
+
"--side-effects-cache=true"
|
|
1896
|
+
];
|
|
1897
|
+
return store === void 0 ? common : [...common, "--offline", "--trust-lockfile", "--store-dir", store];
|
|
1898
|
+
}
|
|
1899
|
+
function supportedPnpm(version) {
|
|
1900
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
1901
|
+
return major === 11 && minor >= 7;
|
|
1902
|
+
}
|
|
1903
|
+
async function currentPnpm() {
|
|
1904
|
+
try {
|
|
1905
|
+
const result = await execFileAsync("pnpm", ["--version"], { maxBuffer: 1024 * 1024 });
|
|
1906
|
+
const version = result.stdout.trim();
|
|
1907
|
+
if (version.length === 0) throw new Error("pnpm returned an empty version");
|
|
1908
|
+
return { ok: true, value: version };
|
|
1909
|
+
} catch (cause) {
|
|
1910
|
+
return commandFailure(
|
|
1911
|
+
"pnpm-unavailable",
|
|
1912
|
+
"pnpm 11.7.0 or newer in the pnpm 11 line to be available on PATH",
|
|
1913
|
+
"Install or activate pnpm 11, then rerun forgeax init from the SDK root.",
|
|
1914
|
+
{ reason: cause instanceof Error ? cause.message : String(cause) }
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
function matchesState(sdk, state) {
|
|
1919
|
+
if (state === null || typeof state !== "object" || Array.isArray(state)) return false;
|
|
1920
|
+
const value = state;
|
|
1921
|
+
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;
|
|
1922
|
+
}
|
|
1923
|
+
async function readSdkInitState(sdk) {
|
|
1924
|
+
try {
|
|
1925
|
+
const value = JSON.parse(await readFile(sdkInitPath(sdk), "utf8"));
|
|
1926
|
+
return matchesState(sdk, value) ? value : void 0;
|
|
1927
|
+
} catch {
|
|
1928
|
+
return void 0;
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
async function requireSdkInitialization(sdk) {
|
|
1932
|
+
const state = await readSdkInitState(sdk);
|
|
1933
|
+
if (state === void 0) {
|
|
1934
|
+
return commandFailure(
|
|
1935
|
+
"sdk-not-initialized",
|
|
1936
|
+
"the downloaded SDK to be initialized for this Node/pnpm/platform tuple",
|
|
1937
|
+
"Run ./bin/forgeax init from the SDK root once, then run forgeax new outside the SDK.",
|
|
1938
|
+
{
|
|
1939
|
+
sdkRoot: sdk.root,
|
|
1940
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
1941
|
+
pnpm: sdk.manifest.requirements.pnpm,
|
|
1942
|
+
node: process.versions.node,
|
|
1943
|
+
platform: process.platform,
|
|
1944
|
+
arch: process.arch
|
|
1945
|
+
}
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
return { ok: true, value: state };
|
|
1949
|
+
}
|
|
1950
|
+
async function copyBootstrapInputs(sdk, root) {
|
|
1951
|
+
const template = sdk.templates.get(sdk.defaultTemplate);
|
|
1952
|
+
if (template === void 0) throw new Error("sdk-default-template-missing");
|
|
1953
|
+
for (const name of ["package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml"]) {
|
|
1954
|
+
await cp(resolve(template, name), resolve(root, name));
|
|
1955
|
+
}
|
|
1956
|
+
try {
|
|
1957
|
+
await cp(resolve(template, ".npmrc"), resolve(root, ".npmrc"));
|
|
1958
|
+
} catch (cause) {
|
|
1959
|
+
if (cause === null || typeof cause !== "object" || !("code" in cause) || cause.code !== "ENOENT") {
|
|
1960
|
+
throw cause;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
async function sdkInitCommand(sdk, options = {}) {
|
|
1965
|
+
const pnpmResult = await currentPnpm();
|
|
1966
|
+
if (!pnpmResult.ok) return pnpmResult;
|
|
1967
|
+
if (!supportedPnpm(pnpmResult.value)) {
|
|
1968
|
+
return commandFailure(
|
|
1969
|
+
"pnpm-version-unsupported",
|
|
1970
|
+
"pnpm >=11.7.0 <12",
|
|
1971
|
+
"Activate pnpm 11.7.0 or newer in the pnpm 11 line, then rerun forgeax init.",
|
|
1972
|
+
{ actual: pnpmResult.value, expected: sdk.manifest.requirements.pnpm }
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
const state = {
|
|
1976
|
+
schemaVersion: SDK_INIT_SCHEMA_VERSION,
|
|
1977
|
+
sdkVersion: sdk.manifest.sdkVersion,
|
|
1978
|
+
engineCommit: sdk.manifest.engineCommit,
|
|
1979
|
+
pnpm: pnpmResult.value,
|
|
1980
|
+
node: process.versions.node,
|
|
1981
|
+
platform: process.platform,
|
|
1982
|
+
arch: process.arch
|
|
1983
|
+
};
|
|
1984
|
+
const report = {
|
|
1985
|
+
...state,
|
|
1986
|
+
root: sdk.root,
|
|
1987
|
+
store: sdk.store === void 0 ? "registry" : "offline"
|
|
1988
|
+
};
|
|
1989
|
+
if (options.dryRun === true || options.install === false) return { ok: true, value: report };
|
|
1990
|
+
let staging;
|
|
1991
|
+
try {
|
|
1992
|
+
staging = await mkdtemp(resolve(tmpdir(), "forgeax-sdk-init-"));
|
|
1993
|
+
await copyBootstrapInputs(sdk, staging);
|
|
1994
|
+
await execFileAsync("pnpm", sdkBootstrapInstallArgs(sdk.store), {
|
|
1995
|
+
cwd: staging,
|
|
1996
|
+
env: { ...process.env, CI: "true" },
|
|
1997
|
+
maxBuffer: 16 * 1024 * 1024
|
|
1998
|
+
});
|
|
1999
|
+
await mkdir(resolve(sdk.root, ".forgeax"), { recursive: true });
|
|
2000
|
+
await writeFile(sdkInitPath(sdk), `${JSON.stringify(state, null, 2)}
|
|
2001
|
+
`);
|
|
2002
|
+
return { ok: true, value: report };
|
|
2003
|
+
} catch (cause) {
|
|
2004
|
+
return commandFailure(
|
|
2005
|
+
"sdk-init-failed",
|
|
2006
|
+
"the SDK dependency closure to install and build native packages successfully",
|
|
2007
|
+
"Inspect the pnpm output, repair Node/pnpm or platform permissions, then rerun forgeax init from the SDK root.",
|
|
2008
|
+
{ reason: cause instanceof Error ? cause.message : String(cause), sdkRoot: sdk.root }
|
|
2009
|
+
);
|
|
2010
|
+
} finally {
|
|
2011
|
+
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
var execFileAsync, SDK_INIT_PATH, SDK_INIT_SCHEMA_VERSION;
|
|
2015
|
+
var init_sdk_bootstrap = __esm({
|
|
2016
|
+
"src/sdk-bootstrap.ts"() {
|
|
2017
|
+
execFileAsync = promisify(execFile);
|
|
2018
|
+
SDK_INIT_PATH = [".forgeax", "sdk-init.json"];
|
|
2019
|
+
SDK_INIT_SCHEMA_VERSION = "1.0.0";
|
|
2020
|
+
}
|
|
2021
|
+
});
|
|
1799
2022
|
function slash(path) {
|
|
1800
2023
|
return path.split(sep).join("/");
|
|
1801
2024
|
}
|
|
@@ -2161,6 +2384,20 @@ async function canonicalProspectivePath(path) {
|
|
|
2161
2384
|
}
|
|
2162
2385
|
}
|
|
2163
2386
|
}
|
|
2387
|
+
async function configureSdkStore(root, store) {
|
|
2388
|
+
if (store === void 0) return;
|
|
2389
|
+
const path = resolve(root, ".npmrc");
|
|
2390
|
+
let content = "";
|
|
2391
|
+
try {
|
|
2392
|
+
content = await readFile(path, "utf8");
|
|
2393
|
+
} catch (cause) {
|
|
2394
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
2395
|
+
}
|
|
2396
|
+
const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0 && !/^\s*store-dir\s*=/.test(line));
|
|
2397
|
+
lines.push(`store-dir=${store}`);
|
|
2398
|
+
await writeFile(path, `${lines.join("\n")}
|
|
2399
|
+
`);
|
|
2400
|
+
}
|
|
2164
2401
|
function containsOrEquals(parent, child) {
|
|
2165
2402
|
const path = relative(parent, child);
|
|
2166
2403
|
return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
@@ -2171,25 +2408,14 @@ function nodeSupported() {
|
|
|
2171
2408
|
}
|
|
2172
2409
|
function pnpmSupported(version) {
|
|
2173
2410
|
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
2174
|
-
return major ===
|
|
2175
|
-
}
|
|
2176
|
-
function sdkInstallArgs(store) {
|
|
2177
|
-
if (store === void 0) return ["install", "--frozen-lockfile"];
|
|
2178
|
-
return [
|
|
2179
|
-
"install",
|
|
2180
|
-
"--offline",
|
|
2181
|
-
"--frozen-lockfile",
|
|
2182
|
-
"--side-effects-cache=false",
|
|
2183
|
-
"--store-dir",
|
|
2184
|
-
store
|
|
2185
|
-
];
|
|
2411
|
+
return major === 11 && minor >= 7;
|
|
2186
2412
|
}
|
|
2187
2413
|
async function doctorCommand(options = {}) {
|
|
2188
2414
|
const facts = await readProjectFacts(options.root);
|
|
2189
2415
|
if (!facts.ok) return facts;
|
|
2190
2416
|
let pnpm;
|
|
2191
2417
|
try {
|
|
2192
|
-
pnpm = (await
|
|
2418
|
+
pnpm = (await execFileAsync2("pnpm", ["--version"], { cwd: facts.value.root })).stdout.trim();
|
|
2193
2419
|
} catch (cause) {
|
|
2194
2420
|
return {
|
|
2195
2421
|
ok: false,
|
|
@@ -2217,7 +2443,7 @@ async function doctorCommand(options = {}) {
|
|
|
2217
2443
|
ok: false,
|
|
2218
2444
|
error: {
|
|
2219
2445
|
code: "pnpm-version-unsupported",
|
|
2220
|
-
expected: "pnpm >=
|
|
2446
|
+
expected: "pnpm >=11.7.0 <12",
|
|
2221
2447
|
hint: "Enable the packageManager-declared pnpm version with Corepack and retry.",
|
|
2222
2448
|
detail: { actual: pnpm }
|
|
2223
2449
|
}
|
|
@@ -2256,12 +2482,20 @@ async function doctorCommand(options = {}) {
|
|
|
2256
2482
|
};
|
|
2257
2483
|
}
|
|
2258
2484
|
async function initCommand(options = {}) {
|
|
2259
|
-
const facts = await readProjectFacts(options.root);
|
|
2260
|
-
if (!facts.ok) return facts;
|
|
2261
2485
|
try {
|
|
2262
2486
|
const sdk = await findSdkContext();
|
|
2487
|
+
const root = await canonicalProspectivePath(options.root ?? process.cwd());
|
|
2488
|
+
if (sdk !== void 0 && root === await canonicalProspectivePath(sdk.root)) {
|
|
2489
|
+
return sdkInitCommand(sdk, options);
|
|
2490
|
+
}
|
|
2491
|
+
const facts = await readProjectFacts(root);
|
|
2492
|
+
if (!facts.ok) return facts;
|
|
2263
2493
|
const plan = createInitPlan(facts.value, sdk?.manifest);
|
|
2264
2494
|
if (!plan.ok) return plan;
|
|
2495
|
+
if (sdk !== void 0 && options.dryRun !== true) {
|
|
2496
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
2497
|
+
if (!initialized.ok) return initialized;
|
|
2498
|
+
}
|
|
2265
2499
|
const applied = await applyInitPlan(facts.value, plan.value, options);
|
|
2266
2500
|
if (!applied.ok || options.dryRun === true) return applied;
|
|
2267
2501
|
if (sdk !== void 0) {
|
|
@@ -2271,12 +2505,31 @@ async function initCommand(options = {}) {
|
|
|
2271
2505
|
resolve(template, "pnpm-lock.yaml"),
|
|
2272
2506
|
resolve(facts.value.root, "pnpm-lock.yaml")
|
|
2273
2507
|
);
|
|
2508
|
+
await copyFile(
|
|
2509
|
+
resolve(template, "pnpm-workspace.yaml"),
|
|
2510
|
+
resolve(facts.value.root, "pnpm-workspace.yaml")
|
|
2511
|
+
);
|
|
2512
|
+
try {
|
|
2513
|
+
await readFile(resolve(facts.value.root, ".npmrc"), "utf8");
|
|
2514
|
+
} catch (cause) {
|
|
2515
|
+
if (!isMissingPathError(cause)) throw cause;
|
|
2516
|
+
try {
|
|
2517
|
+
await copyFile(resolve(template, ".npmrc"), resolve(facts.value.root, ".npmrc"));
|
|
2518
|
+
} catch (templateCause) {
|
|
2519
|
+
if (!isMissingPathError(templateCause)) throw templateCause;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2274
2522
|
await copySdkSkills(sdk, facts.value.root);
|
|
2275
2523
|
await installProjectSkills(facts.value.root, sdk.manifest);
|
|
2524
|
+
await configureSdkStore(
|
|
2525
|
+
facts.value.root,
|
|
2526
|
+
sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store)
|
|
2527
|
+
);
|
|
2276
2528
|
}
|
|
2277
2529
|
if (options.install === false) return applied;
|
|
2278
|
-
const
|
|
2279
|
-
|
|
2530
|
+
const store = sdk === void 0 || sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
2531
|
+
const installArgs = sdk === void 0 ? ["install", "--frozen-lockfile=false"] : sdkProjectInstallArgs(store);
|
|
2532
|
+
await execFileAsync2("pnpm", installArgs, {
|
|
2280
2533
|
cwd: facts.value.root,
|
|
2281
2534
|
env: { ...process.env, CI: "true" },
|
|
2282
2535
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -2354,12 +2607,15 @@ async function newCommand(options = {}) {
|
|
|
2354
2607
|
value: { root, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
|
|
2355
2608
|
};
|
|
2356
2609
|
}
|
|
2610
|
+
const initialized = await requireSdkInitialization(sdk);
|
|
2611
|
+
if (!initialized.ok) return initialized;
|
|
2357
2612
|
const parent = dirname(root);
|
|
2358
2613
|
await mkdir(parent, { recursive: true });
|
|
2359
2614
|
let staging = await mkdtemp(
|
|
2360
2615
|
resolve(parent, `.${basename(root)}.forgeax-staging-`)
|
|
2361
2616
|
);
|
|
2362
2617
|
const committedNames = [];
|
|
2618
|
+
let committed = false;
|
|
2363
2619
|
try {
|
|
2364
2620
|
for (const name of await readdir(template)) {
|
|
2365
2621
|
await cp(resolve(template, name), resolve(staging, name), {
|
|
@@ -2370,14 +2626,12 @@ async function newCommand(options = {}) {
|
|
|
2370
2626
|
}
|
|
2371
2627
|
await copySdkSkills(sdk, staging);
|
|
2372
2628
|
await installProjectSkills(staging, sdk.manifest);
|
|
2373
|
-
await
|
|
2374
|
-
|
|
2375
|
-
env: { ...process.env, CI: "true" },
|
|
2376
|
-
maxBuffer: 16 * 1024 * 1024
|
|
2377
|
-
});
|
|
2629
|
+
const store = sdk.store === void 0 ? void 0 : await canonicalProspectivePath(sdk.store);
|
|
2630
|
+
await configureSdkStore(staging, store);
|
|
2378
2631
|
if (!targetExists) {
|
|
2379
2632
|
await rename(staging, root);
|
|
2380
2633
|
staging = void 0;
|
|
2634
|
+
committedNames.push(...await readdir(root));
|
|
2381
2635
|
} else {
|
|
2382
2636
|
for (const name of await readdir(staging)) {
|
|
2383
2637
|
await rename(resolve(staging, name), resolve(root, name));
|
|
@@ -2386,29 +2640,49 @@ async function newCommand(options = {}) {
|
|
|
2386
2640
|
await rm(staging, { recursive: true, force: true });
|
|
2387
2641
|
staging = void 0;
|
|
2388
2642
|
}
|
|
2643
|
+
committed = true;
|
|
2644
|
+
await execFileAsync2("pnpm", sdkProjectInstallArgs(store), {
|
|
2645
|
+
cwd: root,
|
|
2646
|
+
env: { ...process.env, CI: "true" },
|
|
2647
|
+
maxBuffer: 16 * 1024 * 1024
|
|
2648
|
+
});
|
|
2389
2649
|
return {
|
|
2390
2650
|
ok: true,
|
|
2391
2651
|
value: { root, template: templateId, sdkVersion: sdk.manifest.sdkVersion }
|
|
2392
2652
|
};
|
|
2393
2653
|
} catch (cause) {
|
|
2394
2654
|
if (staging !== void 0) await rm(staging, { recursive: true, force: true });
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2655
|
+
if (committed) {
|
|
2656
|
+
if (!targetExists) {
|
|
2657
|
+
await rm(root, { recursive: true, force: true });
|
|
2658
|
+
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2659
|
+
}
|
|
2660
|
+
const cleanupNames = /* @__PURE__ */ new Set([...committedNames, "node_modules"]);
|
|
2661
|
+
await Promise.all(
|
|
2662
|
+
[...cleanupNames].map(
|
|
2663
|
+
(name) => rm(resolve(root, name), { recursive: true, force: true })
|
|
2664
|
+
)
|
|
2665
|
+
);
|
|
2666
|
+
} else {
|
|
2667
|
+
await Promise.all(
|
|
2668
|
+
committedNames.map((name) => rm(resolve(root, name), { recursive: true, force: true }))
|
|
2669
|
+
);
|
|
2670
|
+
}
|
|
2398
2671
|
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2399
2672
|
}
|
|
2400
2673
|
} catch (cause) {
|
|
2401
2674
|
return { ok: false, error: commandError(cause, "project-create-failed") };
|
|
2402
2675
|
}
|
|
2403
2676
|
}
|
|
2404
|
-
var
|
|
2677
|
+
var execFileAsync2;
|
|
2405
2678
|
var init_bootstrap_commands = __esm({
|
|
2406
2679
|
"src/bootstrap-commands.ts"() {
|
|
2407
2680
|
init_init();
|
|
2408
2681
|
init_project();
|
|
2409
2682
|
init_sdk();
|
|
2683
|
+
init_sdk_bootstrap();
|
|
2410
2684
|
init_skill_install();
|
|
2411
|
-
|
|
2685
|
+
execFileAsync2 = promisify(execFile);
|
|
2412
2686
|
}
|
|
2413
2687
|
});
|
|
2414
2688
|
|
|
@@ -2467,7 +2741,7 @@ function withoutEntry(entries2, id) {
|
|
|
2467
2741
|
}
|
|
2468
2742
|
async function mutateDependency(root, action, dependency) {
|
|
2469
2743
|
if (dependency === void 0) return;
|
|
2470
|
-
await
|
|
2744
|
+
await execFileAsync3("pnpm", [action, dependency], { cwd: root, maxBuffer: 16 * 1024 * 1024 });
|
|
2471
2745
|
}
|
|
2472
2746
|
async function pluginInstallCommand(options) {
|
|
2473
2747
|
const root = resolve(options.root ?? process.cwd());
|
|
@@ -2569,10 +2843,10 @@ async function pluginUninstallCommand(options) {
|
|
|
2569
2843
|
};
|
|
2570
2844
|
}
|
|
2571
2845
|
}
|
|
2572
|
-
var
|
|
2846
|
+
var execFileAsync3;
|
|
2573
2847
|
var init_plugin_authoring = __esm({
|
|
2574
2848
|
"src/plugin-authoring.ts"() {
|
|
2575
|
-
|
|
2849
|
+
execFileAsync3 = promisify(execFile);
|
|
2576
2850
|
}
|
|
2577
2851
|
});
|
|
2578
2852
|
var RhiError;
|
|
@@ -3427,7 +3701,7 @@ async function sdkInstallCommand(options) {
|
|
|
3427
3701
|
try {
|
|
3428
3702
|
staging = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-staging-`));
|
|
3429
3703
|
download = await mkdtemp(resolve(parent, `.${basename(root)}.forgeax-sdk-download-`));
|
|
3430
|
-
await
|
|
3704
|
+
await execFileAsync4(
|
|
3431
3705
|
process.env.FORGEAX_NPM_CLIENT ?? "npm",
|
|
3432
3706
|
[
|
|
3433
3707
|
"install",
|
|
@@ -3468,11 +3742,11 @@ async function sdkInstallCommand(options) {
|
|
|
3468
3742
|
if (download !== void 0) await rm(download, { recursive: true, force: true });
|
|
3469
3743
|
}
|
|
3470
3744
|
}
|
|
3471
|
-
var
|
|
3745
|
+
var execFileAsync4;
|
|
3472
3746
|
var init_sdk_install = __esm({
|
|
3473
3747
|
"src/sdk-install.ts"() {
|
|
3474
3748
|
init_project();
|
|
3475
|
-
|
|
3749
|
+
execFileAsync4 = promisify(execFile);
|
|
3476
3750
|
}
|
|
3477
3751
|
});
|
|
3478
3752
|
async function shaderCheckCommand(options = {}) {
|
|
@@ -4293,7 +4567,7 @@ __export(contributions_exports, {
|
|
|
4293
4567
|
createBuildContribution: () => createBuildContribution,
|
|
4294
4568
|
createDefaultContributions: () => createDefaultContributions
|
|
4295
4569
|
});
|
|
4296
|
-
function
|
|
4570
|
+
function commandFailure2(error) {
|
|
4297
4571
|
return { ok: false, error: { ...error, detail: error.detail } };
|
|
4298
4572
|
}
|
|
4299
4573
|
function createBuildContribution(projectRoot2 = process.cwd()) {
|
|
@@ -4302,7 +4576,7 @@ function createBuildContribution(projectRoot2 = process.cwd()) {
|
|
|
4302
4576
|
async (options) => {
|
|
4303
4577
|
const { buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_commands(), commands_exports));
|
|
4304
4578
|
const result = await buildCommand2({ ...options, root: options.root ?? projectRoot2 });
|
|
4305
|
-
return result.ok ? result.value :
|
|
4579
|
+
return result.ok ? result.value : commandFailure2(result.error);
|
|
4306
4580
|
}
|
|
4307
4581
|
);
|
|
4308
4582
|
}
|
|
@@ -4312,7 +4586,7 @@ function createAuthorContribution(projectRoot2 = process.cwd()) {
|
|
|
4312
4586
|
async (options) => {
|
|
4313
4587
|
const { pluginInstallCommand: pluginInstallCommand2 } = await Promise.resolve().then(() => (init_plugin_authoring(), plugin_authoring_exports));
|
|
4314
4588
|
const result = await pluginInstallCommand2({ ...options, root: options.root ?? projectRoot2 });
|
|
4315
|
-
return result.ok ? result.value :
|
|
4589
|
+
return result.ok ? result.value : commandFailure2(result.error);
|
|
4316
4590
|
}
|
|
4317
4591
|
);
|
|
4318
4592
|
}
|