@primocaredentgroup/dental-digital-intake-shared 0.1.1
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/dist/index.d.ts +585 -0
- package/dist/index.js +1568 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/externalReferences.ts","../src/exocadParser.ts","../src/threeShapeParser.ts","../src/cadProjectParser.ts","../src/zipExtractor.ts"],"sourcesContent":["/**\n * Borsa riferimenti esterni vendor-agnostic (persistita su `digitalAcquisitions` e nel manifest).\n * Solo valori sicuri come JSON Convex — niente `Date`, `undefined`, prototipi custom.\n */\nexport type ExternalReferenceJsonValue =\n | string\n | number\n | boolean\n | null\n | ExternalReferenceJsonValue[]\n | ExternalReferencesMap;\n\nexport type ExternalReferencesMap = {\n readonly [key: string]: ExternalReferenceJsonValue;\n};\n\n/** Merge superficiale: le chiavi in `patch` sovrascrivono quelle in `base`. */\nexport function mergeExternalReferences(\n base: ExternalReferencesMap | undefined,\n patch: ExternalReferencesMap,\n): ExternalReferencesMap {\n return { ...(base ?? {}), ...patch };\n}\n","/**\n * Parser Exocad strutturato per pacchetti ZIP di progetto.\n *\n * Obiettivo: dato l'insieme dei file estratti da uno ZIP Exocad, ricostruire\n * informazioni cliniche/protesiche e soprattutto la **componentistica implantare**.\n *\n * Regole chiave (vedi prompt di riferimento):\n * - `.constructionInfo` è la fonte principale per la componentistica implantare.\n * - `.dentalProject` da solo NON basta a capire se il caso è implantare.\n * - `.modelInfo` arricchisce con librerie analoghi / modello.\n * - `.dentalCAD` è fallback: solo estrazione stringhe ASCII/UTF-8.\n * - Mai inventare SKU commerciali: l'interpretazione del codice geometria è `inferred`.\n *\n * Tutto case-insensitive, robusto a path Windows (`\\\\`) e Unix (`/`), e funziona\n * anche se alcuni file mancano.\n */\n\nexport type ExocadConfidence = \"high\" | \"medium\" | \"low\" | \"inferred\";\n\n/** Valore con livello di confidenza (usato per le interpretazioni dei codici). */\nexport type ExocadInferredValue = {\n value: string;\n confidence: ExocadConfidence;\n};\n\nexport type ExocadParsedGeometry = {\n baseType?: ExocadInferredValue;\n diameterOrPlatform?: ExocadInferredValue;\n abutmentHeight?: ExocadInferredValue;\n};\n\nexport type ExocadImplantComponent = {\n tooth?: string;\n manufacturer?: string;\n platform?: string;\n connection?: string;\n component?: string;\n libraryName?: string;\n libraryPath?: string;\n geometryFile?: string;\n geometryCode?: string;\n parsedGeometry?: ExocadParsedGeometry;\n sourceFiles: string[];\n confidence: ExocadConfidence;\n};\n\nexport type ExocadRestorationItem = {\n tooth: string;\n type?: string;\n material?: string;\n materialCode?: string;\n shade?: string;\n preparationType?: string;\n implantType?: string;\n scanAbutmentScan?: boolean;\n mesialConnector?: boolean;\n};\n\nexport type ExocadStlEntry = {\n fileName: string;\n role: string;\n};\n\nexport type ExocadCaseInfo = {\n patientName: string | null;\n practiceName: string | null;\n practiceId: string | null;\n projectDate: string | null;\n exocadVersion: string | null;\n};\n\nexport type ExocadModelInfo = {\n hasModelBase: boolean;\n hasGingiva: boolean;\n hasWaxup: boolean;\n analogLibrary: string | null;\n analogManufacturer: string | null;\n modelWorkflow: string | null;\n};\n\nexport type ExocadFilesIndex = {\n dentalProject: string[];\n constructionInfo: string[];\n modelInfo: string[];\n dentalCAD: string[];\n stl: string[];\n};\n\nexport type ExocadSummary = {\n case: ExocadCaseInfo;\n restoration: {\n teeth: string[];\n items: ExocadRestorationItem[];\n span: string | null;\n isBridge: boolean;\n };\n implantComponents: ExocadImplantComponent[];\n model: ExocadModelInfo;\n files: ExocadFilesIndex;\n /** Caso implantare se constructionInfo contiene geometria/scan abutment impianto. */\n isImplantCase: boolean;\n warnings: string[];\n};\n\n/** File estratto dal pacchetto (path + byte). */\nexport type ExocadFileEntry = {\n /** Path normalizzato all'interno dello ZIP (slash). */\n path: string;\n data: Uint8Array;\n};\n\ntype ExocadFileKind =\n | \"dentalProject\"\n | \"constructionInfo\"\n | \"modelInfo\"\n | \"dentalCAD\"\n | \"stl\"\n | \"other\";\n\ntype ClassifiedFile = {\n path: string;\n baseName: string;\n ext: string;\n kind: ExocadFileKind;\n data: Uint8Array;\n};\n\nconst KNOWN_MANUFACTURERS = [\n \"Neodent\",\n \"Straumann\",\n \"Nobel Biocare\",\n \"Nobel\",\n \"Camlog\",\n \"BioHorizons\",\n \"Zimmer\",\n \"Dentsply\",\n \"Ankylos\",\n \"Astra\",\n \"Megagen\",\n \"Bredent\",\n \"Rhein\",\n \"Sweden & Martina\",\n \"Sweden\",\n];\n\n/** Mappa abbreviazioni connessione → nome esteso (solo deduzioni sicure). */\nconst CONNECTION_MAP: Record<string, string> = {\n GM: \"Grand Morse\",\n CM: \"Cone Morse\",\n};\n\nconst DENTAL_CAD_KEYWORDS = [\n \"Neodent\",\n \"Straumann\",\n \"Nobel\",\n \"Sweden\",\n \"Camlog\",\n \"BioHorizons\",\n \"Zimmer\",\n \"Dentsply\",\n \"Ankylos\",\n \"Astra\",\n \"Megagen\",\n \"Bredent\",\n \"Rhein\",\n \"analog\",\n \"implant\",\n \"scanbody\",\n \"tibase\",\n \"abutment\",\n \"local-milling\",\n \"local-printing\",\n \"sdfa\",\n \"library\\\\implant\",\n];\n\nfunction baseName(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n return normalized.split(\"/\").pop() ?? normalized;\n}\n\nfunction extensionOf(name: string): string {\n const b = baseName(name);\n const i = b.lastIndexOf(\".\");\n return i <= 0 ? \"\" : b.slice(i + 1).toLowerCase();\n}\n\nfunction decodeText(data: Uint8Array): string {\n let start = 0;\n if (\n data.length >= 3 &&\n data[0] === 0xef &&\n data[1] === 0xbb &&\n data[2] === 0xbf\n ) {\n start = 3;\n }\n return new TextDecoder(\"utf-8\", { fatal: false }).decode(data.subarray(start));\n}\n\nfunction stripTags(raw: string): string {\n return raw\n .replace(/<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>/gi, \"$1\")\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction decodeEntities(raw: string): string {\n return raw\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\");\n}\n\n/** Estensione → kind Exocad. */\nexport function classifyExocadFile(path: string): ExocadFileKind {\n const ext = extensionOf(path);\n switch (ext) {\n case \"dentalproject\":\n return \"dentalProject\";\n case \"constructioninfo\":\n return \"constructionInfo\";\n case \"modelinfo\":\n return \"modelInfo\";\n case \"dentalcad\":\n return \"dentalCAD\";\n case \"stl\":\n return \"stl\";\n default:\n return \"other\";\n }\n}\n\n/** Indicizza i file rilevanti del pacchetto Exocad. */\nexport function detectExocadFiles(entries: ExocadFileEntry[]): {\n classified: ClassifiedFile[];\n files: ExocadFilesIndex;\n} {\n const classified: ClassifiedFile[] = [];\n const files: ExocadFilesIndex = {\n dentalProject: [],\n constructionInfo: [],\n modelInfo: [],\n dentalCAD: [],\n stl: [],\n };\n\n for (const e of entries) {\n const bn = baseName(e.path);\n const ext = extensionOf(e.path);\n const kind = classifyExocadFile(e.path);\n classified.push({ path: e.path, baseName: bn, ext, kind, data: e.data });\n if (kind === \"dentalProject\") files.dentalProject.push(bn);\n else if (kind === \"constructionInfo\") files.constructionInfo.push(bn);\n else if (kind === \"modelInfo\") files.modelInfo.push(bn);\n else if (kind === \"dentalCAD\") files.dentalCAD.push(bn);\n else if (kind === \"stl\") files.stl.push(bn);\n }\n\n return { classified, files };\n}\n\n/** True se nel pacchetto esiste almeno un file Exocad riconoscibile. */\nexport function isExocadPackage(entries: ExocadFileEntry[]): boolean {\n return entries.some((e) => classifyExocadFile(e.path) !== \"other\"\n && classifyExocadFile(e.path) !== \"stl\")\n || entries.some((e) => classifyExocadFile(e.path) !== \"other\");\n}\n\nfunction firstTag(xml: string, tags: string[]): string | undefined {\n for (const tag of tags) {\n const re = new RegExp(`<${tag}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${tag}>`, \"i\");\n const m = xml.match(re);\n if (m?.[1]) {\n const val = stripTags(m[1]);\n if (val) return val;\n }\n }\n return undefined;\n}\n\n/**\n * Estrae il valore di una \"chiave\" sia in forma XML `<Key>val</Key>` sia in\n * forma testuale `Key: val` (case-insensitive). Per la forma testuale prende il\n * resto della riga. Restituisce tutte le occorrenze in ordine.\n */\nfunction valuesForKey(text: string, key: string): string[] {\n const out: string[] = [];\n const tagRe = new RegExp(`<${key}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${key}>`, \"gi\");\n let m: RegExpExecArray | null;\n while ((m = tagRe.exec(text)) !== null) {\n const val = decodeEntities(m[1]!).trim();\n if (val) out.push(val);\n }\n if (out.length === 0) {\n const lineRe = new RegExp(`${key}\\\\s*[:=]\\\\s*([^\\\\r\\\\n]+)`, \"gi\");\n while ((m = lineRe.exec(text)) !== null) {\n const val = decodeEntities(m[1]!).trim();\n if (val) out.push(val);\n }\n }\n return out;\n}\n\nfunction parseBool(raw: string | undefined): boolean | undefined {\n if (raw === undefined) return undefined;\n const v = raw.trim().toLowerCase();\n if (v === \"true\" || v === \"1\" || v === \"yes\") return true;\n if (v === \"false\" || v === \"0\" || v === \"no\") return false;\n return undefined;\n}\n\n// -------------------------------------------------------------------------\n// .dentalProject\n// -------------------------------------------------------------------------\n\nconst TOOTH_BLOCK_RE = /<Tooth\\b[^>]*>([\\s\\S]*?)<\\/Tooth>/gi;\n\nexport function parseDentalProject(text: string): {\n case: ExocadCaseInfo;\n items: ExocadRestorationItem[];\n} {\n const patientLast = firstTag(text, [\"PatientName\"]);\n const patientFirst = firstTag(text, [\"PatientFirstName\"]);\n const patientName =\n [patientFirst, patientLast].filter(Boolean).join(\" \").trim() || null;\n\n const caseInfo: ExocadCaseInfo = {\n patientName,\n practiceName: firstTag(text, [\"PracticeName\"]) ?? null,\n practiceId: firstTag(text, [\"PracticeId\"]) ?? null,\n projectDate: firstTag(text, [\"DateTime\", \"Date\"]) ?? null,\n exocadVersion: firstTag(text, [\"DentalDBProductName\"]) ?? null,\n };\n\n const items: ExocadRestorationItem[] = [];\n for (const m of text.matchAll(TOOTH_BLOCK_RE)) {\n const block = m[1] ?? \"\";\n const tooth = firstTag(block, [\"Number\", \"ToothNumber\", \"FDI\"]);\n if (!tooth) continue;\n const type = firstTag(block, [\"ReconstructionType\"]);\n const material = firstTag(block, [\"MaterialName\"]);\n const materialCode = firstTag(block, [\"Material\"]);\n const shade = firstTag(block, [\"Shade\", \"Color\", \"ToothColor\"]);\n const preparationType = firstTag(block, [\"PreparationType\"]);\n const implantType = firstTag(block, [\"ImplantType\"]);\n const scanAbutmentScan = parseBool(firstTag(block, [\"ScanAbutmentScan\"]));\n const mesialConnector = parseBool(firstTag(block, [\"MesialConnector\"]));\n\n items.push({\n tooth,\n ...(type ? { type } : {}),\n ...(material ? { material } : {}),\n ...(materialCode ? { materialCode } : {}),\n ...(shade && shade !== \"---\" ? { shade } : {}),\n ...(preparationType ? { preparationType } : {}),\n ...(implantType ? { implantType } : {}),\n ...(scanAbutmentScan !== undefined ? { scanAbutmentScan } : {}),\n ...(mesialConnector !== undefined ? { mesialConnector } : {}),\n });\n }\n\n return { case: caseInfo, items };\n}\n\n// -------------------------------------------------------------------------\n// codici geometria\n// -------------------------------------------------------------------------\n\nconst BASE_TYPE_MAP: Record<string, string> = {\n mini: \"Mini Abutment\",\n base: \"Ti-Base\",\n tibase: \"Ti-Base\",\n uni: \"Universal Base\",\n pro: \"Pro Abutment\",\n};\n\nfunction tenths(digits: string): string {\n const n = Number(digits);\n if (!Number.isFinite(n)) return digits;\n return (n / 10).toFixed(1);\n}\n\n/**\n * Interpretazione best-effort (NON definitiva) di un codice geometria tipo\n * `Base_Mini41-D48-AH46`. Tutti i valori sono marcati `inferred`.\n */\nexport function inferGeometryCode(code: string): ExocadParsedGeometry | undefined {\n if (!code) return undefined;\n const out: ExocadParsedGeometry = {};\n\n const noBase = code.replace(/^base[_-]?/i, \"\");\n const firstToken = noBase.split(/[_\\-\\s.]/)[0] ?? \"\";\n const word = firstToken.replace(/\\d+$/, \"\");\n if (word && /[a-z]/i.test(word)) {\n const mapped = BASE_TYPE_MAP[word.toLowerCase()];\n out.baseType = {\n value: mapped ?? word,\n confidence: \"inferred\",\n };\n }\n\n const d = code.match(/D(\\d{2,3})\\b/i);\n if (d?.[1]) {\n out.diameterOrPlatform = { value: tenths(d[1]), confidence: \"inferred\" };\n }\n\n const ah = code.match(/AH(\\d{2,3})\\b/i);\n if (ah?.[1]) {\n out.abutmentHeight = { value: tenths(ah[1]), confidence: \"inferred\" };\n }\n\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction detectManufacturer(...sources: string[]): string | undefined {\n const hay = sources.join(\" \").toLowerCase();\n for (const m of KNOWN_MANUFACTURERS) {\n if (hay.includes(m.toLowerCase())) return m;\n }\n return undefined;\n}\n\nfunction detectConnection(...sources: string[]): string | undefined {\n const text = sources.join(\" \");\n for (const [abbr, full] of Object.entries(CONNECTION_MAP)) {\n const re = new RegExp(`\\\\b${abbr}\\\\b`);\n if (re.test(text)) return full;\n }\n return undefined;\n}\n\n/**\n * Interpreta la stringa `ImplantLibraryEntryDisplayInformation`.\n * Esempio (segmenti separati da `:` o newline):\n * \"Neodent® - Abutment Level (Local Milling) : Mini Abutment GM IO : New SRBB One Step Hybrid\"\n */\nfunction parseDisplayInfo(raw: string): {\n manufacturer?: string;\n platform?: string;\n component?: string;\n libraryName?: string;\n workflow?: string;\n} {\n const segs = raw\n .split(/[\\r\\n:]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n if (segs.length === 0) return {};\n\n const result: ReturnType<typeof parseDisplayInfo> = {};\n\n const seg0 = segs[0]!;\n const dashSplit = seg0.split(/\\s[-–]\\s/);\n if (dashSplit.length >= 2) {\n result.manufacturer = dashSplit[0]!.replace(/[®™©]/g, \"\").trim();\n let platformRaw = dashSplit.slice(1).join(\" - \").trim();\n const wf = platformRaw.match(/\\(([^)]+)\\)/);\n if (wf?.[1]) result.workflow = wf[1].trim();\n platformRaw = platformRaw.replace(/\\([^)]*\\)/g, \"\").trim();\n if (platformRaw) result.platform = platformRaw;\n } else {\n const mfg = detectManufacturer(seg0);\n if (mfg) result.manufacturer = mfg;\n }\n\n if (segs[1]) result.component = segs[1];\n if (segs[2]) result.libraryName = segs[2];\n\n return result;\n}\n\n// -------------------------------------------------------------------------\n// .constructionInfo (fonte primaria componentistica)\n// -------------------------------------------------------------------------\n\nexport function parseConstructionInfo(\n text: string,\n sourceFile: string,\n): { components: ExocadImplantComponent[]; isImplantCase: boolean } {\n const geometryPaths = valuesForKey(text, \"FilenameImplantGeometry\");\n const displayInfos = valuesForKey(text, \"ImplantLibraryEntryDisplayInformation\");\n const scanAbutment = valuesForKey(text, \"ScanAbutmentScan\").some(\n (v) => parseBool(v) === true,\n );\n\n const isImplantCase =\n geometryPaths.length > 0 ||\n scanAbutment ||\n /FilenameImplantGeometry|ImplantLibrary\\b/i.test(text);\n\n const count = Math.max(geometryPaths.length, displayInfos.length);\n const components: ExocadImplantComponent[] = [];\n\n for (let i = 0; i < count; i++) {\n const geomPath = geometryPaths[i];\n const display = displayInfos[i];\n\n const component: ExocadImplantComponent = {\n sourceFiles: [sourceFile],\n confidence: \"high\",\n };\n\n if (geomPath) {\n component.libraryPath = geomPath;\n const file = baseName(geomPath);\n component.geometryFile = file;\n const code = file.replace(/\\.[^.]+$/, \"\");\n component.geometryCode = code;\n const parsed = inferGeometryCode(code);\n if (parsed) component.parsedGeometry = parsed;\n }\n\n if (display) {\n const info = parseDisplayInfo(display);\n if (info.manufacturer) component.manufacturer = info.manufacturer;\n if (info.platform) component.platform = info.platform;\n if (info.component) component.component = info.component;\n if (info.libraryName) component.libraryName = info.libraryName;\n }\n\n if (!component.manufacturer) {\n const mfg = detectManufacturer(geomPath ?? \"\", display ?? \"\");\n if (mfg) component.manufacturer = mfg;\n }\n const connection = detectConnection(\n component.component ?? \"\",\n display ?? \"\",\n geomPath ?? \"\",\n );\n if (connection) component.connection = connection;\n\n if (component.geometryFile || component.component || component.libraryName) {\n components.push(component);\n }\n }\n\n return { components, isImplantCase };\n}\n\n// -------------------------------------------------------------------------\n// .modelInfo\n// -------------------------------------------------------------------------\n\nexport function parseModelInfo(text: string): Partial<ExocadModelInfo> {\n const out: Partial<ExocadModelInfo> = {};\n\n const analogLib =\n valuesForKey(text, \"AnalogLibrary\")[0] ??\n text.match(/([A-Za-z][\\w&]*_Analogs?[\\w-]*)/)?.[1] ??\n text.match(/([A-Za-z][\\w ]*Analog[\\w. ]*)/)?.[1]?.trim();\n if (analogLib) out.analogLibrary = analogLib;\n\n const analogMfg = detectManufacturer(text);\n if (analogMfg) out.analogManufacturer = analogMfg;\n\n if (/local-printing/i.test(text)) out.modelWorkflow = \"local-printing\";\n else if (/local-milling/i.test(text)) out.modelWorkflow = \"local-milling\";\n else if (/printed/i.test(text)) out.modelWorkflow = \"printed model\";\n\n return out;\n}\n\n// -------------------------------------------------------------------------\n// .dentalCAD (solo estrazione stringhe / keyword)\n// -------------------------------------------------------------------------\n\n/** Estrae sequenze stampabili (ASCII/UTF-8) leggibili da un binario. */\nexport function extractStringsFromDentalCAD(\n data: Uint8Array,\n minLength = 4,\n): string[] {\n const out: string[] = [];\n let current = \"\";\n for (let i = 0; i < data.length; i++) {\n const c = data[i]!;\n if (c >= 0x20 && c <= 0x7e) {\n current += String.fromCharCode(c);\n } else {\n if (current.length >= minLength) out.push(current);\n current = \"\";\n }\n }\n if (current.length >= minLength) out.push(current);\n return out;\n}\n\n/** Trova keyword di interesse nelle stringhe estratte dal `.dentalCAD`. */\nexport function findDentalCadKeywords(strings: string[]): string[] {\n const found = new Set<string>();\n const joined = strings.join(\"\\n\");\n for (const kw of DENTAL_CAD_KEYWORDS) {\n const re = new RegExp(kw.replace(/[\\\\]/g, \"\\\\\\\\\"), \"i\");\n if (re.test(joined)) found.add(kw);\n }\n return [...found];\n}\n\n// -------------------------------------------------------------------------\n// STL: ruolo dedotto dal nome file\n// -------------------------------------------------------------------------\n\nexport function inferStlRole(fileName: string): string {\n const n = fileName.toLowerCase();\n if (/modelbase|model_base|baseplate/.test(n)) return \"modelbase\";\n if (/gingiva/.test(n)) return \"gingiva\";\n if (/waxup|wax_up|wax-up/.test(n)) return \"waxup\";\n if (/antagonist|opposing/.test(n)) return \"antagonist\";\n if (/scanbody|scan_body|scan-body/.test(n)) return \"scanbody\";\n if (/abutment/.test(n)) return \"abutment\";\n if (/upper|maxilla|oberkiefer/.test(n)) return \"upper\";\n if (/lower|mandible|unterkiefer/.test(n)) return \"lower\";\n if (/scan|matrix/.test(n)) return \"scan\";\n return \"unknown\";\n}\n\n// -------------------------------------------------------------------------\n// componente: normalizzazione / dedup\n// -------------------------------------------------------------------------\n\nfunction componentKey(c: ExocadImplantComponent): string {\n return [c.geometryCode ?? \"\", c.component ?? \"\", c.libraryName ?? \"\"]\n .join(\"|\")\n .toLowerCase();\n}\n\n/** Unisce componenti duplicati mantenendo la confidenza più alta e i sourceFiles. */\nexport function normalizeImplantComponents(\n components: ExocadImplantComponent[],\n): ExocadImplantComponent[] {\n const byKey = new Map<string, ExocadImplantComponent>();\n const order: string[] = [];\n for (const c of components) {\n const key = componentKey(c);\n const existing = byKey.get(key);\n if (!existing) {\n byKey.set(key, { ...c, sourceFiles: [...new Set(c.sourceFiles)] });\n order.push(key);\n continue;\n }\n existing.sourceFiles = [\n ...new Set([...existing.sourceFiles, ...c.sourceFiles]),\n ];\n existing.manufacturer ??= c.manufacturer;\n existing.platform ??= c.platform;\n existing.connection ??= c.connection;\n existing.component ??= c.component;\n existing.libraryName ??= c.libraryName;\n existing.libraryPath ??= c.libraryPath;\n existing.geometryFile ??= c.geometryFile;\n existing.geometryCode ??= c.geometryCode;\n existing.parsedGeometry ??= c.parsedGeometry;\n existing.tooth ??= c.tooth;\n }\n return order.map((k) => byKey.get(k)!);\n}\n\n// -------------------------------------------------------------------------\n// span / bridge\n// -------------------------------------------------------------------------\n\nfunction computeSpan(teeth: string[]): { span: string | null; isBridge: boolean } {\n if (teeth.length < 2) return { span: null, isBridge: false };\n const nums = teeth.map(Number).sort((a, b) => a - b);\n const span = `${nums[0]}-${nums[nums.length - 1]!}`;\n return { span, isBridge: true };\n}\n\n// -------------------------------------------------------------------------\n// builder principale\n// -------------------------------------------------------------------------\n\nconst EMPTY_CASE: ExocadCaseInfo = {\n patientName: null,\n practiceName: null,\n practiceId: null,\n projectDate: null,\n exocadVersion: null,\n};\n\n/** Costruisce il riassunto Exocad completo dai file estratti dello ZIP. */\nexport function buildExocadSummary(\n entries: ExocadFileEntry[],\n): ExocadSummary | undefined {\n const { classified, files } = detectExocadFiles(entries);\n const hasExocadSidecar = classified.some(\n (f) =>\n f.kind === \"dentalProject\" ||\n f.kind === \"constructionInfo\" ||\n f.kind === \"modelInfo\" ||\n f.kind === \"dentalCAD\",\n );\n if (!hasExocadSidecar) return undefined;\n\n const warnings: string[] = [];\n let caseInfo: ExocadCaseInfo = { ...EMPTY_CASE };\n let restorationItems: ExocadRestorationItem[] = [];\n const rawComponents: ExocadImplantComponent[] = [];\n let isImplantCase = false;\n const model: ExocadModelInfo = {\n hasModelBase: false,\n hasGingiva: false,\n hasWaxup: false,\n analogLibrary: null,\n analogManufacturer: null,\n modelWorkflow: null,\n };\n\n for (const f of classified) {\n if (f.kind === \"dentalProject\") {\n const parsed = parseDentalProject(decodeText(f.data));\n caseInfo = { ...caseInfo, ...nonNull(parsed.case) };\n if (parsed.items.length > 0) restorationItems = parsed.items;\n } else if (f.kind === \"constructionInfo\") {\n const parsed = parseConstructionInfo(decodeText(f.data), f.baseName);\n rawComponents.push(...parsed.components);\n if (parsed.isImplantCase) isImplantCase = true;\n } else if (f.kind === \"modelInfo\") {\n const m = parseModelInfo(decodeText(f.data));\n if (m.analogLibrary) model.analogLibrary = m.analogLibrary;\n if (m.analogManufacturer) model.analogManufacturer = m.analogManufacturer;\n if (m.modelWorkflow) model.modelWorkflow = m.modelWorkflow;\n } else if (f.kind === \"dentalCAD\") {\n const strings = extractStringsFromDentalCAD(f.data);\n const kws = findDentalCadKeywords(strings);\n if (kws.length > 0) {\n warnings.push(\n `dentalCAD: keyword rilevate (medium confidence): ${kws.join(\", \")}.`,\n );\n }\n if (rawComponents.length === 0) {\n const mfg = detectManufacturer(strings.join(\" \"));\n if (mfg) {\n rawComponents.push({\n manufacturer: mfg,\n sourceFiles: [f.baseName],\n confidence: \"medium\",\n });\n }\n }\n }\n }\n\n // model flags dai nomi STL\n const stlEntries: ExocadStlEntry[] = files.stl.map((fileName) => ({\n fileName,\n role: inferStlRole(fileName),\n }));\n model.hasModelBase = stlEntries.some((s) => s.role === \"modelbase\");\n model.hasGingiva = stlEntries.some((s) => s.role === \"gingiva\");\n model.hasWaxup = stlEntries.some((s) => s.role === \"waxup\");\n\n const teeth = [\n ...new Set(restorationItems.map((i) => i.tooth)),\n ].sort((a, b) => Number(a) - Number(b));\n const { span, isBridge } = computeSpan(teeth);\n\n const implantComponents = normalizeImplantComponents(rawComponents);\n\n // warnings standard del prompt\n if (implantComponents.length > 0) {\n warnings.push(\"Commercial SKU not found in Exocad files\");\n if (implantComponents.some((c) => c.parsedGeometry)) {\n warnings.push(\n \"Geometry code interpretation is inferred and requires manufacturer mapping\",\n );\n }\n }\n if (\n !isImplantCase &&\n restorationItems.some((i) => i.implantType && i.implantType !== \"WithoutAbutment\")\n ) {\n isImplantCase = true;\n }\n\n return {\n case: caseInfo,\n restoration: {\n teeth,\n items: restorationItems,\n span,\n isBridge,\n },\n implantComponents,\n model,\n files,\n isImplantCase,\n warnings: [...new Set(warnings)],\n };\n}\n\nfunction nonNull(obj: ExocadCaseInfo): Partial<ExocadCaseInfo> {\n const out: Partial<ExocadCaseInfo> = {};\n (Object.keys(obj) as Array<keyof ExocadCaseInfo>).forEach((k) => {\n if (obj[k] !== null) out[k] = obj[k] as never;\n });\n return out;\n}\n","/**\n * Parser 3Shape Dental System / Dental Designer per pacchetti ZIP esportati.\n *\n * Obiettivo: estrarre dati ordine, restauro, materiale e — quando presente —\n * la **componentistica implantare**, in modo robusto e best-effort.\n *\n * Priorità fonti:\n * 1. XML principale ordine\n * 2. PrintableOrderForm.html\n * 3. Materials.xml\n * 4. External models/<AbutmentKitID>/\n * 5. CAD/*.dcm\n * 6. Scans/**.dcm\n * 7. *.3ml solo se leggibili (spesso ZIP cifrati: saltati senza bloccare)\n *\n * Regole: case-insensitive, path Windows/Unix, mai inventare SKU commerciali,\n * non convertire ToothNumber interno se esiste già il dente FDI nell'ordine.\n */\n\nexport type ThreeShapeConfidence = \"high\" | \"medium\" | \"low\" | \"inferred\";\n\nexport type ThreeShapeImplantComponent = {\n name: string;\n fileName: string;\n type:\n | \"analog\"\n | \"analog_socket\"\n | \"scanbody\"\n | \"link/tibase_candidate\"\n | \"screw_or_transmucosal_component_candidate\"\n | \"manufacturer_component_candidate\"\n | \"unknown\";\n sourcePath: string;\n confidence: ThreeShapeConfidence;\n};\n\nexport type ThreeShapeCase = {\n patientName: string | null;\n orderId: string | null;\n customer: string | null;\n operator: string | null;\n createdAt: string | null;\n groupFolder: string | null;\n software: string;\n softwareVersion: string | null;\n processStatus: string | null;\n scanSource: string | null;\n};\n\nexport type ThreeShapeRestoration = {\n teeth: number[];\n internalToothNumbers: number[];\n name: string | null;\n type: string | null;\n toothElementTypeID: string | null;\n cacheToothTypeClass: string | null;\n materialId: string | null;\n materialName: string | null;\n materialFamily: string | null;\n shaderMaterial: string | null;\n shade: string | null;\n isImplantCase: boolean;\n isScrewRetained: boolean;\n};\n\nexport type ThreeShapeImplant = {\n isImplantCase: boolean;\n manufacturer: string | null;\n connection: string | null;\n connectionId: string | null;\n implantSystemId: string | null;\n abutmentKitId: string | null;\n components: ThreeShapeImplantComponent[];\n};\n\nexport type ThreeShapeFilesIndex = {\n mainXml: string[];\n materialsXml: string[];\n printableOrderForms: string[];\n cadDcm: string[];\n externalModels: string[];\n scans: string[];\n encrypted3ml: string[];\n};\n\nexport type ThreeShapeSummary = {\n source: \"3Shape\";\n case: ThreeShapeCase;\n restoration: ThreeShapeRestoration;\n implant: ThreeShapeImplant;\n files: ThreeShapeFilesIndex;\n warnings: string[];\n};\n\nexport type ThreeShapeFileEntry = {\n /** Path normalizzato all'interno dello ZIP (slash). */\n path: string;\n data: Uint8Array;\n};\n\ntype ThreeShapeFileKind =\n | \"mainXml\"\n | \"materialsXml\"\n | \"printableOrderForm\"\n | \"cadDcm\"\n | \"externalModel\"\n | \"scan\"\n | \"threeMl\"\n | \"other\";\n\ntype ClassifiedFile = {\n path: string;\n baseName: string;\n ext: string;\n kind: ThreeShapeFileKind;\n data: Uint8Array;\n};\n\nconst MANUFACTURERS: Array<{ keys: string[]; name: string }> = [\n { keys: [\"NEODENT\", \"NEOGM\", \"NEO\"], name: \"Neodent\" },\n { keys: [\"STRAUMANN\"], name: \"Straumann\" },\n { keys: [\"NOBEL\"], name: \"Nobel Biocare\" },\n { keys: [\"CAMLOG\"], name: \"Camlog\" },\n { keys: [\"DENTSPLY\"], name: \"Dentsply\" },\n { keys: [\"ASTRA\"], name: \"Astra\" },\n { keys: [\"ZIMMER\"], name: \"Zimmer\" },\n { keys: [\"MEGAGEN\"], name: \"Megagen\" },\n { keys: [\"BREDENT\"], name: \"Bredent\" },\n];\n\nfunction baseName(path: string): string {\n const normalized = path.replace(/\\\\/g, \"/\");\n return normalized.split(\"/\").pop() ?? normalized;\n}\n\nfunction extensionOf(name: string): string {\n const b = baseName(name);\n const i = b.lastIndexOf(\".\");\n return i <= 0 ? \"\" : b.slice(i + 1).toLowerCase();\n}\n\nfunction decodeText(data: Uint8Array): string {\n let start = 0;\n if (\n data.length >= 3 &&\n data[0] === 0xef &&\n data[1] === 0xbb &&\n data[2] === 0xbf\n ) {\n start = 3;\n }\n return new TextDecoder(\"utf-8\", { fatal: false }).decode(data.subarray(start));\n}\n\nfunction decodeEntities(raw: string): string {\n return raw\n .replace(/&/gi, \"&\")\n .replace(/</gi, \"<\")\n .replace(/>/gi, \">\")\n .replace(/"/gi, '\"')\n .replace(/'|'/gi, \"'\")\n .trim();\n}\n\nfunction stripTags(raw: string): string {\n return decodeEntities(\n raw\n .replace(/<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>/gi, \"$1\")\n .replace(/<[^>]+>/g, \" \")\n .replace(/\\s+/g, \" \"),\n );\n}\n\n/** Estrae sequenze stampabili (ASCII) da un binario (best-effort). */\nexport function extractReadableStringsFromBinary(\n data: Uint8Array,\n minLength = 4,\n): string[] {\n const out: string[] = [];\n let current = \"\";\n for (let i = 0; i < data.length; i++) {\n const c = data[i]!;\n if (c >= 0x20 && c <= 0x7e) {\n current += String.fromCharCode(c);\n } else {\n if (current.length >= minLength) out.push(current);\n current = \"\";\n }\n }\n if (current.length >= minLength) out.push(current);\n return out;\n}\n\n/**\n * Valori per una \"chiave\" che può avere suffisso numerico (es. GlobalConnectionID19),\n * in forma XML `<Key>val</Key>`, attributo `Key=\"val\"` o testo `Key: val` / `Key = val`.\n */\nfunction valuesForKey(text: string, base: string): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const push = (v: string) => {\n const val = decodeEntities(v).replace(/^[\"']|[\"']$/g, \"\").trim();\n if (val && !seen.has(val)) {\n seen.add(val);\n out.push(val);\n }\n };\n\n const tagRe = new RegExp(`<(${base}\\\\w*)\\\\b[^>]*>([\\\\s\\\\S]*?)</\\\\1>`, \"gi\");\n let m: RegExpExecArray | null;\n while ((m = tagRe.exec(text)) !== null) push(stripTags(m[2]!));\n\n const attrRe = new RegExp(`\\\\b${base}\\\\w*\\\\s*=\\\\s*\"([^\"]*)\"`, \"gi\");\n while ((m = attrRe.exec(text)) !== null) push(m[1]!);\n\n const kvRe = new RegExp(`\\\\b${base}\\\\w*\\\\s*[:=]\\\\s*([^\\\\r\\\\n<\"]+)`, \"gi\");\n while ((m = kvRe.exec(text)) !== null) push(m[1]!);\n\n return out;\n}\n\nfunction firstValue(text: string, bases: string[]): string | null {\n for (const b of bases) {\n const v = valuesForKey(text, b)[0];\n if (v) return v;\n }\n return null;\n}\n\nconst FDI_RE = /\\b([1-4][1-8])\\b/g;\n\nfunction parseFdi(text: string): number[] {\n const set = new Set<number>();\n for (const m of text.matchAll(FDI_RE)) {\n const n = Number(m[1]);\n if (n >= 11 && n <= 48) set.add(n);\n }\n return [...set].sort((a, b) => a - b);\n}\n\n/** Restituisce true se la lista esiste e contiene elementi/testo non vuoto. */\nfunction hasNonEmptyList(text: string, listName: string): boolean {\n const re = new RegExp(`<${listName}\\\\b[^>]*>([\\\\s\\\\S]*?)</${listName}>`, \"i\");\n const m = text.match(re);\n if (!m) return false;\n const inner = m[1] ?? \"\";\n return /<\\w/.test(inner) || stripTags(inner).length > 0;\n}\n\n// -------------------------------------------------------------------------\n// classificazione file\n// -------------------------------------------------------------------------\n\nexport function classifyThreeShapeFile(path: string): ThreeShapeFileKind {\n const lower = path.replace(/\\\\/g, \"/\").toLowerCase();\n const bn = baseName(lower);\n const ext = extensionOf(lower);\n\n if (lower.includes(\"external models/\")) return \"externalModel\";\n if (ext === \"3ml\") return \"threeMl\";\n if (bn === \"materials.xml\") return \"materialsXml\";\n if (bn.includes(\"printableorderform\")) return \"printableOrderForm\";\n if (lower.includes(\"scans/\")) return \"scan\";\n if (ext === \"dcm\") return \"cadDcm\";\n if (ext === \"xml\") return \"mainXml\";\n if (ext === \"html\" || ext === \"htm\") return \"printableOrderForm\";\n return \"other\";\n}\n\nexport function detectThreeShapeFiles(entries: ThreeShapeFileEntry[]): {\n classified: ClassifiedFile[];\n files: ThreeShapeFilesIndex;\n} {\n const classified: ClassifiedFile[] = [];\n const files: ThreeShapeFilesIndex = {\n mainXml: [],\n materialsXml: [],\n printableOrderForms: [],\n cadDcm: [],\n externalModels: [],\n scans: [],\n encrypted3ml: [],\n };\n\n for (const e of entries) {\n const kind = classifyThreeShapeFile(e.path);\n const bn = baseName(e.path);\n classified.push({\n path: e.path,\n baseName: bn,\n ext: extensionOf(e.path),\n kind,\n data: e.data,\n });\n switch (kind) {\n case \"mainXml\":\n files.mainXml.push(bn);\n break;\n case \"materialsXml\":\n files.materialsXml.push(bn);\n break;\n case \"printableOrderForm\":\n files.printableOrderForms.push(bn);\n break;\n case \"cadDcm\":\n files.cadDcm.push(bn);\n break;\n case \"externalModel\":\n files.externalModels.push(e.path);\n break;\n case \"scan\":\n files.scans.push(bn);\n break;\n default:\n break;\n }\n }\n\n return { classified, files };\n}\n\n/** Heuristica: il pacchetto sembra un export 3Shape? */\nexport function isThreeShapePackage(entries: ThreeShapeFileEntry[]): boolean {\n const { classified } = detectThreeShapeFiles(entries);\n if (\n classified.some(\n (f) =>\n f.kind === \"materialsXml\" ||\n f.kind === \"printableOrderForm\" ||\n f.kind === \"threeMl\" ||\n f.kind === \"externalModel\",\n )\n ) {\n return true;\n }\n return classified.some(\n (f) =>\n f.kind === \"mainXml\" &&\n /3shape|dentaldesigner|abutmentkit|toothelementtype|globalimplantid/i.test(\n decodeText(f.data),\n ),\n );\n}\n\n// -------------------------------------------------------------------------\n// restoration type\n// -------------------------------------------------------------------------\n\nexport function normalizeRestorationType(text: string): {\n type: string | null;\n isImplantCase: boolean;\n isScrewRetained: boolean;\n} {\n if (/AbutmentScrewRetainedCrown/i.test(text)) {\n return {\n type: \"Screw Retained Crown\",\n isImplantCase: true,\n isScrewRetained: true,\n };\n }\n if (/te?TemporaryVPrepCrown/i.test(text) || /TemporaryVPrepCrown/i.test(text)) {\n return {\n type: \"Temporary Virtual Preparation Crown\",\n isImplantCase: false,\n isScrewRetained: false,\n };\n }\n const raw = firstValue(text, [\n \"RestorationType\",\n \"ToothElementType\",\n \"ElementType\",\n ]);\n return {\n type: raw,\n isImplantCase: /abutment|screwretained|screw retained/i.test(text),\n isScrewRetained: /screwretained|screw retained/i.test(text),\n };\n}\n\n// -------------------------------------------------------------------------\n// produttore / connessione\n// -------------------------------------------------------------------------\n\nexport function inferManufacturerFromIdsAndNames(value: string): {\n manufacturer: string | null;\n connection: string | null;\n} {\n const hay = value.toUpperCase();\n let manufacturer: string | null = null;\n for (const m of MANUFACTURERS) {\n if (m.keys.some((k) => hay.includes(k))) {\n manufacturer = m.name;\n break;\n }\n }\n let connection: string | null = null;\n if (/\\bGM\\b|_GM_|_GM\\b|NEOGM/.test(hay)) connection = \"GM / Grand Morse\";\n else if (/\\bCM\\b|_CM_/.test(hay)) connection = \"CM / Cone Morse\";\n return { manufacturer, connection };\n}\n\n// -------------------------------------------------------------------------\n// componenti External models\n// -------------------------------------------------------------------------\n\nexport function normalizeImplantComponent(\n fileName: string,\n sourcePath: string,\n confidence: ThreeShapeConfidence = \"high\",\n): ThreeShapeImplantComponent {\n const n = fileName.toUpperCase();\n let type: ThreeShapeImplantComponent[\"type\"] = \"unknown\";\n if (/SCAN ?BODY/.test(n)) type = \"scanbody\";\n else if (/SOCKET/.test(n)) type = \"analog_socket\";\n else if (/ANALOG/.test(n)) type = \"analog\";\n else if (/\\bLINK/.test(n)) type = \"link/tibase_candidate\";\n else if (/\\bTX\\b|^TX|[-_]TX[-_]/.test(n))\n type = \"screw_or_transmucosal_component_candidate\";\n else if (/\\bVM\\b|^VM|NEO/.test(n)) type = \"manufacturer_component_candidate\";\n\n return {\n name: fileName.replace(/\\.[^.]+$/, \"\"),\n fileName,\n type,\n sourcePath,\n confidence,\n };\n}\n\nexport function parseExternalModels(classified: ClassifiedFile[]): {\n components: ThreeShapeImplantComponent[];\n abutmentKitId: string | null;\n} {\n const externalDcm = classified.filter(\n (f) => f.kind === \"externalModel\" && f.ext === \"dcm\",\n );\n const components: ThreeShapeImplantComponent[] = [];\n let abutmentKitId: string | null = null;\n\n for (const f of externalDcm) {\n const norm = f.path.replace(/\\\\/g, \"/\");\n const idx = norm.toLowerCase().indexOf(\"external models/\");\n if (idx >= 0 && !abutmentKitId) {\n const rest = norm.slice(idx + \"external models/\".length);\n const folder = rest.split(\"/\")[0];\n if (folder && /abutmentkit|kit|\\d{3,}/i.test(folder)) {\n abutmentKitId = folder;\n }\n }\n components.push(normalizeImplantComponent(f.baseName, f.path, \"high\"));\n }\n\n return { components, abutmentKitId };\n}\n\n// -------------------------------------------------------------------------\n// parser per singola fonte\n// -------------------------------------------------------------------------\n\nexport function parseMaterialsXml(\n xml: string,\n): Record<string, { name?: string; family?: string; shader?: string }> {\n const map: Record<string, { name?: string; family?: string; shader?: string }> =\n {};\n\n const blockRe = /<Material\\b[^>]*>([\\s\\S]*?)<\\/Material>/gi;\n let m: RegExpExecArray | null;\n let matched = false;\n while ((m = blockRe.exec(xml)) !== null) {\n const scope = m[0]!;\n const id = valuesForKey(scope, \"MaterialID\")[0];\n if (!id) continue;\n matched = true;\n map[id] = {\n ...(valuesForKey(scope, \"Name\")[0]\n ? { name: valuesForKey(scope, \"Name\")[0] }\n : {}),\n ...(valuesForKey(scope, \"MaterialFamily\")[0]\n ? { family: valuesForKey(scope, \"MaterialFamily\")[0] }\n : {}),\n ...(valuesForKey(scope, \"ShaderMaterial\")[0]\n ? { shader: valuesForKey(scope, \"ShaderMaterial\")[0] }\n : {}),\n };\n }\n\n if (!matched) {\n const id = valuesForKey(xml, \"MaterialID\")[0];\n if (id) {\n map[id] = {\n ...(valuesForKey(xml, \"Name\")[0]\n ? { name: valuesForKey(xml, \"Name\")[0] }\n : {}),\n ...(valuesForKey(xml, \"MaterialFamily\")[0]\n ? { family: valuesForKey(xml, \"MaterialFamily\")[0] }\n : {}),\n ...(valuesForKey(xml, \"ShaderMaterial\")[0]\n ? { shader: valuesForKey(xml, \"ShaderMaterial\")[0] }\n : {}),\n };\n }\n }\n\n return map;\n}\n\nexport type ThreeShapeMainOrderResult = {\n case: Partial<ThreeShapeCase>;\n restoration: Partial<ThreeShapeRestoration>;\n implantIds: {\n abutmentKitId: string | null;\n connectionId: string | null;\n implantSystemId: string | null;\n };\n hasImplantLists: boolean;\n};\n\nexport function parseMainOrderXml(xml: string): ThreeShapeMainOrderResult {\n const restType = normalizeRestorationType(xml);\n const internalToothNumbers = valuesForKey(xml, \"ToothNumber\")\n .map((v) => Number(v.replace(/[^\\d]/g, \"\")))\n .filter((n) => Number.isFinite(n) && n > 0);\n\n const abutmentKitId = firstValue(xml, [\"AbutmentKitID\"]);\n const connectionId = firstValue(xml, [\"GlobalConnectionID\"]);\n const implantSystemId = firstValue(xml, [\"GlobalImplantID\"]);\n const hasImplantLists =\n hasNonEmptyList(xml, \"ImplantSystemList\") ||\n hasNonEmptyList(xml, \"ImplantSystemPartList\") ||\n hasNonEmptyList(xml, \"AbutmentKitList\");\n\n const isImplantCase =\n restType.isImplantCase ||\n Boolean(abutmentKitId || connectionId || implantSystemId) ||\n hasImplantLists;\n\n return {\n case: {\n ...(firstValue(xml, [\"PatientName\", \"Patient\"])\n ? { patientName: firstValue(xml, [\"PatientName\", \"Patient\"]) }\n : {}),\n ...(firstValue(xml, [\"OrderId\", \"OrderNumber\", \"OrderID\"])\n ? { orderId: firstValue(xml, [\"OrderId\", \"OrderNumber\", \"OrderID\"]) }\n : {}),\n ...(firstValue(xml, [\"Customer\", \"Clinic\"])\n ? { customer: firstValue(xml, [\"Customer\", \"Clinic\"]) }\n : {}),\n ...(firstValue(xml, [\"Operator\", \"Technician\", \"User\"])\n ? { operator: firstValue(xml, [\"Operator\", \"Technician\", \"User\"]) }\n : {}),\n ...(firstValue(xml, [\"CreatedAt\", \"CreateDate\", \"OrderDate\", \"Created\"])\n ? {\n createdAt: firstValue(xml, [\n \"CreatedAt\",\n \"CreateDate\",\n \"OrderDate\",\n \"Created\",\n ]),\n }\n : {}),\n ...(firstValue(xml, [\"GroupFolder\", \"Folder\"])\n ? { groupFolder: firstValue(xml, [\"GroupFolder\", \"Folder\"]) }\n : {}),\n ...(firstValue(xml, [\"Software\", \"Application\"])\n ? { software: firstValue(xml, [\"Software\", \"Application\"])! }\n : {}),\n ...(firstValue(xml, [\"SoftwareVersion\", \"Version\"])\n ? { softwareVersion: firstValue(xml, [\"SoftwareVersion\", \"Version\"]) }\n : {}),\n ...(firstValue(xml, [\"ProcessStatus\", \"Status\"])\n ? { processStatus: firstValue(xml, [\"ProcessStatus\", \"Status\"]) }\n : {}),\n ...(firstValue(xml, [\"ScanSource\", \"Source\"])\n ? { scanSource: firstValue(xml, [\"ScanSource\", \"Source\"]) }\n : {}),\n },\n restoration: {\n internalToothNumbers,\n ...(restType.type ? { type: restType.type } : {}),\n ...(firstValue(xml, [\"RestorationName\"])\n ? { name: firstValue(xml, [\"RestorationName\"]) }\n : {}),\n ...(firstValue(xml, [\"ToothElementTypeID\"])\n ? { toothElementTypeID: firstValue(xml, [\"ToothElementTypeID\"]) }\n : {}),\n ...(firstValue(xml, [\"CacheToothTypeClass\"])\n ? { cacheToothTypeClass: firstValue(xml, [\"CacheToothTypeClass\"]) }\n : {}),\n ...(firstValue(xml, [\"MaterialID\"])\n ? { materialId: firstValue(xml, [\"MaterialID\"]) }\n : {}),\n ...(firstValue(xml, [\"Shade\", \"Color\"])\n ? { shade: firstValue(xml, [\"Shade\", \"Color\"]) }\n : {}),\n isImplantCase,\n isScrewRetained: restType.isScrewRetained,\n },\n implantIds: { abutmentKitId, connectionId, implantSystemId },\n hasImplantLists,\n };\n}\n\nexport type ThreeShapePrintableResult = {\n teeth: number[];\n patientName: string | null;\n orderId: string | null;\n customer: string | null;\n operator: string | null;\n shade: string | null;\n};\n\nfunction htmlToLines(html: string): string[] {\n const norm = html\n .replace(/<\\s*br\\s*\\/?>/gi, \"\\n\")\n .replace(/<\\/(tr|p|div|li|h[1-6]|td|th)>/gi, \"\\n\");\n return norm\n .split(/\\n+/)\n .map((l) => stripTags(l))\n .filter((l) => l.length > 0);\n}\n\nexport function parsePrintableOrderForm(html: string): ThreeShapePrintableResult {\n const lines = htmlToLines(html);\n const teeth = parseFdi(lines.join(\"\\n\"));\n\n const kv = (label: string): string | null => {\n const re = new RegExp(`\\\\b${label}\\\\b\\\\s*[:#]\\\\s*(.+)$`, \"i\");\n for (const line of lines) {\n const m = line.match(re);\n if (m?.[1]) return m[1].trim();\n }\n return null;\n };\n\n return {\n teeth,\n patientName: kv(\"Patient\") ?? firstValue(html, [\"PatientName\"]),\n orderId: kv(\"Order\") ?? firstValue(html, [\"OrderId\", \"OrderNumber\"]),\n customer: kv(\"Customer\") ?? kv(\"Clinic\"),\n operator: kv(\"Operator\") ?? kv(\"Technician\"),\n shade: kv(\"Shade\") ?? kv(\"Color\"),\n };\n}\n\n/** Cerca keyword di tipo restauro/impianto nelle stringhe leggibili di un .dcm. */\nexport function parseCadDcm(data: Uint8Array): {\n restorationType: string | null;\n keywords: string[];\n} {\n const strings = extractReadableStringsFromBinary(data);\n const joined = strings.join(\"\\n\");\n const restType = normalizeRestorationType(joined);\n const keywords: string[] = [];\n for (const kw of [\n \"AbutmentScrewRetainedCrown\",\n \"TemporaryVPrepCrown\",\n \"ScanBody\",\n \"Analog\",\n \"TiBase\",\n \"Socket\",\n \"NEODENT\",\n \"STRAUMANN\",\n \"NOBEL\",\n ]) {\n if (new RegExp(kw, \"i\").test(joined)) keywords.push(kw);\n }\n return { restorationType: restType.type, keywords };\n}\n\nexport function parseScans(classified: ClassifiedFile[]): string[] {\n return classified.filter((f) => f.kind === \"scan\").map((f) => f.baseName);\n}\n\n// -------------------------------------------------------------------------\n// builder principale\n// -------------------------------------------------------------------------\n\nconst EMPTY_CASE: ThreeShapeCase = {\n patientName: null,\n orderId: null,\n customer: null,\n operator: null,\n createdAt: null,\n groupFolder: null,\n software: \"3Shape Dental Designer\",\n softwareVersion: null,\n processStatus: null,\n scanSource: null,\n};\n\nfunction looksEncrypted(data: Uint8Array): boolean {\n // ZIP header \"PK\\x03\\x04\": i .3ml sono spesso ZIP cifrati → contenuto non testo utile.\n if (data.length >= 2 && data[0] === 0x50 && data[1] === 0x4b) return true;\n const sample = decodeText(data.subarray(0, Math.min(512, data.length)));\n return !/[<\\w]/.test(sample);\n}\n\n/** Parser completo: dai file estratti dello ZIP a `ThreeShapeSummary`. */\nexport function buildThreeShapeSummary(\n entries: ThreeShapeFileEntry[],\n): ThreeShapeSummary | undefined {\n if (!isThreeShapePackage(entries)) return undefined;\n\n const { classified, files } = detectThreeShapeFiles(entries);\n const warnings: string[] = [];\n\n let caseInfo: ThreeShapeCase = { ...EMPTY_CASE };\n let restoration: ThreeShapeRestoration = {\n teeth: [],\n internalToothNumbers: [],\n name: null,\n type: null,\n toothElementTypeID: null,\n cacheToothTypeClass: null,\n materialId: null,\n materialName: null,\n materialFamily: null,\n shaderMaterial: null,\n shade: null,\n isImplantCase: false,\n isScrewRetained: false,\n };\n const implant: ThreeShapeImplant = {\n isImplantCase: false,\n manufacturer: null,\n connection: null,\n connectionId: null,\n implantSystemId: null,\n abutmentKitId: null,\n components: [],\n };\n\n // 1. XML principale ordine\n for (const f of classified.filter((c) => c.kind === \"mainXml\")) {\n const parsed = parseMainOrderXml(decodeText(f.data));\n caseInfo = { ...caseInfo, ...stripNull(parsed.case) };\n restoration = mergeRestoration(restoration, parsed.restoration);\n if (parsed.restoration.isImplantCase) restoration.isImplantCase = true;\n if (parsed.restoration.isScrewRetained) restoration.isScrewRetained = true;\n if (parsed.implantIds.abutmentKitId)\n implant.abutmentKitId = parsed.implantIds.abutmentKitId;\n if (parsed.implantIds.connectionId)\n implant.connectionId = parsed.implantIds.connectionId;\n if (parsed.implantIds.implantSystemId)\n implant.implantSystemId = parsed.implantIds.implantSystemId;\n }\n\n // 2. PrintableOrderForm.html (dente FDI autorevole)\n let printableTeeth: number[] = [];\n for (const f of classified.filter((c) => c.kind === \"printableOrderForm\")) {\n const p = parsePrintableOrderForm(decodeText(f.data));\n if (p.teeth.length > 0) printableTeeth = p.teeth;\n if (p.patientName && !caseInfo.patientName) caseInfo.patientName = p.patientName;\n if (p.orderId && !caseInfo.orderId) caseInfo.orderId = p.orderId;\n if (p.customer && !caseInfo.customer) caseInfo.customer = p.customer;\n if (p.operator && !caseInfo.operator) caseInfo.operator = p.operator;\n if (p.shade && !restoration.shade) restoration.shade = p.shade;\n }\n\n // 3. Materials.xml\n const materialMap: Record<\n string,\n { name?: string; family?: string; shader?: string }\n > = {};\n for (const f of classified.filter((c) => c.kind === \"materialsXml\")) {\n Object.assign(materialMap, parseMaterialsXml(decodeText(f.data)));\n }\n if (restoration.materialId && materialMap[restoration.materialId]) {\n const mat = materialMap[restoration.materialId]!;\n restoration.materialName = mat.name ?? null;\n restoration.materialFamily = mat.family ?? null;\n restoration.shaderMaterial = mat.shader ?? null;\n } else if (restoration.materialId) {\n warnings.push(\n `MaterialID ${restoration.materialId} non trovato in Materials.xml.`,\n );\n }\n\n // 4. External models/<AbutmentKitID>/\n const ext = parseExternalModels(classified);\n if (ext.components.length > 0) implant.components = ext.components;\n if (ext.abutmentKitId && !implant.abutmentKitId)\n implant.abutmentKitId = ext.abutmentKitId;\n\n // 5. CAD/*.dcm — fallback tipo restauro (medium)\n if (!restoration.type) {\n for (const f of classified.filter((c) => c.kind === \"cadDcm\")) {\n const dcm = parseCadDcm(f.data);\n if (dcm.restorationType) {\n restoration.type = dcm.restorationType;\n warnings.push(\n `Tipo restauro dedotto da CAD/*.dcm (medium): ${dcm.restorationType}.`,\n );\n break;\n }\n }\n }\n\n // 7. .3ml: tentativo lettura, segnala cifrati\n for (const f of classified.filter((c) => c.kind === \"threeMl\")) {\n if (looksEncrypted(f.data)) {\n files.encrypted3ml.push(f.baseName);\n }\n }\n if (files.encrypted3ml.length > 0) {\n warnings.push(\n `File .3ml cifrati/non leggibili saltati: ${files.encrypted3ml.join(\", \")}.`,\n );\n }\n\n // denti: FDI autorevole da PrintableOrderForm, altrimenti da ordine\n if (printableTeeth.length > 0) {\n restoration.teeth = printableTeeth;\n } else {\n restoration.teeth = restoration.internalToothNumbers.filter(\n (n) => n >= 11 && n <= 48,\n );\n }\n if (\n printableTeeth.length > 0 &&\n restoration.internalToothNumbers.length > 0 &&\n restoration.internalToothNumbers.some((n) => !printableTeeth.includes(n))\n ) {\n warnings.push(\n `ToothNumber interno (${restoration.internalToothNumbers.join(\", \")}) diverso dal dente FDI dell'ordine (${printableTeeth.join(\", \")}).`,\n );\n }\n\n // componentistica / implant case finale\n const idText = [\n implant.connectionId,\n implant.implantSystemId,\n implant.abutmentKitId,\n ...implant.components.map((c) => c.fileName),\n ]\n .filter(Boolean)\n .join(\" \");\n if (idText) {\n const inf = inferManufacturerFromIdsAndNames(idText);\n if (inf.manufacturer) implant.manufacturer = inf.manufacturer;\n if (inf.connection) implant.connection = inf.connection;\n }\n\n const isImplantCase =\n restoration.isImplantCase ||\n Boolean(\n implant.abutmentKitId ||\n implant.connectionId ||\n implant.implantSystemId,\n ) ||\n implant.components.length > 0;\n implant.isImplantCase = isImplantCase;\n restoration.isImplantCase = isImplantCase;\n\n // warnings impianto\n if (isImplantCase) {\n if (!implant.manufacturer) {\n warnings.push(\"Caso implantare ma produttore non identificato.\");\n }\n warnings.push(\"Commercial SKU not found in 3Shape files\");\n if (implant.components.length > 0) {\n warnings.push(\n \"Componenti classificati solo dal nome file (verifica manuale consigliata).\",\n );\n }\n }\n\n return {\n source: \"3Shape\",\n case: caseInfo,\n restoration,\n implant,\n files,\n warnings: [...new Set(warnings)],\n };\n}\n\nfunction stripNull<T extends object>(obj: T): Partial<T> {\n const out: Partial<T> = {};\n (Object.keys(obj) as Array<keyof T>).forEach((k) => {\n const val = obj[k];\n if (val !== null && val !== undefined) out[k] = val;\n });\n return out;\n}\n\nfunction mergeRestoration(\n base: ThreeShapeRestoration,\n patch: Partial<ThreeShapeRestoration>,\n): ThreeShapeRestoration {\n const merged = { ...base };\n (Object.keys(patch) as Array<keyof ThreeShapeRestoration>).forEach((k) => {\n const val = patch[k];\n if (val === null || val === undefined) return;\n if (k === \"internalToothNumbers\" && Array.isArray(val)) {\n merged.internalToothNumbers = [\n ...new Set([...merged.internalToothNumbers, ...(val as number[])]),\n ];\n return;\n }\n if (k === \"isImplantCase\" || k === \"isScrewRetained\") return;\n (merged[k] as unknown) = val;\n });\n return merged;\n}\n","/**\n * Estrazione euristica di metadati da sidecar progetto CAD (Exocad e simili).\n * Funziona su testo/XML in browser e Convex action senza dipendenze XML esterne.\n */\n\nimport { buildExocadSummary, type ExocadSummary } from \"./exocadParser.js\";\nimport {\n buildThreeShapeSummary,\n type ThreeShapeSummary,\n} from \"./threeShapeParser.js\";\n\nexport type CadRestorationEntry = {\n toothNumbers: number[];\n material?: string;\n shade?: string;\n reconstructionType?: string;\n meshFileName?: string;\n /** Tipo preparazione (Crown, Pontic, Antagonist, …) — Exocad `<PreparationType>`. */\n preparationType?: string;\n /** Tipo impianto (es. WithoutAbutment) — Exocad `<ImplantType>`. */\n implantType?: string;\n /** Presenza scan abutment — Exocad `<ScanAbutmentScan>`. */\n scanAbutmentScan?: boolean;\n /** Connettore mesiale (indica ponte) — Exocad `<MesialConnector>`. */\n mesialConnector?: boolean;\n};\n\n/** Metadati estratti da un singolo sidecar CAD (es. `.constructionInfo`, `.dentalProject`). */\nexport type CadSourceFileMetadata = {\n sourceFile: string;\n source: \"exocad\" | \"unknown\";\n projectName?: string;\n projectDirectory?: string;\n materials: string[];\n shades: string[];\n toothNumbers: number[];\n reconstructions: CadRestorationEntry[];\n parseWarnings: string[];\n};\n\nexport type CadProjectMetadata = {\n source: \"exocad\" | \"unknown\";\n sourceFiles: string[];\n /** Dettaglio per file sorgente (presente dopo merge / upload con parser aggiornato). */\n bySourceFile?: CadSourceFileMetadata[];\n projectName?: string;\n projectDirectory?: string;\n materials: string[];\n shades: string[];\n toothNumbers: number[];\n reconstructions: CadRestorationEntry[];\n parseWarnings: string[];\n /** Riassunto Exocad strutturato (case, restoration, componentistica implantare). */\n exocad?: ExocadSummary;\n /** Riassunto 3Shape strutturato (ordine, restauro, componentistica implantare). */\n threeShape?: ThreeShapeSummary;\n};\n\nexport type CadProjectParseResult = CadProjectMetadata & {\n /** Singolo file da cui è stato estratto questo frammento. */\n sourceFile: string;\n};\n\nconst CAD_SIDECAR_RE =\n /\\.(constructioninfo|modelinfo|dentalproject|dentalcad)(?:\\.html)?$/i;\n\nconst TOOTH_BLOCK_RE =\n /<(?:Tooth|Restoration|ConstructionElement|Element|RestorationElement)\\b[^>]*>([\\s\\S]*?)<\\/(?:Tooth|Restoration|ConstructionElement|Element|RestorationElement)>/gi;\n\nconst FDI_TOOTH_RE = /\\b([1-4][1-8])\\b/g;\n\nfunction decodeCadText(data: Uint8Array): string {\n let start = 0;\n if (\n data.length >= 3 &&\n data[0] === 0xef &&\n data[1] === 0xbb &&\n data[2] === 0xbf\n ) {\n start = 3;\n }\n return new TextDecoder(\"utf-8\", { fatal: false }).decode(data.subarray(start));\n}\n\nfunction stripXmlText(raw: string): string {\n return raw\n .replace(/<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>/gi, \"$1\")\n .replace(/<[^>]+>/g, \"\")\n .trim();\n}\n\nfunction firstTagValue(xml: string, tags: string[]): string | undefined {\n for (const tag of tags) {\n const re = new RegExp(`<${tag}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${tag}>`, \"i\");\n const m = xml.match(re);\n if (m?.[1]) {\n const val = stripXmlText(m[1]);\n if (val) return val;\n }\n }\n return undefined;\n}\n\nfunction allTagValues(xml: string, tags: string[]): string[] {\n const out: string[] = [];\n for (const tag of tags) {\n const re = new RegExp(`<${tag}\\\\b[^>]*>([\\\\s\\\\S]*?)<\\\\/${tag}>`, \"gi\");\n let m: RegExpExecArray | null;\n while ((m = re.exec(xml)) !== null) {\n const val = stripXmlText(m[1]!);\n if (val) out.push(val);\n }\n }\n return out;\n}\n\nfunction parseBool(raw: string | undefined): boolean | undefined {\n if (raw === undefined) return undefined;\n const v = raw.trim().toLowerCase();\n if (v === \"true\" || v === \"1\" || v === \"yes\") return true;\n if (v === \"false\" || v === \"0\" || v === \"no\") return false;\n return undefined;\n}\n\nfunction parseFdiNumbers(text: string): number[] {\n const nums = new Set<number>();\n for (const m of text.matchAll(FDI_TOOTH_RE)) {\n const n = Number(m[1]);\n if (n >= 11 && n <= 48) nums.add(n);\n }\n return [...nums].sort((a, b) => a - b);\n}\n\nfunction detectCadSource(fileName: string, text: string): \"exocad\" | \"unknown\" {\n const lower = fileName.toLowerCase();\n if (\n lower.includes(\"constructioninfo\") ||\n lower.includes(\"dentalproject\") ||\n lower.includes(\"dentalcad\") ||\n /construction\\s*info/i.test(text) ||\n /<ConstructionInfo\\b/i.test(text) ||\n /<DentalProject\\b/i.test(text)\n ) {\n return \"exocad\";\n }\n return \"unknown\";\n}\n\nfunction parseRestorationBlocks(xml: string): CadRestorationEntry[] {\n const entries: CadRestorationEntry[] = [];\n for (const m of xml.matchAll(TOOTH_BLOCK_RE)) {\n const block = m[1] ?? \"\";\n const toothNumbers = parseFdiNumbers(\n firstTagValue(block, [\n \"Number\",\n \"ToothNumber\",\n \"Tooth\",\n \"FDI\",\n \"ToothID\",\n ]) ?? block,\n );\n const material = firstTagValue(block, [\n \"Material\",\n \"MaterialName\",\n \"MaterialDisplayName\",\n \"BlankMaterial\",\n ]);\n const shade = firstTagValue(block, [\n \"Shade\",\n \"ToothColor\",\n \"Color\",\n \"VitaShade\",\n ]);\n const reconstructionType = firstTagValue(block, [\n \"ReconstructionType\",\n \"Type\",\n \"WorkType\",\n \"RestorationType\",\n ]);\n const meshFileName = firstTagValue(block, [\n \"MeshFileName\",\n \"MeshFile\",\n \"FileName\",\n \"STLFileName\",\n \"ScanFileName\",\n ]);\n const preparationType = firstTagValue(block, [\"PreparationType\"]);\n const implantType = firstTagValue(block, [\"ImplantType\"]);\n const scanAbutmentScan = parseBool(firstTagValue(block, [\"ScanAbutmentScan\"]));\n const mesialConnector = parseBool(firstTagValue(block, [\"MesialConnector\"]));\n\n if (\n toothNumbers.length === 0 &&\n !material &&\n !shade &&\n !reconstructionType &&\n !meshFileName &&\n !preparationType &&\n !implantType\n ) {\n continue;\n }\n\n entries.push({\n toothNumbers,\n ...(material ? { material } : {}),\n ...(shade ? { shade } : {}),\n ...(reconstructionType ? { reconstructionType } : {}),\n ...(meshFileName ? { meshFileName } : {}),\n ...(preparationType ? { preparationType } : {}),\n ...(implantType ? { implantType } : {}),\n ...(scanAbutmentScan !== undefined ? { scanAbutmentScan } : {}),\n ...(mesialConnector !== undefined ? { mesialConnector } : {}),\n });\n }\n return entries;\n}\n\nexport function isCadSidecarFileName(fileName: string): boolean {\n const base = fileName.replace(/\\\\/g, \"/\").split(\"/\").pop() ?? fileName;\n return CAD_SIDECAR_RE.test(base) || /\\.xml$/i.test(base);\n}\n\n/** Analizza un sidecar CAD; `null` se il file non è un candidato o non è testo/XML utile. */\nexport function parseCadSidecarBytes(\n fileName: string,\n data: Uint8Array,\n): CadProjectParseResult | null {\n if (!isCadSidecarFileName(fileName) && data.byteLength > 0) {\n const head = decodeCadText(data.subarray(0, Math.min(256, data.byteLength)));\n if (!head.includes(\"<\") && !head.includes(\"<?xml\")) {\n return null;\n }\n } else if (!isCadSidecarFileName(fileName)) {\n return null;\n }\n\n const text = decodeCadText(data).trim();\n if (!text || text.length < 8) {\n return null;\n }\n\n const parseWarnings: string[] = [];\n if (!text.includes(\"<\")) {\n parseWarnings.push(\"Contenuto non XML: metadati CAD limitati.\");\n }\n\n const source = detectCadSource(fileName, text);\n const projectName = firstTagValue(text, [\n \"ProjectName\",\n \"Name\",\n \"CaseName\",\n \"OrderName\",\n ]);\n const projectDirectory = firstTagValue(text, [\n \"ProjectDirectory\",\n \"ProjectDir\",\n \"Directory\",\n ]);\n\n const reconstructions = parseRestorationBlocks(text);\n const materialsFromTags = allTagValues(text, [\n \"Material\",\n \"MaterialName\",\n \"MaterialDisplayName\",\n \"BlankMaterial\",\n ]);\n const shadesFromTags = allTagValues(text, [\n \"Shade\",\n \"ToothColor\",\n \"Color\",\n \"VitaShade\",\n ]);\n\n if (reconstructions.length === 0 && (materialsFromTags.length || shadesFromTags.length)) {\n reconstructions.push({\n toothNumbers: parseFdiNumbers(text),\n ...(materialsFromTags[0] ? { material: materialsFromTags[0] } : {}),\n ...(shadesFromTags[0] ? { shade: shadesFromTags[0] } : {}),\n });\n }\n\n const toothNumbers = [\n ...new Set(reconstructions.flatMap((r) => r.toothNumbers)),\n ].sort((a, b) => a - b);\n\n const materials = [\n ...new Set([\n ...materialsFromTags,\n ...reconstructions.map((r) => r.material).filter(Boolean) as string[],\n ]),\n ];\n const shades = [\n ...new Set([\n ...shadesFromTags,\n ...reconstructions.map((r) => r.shade).filter(Boolean) as string[],\n ]),\n ];\n\n if (\n !projectName &&\n toothNumbers.length === 0 &&\n materials.length === 0 &&\n reconstructions.length === 0\n ) {\n return null;\n }\n\n return {\n source,\n sourceFile: fileName,\n sourceFiles: [fileName],\n ...(projectName ? { projectName } : {}),\n ...(projectDirectory ? { projectDirectory } : {}),\n materials,\n shades,\n toothNumbers,\n reconstructions,\n parseWarnings,\n };\n}\n\nfunction toSourceFileMetadata(part: CadProjectParseResult): CadSourceFileMetadata {\n return {\n sourceFile: part.sourceFile,\n source: part.source,\n ...(part.projectName ? { projectName: part.projectName } : {}),\n ...(part.projectDirectory ? { projectDirectory: part.projectDirectory } : {}),\n materials: part.materials,\n shades: part.shades,\n toothNumbers: part.toothNumbers,\n reconstructions: part.reconstructions,\n parseWarnings: part.parseWarnings,\n };\n}\n\nexport function mergeCadProjectParseResults(\n parts: CadProjectParseResult[],\n): CadProjectMetadata | undefined {\n if (parts.length === 0) return undefined;\n\n const sourceFiles = [...new Set(parts.flatMap((p) => p.sourceFiles))];\n const bySourceFile = parts.map(toSourceFileMetadata);\n const source = parts.some((p) => p.source === \"exocad\") ? \"exocad\" : \"unknown\";\n const projectName = parts.find((p) => p.projectName)?.projectName;\n const projectDirectory = parts.find((p) => p.projectDirectory)?.projectDirectory;\n\n const reconstructions: CadRestorationEntry[] = [];\n for (const p of parts) {\n reconstructions.push(...p.reconstructions);\n }\n\n const toothNumbers = [\n ...new Set(reconstructions.flatMap((r) => r.toothNumbers)),\n ].sort((a, b) => a - b);\n\n const materials = [\n ...new Set([\n ...parts.flatMap((p) => p.materials),\n ...reconstructions.map((r) => r.material).filter(Boolean) as string[],\n ]),\n ];\n const shades = [\n ...new Set([\n ...parts.flatMap((p) => p.shades),\n ...reconstructions.map((r) => r.shade).filter(Boolean) as string[],\n ]),\n ];\n\n const parseWarnings = [...new Set(parts.flatMap((p) => p.parseWarnings))];\n\n return {\n source,\n sourceFiles,\n bySourceFile,\n ...(projectName ? { projectName } : {}),\n ...(projectDirectory ? { projectDirectory } : {}),\n materials,\n shades,\n toothNumbers,\n reconstructions,\n parseWarnings,\n };\n}\n\nexport function collectCadMetadataFromZipEntries(\n entries: Array<{ safePath: string; data: Uint8Array }>,\n): CadProjectMetadata | undefined {\n const parts: CadProjectParseResult[] = [];\n for (const e of entries) {\n const baseName = e.safePath.split(\"/\").pop() ?? e.safePath;\n const parsed = parseCadSidecarBytes(baseName, e.data);\n if (parsed) parts.push(parsed);\n }\n const merged = mergeCadProjectParseResults(parts);\n\n const fileEntries = entries.map((e) => ({ path: e.safePath, data: e.data }));\n\n // Riassunto Exocad strutturato (componentistica implantare): legge anche\n // .dentalCAD (binario) che il parser sidecar generico ignora.\n const exocad = buildExocadSummary(fileEntries);\n\n // Riassunto 3Shape strutturato (ordine XML, PrintableOrderForm, External models).\n const threeShape = buildThreeShapeSummary(fileEntries);\n\n if (!merged) {\n if (!exocad && !threeShape) return undefined;\n const base: CadProjectMetadata = {\n source: exocad ? \"exocad\" : \"unknown\",\n sourceFiles: exocad\n ? [\n ...exocad.files.dentalProject,\n ...exocad.files.constructionInfo,\n ...exocad.files.modelInfo,\n ...exocad.files.dentalCAD,\n ]\n : [\n ...(threeShape?.files.mainXml ?? []),\n ...(threeShape?.files.materialsXml ?? []),\n ...(threeShape?.files.printableOrderForms ?? []),\n ],\n materials: [],\n shades: [],\n toothNumbers: exocad\n ? exocad.restoration.teeth.map(Number)\n : (threeShape?.restoration.teeth ?? []),\n reconstructions: [],\n parseWarnings: [],\n ...(exocad ? { exocad } : {}),\n ...(threeShape ? { threeShape } : {}),\n };\n return base;\n }\n\n if (exocad) {\n merged.exocad = exocad;\n if (exocad.implantComponents.length > 0 || exocad.isImplantCase) {\n merged.source = \"exocad\";\n }\n }\n if (threeShape) {\n merged.threeShape = threeShape;\n }\n return merged;\n}\n","/**\n * Estrazione ZIP sicura — stessa logica in **browser** (Vite) e **Convex** (action).\n *\n * Libreria: **fflate** (`unzipSync`). L’estrazione lato server resta limitata dalla RAM\n * dell’action (~64 MB); per ZIP grandi usare l’estrazione client e caricare i file già estratti.\n */\nimport { unzipSync } from \"fflate\";\n\nexport const MAX_ZIP_ENTRIES = 200;\nexport const MAX_FILE_BYTES = 250 * 1024 * 1024;\nexport const MAX_TOTAL_EXTRACTED_BYTES = 1024 * 1024 * 1024;\n\n/**\n * Dimensione massima del file ZIP **compresso** accettata dall’action di estrazione server.\n * Le Convex action hanno ~64 MB di heap.\n */\nexport const MAX_ZIP_PACKAGE_BYTES = 40 * 1024 * 1024;\n\nexport function zipPackageTooLargeMessage(sizeBytes: number): string {\n const mb = (sizeBytes / (1024 * 1024)).toFixed(1);\n const limit = (MAX_ZIP_PACKAGE_BYTES / (1024 * 1024)).toFixed(0);\n return (\n `ZIP troppo grande (${mb} MB). Limite estrazione su Convex: ${limit} MB ` +\n `(runtime action ~64 MB). Usa l’estrazione nel browser nell’app o carica i file singolarmente.`\n );\n}\n\nconst WHITELIST = new Set([\n \"stl\",\n \"ply\",\n \"obj\",\n \"dcm\",\n \"xml\",\n \"json\",\n \"png\",\n \"jpg\",\n \"jpeg\",\n \"pdf\",\n \"txt\",\n /** Metadati progetto CAD (es. Exocad / pacchetti dental CAD export) */\n \"constructioninfo\",\n \"modelinfo\",\n \"dentalproject\",\n \"dentalcad\",\n \"html\",\n /** 3Shape Dental System / Dental Designer (spesso ZIP cifrati, lettura best-effort) */\n \"3ml\",\n]);\n\nconst BLOCKED = new Set([\n \"exe\",\n \"dmg\",\n \"app\",\n \"sh\",\n \"bat\",\n \"cmd\",\n \"js\",\n \"msi\",\n \"jar\",\n \"ps1\",\n]);\n\nexport type SafeZipSuccess = {\n ok: true;\n entries: Array<{\n safePath: string;\n extension: string;\n data: Uint8Array;\n }>;\n warnings: string[];\n};\n\nexport type SafeZipFailure = {\n ok: false;\n fatal: string;\n warnings: string[];\n};\n\nexport type SafeZipResult = SafeZipSuccess | SafeZipFailure;\n\n/** Normalizza il path ZIP e blocca path traversal / assoluti. */\nexport function sanitizeZipEntryPath(entryPath: string): string | null {\n const normalized = entryPath.replace(/\\\\/g, \"/\").replace(/^\\uFEFF/, \"\");\n if (/^[a-zA-Z]:/.test(normalized)) return null;\n if (normalized.startsWith(\"/\") || normalized.startsWith(\"\\\\\")) return null;\n const parts = normalized.split(\"/\").filter((p) => p.length > 0);\n const built: string[] = [];\n for (const p of parts) {\n if (p === \"..\") return null;\n if (p === \".\") continue;\n built.push(p);\n }\n if (built.length === 0) return null;\n return built.join(\"/\");\n}\n\nfunction extensionFromSafePath(safePath: string): string {\n const base = safePath.split(\"/\").pop() ?? safePath;\n const i = base.lastIndexOf(\".\");\n if (i <= 0) return \"\";\n return base.slice(i + 1).toLowerCase();\n}\n\nexport type SafeZipEntry = SafeZipSuccess[\"entries\"][number];\n\n/**\n * Best-effort: espande i contenitori 3Shape `.3ml` (di fatto ZIP, spesso cifrati).\n *\n * Per ogni `.3ml` prova ad aprirlo come ZIP: se ci riesce estrae i file interni utili\n * (mesh/anteprime/metadati in whitelist) aggiungendoli con path `contenitore.3ml/inner`,\n * così eventuali STL/PLY/OBJ diventano visualizzabili. I `.3ml` cifrati o non-ZIP\n * vengono saltati con un warning, senza bloccare il flusso. Espande **un solo livello**.\n */\nexport function expandThreeShape3mlEntries(entries: SafeZipEntry[]): {\n entries: SafeZipEntry[];\n warnings: string[];\n} {\n const out: SafeZipEntry[] = [];\n const warnings: string[] = [];\n\n for (const entry of entries) {\n out.push(entry);\n if (entry.extension !== \"3ml\") continue;\n\n let inner: Record<string, Uint8Array>;\n try {\n inner = unzipSync(entry.data);\n } catch {\n warnings.push(\n `.3ml non leggibile (probabilmente cifrato/proprietario): ${entry.safePath}`,\n );\n continue;\n }\n\n let found = 0;\n let skippedFormat = 0;\n for (const rawPath of Object.keys(inner)) {\n if (out.length >= MAX_ZIP_ENTRIES) {\n warnings.push(\n `Limite di ${MAX_ZIP_ENTRIES} file raggiunto: contenuto residuo di ${entry.safePath} ignorato.`,\n );\n break;\n }\n const data = inner[rawPath];\n if (!data || data.byteLength === 0) continue;\n const safe = sanitizeZipEntryPath(rawPath);\n if (safe === null) continue;\n const ext = extensionFromSafePath(safe);\n if (!ext || BLOCKED.has(ext) || ext === \"3ml\") {\n skippedFormat++;\n continue;\n }\n if (!WHITELIST.has(ext)) {\n skippedFormat++;\n continue;\n }\n if (data.byteLength > MAX_FILE_BYTES) continue;\n out.push({ safePath: `${entry.safePath}/${safe}`, extension: ext, data });\n found++;\n }\n\n if (found > 0) {\n warnings.push(`.3ml espanso (${found} file estratti): ${entry.safePath}`);\n } else {\n warnings.push(\n `.3ml aperto ma senza file utili estraibili${\n skippedFormat > 0 ? ` (${skippedFormat} interni non in whitelist)` : \"\"\n }: ${entry.safePath}`,\n );\n }\n }\n\n return { entries: out, warnings };\n}\n\nexport function extractZipSafely(buffer: Uint8Array): SafeZipResult {\n const warnings: string[] = [];\n let unzipped: Record<string, Uint8Array>;\n try {\n unzipped = unzipSync(buffer);\n } catch {\n return { ok: false, fatal: \"ZIP non valido o corrotto.\", warnings };\n }\n\n const nonemptyKeys = Object.keys(unzipped).filter((k) => {\n const chunk = unzipped[k];\n return chunk !== undefined && chunk.byteLength > 0;\n });\n\n if (nonemptyKeys.length > MAX_ZIP_ENTRIES) {\n return {\n ok: false,\n fatal: `Troppi file nel ZIP (limite ${MAX_ZIP_ENTRIES}).`,\n warnings,\n };\n }\n\n let totalSize = 0;\n const entries: SafeZipSuccess[\"entries\"] = [];\n\n for (const rawPath of nonemptyKeys) {\n const data = unzipped[rawPath]!;\n const safe = sanitizeZipEntryPath(rawPath);\n if (safe === null) {\n warnings.push(`Percorso ZIP non sicuro ignorato: ${rawPath}`);\n continue;\n }\n if (data.byteLength > MAX_FILE_BYTES) {\n return {\n ok: false,\n fatal: `File troppo grande nello ZIP (limite 250 MB): ${safe}`,\n warnings,\n };\n }\n totalSize += data.byteLength;\n if (totalSize > MAX_TOTAL_EXTRACTED_BYTES) {\n return {\n ok: false,\n fatal: \"Volume totale estratto oltre il limite di 1 GB.\",\n warnings,\n };\n }\n\n const ext = extensionFromSafePath(safe);\n if (!ext) {\n warnings.push(`Estensione assente, ignorato: ${safe}`);\n continue;\n }\n if (BLOCKED.has(ext)) {\n warnings.push(`File bloccato (tipo rischioso .${ext}): ${safe}`);\n continue;\n }\n if (!WHITELIST.has(ext)) {\n warnings.push(`File fuori whitelist ignorato: ${safe}`);\n continue;\n }\n\n entries.push({ safePath: safe, extension: ext, data });\n }\n\n if (entries.length === 0) {\n return {\n ok: false,\n fatal:\n \"Nessun file operativo estratto dal ZIP (solo voci ignorate, vuote o non consent).\",\n warnings,\n };\n }\n\n return { ok: true, entries, warnings };\n}\n"],"mappings":"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","names":["baseName","extensionOf","decodeText","decodeEntities","stripTags","valuesForKey","EMPTY_CASE","TOOTH_BLOCK_RE","parseBool","baseName"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@primocaredentgroup/dental-digital-intake-shared",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Tipi e utilità condivise (ZIP sicuro) per dental-digital-intake",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/primocaredentgroup/componente_zip.git",
|
|
9
|
+
"directory": "packages/shared"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/primocaredentgroup/componente_zip#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/primocaredentgroup/componente_zip/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": ["dist"],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"typecheck": "tsc --noEmit"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"fflate": "^0.8.2"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"tsup": "^8.5.1",
|
|
37
|
+
"typescript": "6.0.3"
|
|
38
|
+
}
|
|
39
|
+
}
|