Merge branch 'master' into codex/puzzle-clear-template-runtime-fixes

# Conflicts:
#	.hermes/shared-memory/decision-log.md
#	.hermes/shared-memory/project-overview.md
#	docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
#	scripts/dev.test.ts
#	server-rs/crates/api-server/src/creation_entry_config.rs
#	server-rs/crates/api-server/src/wooden_fish.rs
#	server-rs/crates/module-auth/src/lib.rs
#	server-rs/crates/spacetime-client/src/wooden_fish.rs
#	server-rs/crates/spacetime-module/src/auth/procedures.rs
#	src/components/custom-world-home/creationWorkShelf.ts
#	src/components/platform-entry/PlatformEntryFlowShellImpl.tsx
#	src/components/rpg-entry/rpgEntryWorldPresentation.ts
#	src/services/miniGameDraftGenerationProgress.test.ts
#	src/services/miniGameDraftGenerationProgress.ts
This commit is contained in:
2026-06-04 11:24:14 +08:00
451 changed files with 18452 additions and 5266 deletions

View File

@@ -9,6 +9,7 @@ import {
assertReusableSpacetimeProcessVersionMatchesWorkspace,
assertSpacetimeToolVersionMatchesWorkspace,
buildApiServerProcessEnv,
buildDevStackSnapshot,
buildSpacetimePublishArgs,
createDevServerSpawnOptions,
createWatchConfigs,
@@ -16,6 +17,7 @@ import {
isSpacetimePublishPermissionError,
parseSpacetimeToolVersion,
parseArgs,
resolveDevStackStatePath,
shouldAcceptWatchEvent,
} from './dev.mjs';
@@ -36,6 +38,8 @@ function workspaceSpacetimeVersionForTest() {
}
describe('dev scheduler argument routing', () => {
const linuxTest = process.platform === 'linux' ? test : test.skip;
test('Windows junction 路径下的直接执行入口也能识别为当前模块', () => {
const moduleUrl =
'file:///F:/DevWorktrees/codex/worktrees/f584/Genarrative/scripts/dev.mjs';
@@ -102,6 +106,67 @@ describe('dev scheduler argument routing', () => {
'http://127.0.0.1:3100',
);
});
linuxTest('Linux 启动时按系统级端口段映射四个 dev 端口', async () => {
const tempDir = mkdtempSync(join(tmpdir(), 'genarrative-dev-port-range-'));
try {
const {command, explicitOptions, options} = parseArgs([], {
USER: 'alice',
LOGNAME: 'alice',
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempDir,
});
const runner = new DevRunner(options, {
USER: 'alice',
LOGNAME: 'alice',
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
GENARRATIVE_DEV_PORT_RANGE_REGISTRY_DIR: tempDir,
}, explicitOptions);
await runner.prepareLinuxPortRange(command);
expect(runner.state.portRange.label).toBe('22000-22099');
expect(runner.options.webPort).toBe(22000);
expect(runner.options.apiPort).toBe(22001);
expect(runner.options.spacetimePort).toBe(22002);
expect(runner.options.adminWebPort).toBe(22003);
expect(runner.state.apiTarget).toBe('http://127.0.0.1:22001');
expect(runner.state.spacetimeServer).toBe('http://127.0.0.1:22002');
} finally {
rmSync(tempDir, {recursive: true, force: true});
}
});
test('Windows 仍沿用原有端口解析,不启用 Linux 端口段登记', async () => {
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
Object.defineProperty(process, 'platform', {
configurable: true,
value: 'win32',
});
try {
const {command, explicitOptions, options} = parseArgs([], {
USER: 'alice',
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
});
const runner = new DevRunner(options, {
USER: 'alice',
GENARRATIVE_DEV_PORT_RANGE: '22000-22099',
}, explicitOptions);
await runner.prepareLinuxPortRange(command);
expect(runner.state.portRange).toBeNull();
expect(runner.options.webPort).toBe(3000);
expect(runner.options.apiPort).toBe(8082);
expect(runner.options.spacetimePort).toBe(3101);
expect(runner.options.adminWebPort).toBe(3102);
} finally {
if (originalPlatform) {
Object.defineProperty(process, 'platform', originalPlatform);
}
}
});
});
describe('dev scheduler api-server env', () => {
@@ -119,6 +184,79 @@ describe('dev scheduler api-server env', () => {
});
});
describe('dev scheduler stack state file', () => {
test('状态文件路径固定在根目录 .app/dev-stack.json', () => {
expect(resolveDevStackStatePath('C:\\repo\\Genarrative')).toBe(
join('C:\\repo\\Genarrative', '.app/dev-stack.json'),
);
});
test('状态快照记录服务 pid、端口、URL 和当前命令', () => {
const updatedAt = '2026-05-29T00:00:00.000Z';
const runner = {
command: 'web',
options: {
apiHost: '127.0.0.1',
apiPort: 8090,
webHost: '0.0.0.0',
webPort: 3010,
adminWebHost: '127.0.0.1',
adminWebPort: 3110,
spacetimeHost: '127.0.0.1',
spacetimePort: 3120,
spacetimeDataDir: 'server-rs/.spacetimedb/local/data',
database: 'genarrative-test',
watch: false,
},
state: {
apiTarget: 'http://127.0.0.1:8090',
adminWebTargetHost: '127.0.0.1',
spacetimeServer: 'http://127.0.0.1:3120',
},
services: new Map([
[
'web',
{
child: {pid: 4321},
runtime: {
status: 'running',
pid: 4321,
host: '0.0.0.0',
port: 3010,
url: 'http://127.0.0.1:3010',
command: 'node scripts/vite-cli.mjs --port=3010',
startedAt: updatedAt,
updatedAt,
exitCode: null,
signal: null,
},
},
],
]),
};
const snapshot = buildDevStackSnapshot(runner, updatedAt);
expect(snapshot.schemaVersion).toBe(1);
expect(snapshot.command).toBe('web');
expect(snapshot.database).toBe('genarrative-test');
expect(snapshot.services.web).toMatchObject({
status: 'running',
pid: 4321,
host: '0.0.0.0',
port: 3010,
url: 'http://127.0.0.1:3010',
command: 'node scripts/vite-cli.mjs --port=3010',
});
expect(snapshot.services['api-server']).toMatchObject({
status: 'idle',
pid: null,
port: 8090,
url: 'http://127.0.0.1:8090',
});
});
});
describe('dev scheduler spacetime reuse guard', () => {
test('记录 URL 可 ping 但没有 spacetime.pid 时不复用宿主', async () => {
const tempDir = mkdtempSync(join(tmpdir(), 'genarrative-spacetime-reuse-'));
@@ -266,18 +404,18 @@ describe('dev scheduler watch routing', () => {
describe('dev scheduler spacetime refresh', () => {
test('解析 spacetime --version 输出里的 tool version', () => {
const version = parseSpacetimeToolVersion(`
A new version of SpacetimeDB is available: v2.2.0 (current: v2.1.0)
spacetimedb tool version 2.2.0; spacetimedb-lib version 2.2.0;
A new version of SpacetimeDB is available: v2.3.0 (current: v2.2.0)
spacetimedb tool version 2.3.0; spacetimedb-lib version 2.3.0;
`);
expect(version).toBe('2.2.0');
expect(version).toBe('2.3.0');
});
test('本机 spacetime 版本和 workspace 锁定版本不一致时直接报清楚', () => {
expect(() =>
assertSpacetimeToolVersionMatchesWorkspace({
toolVersion: '2.1.0',
workspaceVersion: '2.2.0',
workspaceVersion: '2.3.0',
}),
).toThrow('procedure 返回值 BSATN 反序列化失败');
});