@peripheries/engine 0.2.0 → 0.3.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peripheries/engine",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Build, check, knowledge and deploy for peripheries component repos",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -12,7 +12,7 @@
12
12
  "node": ">=22"
13
13
  },
14
14
  "bin": {
15
- "engine": "./bin/engine.mjs"
15
+ "engine": "bin/engine.mjs"
16
16
  },
17
17
  "exports": {
18
18
  ".": {
@@ -39,7 +39,15 @@
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@peripheries/renderer": ">=0.6.2 <1",
42
- "@peripheries/runtime": ">=0.6.2 <1"
42
+ "@peripheries/runtime": ">=0.6.2 <1",
43
+ "vite": ">=8.2 <9",
44
+ "@vitejs/plugin-react": ">=6 <7",
45
+ "typescript": ">=5.6 <6"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "typescript": {
49
+ "optional": true
50
+ }
43
51
  },
44
52
  "publishConfig": {
45
53
  "access": "public"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{createRequire}from"node:module";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue}from"../lib/env.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{runCommand}from"../lib/run.mjs";import{EXTERNALS}from"./externals.mjs";const argv=process.argv.slice(2);const only=parseFlagValue(argv,"lineage");const config=await loadConfig();const lineages=only?config.lineages.filter(l=>l.name===only):config.lineages;if(only&&lineages.length===0)throw new Error(`[build] --lineage ${only} is not in the config's lineages`);const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const importFromRepo=name=>import(pathToFileURL(fromRepo.resolve(name)).href);function check(label,status){if(status!==0){console.error(`[build] ${label} failed (exit ${status}).`);process.exit(status??1)}}if(!argv.includes("--no-typecheck")&&existsSync(resolve(REPO_ROOT,"tsconfig.build.json"))){console.log("[build] typecheck: tsc -p tsconfig.build.json --noEmit");const tsc=fromRepo.resolve("typescript/bin/tsc");check("typecheck",spawnSync(process.execPath,[tsc,"-p","tsconfig.build.json","--noEmit"],{cwd:REPO_ROOT,stdio:"inherit"}).status)}process.env.NODE_ENV="production";const{build}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;const external=[...EXTERNALS,...config.shared];for(const lineage of lineages){const entry=resolve(REPO_ROOT,lineage.entry);if(!existsSync(entry))throw new Error(`[build] no barrel at ${lineage.entry} for lineage "${lineage.name}"`);console.log(`
3
+ [build] ${lineage.name}: ${lineage.entry} \u2192 ${lineage.dist}/index.js`);await build({configFile:false,mode:"production",root:REPO_ROOT,logLevel:"warn",plugins:[react()],define:{"process.env.NODE_ENV":JSON.stringify("production"),__OA_BUNDLE_NAME__:JSON.stringify(config.org)},resolve:{alias:{"@":resolve(REPO_ROOT,"src")}},optimizeDeps:{exclude:external},build:{lib:{entry,formats:["es"],fileName:()=>"index.js"},rollupOptions:{external},outDir:resolve(REPO_ROOT,lineage.dist),emptyOutDir:true,sourcemap:true}});check(`${lineage.name} contracts`,runCommand("contracts",["--entry",lineage.entry,"--out",`${lineage.dist}/contracts.json`]));check(`${lineage.name} knowledge`,runCommand("knowledge",["--bundle",lineage.name]))}console.log(`
4
+ [build] \u2705 ${lineages.map(l=>l.name).join(", ")}`);
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{execSync,spawnSync}from"node:child_process";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue,parseTargetEnv}from"../lib/env.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{runCommand}from"../lib/run.mjs";const root=REPO_ROOT;const argv=process.argv.slice(2);const target=parseTargetEnv(argv);const targetFlag=target==="local"?[]:[`--${target}`];const skipBuild=argv.includes("--skip-build");const only=parseFlagValue(argv,"lineage");const{lineages}=await loadConfig();const selected=only?lineages.filter(l=>l.name===only):lineages;if(only&&selected.length===0)throw new Error(`--lineage ${only} is not in the config's lineages (${lineages.map(l=>l.name).join(", ")})`);const git=args=>execSync(`git ${args}`,{cwd:root,encoding:"utf8"}).trim();const version=process.env.VERSION||(git("status --porcelain")===""?`v_${git("rev-parse --short HEAD")}`:`v_${target}_${new Date().toISOString().replace(/[-:T]/g,"").slice(0,14)}`);function check(label,status){if(status!==0){console.error(`[deploy] ${label} failed (exit ${status}). Later lineages were not deployed.`);process.exit(status??1)}}function build(lineage){const{script,env={}}=lineage.build;console.log(`
3
- [deploy] ${lineage.name} build: npm run ${script}`);const r=spawnSync("npm",["run",script],{cwd:root,stdio:"inherit",env:{...process.env,VERSION:version,...env}});check(`${lineage.name} build`,r.status)}function step(lineage,command,args){console.log(`
2
+ import{execSync,spawnSync}from"node:child_process";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue,parseTargetEnv}from"../lib/env.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{runCommand}from"../lib/run.mjs";const root=REPO_ROOT;const argv=process.argv.slice(2);const target=parseTargetEnv(argv);const targetFlag=target==="local"?[]:[`--${target}`];const skipBuild=argv.includes("--skip-build");const only=parseFlagValue(argv,"lineage");const{lineages}=await loadConfig();const selected=only?lineages.filter(l=>l.name===only):lineages;if(only&&selected.length===0)throw new Error(`--lineage ${only} is not in the config's lineages (${lineages.map(l=>l.name).join(", ")})`);const git=args=>execSync(`git ${args}`,{cwd:root,encoding:"utf8"}).trim();const version=process.env.VERSION||(git("status --porcelain")===""?`v_${git("rev-parse --short HEAD")}`:`v_${target}_${new Date().toISOString().replace(/[-:T]/g,"").slice(0,14)}`);function check(label,status){if(status!==0){console.error(`[deploy] ${label} failed (exit ${status}). Later lineages were not deployed.`);process.exit(status??1)}}function build(lineage){if(lineage.build?.script){const{script,env={}}=lineage.build;console.log(`
3
+ [deploy] ${lineage.name} build: npm run ${script}`);const r=spawnSync("npm",["run",script],{cwd:root,stdio:"inherit",env:{...process.env,VERSION:version,...env}});check(`${lineage.name} build`,r.status);return}console.log(`
4
+ [deploy] ${lineage.name} build: engine build --lineage ${lineage.name}`);check(`${lineage.name} build`,runCommand("build",["--lineage",lineage.name],{VERSION:version}))}function step(lineage,command,args){console.log(`
4
5
  [deploy] ${lineage.name} ${command}: engine ${command} ${args.join(" ")}`);check(`${lineage.name} ${command}`,runCommand(command,args,{VERSION:version}))}console.log(`[deploy] target=${target} version=${version} lineages=${selected.map(l=>l.name).join(", ")}`);for(const lineage of selected){if(!skipBuild)build(lineage);const bundle=lineage.name==="main"?[]:["--bundle",lineage.name];step(lineage,"upload",[...targetFlag,...bundle]);step(lineage,"register",[...targetFlag,...bundle]);step(lineage,"cutover",[...targetFlag,"--version",version,...bundle])}console.log(`
