19 lines
6.4 KiB
TypeScript
19 lines
6.4 KiB
TypeScript
import { createMockFacade } from './facade/mockFacade'
|
|
import { createInspection } from './facade/inspection'
|
|
type InspectionReport = { schemaVersion: 1; status: 'pass' | 'failed'; browserId: 'chrome'; crossOriginIsolated: boolean; shape?: { volume: number; area: number; valid: boolean; solids: number; faces: number; vertices: number }; measurements?: { distance: number; angle: number; volume: number; area: number; deviation: number; deviationStatus: string; sectionArea: number; topoRefs: number; stableRefs: number; unresolvedRefs: string[]; csvRows: number }; persistence?: { mode: string; byteLength: number; hash: string; roundTrip: boolean; released: boolean }; opfs?: { markerSuite: string; measurements: number; markerRemoved: boolean }; afterRelease?: { shapeCount: number; kernelReferenceCount: number }; error?: string }
|
|
const sha256 = async (bytes: Uint8Array) => [...new Uint8Array(await crypto.subtle.digest('SHA-256', Uint8Array.from(bytes).buffer))].map((value) => value.toString(16).padStart(2, '0')).join('')
|
|
const run = async (): Promise<InspectionReport> => {
|
|
const facade = createMockFacade(); const report: InspectionReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true }
|
|
try {
|
|
if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome Inspection evidence requires isolated OPFS.')
|
|
await facade.project.list(); const shape = await facade.geometry.createBox({ documentId: 'inspection', documentVersion: 1, width: 4, length: 5, height: 6 }); const mass = await facade.geometry.massProperties(shape); const quality = await facade.geometry.qualityReport(shape)
|
|
const inspection = createInspection('inspection', 'Box inspection'); inspection.registerShape({ id: shape.id, label: 'Bitbybit Box', volume: mass.volume, area: mass.surfaceArea, structuralValid: quality.structuralValid, bounds: { min: quality.boundingBox.min, max: quality.boundingBox.max }, topoRefs: [{ persistentId: 'Face1', kind: 'face', status: 'stable' }, { persistentId: 'Face2', kind: 'face', status: 'ambiguous' }] }); const distance = inspection.measureDistance('distance', [0, 0, 0], [3, 4, 0]); const angle = inspection.measureAngle('angle', [1, 0, 0], [0, 1, 0]); const volume = inspection.measureShape('volume', shape.id, 'volume'); const area = inspection.measureShape('area', shape.id, 'area'); const deviation = inspection.measureDeviation('deviation', 10, 10.01, 0.02); const section = inspection.section(shape.id, (quality.boundingBox.min[2] + quality.boundingBox.max[2]) / 2); const topoRefs = inspection.topoRefReport(shape.id); const csv = inspection.exportCsv(); const snapshot = inspection.snapshot();
|
|
const payload = new TextEncoder().encode(JSON.stringify(snapshot)); const stored = await facade.project.resource.put(payload, 'application/vnd.bitbybit.inspection+json'); const loaded = await facade.project.resource.get(stored.hash); const roundTrip = loaded !== null && await sha256(loaded) === await sha256(payload); await facade.project.resource.release(stored.hash); const released = await facade.project.resource.get(stored.hash) === null
|
|
const directory = await navigator.storage.getDirectory(); const markerName = 'inspection-chrome-marker.json'; const marker = await directory.getFileHandle(markerName, { create: true }); const writable = await marker.createWritable(); await writable.write(JSON.stringify({ suite: 'INSP-ALL', measurements: snapshot.measurements.length })); await writable.close(); const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; measurements: number }; await directory.removeEntry(markerName); let markerRemoved = false; try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
|
|
report.shape = { volume: mass.volume, area: mass.surfaceArea, valid: quality.structuralValid, solids: quality.solids, faces: quality.faces, vertices: quality.vertices }; report.measurements = { distance: distance.value, angle: angle.value, volume: volume.value, area: area.value, deviation: deviation.value, deviationStatus: deviation.status, sectionArea: section.area, topoRefs: topoRefs.total, stableRefs: topoRefs.stable, unresolvedRefs: topoRefs.unresolved, csvRows: csv.trim().split('\n').length }; report.persistence = { mode: facade.project.capabilities().mode, byteLength: stored.byteLength, hash: stored.hash, roundTrip, released }; report.opfs = { markerSuite: markerPayload.suite, measurements: markerPayload.measurements, markerRemoved }; report.status = report.shape.valid && report.shape.solids === 1 && report.measurements.distance === 5 && report.measurements.angle === 90 && report.measurements.volume === report.shape.volume && report.measurements.area === report.shape.area && Math.abs(report.measurements.deviation - 0.01) < 1e-12 && report.measurements.deviationStatus === 'resolved' && Math.abs(report.measurements.sectionArea - 24) < 1e-5 && report.measurements.topoRefs === 2 && report.measurements.stableRefs === 1 && JSON.stringify(report.measurements.unresolvedRefs) === JSON.stringify(['Face2']) && report.measurements.csvRows === 6 && report.persistence.mode === 'sqlite-opfs' && report.persistence.roundTrip && report.persistence.released && report.opfs.markerSuite === 'INSP-ALL' && report.opfs.measurements === 5 && report.opfs.markerRemoved ? 'pass' : 'failed'
|
|
} catch (error) { report.error = error instanceof Error ? error.stack || error.message : String(error) } finally { facade.geometry.dispose(); await new Promise((resolve) => setTimeout(resolve, 0)); const capabilities = facade.geometry.capabilities(); report.afterRelease = { shapeCount: capabilities.shapeCount, kernelReferenceCount: capabilities.kernelReferenceCount }; if (capabilities.shapeCount !== 0 || capabilities.kernelReferenceCount !== 0) { report.status = 'failed'; report.error = `${report.error ? `${report.error} ` : ''}Shape ownership gate failed.` } }
|
|
return report
|
|
}
|
|
run().then((report) => { ;(window as Window & { __bitbybitInspectionReport?: InspectionReport }).__bitbybitInspectionReport = report; document.documentElement.dataset.status = report.status; document.querySelector('#result')!.textContent = JSON.stringify(report, null, 2) }).catch((error) => { document.documentElement.dataset.status = 'failed'; document.querySelector('#result')!.textContent = error instanceof Error ? error.stack || error.message : String(error) })
|
|
export {}
|