This commit is contained in:
33333-33333 2026-07-31 12:54:46 +09:00
commit c3a6f5ff37
80 changed files with 2512 additions and 1625 deletions

View file

@ -0,0 +1,28 @@
'use strict';
const fs=require('fs');
const path=require('path');
const root=path.resolve(__dirname,'..');
const governed=[
'archive-codec.js',
'build-meta.js',
'runtime-config.js',
'shared-contracts.js',
'client/input/frame-scheduler.js',
'client/input/drag.js',
'client/input/gesture-coordinator.js',
'client/input/interaction-state.js',
'client/ui/cursor.js',
'server/http-router.js',
'server/auth.js',
'server/json-repository.js',
'server/player-service.js'
];
const maximum=180,violations=[];
for(const relative of governed){
const file=path.join(root,relative),lines=fs.readFileSync(file,'utf8').split(/\r?\n/);
lines.forEach((line,index)=>{if(line.length>maximum)violations.push(`${relative}:${index+1} (${line.length})`)});
}
if(violations.length)throw new Error(`Handwritten source lines exceed ${maximum} columns:\n${violations.join('\n')}`);
console.log(`Source policy passed for ${governed.length} handwritten modules (${maximum}-column maximum)`);

View file

@ -0,0 +1,14 @@
'use strict';
const fs=require('fs');
const path=require('path');
const root=path.resolve(__dirname,'..');
const packageJson=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8'));
const config=JSON.parse(fs.readFileSync(path.join(root,'build-config.json'),'utf8'));
if(!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(packageJson.version))throw new Error('package.json version must use major.minor.patch');
const appVersion=packageJson.version.split('.').slice(0,2).join('.');
for(const key of['SAVE_SCHEMA','STORAGE_SCHEMA','IDB_LAYOUT_VERSION','FIELD_STORAGE_FORMAT','GAMEPLAY_DATA_VERSION','GENERATOR_VERSION'])if(!Number.isSafeInteger(config[key])||config[key]<1)throw new Error(`Invalid ${key}`);
if(typeof config.WORLD_GENERATION!=='string'||!config.WORLD_GENERATION)throw new Error('Invalid WORLD_GENERATION');
const meta={APP_VERSION:appVersion,PACKAGE_VERSION:packageJson.version,...config};
const source=`'use strict';\n// Generated from package.json and build-config.json. Do not edit.\n(function attachBuildMeta(root,factory){\n const api=factory();\n if(typeof module==='object'&&module.exports)module.exports=api;\n if(root)root.BendBuildMeta=api;\n})(typeof globalThis!=='undefined'?globalThis:this,()=>Object.freeze(${JSON.stringify(meta,null,2)}));\n`;
fs.writeFileSync(path.join(root,'build-meta.js'),source);

View file

@ -0,0 +1,22 @@
'use strict';
const fs=require('fs');
const path=require('path');
const root=path.resolve(__dirname,'..');
const sourcePath=path.join(root,'store-catalog.json');
const targetPath=path.join(root,'store-catalog.generated.js');
const catalog=JSON.parse(fs.readFileSync(sourcePath,'utf8'));
if(!Array.isArray(catalog)||catalog.length===0)throw new Error('Store catalog must be a non-empty array');
const ids=new Set();
for(const item of catalog){
if(!item||typeof item!=='object'||typeof item.id!=='string'||!/^[A-Za-z0-9:_-]+$/.test(item.id))throw new Error('Store catalog contains an invalid item ID');
if(ids.has(item.id))throw new Error(`Duplicate store item: ${item.id}`);
if(!Number.isSafeInteger(item.cost)||item.cost<=0)throw new Error(`Invalid cost for ${item.id}`);
if(item.cursorStyle!==null&&typeof item.cursorStyle!=='string')throw new Error(`Invalid cursorStyle for ${item.id}`);
if(typeof item.scoreLens!=='boolean')throw new Error(`Invalid scoreLens for ${item.id}`);
ids.add(item.id);
}
const body=`'use strict';\n// Generated from store-catalog.json by scripts/generate-store-catalog.js. Do not edit.\nglobalThis.BendStoreCatalog=Object.freeze(${JSON.stringify(catalog)}.map(item=>Object.freeze(item)));\n`;
fs.writeFileSync(targetPath,body);

View file

@ -0,0 +1,20 @@
'use strict';
const fs=require('fs');
const path=require('path');
const root=path.resolve(__dirname,'..');
const corePath=path.join(root,'puzzle-core.js');
const patternsPath=path.join(root,'puzzle-patterns.js');
const source=fs.readFileSync(corePath,'utf8');
const loader="const PuzzlePatterns=root.BendPuzzlePatterns||(typeof module==='object'&&module.exports?require('./puzzle-patterns'):null);\nif(!PuzzlePatterns)throw new Error('BendPuzzlePatterns is not loaded');\nconst MICRO_PATTERN_SETS=PuzzlePatterns.MICRO_PATTERN_SETS;\n";
if(source.includes("require('./puzzle-patterns')")){
if(!fs.existsSync(patternsPath))throw new Error('puzzle-core.js is split but puzzle-patterns.js is missing');
process.exit(0);
}
const start=source.indexOf('const MICRO_PATTERN_SETS='),end=source.indexOf('\nconst H_PORT_PROFILES=',start);
if(start<0||end<0)throw new Error('Could not locate the generated micro-pattern table');
const declaration=source.slice(start,end),expression=declaration.slice('const MICRO_PATTERN_SETS='.length,-1);
const patternModule=`'use strict';\n// Generated puzzle routing patterns, split from puzzle-core.js. Do not edit by hand.\n(function(root,factory){const api=factory();if(typeof module==='object'&&module.exports)module.exports=api;if(root)root.BendPuzzlePatterns=api})(typeof globalThis!=='undefined'?globalThis:this,()=>Object.freeze({MICRO_PATTERN_SETS:Object.freeze(${expression})}));\n`;
fs.writeFileSync(patternsPath,patternModule);
fs.writeFileSync(corePath,source.slice(0,start)+loader+source.slice(end+1));