abse/museair-usb
USB-C–powered ESP32-C3 controller board with AP63203 buck conversion, AP2112K 3.3V regulation, I²C SEN62 sensor interface, status LED, buttons, UART/I²C headers, and extensive decoupling.
- Version
- 0.5.7
- License
- unset
- Stars
- 0
scripts/compose-native-assembly.mjs
import fs from 'node:fs'
import assert from 'node:assert/strict'
import {createHash} from 'node:crypto'
const read=p=>JSON.parse(fs.readFileSync(p,'utf8'))
const sha=b=>createHash('sha256').update(b).digest('hex')
const manifest=read('routing/reviewed-source-manifest.json')
for(const [p,h]of Object.entries(manifest.files))assert.equal(sha(fs.readFileSync(p)),h,`Source changed: ${p}; review/reroute the circuit before reusing reviewed copper`)
assert.equal(sha(fs.readFileSync('routing/reviewed-index.json')),manifest.reviewedCircuitSha256,'Reviewed circuit changed')
const checked=read('routing/reviewed-index.json'),native=read('dist/assembly/circuit.json')
// SDK migration may add metadata, but must not move copper, terminals or parts.
for(const t of ['source_trace','pcb_smtpad','pcb_plated_hole','pcb_hole'])assert.deepEqual(native.filter(x=>x.type===t),checked.filter(x=>x.type===t),`Native assembly changed ${t}`)
const oldComponents=checked.filter(x=>x.type==='pcb_component'),newComponents=native.filter(x=>x.type==='pcb_component')
assert.equal(newComponents.length,oldComponents.length)
for(let i=0;i<oldComponents.length;i++)for(const key of ['pcb_component_id','source_component_id','center','width','height','rotation','layer'])assert.deepEqual(newComponents[i][key],oldComponents[i][key],`Component ${i} ${key} changed`)
const mechanicalSources=native.filter(x=>x.type==='source_component'&&x.ftype==='subassembly')
assert.equal(mechanicalSources.length,18,'Expected 18 independent mechanical parts')
const byId=new Map(mechanicalSources.map((x,i)=>[x.source_component_id,`source_component_mechanical_${i}`]))
const mechanicalCad=native.filter(x=>x.type==='cad_component'&&byId.has(x.source_component_id))
assert.equal(mechanicalCad.length,18)
// Publish small immutable GLBs; the board itself stays native circuit JSON.
const releaseId='2085d6f0-60b7-4a04-ae10-7dd1e928ffb7'
for(const cad of mechanicalCad){
if(!cad.model_glb_url?.startsWith('data:model/gltf-binary;base64,'))throw Error('Expected imported mechanical GLB')
const bytes=Buffer.from(cad.model_glb_url.split(',')[1],'base64')
const source=mechanicalSources.find(x=>x.source_component_id===cad.source_component_id)
const path=`assembly-assets/native-${source.name.toLowerCase()}-${sha(bytes).slice(0,16)}.glb`
fs.writeFileSync(path,bytes)
cad.model_glb_url=`https://registry-api.tscircuit.com/package_files/download?package_release_id=${releaseId}&file_path=${encodeURIComponent(path)}`
}
const mechanical=[...mechanicalSources.map(x=>({...x,source_component_id:byId.get(x.source_component_id)})),...mechanicalCad.map((x,i)=>({...x,cad_component_id:`cad_component_mechanical_${i}`,source_component_id:byId.get(x.source_component_id)}))]
const sensor=checked.find(x=>x.type==='source_component'&&x.name==='U3')
const boardForAssembly=checked.filter(x=>!(x.type==='cad_component'&&x.source_component_id===sensor.source_component_id))
const assembly=[...structuredClone(boardForAssembly),...mechanical]
assert.equal(assembly.filter(x=>x.type==='pcb_board').length,1)
assert.equal(assembly.filter(x=>x.type==='cad_component'&&x.pcb_component_id).length,65)
assert(!assembly.some(x=>x.type.includes('error')))
for(const t of ['pcb_trace','pcb_via','pcb_copper_pour'])assert.deepEqual(assembly.filter(x=>x.type===t),checked.filter(x=>x.type===t))
fs.mkdirSync('dist/index',{recursive:true});fs.mkdirSync('dist/assembly.circuit',{recursive:true})
fs.writeFileSync('dist/index/circuit.json',JSON.stringify(checked))
for(const p of ['dist/assembly/circuit.json','dist/assembly.circuit/circuit.json'])fs.writeFileSync(p,JSON.stringify(assembly))
const {convertCircuitJsonToPcbSvg,convertCircuitJsonToSchematicSvg}=await import('circuit-to-svg')
for(const [name,j]of [['index',checked],['assembly',assembly]]){
fs.writeFileSync(`dist/${name}/pcb.svg`,convertCircuitJsonToPcbSvg(j))
fs.writeFileSync(`dist/${name}/schematic.svg`,convertCircuitJsonToSchematicSvg(j))
}
const report={status:'pass',real_pcb_boards:1,real_pcb_component_models:65,mechanical_parts:18,no_exported_pcb_model:true,reviewed_main_circuit_exactly_preserved:true,geometry_and_terminal_guard:'pads, holes, source trace connectivity and component positions verified against the new SDK render',reviewed_copper_exactly_preserved:true,mechanical_frame:{rotation_z_degrees:-90,translation_mm:[0,2.5,-37.8]},fabrication_files_changed:false}
fs.mkdirSync('validation',{recursive:true});fs.writeFileSync('validation/native-assembly-review.json',JSON.stringify(report,null,2));console.log('Native assembly verified:',JSON.stringify(report))