1use crate::core::find_repo_root;
10use crate::errors::LitError;
11use crate::response::ContentTypeResponse;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::fs;
15use std::path::Path;
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21pub enum DiffStrategy {
22 Text,
24 Binary,
26 Structural,
28 Semantic,
30 Opaque,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36pub enum MergeStrategy {
37 TextThreeWay,
39 ManualResolve,
41 SchemaAware,
43 ComponentLevel,
45 AppendOnly,
47 LastWriterWins,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
53pub enum StorageTier {
54 Standard,
56 Lfs,
58 Chunked,
60 External,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
66pub enum ContentDomain {
67 Software,
68 Cad,
69 Eda,
70 Cam,
71 Simulation,
72 MlModel,
73 Manuscript,
74 Database,
75 Scientific,
76 Media,
77 Geospatial,
78 Legal,
79 Financial,
80 Config,
81 Documentation,
82 Custom(String),
83}
84
85impl std::fmt::Display for ContentDomain {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 ContentDomain::Software => write!(f, "software"),
89 ContentDomain::Cad => write!(f, "cad"),
90 ContentDomain::Eda => write!(f, "eda"),
91 ContentDomain::Cam => write!(f, "cam"),
92 ContentDomain::Simulation => write!(f, "simulation"),
93 ContentDomain::MlModel => write!(f, "ml-model"),
94 ContentDomain::Manuscript => write!(f, "manuscript"),
95 ContentDomain::Database => write!(f, "database"),
96 ContentDomain::Scientific => write!(f, "scientific"),
97 ContentDomain::Media => write!(f, "media"),
98 ContentDomain::Geospatial => write!(f, "geospatial"),
99 ContentDomain::Legal => write!(f, "legal"),
100 ContentDomain::Financial => write!(f, "financial"),
101 ContentDomain::Config => write!(f, "config"),
102 ContentDomain::Documentation => write!(f, "documentation"),
103 ContentDomain::Custom(s) => write!(f, "custom:{}", s),
104 }
105 }
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ContentType {
111 pub id: String,
113 pub name: String,
115 pub domain: ContentDomain,
117 pub mime_types: Vec<String>,
119 pub extensions: Vec<String>,
121 #[serde(default)]
123 pub magic_bytes: Vec<String>,
124 pub diff_strategy: DiffStrategy,
126 pub merge_strategy: MergeStrategy,
128 pub storage_tier: StorageTier,
130 pub lfs_threshold: Option<u64>,
132 #[serde(default)]
134 pub metadata_schema: Option<serde_json::Value>,
135 pub structural_diff: bool,
137 pub component_locking: bool,
139 pub description: String,
141}
142
143fn builtin_types() -> Vec<ContentType> {
146 vec![
147 ContentType {
149 id: "cad/step".into(),
150 name: "STEP CAD Model".into(),
151 domain: ContentDomain::Cad,
152 mime_types: vec!["model/step".into()],
153 extensions: vec!["step".into(), "stp".into(), "p21".into()],
154 magic_bytes: vec!["49534F2D".into()], diff_strategy: DiffStrategy::Structural,
156 merge_strategy: MergeStrategy::ManualResolve,
157 storage_tier: StorageTier::Lfs,
158 lfs_threshold: Some(1024 * 1024),
159 metadata_schema: Some(serde_json::json!({
160 "type": "object",
161 "properties": {
162 "units": {"type": "string", "enum": ["mm", "in", "m"]},
163 "assembly_count": {"type": "integer"},
164 "bounding_box": {"type": "array", "items": {"type": "number"}}
165 }
166 })),
167 structural_diff: true,
168 component_locking: true,
169 description: "ISO 10303 STEP geometry exchange format".into(),
170 },
171 ContentType {
172 id: "cad/stl".into(),
173 name: "STL Mesh".into(),
174 domain: ContentDomain::Cad,
175 mime_types: vec!["model/stl".into()],
176 extensions: vec!["stl".into()],
177 magic_bytes: vec!["736F6C6964".into()], diff_strategy: DiffStrategy::Opaque,
179 merge_strategy: MergeStrategy::LastWriterWins,
180 storage_tier: StorageTier::Lfs,
181 lfs_threshold: Some(512 * 1024),
182 metadata_schema: Some(serde_json::json!({
183 "type": "object",
184 "properties": {
185 "triangle_count": {"type": "integer"},
186 "format": {"type": "string", "enum": ["ascii", "binary"]}
187 }
188 })),
189 structural_diff: false,
190 component_locking: false,
191 description: "Stereolithography mesh format for 3D printing".into(),
192 },
193 ContentType {
194 id: "cad/iges".into(),
195 name: "IGES CAD Model".into(),
196 domain: ContentDomain::Cad,
197 mime_types: vec!["model/iges".into()],
198 extensions: vec!["igs".into(), "iges".into()],
199 magic_bytes: vec![],
200 diff_strategy: DiffStrategy::Structural,
201 merge_strategy: MergeStrategy::ManualResolve,
202 storage_tier: StorageTier::Lfs,
203 lfs_threshold: Some(1024 * 1024),
204 metadata_schema: None,
205 structural_diff: true,
206 component_locking: false,
207 description: "Initial Graphics Exchange Specification".into(),
208 },
209 ContentType {
210 id: "cad/3mf".into(),
211 name: "3MF Model".into(),
212 domain: ContentDomain::Cad,
213 mime_types: vec![
214 "model/3mf".into(),
215 "application/vnd.ms-package.3dmanufacturing-3dmodel+xml".into(),
216 ],
217 extensions: vec!["3mf".into()],
218 magic_bytes: vec!["504B0304".into()], diff_strategy: DiffStrategy::Structural,
220 merge_strategy: MergeStrategy::ComponentLevel,
221 storage_tier: StorageTier::Lfs,
222 lfs_threshold: Some(1024 * 1024),
223 metadata_schema: None,
224 structural_diff: true,
225 component_locking: true,
226 description: "3D Manufacturing Format (ZIP-based XML)".into(),
227 },
228 ContentType {
230 id: "cad/dwg".into(),
231 name: "AutoCAD DWG".into(),
232 domain: ContentDomain::Cad,
233 mime_types: vec!["image/vnd.dwg".into()],
234 extensions: vec!["dwg".into()],
235 magic_bytes: vec!["4143".into()],
236 diff_strategy: DiffStrategy::Opaque,
237 merge_strategy: MergeStrategy::ManualResolve,
238 storage_tier: StorageTier::Lfs,
239 lfs_threshold: Some(1024 * 1024),
240 metadata_schema: None,
241 structural_diff: false,
242 component_locking: true,
243 description: "Autodesk AutoCAD native drawing".into(),
244 },
245 ContentType {
246 id: "cad/dxf".into(),
247 name: "AutoCAD DXF".into(),
248 domain: ContentDomain::Cad,
249 mime_types: vec!["image/vnd.dxf".into()],
250 extensions: vec!["dxf".into()],
251 magic_bytes: vec![],
252 diff_strategy: DiffStrategy::Structural,
253 merge_strategy: MergeStrategy::ManualResolve,
254 storage_tier: StorageTier::Standard,
255 lfs_threshold: Some(20 * 1024 * 1024),
256 metadata_schema: None,
257 structural_diff: true,
258 component_locking: false,
259 description: "Drawing Exchange Format (ASCII/binary CAD interchange)".into(),
260 },
261 ContentType {
262 id: "cad/solidworks".into(),
263 name: "SolidWorks Document".into(),
264 domain: ContentDomain::Cad,
265 mime_types: vec!["application/x-solidworks".into()],
266 extensions: vec!["sldprt".into(), "sldasm".into(), "slddrw".into()],
267 magic_bytes: vec![],
268 diff_strategy: DiffStrategy::Opaque,
269 merge_strategy: MergeStrategy::ManualResolve,
270 storage_tier: StorageTier::Lfs,
271 lfs_threshold: Some(1024 * 1024),
272 metadata_schema: None,
273 structural_diff: false,
274 component_locking: true,
275 description: "Dassault SolidWorks part/assembly/drawing".into(),
276 },
277 ContentType {
278 id: "cad/catia".into(),
279 name: "CATIA Document".into(),
280 domain: ContentDomain::Cad,
281 mime_types: vec!["application/x-catia".into()],
282 extensions: vec![
283 "catpart".into(),
284 "catproduct".into(),
285 "catdrawing".into(),
286 "cgr".into(),
287 ],
288 magic_bytes: vec![],
289 diff_strategy: DiffStrategy::Opaque,
290 merge_strategy: MergeStrategy::ManualResolve,
291 storage_tier: StorageTier::Lfs,
292 lfs_threshold: Some(1024 * 1024),
293 metadata_schema: None,
294 structural_diff: false,
295 component_locking: true,
296 description: "Dassault CATIA V5 part/product/drawing".into(),
297 },
298 ContentType {
299 id: "cad/inventor".into(),
300 name: "Autodesk Inventor".into(),
301 domain: ContentDomain::Cad,
302 mime_types: vec!["application/x-inventor".into()],
303 extensions: vec!["ipt".into(), "iam".into(), "idw".into(), "ipn".into()],
304 magic_bytes: vec![],
305 diff_strategy: DiffStrategy::Opaque,
306 merge_strategy: MergeStrategy::ManualResolve,
307 storage_tier: StorageTier::Lfs,
308 lfs_threshold: Some(1024 * 1024),
309 metadata_schema: None,
310 structural_diff: false,
311 component_locking: true,
312 description: "Autodesk Inventor part/assembly/drawing/presentation".into(),
313 },
314 ContentType {
315 id: "cad/fusion360".into(),
316 name: "Fusion 360 Archive".into(),
317 domain: ContentDomain::Cad,
318 mime_types: vec!["application/x-fusion360".into()],
319 extensions: vec!["f3d".into(), "f3z".into()],
320 magic_bytes: vec!["504B0304".into()],
321 diff_strategy: DiffStrategy::Opaque,
322 merge_strategy: MergeStrategy::ManualResolve,
323 storage_tier: StorageTier::Lfs,
324 lfs_threshold: Some(1024 * 1024),
325 metadata_schema: None,
326 structural_diff: false,
327 component_locking: true,
328 description: "Autodesk Fusion 360 design archive".into(),
329 },
330 ContentType {
331 id: "cad/creo".into(),
332 name: "PTC Creo / Pro-ENGINEER".into(),
333 domain: ContentDomain::Cad,
334 mime_types: vec!["application/x-creo".into()],
335 extensions: vec!["prt".into(), "asm".into(), "drw".into(), "frm".into()],
336 magic_bytes: vec![],
337 diff_strategy: DiffStrategy::Opaque,
338 merge_strategy: MergeStrategy::ManualResolve,
339 storage_tier: StorageTier::Lfs,
340 lfs_threshold: Some(1024 * 1024),
341 metadata_schema: None,
342 structural_diff: false,
343 component_locking: true,
344 description: "PTC Creo/Pro-E part/assembly/drawing".into(),
345 },
346 ContentType {
347 id: "cad/siemens-nx".into(),
348 name: "Siemens NX".into(),
349 domain: ContentDomain::Cad,
350 mime_types: vec!["application/x-siemens-nx".into()],
351 extensions: vec!["prt".into()],
353 magic_bytes: vec![],
354 diff_strategy: DiffStrategy::Opaque,
355 merge_strategy: MergeStrategy::ManualResolve,
356 storage_tier: StorageTier::Lfs,
357 lfs_threshold: Some(1024 * 1024),
358 metadata_schema: None,
359 structural_diff: false,
360 component_locking: true,
361 description: "Siemens NX (Unigraphics) part/assembly/drawing".into(),
362 },
363 ContentType {
364 id: "cad/solid-edge".into(),
365 name: "Solid Edge".into(),
366 domain: ContentDomain::Cad,
367 mime_types: vec!["application/x-solid-edge".into()],
368 extensions: vec![
369 "par".into(),
370 "psm".into(),
371 "pwd".into(),
372 "asm".into(),
373 "dft".into(),
374 ],
375 magic_bytes: vec![],
376 diff_strategy: DiffStrategy::Opaque,
377 merge_strategy: MergeStrategy::ManualResolve,
378 storage_tier: StorageTier::Lfs,
379 lfs_threshold: Some(1024 * 1024),
380 metadata_schema: None,
381 structural_diff: false,
382 component_locking: true,
383 description: "Siemens Solid Edge part/sheet-metal/weldment/assembly/draft".into(),
384 },
385 ContentType {
386 id: "cad/rhino".into(),
387 name: "Rhino 3DM".into(),
388 domain: ContentDomain::Cad,
389 mime_types: vec!["model/vnd.rhino".into()],
390 extensions: vec!["3dm".into()],
391 magic_bytes: vec![],
392 diff_strategy: DiffStrategy::Opaque,
393 merge_strategy: MergeStrategy::ManualResolve,
394 storage_tier: StorageTier::Lfs,
395 lfs_threshold: Some(1024 * 1024),
396 metadata_schema: None,
397 structural_diff: false,
398 component_locking: true,
399 description: "Rhinoceros 3D NURBS model (openNURBS)".into(),
400 },
401 ContentType {
402 id: "cad/sketchup".into(),
403 name: "SketchUp Model".into(),
404 domain: ContentDomain::Cad,
405 mime_types: vec!["application/vnd.sketchup.skp".into()],
406 extensions: vec!["skp".into()],
407 magic_bytes: vec![],
408 diff_strategy: DiffStrategy::Opaque,
409 merge_strategy: MergeStrategy::ManualResolve,
410 storage_tier: StorageTier::Lfs,
411 lfs_threshold: Some(1024 * 1024),
412 metadata_schema: None,
413 structural_diff: false,
414 component_locking: false,
415 description: "Trimble SketchUp model".into(),
416 },
417 ContentType {
418 id: "cad/freecad".into(),
419 name: "FreeCAD Document".into(),
420 domain: ContentDomain::Cad,
421 mime_types: vec!["application/x-extension-fcstd".into()],
422 extensions: vec!["fcstd".into(), "fcstd1".into()],
423 magic_bytes: vec!["504B0304".into()],
424 diff_strategy: DiffStrategy::Structural,
425 merge_strategy: MergeStrategy::ComponentLevel,
426 storage_tier: StorageTier::Lfs,
427 lfs_threshold: Some(1024 * 1024),
428 metadata_schema: None,
429 structural_diff: true,
430 component_locking: true,
431 description: "FreeCAD parametric document (ZIP-based)".into(),
432 },
433 ContentType {
434 id: "cad/openscad".into(),
435 name: "OpenSCAD Script".into(),
436 domain: ContentDomain::Cad,
437 mime_types: vec!["application/x-openscad".into()],
438 extensions: vec!["scad".into()],
439 magic_bytes: vec![],
440 diff_strategy: DiffStrategy::Text,
441 merge_strategy: MergeStrategy::TextThreeWay,
442 storage_tier: StorageTier::Standard,
443 lfs_threshold: None,
444 metadata_schema: None,
445 structural_diff: false,
446 component_locking: false,
447 description: "OpenSCAD programmatic solid model source".into(),
448 },
449 ContentType {
450 id: "cad/parasolid".into(),
451 name: "Parasolid Model".into(),
452 domain: ContentDomain::Cad,
453 mime_types: vec!["application/x-parasolid".into()],
454 extensions: vec![
455 "x_t".into(),
456 "x_b".into(),
457 "xmt_txt".into(),
458 "xmt_bin".into(),
459 ],
460 magic_bytes: vec![],
461 diff_strategy: DiffStrategy::Opaque,
462 merge_strategy: MergeStrategy::ManualResolve,
463 storage_tier: StorageTier::Lfs,
464 lfs_threshold: Some(1024 * 1024),
465 metadata_schema: None,
466 structural_diff: false,
467 component_locking: true,
468 description: "Siemens Parasolid B-rep geometry kernel format".into(),
469 },
470 ContentType {
471 id: "cad/acis".into(),
472 name: "ACIS SAT".into(),
473 domain: ContentDomain::Cad,
474 mime_types: vec!["application/x-acis".into()],
475 extensions: vec!["sat".into(), "sab".into()],
476 magic_bytes: vec![],
477 diff_strategy: DiffStrategy::Opaque,
478 merge_strategy: MergeStrategy::ManualResolve,
479 storage_tier: StorageTier::Lfs,
480 lfs_threshold: Some(1024 * 1024),
481 metadata_schema: None,
482 structural_diff: false,
483 component_locking: true,
484 description: "Spatial ACIS solid model (SAT/SAB)".into(),
485 },
486 ContentType {
487 id: "cad/jt".into(),
488 name: "JT Visualization".into(),
489 domain: ContentDomain::Cad,
490 mime_types: vec!["model/jt".into()],
491 extensions: vec!["jt".into()],
492 magic_bytes: vec![],
493 diff_strategy: DiffStrategy::Opaque,
494 merge_strategy: MergeStrategy::ManualResolve,
495 storage_tier: StorageTier::Lfs,
496 lfs_threshold: Some(1024 * 1024),
497 metadata_schema: None,
498 structural_diff: false,
499 component_locking: true,
500 description: "ISO 14306 JT lightweight 3D visualization".into(),
501 },
502 ContentType {
504 id: "cad/obj".into(),
505 name: "Wavefront OBJ".into(),
506 domain: ContentDomain::Cad,
507 mime_types: vec!["model/obj".into()],
508 extensions: vec!["obj".into()],
509 magic_bytes: vec![],
510 diff_strategy: DiffStrategy::Text,
511 merge_strategy: MergeStrategy::ManualResolve,
512 storage_tier: StorageTier::Lfs,
513 lfs_threshold: Some(1024 * 1024),
514 metadata_schema: None,
515 structural_diff: false,
516 component_locking: false,
517 description: "Wavefront OBJ geometry mesh".into(),
518 },
519 ContentType {
520 id: "cad/fbx".into(),
521 name: "Autodesk FBX".into(),
522 domain: ContentDomain::Cad,
523 mime_types: vec!["application/octet-stream".into()],
524 extensions: vec!["fbx".into()],
525 magic_bytes: vec!["4B6179646172612046425820".into()],
526 diff_strategy: DiffStrategy::Opaque,
527 merge_strategy: MergeStrategy::LastWriterWins,
528 storage_tier: StorageTier::Lfs,
529 lfs_threshold: Some(1024 * 1024),
530 metadata_schema: None,
531 structural_diff: false,
532 component_locking: false,
533 description: "Autodesk FBX scene/asset interchange".into(),
534 },
535 ContentType {
536 id: "cad/gltf".into(),
537 name: "glTF / GLB".into(),
538 domain: ContentDomain::Cad,
539 mime_types: vec!["model/gltf+json".into(), "model/gltf-binary".into()],
540 extensions: vec!["gltf".into(), "glb".into()],
541 magic_bytes: vec!["676C5446".into()],
542 diff_strategy: DiffStrategy::Structural,
543 merge_strategy: MergeStrategy::ComponentLevel,
544 storage_tier: StorageTier::Lfs,
545 lfs_threshold: Some(1024 * 1024),
546 metadata_schema: Some(serde_json::json!({
547 "type": "object",
548 "properties": {
549 "mesh_count": {"type": "integer"},
550 "material_count": {"type": "integer"},
551 "animation_count": {"type": "integer"},
552 "generator": {"type": "string"}
553 }
554 })),
555 structural_diff: true,
556 component_locking: false,
557 description: "Khronos glTF 2.0 runtime 3D asset (text/binary)".into(),
558 },
559 ContentType {
560 id: "cad/collada".into(),
561 name: "COLLADA".into(),
562 domain: ContentDomain::Cad,
563 mime_types: vec!["model/vnd.collada+xml".into()],
564 extensions: vec!["dae".into()],
565 magic_bytes: vec![],
566 diff_strategy: DiffStrategy::Structural,
567 merge_strategy: MergeStrategy::ManualResolve,
568 storage_tier: StorageTier::Lfs,
569 lfs_threshold: Some(1024 * 1024),
570 metadata_schema: None,
571 structural_diff: true,
572 component_locking: false,
573 description: "COLLADA (.dae) XML 3D asset interchange".into(),
574 },
575 ContentType {
576 id: "cad/usd".into(),
577 name: "Universal Scene Description".into(),
578 domain: ContentDomain::Cad,
579 mime_types: vec!["model/vnd.usd".into()],
580 extensions: vec!["usd".into(), "usda".into(), "usdc".into(), "usdz".into()],
581 magic_bytes: vec![],
582 diff_strategy: DiffStrategy::Structural,
583 merge_strategy: MergeStrategy::ComponentLevel,
584 storage_tier: StorageTier::Lfs,
585 lfs_threshold: Some(1024 * 1024),
586 metadata_schema: None,
587 structural_diff: true,
588 component_locking: true,
589 description: "Pixar OpenUSD scene description (ascii/crate/zip)".into(),
590 },
591 ContentType {
592 id: "cad/ply".into(),
593 name: "Polygon File Format".into(),
594 domain: ContentDomain::Cad,
595 mime_types: vec!["application/x-ply".into()],
596 extensions: vec!["ply".into()],
597 magic_bytes: vec!["706C79".into()],
598 diff_strategy: DiffStrategy::Opaque,
599 merge_strategy: MergeStrategy::LastWriterWins,
600 storage_tier: StorageTier::Lfs,
601 lfs_threshold: Some(1024 * 1024),
602 metadata_schema: None,
603 structural_diff: false,
604 component_locking: false,
605 description: "Stanford PLY polygon/point-cloud mesh".into(),
606 },
607 ContentType {
608 id: "cad/blender".into(),
609 name: "Blender Scene".into(),
610 domain: ContentDomain::Cad,
611 mime_types: vec!["application/x-blender".into()],
612 extensions: vec!["blend".into()],
613 magic_bytes: vec!["424C454E444552".into()],
614 diff_strategy: DiffStrategy::Opaque,
615 merge_strategy: MergeStrategy::ManualResolve,
616 storage_tier: StorageTier::Lfs,
617 lfs_threshold: Some(1024 * 1024),
618 metadata_schema: None,
619 structural_diff: false,
620 component_locking: false,
621 description: "Blender .blend scene file".into(),
622 },
623 ContentType {
624 id: "cad/alembic".into(),
625 name: "Alembic Cache".into(),
626 domain: ContentDomain::Cad,
627 mime_types: vec!["application/x-alembic".into()],
628 extensions: vec!["abc".into()],
629 magic_bytes: vec!["4F6761776100".into()],
630 diff_strategy: DiffStrategy::Opaque,
631 merge_strategy: MergeStrategy::LastWriterWins,
632 storage_tier: StorageTier::Lfs,
633 lfs_threshold: Some(1024 * 1024),
634 metadata_schema: None,
635 structural_diff: false,
636 component_locking: false,
637 description: "Alembic baked geometry/animation cache".into(),
638 },
639 ContentType {
640 id: "cad/3ds".into(),
641 name: "Autodesk 3DS".into(),
642 domain: ContentDomain::Cad,
643 mime_types: vec!["application/x-3ds".into()],
644 extensions: vec!["3ds".into()],
645 magic_bytes: vec!["4D4D".into()],
646 diff_strategy: DiffStrategy::Opaque,
647 merge_strategy: MergeStrategy::LastWriterWins,
648 storage_tier: StorageTier::Lfs,
649 lfs_threshold: Some(512 * 1024),
650 metadata_schema: None,
651 structural_diff: false,
652 component_locking: false,
653 description: "Autodesk 3D Studio legacy mesh".into(),
654 },
655 ContentType {
657 id: "eda/kicad-pcb".into(),
658 name: "KiCad PCB Layout".into(),
659 domain: ContentDomain::Eda,
660 mime_types: vec!["application/x-kicad-pcb".into()],
661 extensions: vec!["kicad_pcb".into()],
662 magic_bytes: vec![],
663 diff_strategy: DiffStrategy::Structural,
664 merge_strategy: MergeStrategy::ComponentLevel,
665 storage_tier: StorageTier::Standard,
666 lfs_threshold: Some(50 * 1024 * 1024),
667 metadata_schema: Some(serde_json::json!({
668 "type": "object",
669 "properties": {
670 "layers": {"type": "integer"},
671 "component_count": {"type": "integer"},
672 "net_count": {"type": "integer"},
673 "board_dimensions": {"type": "object", "properties": {
674 "width_mm": {"type": "number"},
675 "height_mm": {"type": "number"}
676 }}
677 }
678 })),
679 structural_diff: true,
680 component_locking: true,
681 description: "KiCad PCB layout (S-expression format)".into(),
682 },
683 ContentType {
684 id: "eda/kicad-sch".into(),
685 name: "KiCad Schematic".into(),
686 domain: ContentDomain::Eda,
687 mime_types: vec!["application/x-kicad-schematic".into()],
688 extensions: vec!["kicad_sch".into()],
689 magic_bytes: vec![],
690 diff_strategy: DiffStrategy::Structural,
691 merge_strategy: MergeStrategy::ComponentLevel,
692 storage_tier: StorageTier::Standard,
693 lfs_threshold: None,
694 metadata_schema: None,
695 structural_diff: true,
696 component_locking: true,
697 description: "KiCad schematic (S-expression format)".into(),
698 },
699 ContentType {
700 id: "eda/gerber".into(),
701 name: "Gerber PCB Fabrication".into(),
702 domain: ContentDomain::Eda,
703 mime_types: vec!["application/x-gerber".into()],
704 extensions: vec![
705 "gbr".into(),
706 "ger".into(),
707 "gtl".into(),
708 "gbl".into(),
709 "gts".into(),
710 "gbs".into(),
711 ],
712 magic_bytes: vec![],
713 diff_strategy: DiffStrategy::Text,
714 merge_strategy: MergeStrategy::LastWriterWins,
715 storage_tier: StorageTier::Standard,
716 lfs_threshold: None,
717 metadata_schema: None,
718 structural_diff: false,
719 component_locking: false,
720 description: "Gerber RS-274X PCB fabrication data".into(),
721 },
722 ContentType {
723 id: "eda/spice".into(),
724 name: "SPICE Netlist".into(),
725 domain: ContentDomain::Eda,
726 mime_types: vec!["text/x-spice".into()],
727 extensions: vec!["spice".into(), "sp".into(), "cir".into()],
728 magic_bytes: vec![],
729 diff_strategy: DiffStrategy::Text,
730 merge_strategy: MergeStrategy::TextThreeWay,
731 storage_tier: StorageTier::Standard,
732 lfs_threshold: None,
733 metadata_schema: None,
734 structural_diff: false,
735 component_locking: false,
736 description: "SPICE circuit simulation netlist".into(),
737 },
738 ContentType {
740 id: "eda/altium-sch".into(),
741 name: "Altium Schematic".into(),
742 domain: ContentDomain::Eda,
743 mime_types: vec!["application/x-altium-schdoc".into()],
744 extensions: vec!["schdoc".into()],
745 magic_bytes: vec![],
746 diff_strategy: DiffStrategy::Opaque,
747 merge_strategy: MergeStrategy::ManualResolve,
748 storage_tier: StorageTier::Lfs,
749 lfs_threshold: Some(1024 * 1024),
750 metadata_schema: None,
751 structural_diff: false,
752 component_locking: true,
753 description: "Altium Designer schematic document".into(),
754 },
755 ContentType {
756 id: "eda/altium-pcb".into(),
757 name: "Altium PCB".into(),
758 domain: ContentDomain::Eda,
759 mime_types: vec!["application/x-altium-pcbdoc".into()],
760 extensions: vec!["pcbdoc".into()],
761 magic_bytes: vec![],
762 diff_strategy: DiffStrategy::Opaque,
763 merge_strategy: MergeStrategy::ManualResolve,
764 storage_tier: StorageTier::Lfs,
765 lfs_threshold: Some(1024 * 1024),
766 metadata_schema: None,
767 structural_diff: false,
768 component_locking: true,
769 description: "Altium Designer PCB layout document".into(),
770 },
771 ContentType {
772 id: "eda/altium-project".into(),
773 name: "Altium Project".into(),
774 domain: ContentDomain::Eda,
775 mime_types: vec!["application/x-altium-project".into()],
776 extensions: vec!["prjpcb".into(), "prjfpg".into()],
777 magic_bytes: vec![],
778 diff_strategy: DiffStrategy::Text,
779 merge_strategy: MergeStrategy::ManualResolve,
780 storage_tier: StorageTier::Standard,
781 lfs_threshold: None,
782 metadata_schema: None,
783 structural_diff: false,
784 component_locking: false,
785 description: "Altium Designer project file".into(),
786 },
787 ContentType {
788 id: "eda/eagle".into(),
789 name: "EAGLE Design".into(),
790 domain: ContentDomain::Eda,
791 mime_types: vec!["application/x-eagle".into()],
792 extensions: vec!["brd".into(), "lbr".into()],
793 magic_bytes: vec![],
794 diff_strategy: DiffStrategy::Structural,
795 merge_strategy: MergeStrategy::ComponentLevel,
796 storage_tier: StorageTier::Standard,
797 lfs_threshold: Some(20 * 1024 * 1024),
798 metadata_schema: None,
799 structural_diff: true,
800 component_locking: true,
801 description: "Autodesk EAGLE board/library (XML)".into(),
802 },
803 ContentType {
804 id: "eda/orcad".into(),
805 name: "OrCAD Design".into(),
806 domain: ContentDomain::Eda,
807 mime_types: vec!["application/x-orcad".into()],
808 extensions: vec!["dsn".into(), "opj".into(), "olb".into()],
809 magic_bytes: vec![],
810 diff_strategy: DiffStrategy::Opaque,
811 merge_strategy: MergeStrategy::ManualResolve,
812 storage_tier: StorageTier::Lfs,
813 lfs_threshold: Some(1024 * 1024),
814 metadata_schema: None,
815 structural_diff: false,
816 component_locking: true,
817 description: "Cadence OrCAD schematic/project/library".into(),
818 },
819 ContentType {
820 id: "eda/verilog".into(),
821 name: "Verilog / SystemVerilog".into(),
822 domain: ContentDomain::Eda,
823 mime_types: vec!["text/x-verilog".into()],
824 extensions: vec!["v".into(), "sv".into(), "svh".into(), "vh".into()],
825 magic_bytes: vec![],
826 diff_strategy: DiffStrategy::Text,
827 merge_strategy: MergeStrategy::TextThreeWay,
828 storage_tier: StorageTier::Standard,
829 lfs_threshold: None,
830 metadata_schema: None,
831 structural_diff: false,
832 component_locking: false,
833 description: "Verilog/SystemVerilog HDL source".into(),
834 },
835 ContentType {
836 id: "eda/vhdl".into(),
837 name: "VHDL".into(),
838 domain: ContentDomain::Eda,
839 mime_types: vec!["text/x-vhdl".into()],
840 extensions: vec!["vhd".into(), "vhdl".into()],
841 magic_bytes: vec![],
842 diff_strategy: DiffStrategy::Text,
843 merge_strategy: MergeStrategy::TextThreeWay,
844 storage_tier: StorageTier::Standard,
845 lfs_threshold: None,
846 metadata_schema: None,
847 structural_diff: false,
848 component_locking: false,
849 description: "VHDL hardware description language source".into(),
850 },
851 ContentType {
852 id: "eda/excellon".into(),
853 name: "Excellon Drill".into(),
854 domain: ContentDomain::Eda,
855 mime_types: vec!["application/x-excellon".into()],
856 extensions: vec!["drl".into(), "xln".into(), "exc".into()],
857 magic_bytes: vec![],
858 diff_strategy: DiffStrategy::Text,
859 merge_strategy: MergeStrategy::LastWriterWins,
860 storage_tier: StorageTier::Standard,
861 lfs_threshold: None,
862 metadata_schema: None,
863 structural_diff: false,
864 component_locking: false,
865 description: "Excellon NC drill/route data for PCB fabrication".into(),
866 },
867 ContentType {
868 id: "eda/gdsii".into(),
869 name: "GDSII Layout".into(),
870 domain: ContentDomain::Eda,
871 mime_types: vec!["application/x-gdsii".into()],
872 extensions: vec!["gds".into(), "gds2".into(), "gdsii".into()],
873 magic_bytes: vec![],
874 diff_strategy: DiffStrategy::Opaque,
875 merge_strategy: MergeStrategy::ManualResolve,
876 storage_tier: StorageTier::Lfs,
877 lfs_threshold: Some(1024 * 1024),
878 metadata_schema: None,
879 structural_diff: false,
880 component_locking: true,
881 description: "Calma GDSII IC mask layout stream".into(),
882 },
883 ContentType {
884 id: "eda/oasis".into(),
885 name: "OASIS Layout".into(),
886 domain: ContentDomain::Eda,
887 mime_types: vec!["application/x-oasis".into()],
888 extensions: vec!["oas".into()],
889 magic_bytes: vec![],
890 diff_strategy: DiffStrategy::Opaque,
891 merge_strategy: MergeStrategy::ManualResolve,
892 storage_tier: StorageTier::Lfs,
893 lfs_threshold: Some(1024 * 1024),
894 metadata_schema: None,
895 structural_diff: false,
896 component_locking: true,
897 description: "SEMI OASIS IC mask layout (GDSII successor)".into(),
898 },
899 ContentType {
900 id: "eda/ipc2581".into(),
901 name: "IPC-2581".into(),
902 domain: ContentDomain::Eda,
903 mime_types: vec!["application/xml".into()],
904 extensions: vec!["cvg".into(), "xml2581".into()],
905 magic_bytes: vec![],
906 diff_strategy: DiffStrategy::Structural,
907 merge_strategy: MergeStrategy::ComponentLevel,
908 storage_tier: StorageTier::Standard,
909 lfs_threshold: Some(50 * 1024 * 1024),
910 metadata_schema: None,
911 structural_diff: true,
912 component_locking: true,
913 description: "IPC-2581 open PCB manufacturing data (XML)".into(),
914 },
915 ContentType {
916 id: "eda/touchstone".into(),
917 name: "Touchstone S-Parameters".into(),
918 domain: ContentDomain::Eda,
919 mime_types: vec!["text/x-touchstone".into()],
920 extensions: vec![
921 "s1p".into(),
922 "s2p".into(),
923 "s3p".into(),
924 "s4p".into(),
925 "snp".into(),
926 ],
927 magic_bytes: vec![],
928 diff_strategy: DiffStrategy::Text,
929 merge_strategy: MergeStrategy::LastWriterWins,
930 storage_tier: StorageTier::Standard,
931 lfs_threshold: None,
932 metadata_schema: None,
933 structural_diff: false,
934 component_locking: false,
935 description: "Touchstone RF/microwave network parameter data".into(),
936 },
937 ContentType {
938 id: "eda/lef-def".into(),
939 name: "LEF / DEF".into(),
940 domain: ContentDomain::Eda,
941 mime_types: vec!["text/x-lefdef".into()],
942 extensions: vec!["lef".into(), "def".into()],
943 magic_bytes: vec![],
944 diff_strategy: DiffStrategy::Text,
945 merge_strategy: MergeStrategy::ManualResolve,
946 storage_tier: StorageTier::Standard,
947 lfs_threshold: Some(50 * 1024 * 1024),
948 metadata_schema: None,
949 structural_diff: false,
950 component_locking: false,
951 description: "Library/Design Exchange Format for IC place-and-route".into(),
952 },
953 ContentType {
954 id: "eda/spef".into(),
955 name: "SPEF Parasitics".into(),
956 domain: ContentDomain::Eda,
957 mime_types: vec!["text/x-spef".into()],
958 extensions: vec!["spef".into()],
959 magic_bytes: vec![],
960 diff_strategy: DiffStrategy::Text,
961 merge_strategy: MergeStrategy::LastWriterWins,
962 storage_tier: StorageTier::Standard,
963 lfs_threshold: Some(50 * 1024 * 1024),
964 metadata_schema: None,
965 structural_diff: false,
966 component_locking: false,
967 description: "Standard Parasitic Exchange Format (IC timing)".into(),
968 },
969 ContentType {
971 id: "cam/gcode".into(),
972 name: "G-code Toolpath".into(),
973 domain: ContentDomain::Cam,
974 mime_types: vec!["text/x-gcode".into()],
975 extensions: vec![
976 "gcode".into(),
977 "gco".into(),
978 "nc".into(),
979 "tap".into(),
980 "cnc".into(),
981 "ngc".into(),
982 "mpf".into(),
983 "g".into(),
984 ],
985 magic_bytes: vec![],
986 diff_strategy: DiffStrategy::Text,
987 merge_strategy: MergeStrategy::LastWriterWins,
988 storage_tier: StorageTier::Standard,
989 lfs_threshold: Some(50 * 1024 * 1024),
990 metadata_schema: Some(serde_json::json!({
991 "type": "object",
992 "properties": {
993 "line_count": {"type": "integer"},
994 "flavor": {"type": "string"},
995 "machine": {"type": "string"},
996 "estimated_time_s": {"type": "number"}
997 }
998 })),
999 structural_diff: false,
1000 component_locking: false,
1001 description: "RS-274 G-code CNC/3D-printer toolpath".into(),
1002 },
1003 ContentType {
1004 id: "cam/step-nc".into(),
1005 name: "STEP-NC".into(),
1006 domain: ContentDomain::Cam,
1007 mime_types: vec!["model/step-nc".into()],
1008 extensions: vec!["stpnc".into(), "238".into()],
1009 magic_bytes: vec![],
1010 diff_strategy: DiffStrategy::Structural,
1011 merge_strategy: MergeStrategy::ManualResolve,
1012 storage_tier: StorageTier::Lfs,
1013 lfs_threshold: Some(1024 * 1024),
1014 metadata_schema: None,
1015 structural_diff: true,
1016 component_locking: true,
1017 description: "ISO 14649 STEP-NC machining data".into(),
1018 },
1019 ContentType {
1020 id: "cam/apt".into(),
1021 name: "APT CL Data".into(),
1022 domain: ContentDomain::Cam,
1023 mime_types: vec!["text/x-apt".into()],
1024 extensions: vec!["apt".into(), "cls".into(), "cl".into()],
1025 magic_bytes: vec![],
1026 diff_strategy: DiffStrategy::Text,
1027 merge_strategy: MergeStrategy::LastWriterWins,
1028 storage_tier: StorageTier::Standard,
1029 lfs_threshold: None,
1030 metadata_schema: None,
1031 structural_diff: false,
1032 component_locking: false,
1033 description: "APT cutter-location source / CL data".into(),
1034 },
1035 ContentType {
1036 id: "cam/mastercam".into(),
1037 name: "Mastercam".into(),
1038 domain: ContentDomain::Cam,
1039 mime_types: vec!["application/x-mastercam".into()],
1040 extensions: vec![
1041 "mcam".into(),
1042 "mcx".into(),
1043 "mcx-7".into(),
1044 "mcx-8".into(),
1045 "mcx-9".into(),
1046 ],
1047 magic_bytes: vec![],
1048 diff_strategy: DiffStrategy::Opaque,
1049 merge_strategy: MergeStrategy::ManualResolve,
1050 storage_tier: StorageTier::Lfs,
1051 lfs_threshold: Some(1024 * 1024),
1052 metadata_schema: None,
1053 structural_diff: false,
1054 component_locking: true,
1055 description: "Mastercam part/toolpath document".into(),
1056 },
1057 ContentType {
1059 id: "manuscript/latex".into(),
1060 name: "LaTeX Document".into(),
1061 domain: ContentDomain::Manuscript,
1062 mime_types: vec!["application/x-latex".into(), "text/x-tex".into()],
1063 extensions: vec!["tex".into(), "latex".into(), "ltx".into()],
1064 magic_bytes: vec![],
1065 diff_strategy: DiffStrategy::Text,
1066 merge_strategy: MergeStrategy::TextThreeWay,
1067 storage_tier: StorageTier::Standard,
1068 lfs_threshold: None,
1069 metadata_schema: Some(serde_json::json!({
1070 "type": "object",
1071 "properties": {
1072 "document_class": {"type": "string"},
1073 "word_count": {"type": "integer"},
1074 "bibliography_entries": {"type": "integer"}
1075 }
1076 })),
1077 structural_diff: false,
1078 component_locking: false,
1079 description: "LaTeX typesetting source".into(),
1080 },
1081 ContentType {
1082 id: "manuscript/docx".into(),
1083 name: "Word Document".into(),
1084 domain: ContentDomain::Manuscript,
1085 mime_types: vec![
1086 "application/vnd.openxmlformats-officedocument.wordprocessingml.document".into(),
1087 ],
1088 extensions: vec!["docx".into()],
1089 magic_bytes: vec!["504B0304".into()], diff_strategy: DiffStrategy::Structural,
1091 merge_strategy: MergeStrategy::ManualResolve,
1092 storage_tier: StorageTier::Lfs,
1093 lfs_threshold: Some(5 * 1024 * 1024),
1094 metadata_schema: Some(serde_json::json!({
1095 "type": "object",
1096 "properties": {
1097 "page_count": {"type": "integer"},
1098 "word_count": {"type": "integer"},
1099 "author": {"type": "string"},
1100 "revision": {"type": "integer"}
1101 }
1102 })),
1103 structural_diff: true,
1104 component_locking: false,
1105 description: "Microsoft Word OOXML document".into(),
1106 },
1107 ContentType {
1108 id: "manuscript/typst".into(),
1109 name: "Typst Document".into(),
1110 domain: ContentDomain::Manuscript,
1111 mime_types: vec!["text/x-typst".into()],
1112 extensions: vec!["typ".into()],
1113 magic_bytes: vec![],
1114 diff_strategy: DiffStrategy::Text,
1115 merge_strategy: MergeStrategy::TextThreeWay,
1116 storage_tier: StorageTier::Standard,
1117 lfs_threshold: None,
1118 metadata_schema: None,
1119 structural_diff: false,
1120 component_locking: false,
1121 description: "Typst markup language source".into(),
1122 },
1123 ContentType {
1124 id: "manuscript/asciidoc".into(),
1125 name: "AsciiDoc".into(),
1126 domain: ContentDomain::Manuscript,
1127 mime_types: vec!["text/asciidoc".into()],
1128 extensions: vec!["adoc".into(), "asciidoc".into(), "asc".into()],
1129 magic_bytes: vec![],
1130 diff_strategy: DiffStrategy::Text,
1131 merge_strategy: MergeStrategy::TextThreeWay,
1132 storage_tier: StorageTier::Standard,
1133 lfs_threshold: None,
1134 metadata_schema: None,
1135 structural_diff: false,
1136 component_locking: false,
1137 description: "AsciiDoc markup language".into(),
1138 },
1139 ContentType {
1141 id: "db/sqlite".into(),
1142 name: "SQLite Database".into(),
1143 domain: ContentDomain::Database,
1144 mime_types: vec![
1145 "application/vnd.sqlite3".into(),
1146 "application/x-sqlite3".into(),
1147 ],
1148 extensions: vec!["sqlite".into(), "sqlite3".into(), "db".into()],
1149 magic_bytes: vec!["53514C69746520666F726D6174".into()], diff_strategy: DiffStrategy::Semantic,
1151 merge_strategy: MergeStrategy::SchemaAware,
1152 storage_tier: StorageTier::Chunked,
1153 lfs_threshold: Some(10 * 1024 * 1024),
1154 metadata_schema: Some(serde_json::json!({
1155 "type": "object",
1156 "properties": {
1157 "table_count": {"type": "integer"},
1158 "row_count": {"type": "integer"},
1159 "schema_version": {"type": "string"},
1160 "page_size": {"type": "integer"}
1161 }
1162 })),
1163 structural_diff: true,
1164 component_locking: true,
1165 description: "SQLite embedded database file".into(),
1166 },
1167 ContentType {
1168 id: "db/csv".into(),
1169 name: "CSV Data".into(),
1170 domain: ContentDomain::Database,
1171 mime_types: vec!["text/csv".into()],
1172 extensions: vec!["csv".into(), "tsv".into()],
1173 magic_bytes: vec![],
1174 diff_strategy: DiffStrategy::Structural,
1175 merge_strategy: MergeStrategy::SchemaAware,
1176 storage_tier: StorageTier::Standard,
1177 lfs_threshold: Some(50 * 1024 * 1024),
1178 metadata_schema: Some(serde_json::json!({
1179 "type": "object",
1180 "properties": {
1181 "column_count": {"type": "integer"},
1182 "row_count": {"type": "integer"},
1183 "delimiter": {"type": "string"},
1184 "has_header": {"type": "boolean"}
1185 }
1186 })),
1187 structural_diff: true,
1188 component_locking: false,
1189 description: "Comma/tab-separated values".into(),
1190 },
1191 ContentType {
1192 id: "db/parquet".into(),
1193 name: "Apache Parquet".into(),
1194 domain: ContentDomain::Database,
1195 mime_types: vec!["application/x-parquet".into()],
1196 extensions: vec!["parquet".into()],
1197 magic_bytes: vec!["50415231".into()], diff_strategy: DiffStrategy::Semantic,
1199 merge_strategy: MergeStrategy::SchemaAware,
1200 storage_tier: StorageTier::Lfs,
1201 lfs_threshold: Some(10 * 1024 * 1024),
1202 metadata_schema: Some(serde_json::json!({
1203 "type": "object",
1204 "properties": {
1205 "row_groups": {"type": "integer"},
1206 "row_count": {"type": "integer"},
1207 "column_count": {"type": "integer"},
1208 "compression": {"type": "string"}
1209 }
1210 })),
1211 structural_diff: true,
1212 component_locking: false,
1213 description: "Apache Parquet columnar storage".into(),
1214 },
1215 ContentType {
1216 id: "db/sql-migration".into(),
1217 name: "SQL Migration".into(),
1218 domain: ContentDomain::Database,
1219 mime_types: vec!["application/sql".into()],
1220 extensions: vec!["sql".into()],
1221 magic_bytes: vec![],
1222 diff_strategy: DiffStrategy::Text,
1223 merge_strategy: MergeStrategy::AppendOnly,
1224 storage_tier: StorageTier::Standard,
1225 lfs_threshold: None,
1226 metadata_schema: Some(serde_json::json!({
1227 "type": "object",
1228 "properties": {
1229 "direction": {"type": "string", "enum": ["up", "down"]},
1230 "version": {"type": "string"},
1231 "idempotent": {"type": "boolean"}
1232 }
1233 })),
1234 structural_diff: false,
1235 component_locking: false,
1236 description: "SQL database migration script".into(),
1237 },
1238 ContentType {
1240 id: "scientific/hdf5".into(),
1241 name: "HDF5 Dataset".into(),
1242 domain: ContentDomain::Scientific,
1243 mime_types: vec!["application/x-hdf5".into()],
1244 extensions: vec!["h5".into(), "hdf5".into(), "he5".into()],
1245 magic_bytes: vec!["894844460D0A1A0A".into()],
1246 diff_strategy: DiffStrategy::Semantic,
1247 merge_strategy: MergeStrategy::SchemaAware,
1248 storage_tier: StorageTier::Chunked,
1249 lfs_threshold: Some(10 * 1024 * 1024),
1250 metadata_schema: Some(serde_json::json!({
1251 "type": "object",
1252 "properties": {
1253 "dataset_count": {"type": "integer"},
1254 "total_size": {"type": "integer"},
1255 "compression": {"type": "string"}
1256 }
1257 })),
1258 structural_diff: true,
1259 component_locking: true,
1260 description: "Hierarchical Data Format 5 for scientific datasets".into(),
1261 },
1262 ContentType {
1263 id: "scientific/fits".into(),
1264 name: "FITS Astronomical Data".into(),
1265 domain: ContentDomain::Scientific,
1266 mime_types: vec!["application/fits".into()],
1267 extensions: vec!["fits".into(), "fit".into()],
1268 magic_bytes: vec!["53494D504C45".into()], diff_strategy: DiffStrategy::Opaque,
1270 merge_strategy: MergeStrategy::LastWriterWins,
1271 storage_tier: StorageTier::Lfs,
1272 lfs_threshold: Some(5 * 1024 * 1024),
1273 metadata_schema: None,
1274 structural_diff: false,
1275 component_locking: false,
1276 description: "Flexible Image Transport System (astronomy)".into(),
1277 },
1278 ContentType {
1279 id: "scientific/jupyter".into(),
1280 name: "Jupyter Notebook".into(),
1281 domain: ContentDomain::Scientific,
1282 mime_types: vec!["application/x-ipynb+json".into()],
1283 extensions: vec!["ipynb".into()],
1284 magic_bytes: vec![],
1285 diff_strategy: DiffStrategy::Structural,
1286 merge_strategy: MergeStrategy::ComponentLevel,
1287 storage_tier: StorageTier::Standard,
1288 lfs_threshold: Some(50 * 1024 * 1024),
1289 metadata_schema: Some(serde_json::json!({
1290 "type": "object",
1291 "properties": {
1292 "cell_count": {"type": "integer"},
1293 "kernel": {"type": "string"},
1294 "language": {"type": "string"}
1295 }
1296 })),
1297 structural_diff: true,
1298 component_locking: true,
1299 description: "Jupyter/IPython notebook (cell-level versioning)".into(),
1300 },
1301 ContentType {
1303 id: "media/image".into(),
1304 name: "Image Asset".into(),
1305 domain: ContentDomain::Media,
1306 mime_types: vec![
1307 "image/png".into(),
1308 "image/jpeg".into(),
1309 "image/webp".into(),
1310 "image/tiff".into(),
1311 ],
1312 extensions: vec![
1313 "png".into(),
1314 "jpg".into(),
1315 "jpeg".into(),
1316 "webp".into(),
1317 "tiff".into(),
1318 "tif".into(),
1319 "bmp".into(),
1320 ],
1321 magic_bytes: vec!["89504E47".into(), "FFD8FF".into()], diff_strategy: DiffStrategy::Opaque,
1323 merge_strategy: MergeStrategy::LastWriterWins,
1324 storage_tier: StorageTier::Lfs,
1325 lfs_threshold: Some(256 * 1024),
1326 metadata_schema: Some(serde_json::json!({
1327 "type": "object",
1328 "properties": {
1329 "width": {"type": "integer"},
1330 "height": {"type": "integer"},
1331 "format": {"type": "string"},
1332 "color_space": {"type": "string"}
1333 }
1334 })),
1335 structural_diff: false,
1336 component_locking: false,
1337 description: "Raster image asset".into(),
1338 },
1339 ContentType {
1340 id: "media/video".into(),
1341 name: "Video Asset".into(),
1342 domain: ContentDomain::Media,
1343 mime_types: vec![
1344 "video/mp4".into(),
1345 "video/webm".into(),
1346 "video/quicktime".into(),
1347 ],
1348 extensions: vec![
1349 "mp4".into(),
1350 "webm".into(),
1351 "mov".into(),
1352 "mkv".into(),
1353 "avi".into(),
1354 ],
1355 magic_bytes: vec![],
1356 diff_strategy: DiffStrategy::Opaque,
1357 merge_strategy: MergeStrategy::LastWriterWins,
1358 storage_tier: StorageTier::External,
1359 lfs_threshold: Some(10 * 1024 * 1024),
1360 metadata_schema: Some(serde_json::json!({
1361 "type": "object",
1362 "properties": {
1363 "duration_seconds": {"type": "number"},
1364 "resolution": {"type": "string"},
1365 "codec": {"type": "string"}
1366 }
1367 })),
1368 structural_diff: false,
1369 component_locking: false,
1370 description: "Video media asset".into(),
1371 },
1372 ContentType {
1373 id: "media/audio".into(),
1374 name: "Audio Asset".into(),
1375 domain: ContentDomain::Media,
1376 mime_types: vec![
1377 "audio/mpeg".into(),
1378 "audio/wav".into(),
1379 "audio/flac".into(),
1380 "audio/ogg".into(),
1381 ],
1382 extensions: vec![
1383 "mp3".into(),
1384 "wav".into(),
1385 "flac".into(),
1386 "ogg".into(),
1387 "aac".into(),
1388 ],
1389 magic_bytes: vec!["494433".into(), "52494646".into()], diff_strategy: DiffStrategy::Opaque,
1391 merge_strategy: MergeStrategy::LastWriterWins,
1392 storage_tier: StorageTier::Lfs,
1393 lfs_threshold: Some(1024 * 1024),
1394 metadata_schema: None,
1395 structural_diff: false,
1396 component_locking: false,
1397 description: "Audio media asset".into(),
1398 },
1399 ContentType {
1401 id: "geo/geojson".into(),
1402 name: "GeoJSON".into(),
1403 domain: ContentDomain::Geospatial,
1404 mime_types: vec!["application/geo+json".into()],
1405 extensions: vec!["geojson".into()],
1406 magic_bytes: vec![],
1407 diff_strategy: DiffStrategy::Structural,
1408 merge_strategy: MergeStrategy::ComponentLevel,
1409 storage_tier: StorageTier::Standard,
1410 lfs_threshold: Some(50 * 1024 * 1024),
1411 metadata_schema: Some(serde_json::json!({
1412 "type": "object",
1413 "properties": {
1414 "feature_count": {"type": "integer"},
1415 "geometry_types": {"type": "array", "items": {"type": "string"}},
1416 "crs": {"type": "string"}
1417 }
1418 })),
1419 structural_diff: true,
1420 component_locking: false,
1421 description: "RFC 7946 GeoJSON geographic data".into(),
1422 },
1423 ContentType {
1424 id: "geo/shapefile".into(),
1425 name: "Shapefile".into(),
1426 domain: ContentDomain::Geospatial,
1427 mime_types: vec!["application/x-shapefile".into()],
1428 extensions: vec!["shp".into(), "shx".into(), "dbf".into(), "prj".into()],
1429 magic_bytes: vec![],
1430 diff_strategy: DiffStrategy::Opaque,
1431 merge_strategy: MergeStrategy::ManualResolve,
1432 storage_tier: StorageTier::Lfs,
1433 lfs_threshold: Some(5 * 1024 * 1024),
1434 metadata_schema: None,
1435 structural_diff: false,
1436 component_locking: false,
1437 description: "ESRI Shapefile geospatial vector data".into(),
1438 },
1439 ContentType {
1441 id: "legal/pdf".into(),
1442 name: "PDF Document".into(),
1443 domain: ContentDomain::Legal,
1444 mime_types: vec!["application/pdf".into()],
1445 extensions: vec!["pdf".into()],
1446 magic_bytes: vec!["25504446".into()], diff_strategy: DiffStrategy::Opaque,
1448 merge_strategy: MergeStrategy::ManualResolve,
1449 storage_tier: StorageTier::Lfs,
1450 lfs_threshold: Some(1024 * 1024),
1451 metadata_schema: Some(serde_json::json!({
1452 "type": "object",
1453 "properties": {
1454 "page_count": {"type": "integer"},
1455 "signed": {"type": "boolean"},
1456 "version": {"type": "string"}
1457 }
1458 })),
1459 structural_diff: false,
1460 component_locking: false,
1461 description: "Portable Document Format".into(),
1462 },
1463 ContentType {
1464 id: "financial/xlsx".into(),
1465 name: "Excel Spreadsheet".into(),
1466 domain: ContentDomain::Financial,
1467 mime_types: vec![
1468 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".into(),
1469 ],
1470 extensions: vec!["xlsx".into()],
1471 magic_bytes: vec!["504B0304".into()], diff_strategy: DiffStrategy::Structural,
1473 merge_strategy: MergeStrategy::SchemaAware,
1474 storage_tier: StorageTier::Lfs,
1475 lfs_threshold: Some(5 * 1024 * 1024),
1476 metadata_schema: Some(serde_json::json!({
1477 "type": "object",
1478 "properties": {
1479 "sheet_count": {"type": "integer"},
1480 "row_count": {"type": "integer"},
1481 "has_macros": {"type": "boolean"}
1482 }
1483 })),
1484 structural_diff: true,
1485 component_locking: true,
1486 description: "Microsoft Excel OOXML spreadsheet".into(),
1487 },
1488 ContentType {
1490 id: "config/terraform".into(),
1491 name: "Terraform HCL".into(),
1492 domain: ContentDomain::Config,
1493 mime_types: vec!["text/x-hcl".into()],
1494 extensions: vec!["tf".into(), "tfvars".into()],
1495 magic_bytes: vec![],
1496 diff_strategy: DiffStrategy::Text,
1497 merge_strategy: MergeStrategy::TextThreeWay,
1498 storage_tier: StorageTier::Standard,
1499 lfs_threshold: None,
1500 metadata_schema: None,
1501 structural_diff: false,
1502 component_locking: false,
1503 description: "HashiCorp Terraform infrastructure-as-code".into(),
1504 },
1505 ContentType {
1506 id: "config/kubernetes".into(),
1507 name: "Kubernetes Manifest".into(),
1508 domain: ContentDomain::Config,
1509 mime_types: vec!["application/x-yaml".into()],
1510 extensions: vec!["yaml".into(), "yml".into()],
1511 magic_bytes: vec![],
1512 diff_strategy: DiffStrategy::Structural,
1513 merge_strategy: MergeStrategy::SchemaAware,
1514 storage_tier: StorageTier::Standard,
1515 lfs_threshold: None,
1516 metadata_schema: None,
1517 structural_diff: true,
1518 component_locking: false,
1519 description: "Kubernetes resource manifests (YAML)".into(),
1520 },
1521 ContentType {
1523 id: "sim/nastran".into(),
1524 name: "Nastran Bulk Data".into(),
1525 domain: ContentDomain::Simulation,
1526 mime_types: vec!["text/x-nastran".into()],
1527 extensions: vec!["bdf".into(), "nas".into(), "dat".into()],
1528 magic_bytes: vec![],
1529 diff_strategy: DiffStrategy::Text,
1530 merge_strategy: MergeStrategy::ManualResolve,
1531 storage_tier: StorageTier::Standard,
1532 lfs_threshold: Some(50 * 1024 * 1024),
1533 metadata_schema: None,
1534 structural_diff: false,
1535 component_locking: false,
1536 description: "MSC/NX Nastran bulk-data input deck".into(),
1537 },
1538 ContentType {
1539 id: "sim/nastran-op2".into(),
1540 name: "Nastran OP2 Results".into(),
1541 domain: ContentDomain::Simulation,
1542 mime_types: vec!["application/x-nastran-op2".into()],
1543 extensions: vec!["op2".into()],
1544 magic_bytes: vec![],
1545 diff_strategy: DiffStrategy::Opaque,
1546 merge_strategy: MergeStrategy::LastWriterWins,
1547 storage_tier: StorageTier::Lfs,
1548 lfs_threshold: Some(1024 * 1024),
1549 metadata_schema: None,
1550 structural_diff: false,
1551 component_locking: false,
1552 description: "Nastran OUTPUT2 binary results database".into(),
1553 },
1554 ContentType {
1555 id: "sim/abaqus".into(),
1556 name: "Abaqus Input Deck".into(),
1557 domain: ContentDomain::Simulation,
1558 mime_types: vec!["text/x-abaqus".into()],
1559 extensions: vec!["inp".into()],
1560 magic_bytes: vec![],
1561 diff_strategy: DiffStrategy::Text,
1562 merge_strategy: MergeStrategy::ManualResolve,
1563 storage_tier: StorageTier::Standard,
1564 lfs_threshold: Some(50 * 1024 * 1024),
1565 metadata_schema: None,
1566 structural_diff: false,
1567 component_locking: false,
1568 description: "Abaqus/Standard keyword input deck".into(),
1569 },
1570 ContentType {
1571 id: "sim/abaqus-odb".into(),
1572 name: "Abaqus ODB Results".into(),
1573 domain: ContentDomain::Simulation,
1574 mime_types: vec!["application/x-abaqus-odb".into()],
1575 extensions: vec!["odb".into()],
1576 magic_bytes: vec![],
1577 diff_strategy: DiffStrategy::Opaque,
1578 merge_strategy: MergeStrategy::LastWriterWins,
1579 storage_tier: StorageTier::Lfs,
1580 lfs_threshold: Some(1024 * 1024),
1581 metadata_schema: None,
1582 structural_diff: false,
1583 component_locking: false,
1584 description: "Abaqus output database (binary results)".into(),
1585 },
1586 ContentType {
1587 id: "sim/ansys-cdb".into(),
1588 name: "ANSYS CDB Archive".into(),
1589 domain: ContentDomain::Simulation,
1590 mime_types: vec!["text/x-ansys-cdb".into()],
1591 extensions: vec!["cdb".into()],
1592 magic_bytes: vec![],
1593 diff_strategy: DiffStrategy::Text,
1594 merge_strategy: MergeStrategy::ManualResolve,
1595 storage_tier: StorageTier::Standard,
1596 lfs_threshold: Some(50 * 1024 * 1024),
1597 metadata_schema: None,
1598 structural_diff: false,
1599 component_locking: false,
1600 description: "ANSYS APDL CDB model archive".into(),
1601 },
1602 ContentType {
1603 id: "sim/ansys-db".into(),
1604 name: "ANSYS Database / Results".into(),
1605 domain: ContentDomain::Simulation,
1606 mime_types: vec!["application/x-ansys".into()],
1607 extensions: vec!["db".into(), "rst".into(), "rth".into(), "rmg".into()],
1608 magic_bytes: vec![],
1609 diff_strategy: DiffStrategy::Opaque,
1610 merge_strategy: MergeStrategy::LastWriterWins,
1611 storage_tier: StorageTier::Lfs,
1612 lfs_threshold: Some(1024 * 1024),
1613 metadata_schema: None,
1614 structural_diff: false,
1615 component_locking: false,
1616 description: "ANSYS binary database/results files".into(),
1617 },
1618 ContentType {
1619 id: "sim/lsdyna".into(),
1620 name: "LS-DYNA Keyword".into(),
1621 domain: ContentDomain::Simulation,
1622 mime_types: vec!["text/x-lsdyna".into()],
1623 extensions: vec!["k".into(), "key".into(), "dyn".into()],
1624 magic_bytes: vec![],
1625 diff_strategy: DiffStrategy::Text,
1626 merge_strategy: MergeStrategy::ManualResolve,
1627 storage_tier: StorageTier::Standard,
1628 lfs_threshold: Some(50 * 1024 * 1024),
1629 metadata_schema: None,
1630 structural_diff: false,
1631 component_locking: false,
1632 description: "LS-DYNA keyword input deck".into(),
1633 },
1634 ContentType {
1635 id: "sim/openfoam".into(),
1636 name: "OpenFOAM Case".into(),
1637 domain: ContentDomain::Simulation,
1638 mime_types: vec!["text/x-openfoam".into()],
1639 extensions: vec!["foam".into()],
1640 magic_bytes: vec![],
1641 diff_strategy: DiffStrategy::Text,
1642 merge_strategy: MergeStrategy::ManualResolve,
1643 storage_tier: StorageTier::Standard,
1644 lfs_threshold: Some(50 * 1024 * 1024),
1645 metadata_schema: None,
1646 structural_diff: false,
1647 component_locking: false,
1648 description: "OpenFOAM case dictionary / field data".into(),
1649 },
1650 ContentType {
1651 id: "sim/comsol".into(),
1652 name: "COMSOL Model".into(),
1653 domain: ContentDomain::Simulation,
1654 mime_types: vec!["application/x-comsol".into()],
1655 extensions: vec!["mph".into()],
1656 magic_bytes: vec!["504B0304".into()],
1657 diff_strategy: DiffStrategy::Opaque,
1658 merge_strategy: MergeStrategy::ManualResolve,
1659 storage_tier: StorageTier::Lfs,
1660 lfs_threshold: Some(1024 * 1024),
1661 metadata_schema: None,
1662 structural_diff: false,
1663 component_locking: true,
1664 description: "COMSOL Multiphysics model file".into(),
1665 },
1666 ContentType {
1667 id: "sim/gmsh".into(),
1668 name: "Gmsh Mesh / Geometry".into(),
1669 domain: ContentDomain::Simulation,
1670 mime_types: vec!["text/x-gmsh".into()],
1671 extensions: vec!["msh".into(), "geo".into()],
1672 magic_bytes: vec![],
1673 diff_strategy: DiffStrategy::Text,
1674 merge_strategy: MergeStrategy::ManualResolve,
1675 storage_tier: StorageTier::Standard,
1676 lfs_threshold: Some(50 * 1024 * 1024),
1677 metadata_schema: None,
1678 structural_diff: false,
1679 component_locking: false,
1680 description: "Gmsh mesh (.msh) and geometry (.geo)".into(),
1681 },
1682 ContentType {
1683 id: "sim/vtk".into(),
1684 name: "VTK Visualization".into(),
1685 domain: ContentDomain::Simulation,
1686 mime_types: vec!["application/x-vtk".into()],
1687 extensions: vec![
1688 "vtk".into(),
1689 "vtu".into(),
1690 "vtp".into(),
1691 "vti".into(),
1692 "vtr".into(),
1693 "vts".into(),
1694 "pvd".into(),
1695 "pvtu".into(),
1696 ],
1697 magic_bytes: vec![],
1698 diff_strategy: DiffStrategy::Opaque,
1699 merge_strategy: MergeStrategy::LastWriterWins,
1700 storage_tier: StorageTier::Lfs,
1701 lfs_threshold: Some(5 * 1024 * 1024),
1702 metadata_schema: None,
1703 structural_diff: false,
1704 component_locking: false,
1705 description: "VTK/ParaView mesh & field visualization data".into(),
1706 },
1707 ContentType {
1708 id: "sim/cgns".into(),
1709 name: "CGNS".into(),
1710 domain: ContentDomain::Simulation,
1711 mime_types: vec!["application/x-cgns".into()],
1712 extensions: vec!["cgns".into()],
1713 magic_bytes: vec![],
1714 diff_strategy: DiffStrategy::Opaque,
1715 merge_strategy: MergeStrategy::LastWriterWins,
1716 storage_tier: StorageTier::Lfs,
1717 lfs_threshold: Some(5 * 1024 * 1024),
1718 metadata_schema: None,
1719 structural_diff: false,
1720 component_locking: false,
1721 description: "CFD General Notation System (HDF5-based)".into(),
1722 },
1723 ContentType {
1724 id: "sim/exodus".into(),
1725 name: "Exodus II".into(),
1726 domain: ContentDomain::Simulation,
1727 mime_types: vec!["application/x-exodus".into()],
1728 extensions: vec!["exo".into(), "exoii".into()],
1729 magic_bytes: vec![],
1730 diff_strategy: DiffStrategy::Opaque,
1731 merge_strategy: MergeStrategy::LastWriterWins,
1732 storage_tier: StorageTier::Lfs,
1733 lfs_threshold: Some(5 * 1024 * 1024),
1734 metadata_schema: None,
1735 structural_diff: false,
1736 component_locking: false,
1737 description: "Exodus II finite-element results (netCDF-based)".into(),
1738 },
1739 ContentType {
1740 id: "sim/modelica".into(),
1741 name: "Modelica Model".into(),
1742 domain: ContentDomain::Simulation,
1743 mime_types: vec!["text/x-modelica".into()],
1744 extensions: vec!["mo".into()],
1745 magic_bytes: vec![],
1746 diff_strategy: DiffStrategy::Text,
1747 merge_strategy: MergeStrategy::TextThreeWay,
1748 storage_tier: StorageTier::Standard,
1749 lfs_threshold: None,
1750 metadata_schema: None,
1751 structural_diff: false,
1752 component_locking: false,
1753 description: "Modelica equation-based system model source".into(),
1754 },
1755 ContentType {
1756 id: "sim/simulink".into(),
1757 name: "Simulink Model".into(),
1758 domain: ContentDomain::Simulation,
1759 mime_types: vec!["application/x-simulink".into()],
1760 extensions: vec!["slx".into(), "mdl".into()],
1761 magic_bytes: vec![],
1762 diff_strategy: DiffStrategy::Opaque,
1763 merge_strategy: MergeStrategy::ManualResolve,
1764 storage_tier: StorageTier::Lfs,
1765 lfs_threshold: Some(1024 * 1024),
1766 metadata_schema: None,
1767 structural_diff: false,
1768 component_locking: true,
1769 description: "MathWorks Simulink block-diagram model".into(),
1770 },
1771 ContentType {
1772 id: "sim/fmu".into(),
1773 name: "Functional Mock-up Unit".into(),
1774 domain: ContentDomain::Simulation,
1775 mime_types: vec!["application/x-fmu".into()],
1776 extensions: vec!["fmu".into()],
1777 magic_bytes: vec!["504B0304".into()],
1778 diff_strategy: DiffStrategy::Opaque,
1779 merge_strategy: MergeStrategy::ManualResolve,
1780 storage_tier: StorageTier::Lfs,
1781 lfs_threshold: Some(1024 * 1024),
1782 metadata_schema: None,
1783 structural_diff: false,
1784 component_locking: true,
1785 description: "FMI Functional Mock-up Unit (co-simulation)".into(),
1786 },
1787 ContentType {
1789 id: "ml/onnx".into(),
1790 name: "ONNX Model".into(),
1791 domain: ContentDomain::MlModel,
1792 mime_types: vec!["application/x-onnx".into()],
1793 extensions: vec!["onnx".into()],
1794 magic_bytes: vec![],
1795 diff_strategy: DiffStrategy::Opaque,
1796 merge_strategy: MergeStrategy::LastWriterWins,
1797 storage_tier: StorageTier::Lfs,
1798 lfs_threshold: Some(1024 * 1024),
1799 metadata_schema: Some(serde_json::json!({
1800 "type": "object",
1801 "properties": {
1802 "opset_version": {"type": "integer"},
1803 "producer": {"type": "string"},
1804 "input_count": {"type": "integer"},
1805 "output_count": {"type": "integer"},
1806 "parameter_count": {"type": "integer"}
1807 }
1808 })),
1809 structural_diff: false,
1810 component_locking: false,
1811 description: "Open Neural Network Exchange model (protobuf)".into(),
1812 },
1813 ContentType {
1814 id: "ml/safetensors".into(),
1815 name: "SafeTensors Weights".into(),
1816 domain: ContentDomain::MlModel,
1817 mime_types: vec!["application/x-safetensors".into()],
1818 extensions: vec!["safetensors".into()],
1819 magic_bytes: vec![],
1820 diff_strategy: DiffStrategy::Opaque,
1821 merge_strategy: MergeStrategy::LastWriterWins,
1822 storage_tier: StorageTier::Lfs,
1823 lfs_threshold: Some(1024 * 1024),
1824 metadata_schema: Some(serde_json::json!({
1825 "type": "object",
1826 "properties": {
1827 "tensor_count": {"type": "integer"},
1828 "dtype": {"type": "string"},
1829 "total_parameters": {"type": "integer"}
1830 }
1831 })),
1832 structural_diff: false,
1833 component_locking: false,
1834 description: "SafeTensors safe zero-copy tensor weights".into(),
1835 },
1836 ContentType {
1837 id: "ml/pytorch".into(),
1838 name: "PyTorch Checkpoint".into(),
1839 domain: ContentDomain::MlModel,
1840 mime_types: vec!["application/x-pytorch".into()],
1841 extensions: vec!["pt".into(), "pth".into(), "bin".into()],
1842 magic_bytes: vec!["504B0304".into()],
1843 diff_strategy: DiffStrategy::Opaque,
1844 merge_strategy: MergeStrategy::LastWriterWins,
1845 storage_tier: StorageTier::Lfs,
1846 lfs_threshold: Some(1024 * 1024),
1847 metadata_schema: None,
1848 structural_diff: false,
1849 component_locking: false,
1850 description: "PyTorch serialized model/state-dict (ZIP/pickle)".into(),
1851 },
1852 ContentType {
1853 id: "ml/tensorflow".into(),
1854 name: "TensorFlow SavedModel".into(),
1855 domain: ContentDomain::MlModel,
1856 mime_types: vec!["application/x-tensorflow".into()],
1857 extensions: vec!["pb".into()],
1858 magic_bytes: vec![],
1859 diff_strategy: DiffStrategy::Opaque,
1860 merge_strategy: MergeStrategy::LastWriterWins,
1861 storage_tier: StorageTier::Lfs,
1862 lfs_threshold: Some(1024 * 1024),
1863 metadata_schema: None,
1864 structural_diff: false,
1865 component_locking: false,
1866 description: "TensorFlow GraphDef / SavedModel protobuf".into(),
1867 },
1868 ContentType {
1869 id: "ml/keras".into(),
1870 name: "Keras Model".into(),
1871 domain: ContentDomain::MlModel,
1872 mime_types: vec!["application/x-keras".into()],
1873 extensions: vec!["keras".into()],
1874 magic_bytes: vec![],
1875 diff_strategy: DiffStrategy::Opaque,
1876 merge_strategy: MergeStrategy::LastWriterWins,
1877 storage_tier: StorageTier::Lfs,
1878 lfs_threshold: Some(1024 * 1024),
1879 metadata_schema: None,
1880 structural_diff: false,
1881 component_locking: false,
1882 description: "Keras v3 model archive".into(),
1883 },
1884 ContentType {
1885 id: "ml/gguf".into(),
1886 name: "GGUF / GGML Model".into(),
1887 domain: ContentDomain::MlModel,
1888 mime_types: vec!["application/x-gguf".into()],
1889 extensions: vec!["gguf".into(), "ggml".into()],
1890 magic_bytes: vec!["47475546".into()],
1891 diff_strategy: DiffStrategy::Opaque,
1892 merge_strategy: MergeStrategy::LastWriterWins,
1893 storage_tier: StorageTier::External,
1894 lfs_threshold: Some(1024 * 1024),
1895 metadata_schema: Some(serde_json::json!({
1896 "type": "object",
1897 "properties": {
1898 "architecture": {"type": "string"},
1899 "quantization": {"type": "string"},
1900 "parameter_count": {"type": "integer"},
1901 "context_length": {"type": "integer"}
1902 }
1903 })),
1904 structural_diff: false,
1905 component_locking: false,
1906 description: "GGUF/GGML quantized LLM weights (llama.cpp)".into(),
1907 },
1908 ContentType {
1909 id: "ml/tensorrt".into(),
1910 name: "TensorRT Engine".into(),
1911 domain: ContentDomain::MlModel,
1912 mime_types: vec!["application/x-tensorrt".into()],
1913 extensions: vec!["engine".into(), "plan".into(), "trt".into()],
1914 magic_bytes: vec![],
1915 diff_strategy: DiffStrategy::Opaque,
1916 merge_strategy: MergeStrategy::LastWriterWins,
1917 storage_tier: StorageTier::Lfs,
1918 lfs_threshold: Some(1024 * 1024),
1919 metadata_schema: None,
1920 structural_diff: false,
1921 component_locking: false,
1922 description: "NVIDIA TensorRT serialized inference engine".into(),
1923 },
1924 ContentType {
1925 id: "ml/coreml".into(),
1926 name: "Core ML Model".into(),
1927 domain: ContentDomain::MlModel,
1928 mime_types: vec!["application/x-coreml".into()],
1929 extensions: vec!["mlmodel".into(), "mlpackage".into(), "mlmodelc".into()],
1930 magic_bytes: vec![],
1931 diff_strategy: DiffStrategy::Opaque,
1932 merge_strategy: MergeStrategy::ManualResolve,
1933 storage_tier: StorageTier::Lfs,
1934 lfs_threshold: Some(1024 * 1024),
1935 metadata_schema: None,
1936 structural_diff: false,
1937 component_locking: false,
1938 description: "Apple Core ML model package".into(),
1939 },
1940 ContentType {
1941 id: "ml/tflite".into(),
1942 name: "TensorFlow Lite".into(),
1943 domain: ContentDomain::MlModel,
1944 mime_types: vec!["application/x-tflite".into()],
1945 extensions: vec!["tflite".into(), "lite".into()],
1946 magic_bytes: vec![],
1947 diff_strategy: DiffStrategy::Opaque,
1948 merge_strategy: MergeStrategy::LastWriterWins,
1949 storage_tier: StorageTier::Lfs,
1950 lfs_threshold: Some(1024 * 1024),
1951 metadata_schema: None,
1952 structural_diff: false,
1953 component_locking: false,
1954 description: "TensorFlow Lite flatbuffer model (edge/mobile)".into(),
1955 },
1956 ContentType {
1957 id: "ml/pickle".into(),
1958 name: "Python Pickle".into(),
1959 domain: ContentDomain::MlModel,
1960 mime_types: vec!["application/x-python-pickle".into()],
1961 extensions: vec!["pkl".into(), "pickle".into()],
1962 magic_bytes: vec![],
1963 diff_strategy: DiffStrategy::Opaque,
1964 merge_strategy: MergeStrategy::LastWriterWins,
1965 storage_tier: StorageTier::Lfs,
1966 lfs_threshold: Some(1024 * 1024),
1967 metadata_schema: None,
1968 structural_diff: false,
1969 component_locking: false,
1970 description: "Python pickle serialized object (untrusted: arbitrary code on load)"
1971 .into(),
1972 },
1973 ContentType {
1974 id: "ml/numpy".into(),
1975 name: "NumPy Array".into(),
1976 domain: ContentDomain::MlModel,
1977 mime_types: vec!["application/x-numpy".into()],
1978 extensions: vec!["npy".into(), "npz".into()],
1979 magic_bytes: vec!["934E554D5059".into()],
1980 diff_strategy: DiffStrategy::Opaque,
1981 merge_strategy: MergeStrategy::LastWriterWins,
1982 storage_tier: StorageTier::Lfs,
1983 lfs_threshold: Some(1024 * 1024),
1984 metadata_schema: None,
1985 structural_diff: false,
1986 component_locking: false,
1987 description: "NumPy .npy/.npz array data".into(),
1988 },
1989 ContentType {
1990 id: "ml/checkpoint".into(),
1991 name: "Model Checkpoint".into(),
1992 domain: ContentDomain::MlModel,
1993 mime_types: vec!["application/x-checkpoint".into()],
1994 extensions: vec!["ckpt".into()],
1995 magic_bytes: vec![],
1996 diff_strategy: DiffStrategy::Opaque,
1997 merge_strategy: MergeStrategy::LastWriterWins,
1998 storage_tier: StorageTier::Lfs,
1999 lfs_threshold: Some(1024 * 1024),
2000 metadata_schema: None,
2001 structural_diff: false,
2002 component_locking: false,
2003 description: "Generic training checkpoint (Lightning/TF/Diffusers)".into(),
2004 },
2005 ContentType {
2006 id: "ml/joblib".into(),
2007 name: "Joblib Model".into(),
2008 domain: ContentDomain::MlModel,
2009 mime_types: vec!["application/x-joblib".into()],
2010 extensions: vec!["joblib".into()],
2011 magic_bytes: vec![],
2012 diff_strategy: DiffStrategy::Opaque,
2013 merge_strategy: MergeStrategy::LastWriterWins,
2014 storage_tier: StorageTier::Lfs,
2015 lfs_threshold: Some(1024 * 1024),
2016 metadata_schema: None,
2017 structural_diff: false,
2018 component_locking: false,
2019 description: "scikit-learn / joblib serialized estimator".into(),
2020 },
2021 ]
2022}
2023
2024fn types_dir(repo_root: &Path) -> std::path::PathBuf {
2027 repo_root.join(".lit").join("content-types")
2028}
2029
2030fn save_type(repo_root: &Path, ct: &ContentType) -> Result<(), LitError> {
2031 let dir = types_dir(repo_root);
2032 fs::create_dir_all(&dir)
2033 .map_err(|e| LitError::io(format!("Create content-types dir: {}", e)))?;
2034 let safe_id: String = ct.id.replace('/', "_");
2035 let path = dir.join(format!("{}.json", safe_id));
2036 let json = serde_json::to_string_pretty(ct)
2037 .map_err(|e| LitError::general(format!("Serialize content type: {}", e)))?;
2038 fs::write(&path, json).map_err(|e| LitError::io(format!("Write content type: {}", e)))?;
2039 Ok(())
2040}
2041
2042fn load_all_types(repo_root: &Path) -> Result<Vec<ContentType>, LitError> {
2043 let dir = types_dir(repo_root);
2044 let mut types = builtin_types();
2045
2046 if dir.exists() {
2048 for entry in fs::read_dir(&dir).map_err(|e| LitError::io(e.to_string()))? {
2049 let entry = entry.map_err(|e| LitError::io(e.to_string()))?;
2050 if entry
2051 .path()
2052 .extension()
2053 .map(|e| e == "json")
2054 .unwrap_or(false)
2055 {
2056 let json =
2057 fs::read_to_string(entry.path()).map_err(|e| LitError::io(e.to_string()))?;
2058 if let Ok(ct) = serde_json::from_str::<ContentType>(&json) {
2059 types.retain(|t| t.id != ct.id);
2061 types.push(ct);
2062 }
2063 }
2064 }
2065 }
2066 Ok(types)
2067}
2068
2069pub fn detect(file_path: &str, first_bytes: Option<&[u8]>) -> Option<ContentType> {
2071 let ext = file_path
2072 .rsplit('.')
2073 .next()
2074 .map(|e| e.to_lowercase())
2075 .unwrap_or_default();
2076
2077 let all = builtin_types();
2079
2080 if let Some(ct) = all.iter().find(|t| t.extensions.contains(&ext)) {
2082 return Some(ct.clone());
2083 }
2084
2085 if let Some(bytes) = first_bytes {
2087 let hex: String = bytes
2088 .iter()
2089 .take(16)
2090 .map(|b| format!("{:02X}", b))
2091 .collect();
2092 if let Some(ct) = all
2093 .iter()
2094 .find(|t| t.magic_bytes.iter().any(|mb| hex.starts_with(mb)))
2095 {
2096 return Some(ct.clone());
2097 }
2098 }
2099
2100 None
2101}
2102
2103pub fn execute_list(domain_filter: Option<String>) -> Result<ContentTypeResponse, LitError> {
2107 let repo_root = find_repo_root().unwrap_or_else(|_| std::path::PathBuf::from("."));
2108 let mut types = load_all_types(&repo_root)?;
2109
2110 if let Some(ref domain) = domain_filter {
2111 types.retain(|t| t.domain.to_string() == *domain);
2112 }
2113
2114 let count = types.len();
2115 Ok(ContentTypeResponse {
2116 action: "list".into(),
2117 content_type_id: None,
2118 message: format!("{} content type(s)", count),
2119 details: Some(serde_json::to_value(&types).unwrap_or_default()),
2120 })
2121}
2122
2123pub fn execute_show(type_id: String) -> Result<ContentTypeResponse, LitError> {
2125 let repo_root = find_repo_root().unwrap_or_else(|_| std::path::PathBuf::from("."));
2126 let types = load_all_types(&repo_root)?;
2127
2128 let ct = types
2129 .iter()
2130 .find(|t| t.id == type_id)
2131 .ok_or_else(|| LitError::general(format!("Content type not found: {}", type_id)))?;
2132
2133 Ok(ContentTypeResponse {
2134 action: "show".into(),
2135 content_type_id: Some(ct.id.clone()),
2136 message: format!("{} ({})", ct.name, ct.domain),
2137 details: Some(serde_json::to_value(ct).unwrap_or_default()),
2138 })
2139}
2140
2141pub fn execute_register(
2143 id: String,
2144 name: String,
2145 domain: String,
2146 extensions: Vec<String>,
2147 diff_strategy: Option<String>,
2148 merge_strategy: Option<String>,
2149 storage_tier: Option<String>,
2150) -> Result<ContentTypeResponse, LitError> {
2151 let repo_root = find_repo_root()?;
2152
2153 let domain_enum = match domain.as_str() {
2154 "software" => ContentDomain::Software,
2155 "cad" => ContentDomain::Cad,
2156 "eda" => ContentDomain::Eda,
2157 "cam" => ContentDomain::Cam,
2158 "simulation" | "sim" | "fea" | "cfd" => ContentDomain::Simulation,
2159 "ml-model" | "ml" | "ai" | "model" => ContentDomain::MlModel,
2160 "manuscript" => ContentDomain::Manuscript,
2161 "database" => ContentDomain::Database,
2162 "scientific" => ContentDomain::Scientific,
2163 "media" => ContentDomain::Media,
2164 "geospatial" => ContentDomain::Geospatial,
2165 "legal" => ContentDomain::Legal,
2166 "financial" => ContentDomain::Financial,
2167 "config" => ContentDomain::Config,
2168 "documentation" => ContentDomain::Documentation,
2169 other => ContentDomain::Custom(other.to_string()),
2170 };
2171
2172 let diff = match diff_strategy.as_deref() {
2173 Some("text") => DiffStrategy::Text,
2174 Some("binary") => DiffStrategy::Binary,
2175 Some("structural") => DiffStrategy::Structural,
2176 Some("semantic") => DiffStrategy::Semantic,
2177 Some("opaque") => DiffStrategy::Opaque,
2178 _ => DiffStrategy::Binary,
2179 };
2180
2181 let merge = match merge_strategy.as_deref() {
2182 Some("text-three-way") => MergeStrategy::TextThreeWay,
2183 Some("manual-resolve") => MergeStrategy::ManualResolve,
2184 Some("schema-aware") => MergeStrategy::SchemaAware,
2185 Some("component-level") => MergeStrategy::ComponentLevel,
2186 Some("append-only") => MergeStrategy::AppendOnly,
2187 Some("last-writer-wins") => MergeStrategy::LastWriterWins,
2188 _ => MergeStrategy::ManualResolve,
2189 };
2190
2191 let tier = match storage_tier.as_deref() {
2192 Some("standard") => StorageTier::Standard,
2193 Some("lfs") => StorageTier::Lfs,
2194 Some("chunked") => StorageTier::Chunked,
2195 Some("external") => StorageTier::External,
2196 _ => StorageTier::Lfs,
2197 };
2198
2199 let ct = ContentType {
2200 id: id.clone(),
2201 name: name.clone(),
2202 domain: domain_enum,
2203 mime_types: vec![],
2204 extensions,
2205 magic_bytes: vec![],
2206 diff_strategy: diff,
2207 merge_strategy: merge,
2208 storage_tier: tier,
2209 lfs_threshold: None,
2210 metadata_schema: None,
2211 structural_diff: false,
2212 component_locking: false,
2213 description: format!("Custom content type: {}", name),
2214 };
2215
2216 save_type(&repo_root, &ct)?;
2217
2218 Ok(ContentTypeResponse {
2219 action: "register".into(),
2220 content_type_id: Some(id),
2221 message: format!("Content type '{}' registered", name),
2222 details: Some(serde_json::to_value(&ct).unwrap_or_default()),
2223 })
2224}
2225
2226pub fn execute_detect(paths: Vec<String>) -> Result<ContentTypeResponse, LitError> {
2228 let mut results: HashMap<String, serde_json::Value> = HashMap::new();
2229
2230 for path in &paths {
2231 let first_bytes = fs::read(path).ok().map(|b| b[..b.len().min(16)].to_vec());
2232 let detected = detect(path, first_bytes.as_deref());
2233 results.insert(
2234 path.clone(),
2235 match detected {
2236 Some(ct) => serde_json::to_value(&ct).unwrap_or_default(),
2237 None => serde_json::json!({"detected": false}),
2238 },
2239 );
2240 }
2241
2242 Ok(ContentTypeResponse {
2243 action: "detect".into(),
2244 content_type_id: None,
2245 message: format!("Detected types for {} file(s)", paths.len()),
2246 details: Some(serde_json::to_value(&results).unwrap_or_default()),
2247 })
2248}