5
6
  [deploy] \u2705 ${selected.map(l=>l.name).join(", ")} live at ${version} on ${target}.`);
package/src/index.d.mts CHANGED
@@ -14,6 +14,7 @@ export type LineageSpec =
14
14
  export type EngineConfig = {
15
15
  org: string;
16
16
  lineages?: LineageSpec[];
17
+ shared?: string[];
17
18
  checks?: {
18
19
  run?: string[];
19
20
  peerRepos?: string[];
@@ -27,7 +28,7 @@ export type Lineage = {
27
28
  entry: string;
28
29
  sourceRoot: string | null;
29
30
  dist: string;
30
- build: { script: string; env?: Record<string, string> };
31
+ build?: { script: string; env?: Record<string, string> };
31
32
  };
32
33
 
33
34
  export const DEFAULT_CHECKS: string[];
@@ -1 +1 @@
1
- import{existsSync,readFileSync}from"node:fs";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{REPO_ROOT}from"./root.mjs";const MAIN_LINEAGE="main";const DEFAULT_CHECKS=["validate-externals","check-wired","check-routes","check-design-refs","check-skills","check-work-orders","check-cutover-ownership","check-fixtures","knowledge"];const PUBLIC_LINEAGE="public";const CONFIG_FILES=["engine.config.ts","engine.config.mts","engine.config.mjs","engine.config.js"];function defineConfig(config){return config}function lineageDefaults(name,kind){if(name===MAIN_LINEAGE){return{name,kind:"main",entry:"src/index.ts",sourceRoot:null,dist:"dist",build:{script:"build"}}}if(name===PUBLIC_LINEAGE){return{name,kind:"public",entry:"src/index.public.ts",sourceRoot:"components/public/",dist:"dist-public",build:{script:"build:public"}}}const k=kind??"page";return{name,kind:k,entry:`src/index.${name}.ts`,sourceRoot:k==="form"?`components/form/${name}/`:`components/project_page/${name}/`,dist:`dist-bundles/${name}`,build:{script:"build:bundle",env:{OA_PROJECT_BUNDLE:name}}}}function normalize(raw,origin){if(!raw||typeof raw!=="object")throw new Error(`[engine] ${origin} must export a config object`);if(typeof raw.org!=="string"||!raw.org)throw new Error(`[engine] ${origin}: \`org\` (the org slug) is required`);const list=raw.lineages??[MAIN_LINEAGE];const lineages=list.map(l=>{const spec=typeof l==="string"?{name:l}:l;if(!spec?.name)throw new Error(`[engine] ${origin}: every lineage needs a name`);return{...lineageDefaults(spec.name,spec.kind),...spec}});if(lineages[0]?.name!==MAIN_LINEAGE)throw new Error(`[engine] ${origin}: lineages must start with "main"`);const gated=raw.checks?.gated??{};const checks={run:raw.checks?.run??DEFAULT_CHECKS,peerRepos:raw.checks?.peerRepos??[],gated:{projectTemplates:gated.projectTemplates??[],orgTemplates:gated.orgTemplates??[]}};return{org:raw.org,lineages,checks}}let cached;async function loadConfig(){if(cached)return cached;const file=CONFIG_FILES.map(f=>resolve(REPO_ROOT,f)).find(f=>existsSync(f));if(file){const mod=await import(pathToFileURL(file).href);cached=normalize(mod.default??mod,file);return cached}const pkg=JSON.parse(readFileSync(resolve(REPO_ROOT,"package.json"),"utf8"));let org=pkg.oaBundle?.name;const orgJson=resolve(REPO_ROOT,"template/config/org.json");if(existsSync(orgJson))org=JSON.parse(readFileSync(orgJson,"utf8")).slug??org;const lineages=(pkg.oaBundle?.lineages??[MAIN_LINEAGE]).map(n=>n==="workbooks"?{name:n,kind:"form"}:n);cached=normalize({org,lineages},"package.json oaBundle");return cached}async function lineageConfig(name){const{lineages}=await loadConfig();return lineages.find(l=>l.name===name)??lineageDefaults(name)}export{DEFAULT_CHECKS,MAIN_LINEAGE,PUBLIC_LINEAGE,defineConfig,lineageConfig,lineageDefaults,loadConfig};
1
+ import{existsSync,readFileSync}from"node:fs";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{REPO_ROOT}from"./root.mjs";const MAIN_LINEAGE="main";const DEFAULT_CHECKS=["validate-externals","check-wired","check-routes","check-design-refs","check-skills","check-work-orders","check-cutover-ownership","check-fixtures","knowledge"];const PUBLIC_LINEAGE="public";const CONFIG_FILES=["engine.config.ts","engine.config.mts","engine.config.mjs","engine.config.js"];function defineConfig(config){return config}function lineageDefaults(name,kind){if(name===MAIN_LINEAGE){return{name,kind:"main",entry:"src/index.ts",sourceRoot:null,dist:"dist"}}if(name===PUBLIC_LINEAGE){return{name,kind:"public",entry:"src/index.public.ts",sourceRoot:"components/public/",dist:"dist-public"}}const k=kind??"page";return{name,kind:k,entry:`src/index.${name}.ts`,sourceRoot:k==="form"?`components/form/${name}/`:`components/project_page/${name}/`,dist:`dist-bundles/${name}`}}function normalize(raw,origin){if(!raw||typeof raw!=="object")throw new Error(`[engine] ${origin} must export a config object`);if(typeof raw.org!=="string"||!raw.org)throw new Error(`[engine] ${origin}: \`org\` (the org slug) is required`);const list=raw.lineages??[MAIN_LINEAGE];const lineages=list.map(l=>{const spec=typeof l==="string"?{name:l}:l;if(!spec?.name)throw new Error(`[engine] ${origin}: every lineage needs a name`);return{...lineageDefaults(spec.name,spec.kind),...spec}});if(lineages[0]?.name!==MAIN_LINEAGE)throw new Error(`[engine] ${origin}: lineages must start with "main"`);const gated=raw.checks?.gated??{};const checks={run:raw.checks?.run??DEFAULT_CHECKS,peerRepos:raw.checks?.peerRepos??[],gated:{projectTemplates:gated.projectTemplates??[],orgTemplates:gated.orgTemplates??[]}};const shared=raw.shared??[];return{org:raw.org,lineages,checks,shared}}let cached;async function loadConfig(){if(cached)return cached;const file=CONFIG_FILES.map(f=>resolve(REPO_ROOT,f)).find(f=>existsSync(f));if(file){const mod=await import(pathToFileURL(file).href);cached=normalize(mod.default??mod,file);return cached}const pkg=JSON.parse(readFileSync(resolve(REPO_ROOT,"package.json"),"utf8"));let org=pkg.oaBundle?.name;const orgJson=resolve(REPO_ROOT,"template/config/org.json");if(existsSync(orgJson))org=JSON.parse(readFileSync(orgJson,"utf8")).slug??org;const lineages=(pkg.oaBundle?.lineages??[MAIN_LINEAGE]).map(n=>n==="workbooks"?{name:n,kind:"form"}:n);cached=normalize({org,lineages},"package.json oaBundle");return cached}async function lineageConfig(name){const{lineages}=await loadConfig();return lineages.find(l=>l.name===name)??lineageDefaults(name)}export{DEFAULT_CHECKS,MAIN_LINEAGE,PUBLIC_LINEAGE,defineConfig,lineageConfig,lineageDefaults,loadConfig};
package/src/lib/run.mjs CHANGED
@@ -1 +1 @@
1
- import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{dirname,resolve}from"node:path";import{fileURLToPath}from"node:url";import{REPO_ROOT}from"./root.mjs";const COMMANDS_DIR=resolve(dirname(fileURLToPath(import.meta.url)),"..","commands");const TSX=import.meta.resolve("tsx/esm");const COMMANDS={deploy:"deploy.mjs",check:"check.mjs",upload:"upload-bundle.mjs",register:"register-components.mjs",cutover:"cutover.mjs",knowledge:"emit-knowledge.mjs",contracts:"emit-contracts.mjs","bundle-size":"bundle-size.mjs","check-bundle-leak":"check-bundle-leak.mjs","validate-externals":"validate-externals.mjs","check-wired":"check-wired-bindings.mjs","check-routes":"check-template-routes.mjs","check-design-refs":"check-design-refs.mjs","check-skills":"check-skills.mjs","check-work-orders":"check-work-orders.mjs","check-cutover-ownership":"check-cutover-ownership.mjs","check-fixtures":"check-fixtures.mjs","check-props-schema":"check-props-schema.mjs"};function commandFile(name){if(!COMMANDS[name])return null;const file=resolve(COMMANDS_DIR,COMMANDS[name]);const ts=file.replace(/\.mjs$/,".mts");return existsSync(file)?file:existsSync(ts)?ts:null}function runCommand(name,args=[],env={}){const file=commandFile(name);if(!file)throw new Error(`[engine] unknown command "${name}"`);const r=spawnSync(process.execPath,["--import",TSX,file,...args],{cwd:REPO_ROOT,stdio:"inherit",env:{...process.env,ENGINE_ROOT:REPO_ROOT,...env}});return r.status??1}export{COMMANDS,commandFile,runCommand};
1
+ import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{dirname,resolve}from"node:path";import{fileURLToPath}from"node:url";import{REPO_ROOT}from"./root.mjs";const COMMANDS_DIR=resolve(dirname(fileURLToPath(import.meta.url)),"..","commands");const TSX=import.meta.resolve("tsx/esm");const COMMANDS={build:"build.mjs",deploy:"deploy.mjs",check:"check.mjs",upload:"upload-bundle.mjs",register:"register-components.mjs",cutover:"cutover.mjs",knowledge:"emit-knowledge.mjs",contracts:"emit-contracts.mjs","bundle-size":"bundle-size.mjs","check-bundle-leak":"check-bundle-leak.mjs","validate-externals":"validate-externals.mjs","check-wired":"check-wired-bindings.mjs","check-routes":"check-template-routes.mjs","check-design-refs":"check-design-refs.mjs","check-skills":"check-skills.mjs","check-work-orders":"check-work-orders.mjs","check-cutover-ownership":"check-cutover-ownership.mjs","check-fixtures":"check-fixtures.mjs","check-props-schema":"check-props-schema.mjs"};function commandFile(name){if(!COMMANDS[name])return null;const file=resolve(COMMANDS_DIR,COMMANDS[name]);const ts=file.replace(/\.mjs$/,".mts");return existsSync(file)?file:existsSync(ts)?ts:null}function runCommand(name,args=[],env={}){const file=commandFile(name);if(!file)throw new Error(`[engine] unknown command "${name}"`);const r=spawnSync(process.execPath,["--import",TSX,file,...args],{cwd:REPO_ROOT,stdio:"inherit",env:{...process.env,ENGINE_ROOT:REPO_ROOT,...env}});return r.status??1}export{COMMANDS,commandFile,runCommand};