@things-factory/board-import 10.0.0-beta.64

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.
Files changed (70) hide show
  1. package/config/config.development.js +44 -0
  2. package/config/config.production.js +18 -0
  3. package/dist-server/index.d.ts +7 -0
  4. package/dist-server/index.js +11 -0
  5. package/dist-server/index.js.map +1 -0
  6. package/dist-server/service/adapters/dxf-adapter.d.ts +8 -0
  7. package/dist-server/service/adapters/dxf-adapter.js +206 -0
  8. package/dist-server/service/adapters/dxf-adapter.js.map +1 -0
  9. package/dist-server/service/ai/types.d.ts +93 -0
  10. package/dist-server/service/ai/types.js +169 -0
  11. package/dist-server/service/ai/types.js.map +1 -0
  12. package/dist-server/service/converters/generic-to.d.ts +16 -0
  13. package/dist-server/service/converters/generic-to.js +78 -0
  14. package/dist-server/service/converters/generic-to.js.map +1 -0
  15. package/dist-server/service/import-session/import-session-resolver.d.ts +18 -0
  16. package/dist-server/service/import-session/import-session-resolver.js +135 -0
  17. package/dist-server/service/import-session/import-session-resolver.js.map +1 -0
  18. package/dist-server/service/import-session/import-session.d.ts +28 -0
  19. package/dist-server/service/import-session/import-session.js +152 -0
  20. package/dist-server/service/import-session/import-session.js.map +1 -0
  21. package/dist-server/service/import-session/import-worker.d.ts +1 -0
  22. package/dist-server/service/import-session/import-worker.js +79 -0
  23. package/dist-server/service/import-session/import-worker.js.map +1 -0
  24. package/dist-server/service/import-session/index.d.ts +6 -0
  25. package/dist-server/service/import-session/index.js +12 -0
  26. package/dist-server/service/import-session/index.js.map +1 -0
  27. package/dist-server/service/index.d.ts +22 -0
  28. package/dist-server/service/index.js +47 -0
  29. package/dist-server/service/index.js.map +1 -0
  30. package/dist-server/service/pipeline/index.d.ts +33 -0
  31. package/dist-server/service/pipeline/index.js +58 -0
  32. package/dist-server/service/pipeline/index.js.map +1 -0
  33. package/dist-server/service/pipeline/stage2-mapping.d.ts +14 -0
  34. package/dist-server/service/pipeline/stage2-mapping.js +173 -0
  35. package/dist-server/service/pipeline/stage2-mapping.js.map +1 -0
  36. package/dist-server/service/pipeline/stage3-board.d.ts +52 -0
  37. package/dist-server/service/pipeline/stage3-board.js +53 -0
  38. package/dist-server/service/pipeline/stage3-board.js.map +1 -0
  39. package/dist-server/service/pipeline/stage4-binding.d.ts +9 -0
  40. package/dist-server/service/pipeline/stage4-binding.js +28 -0
  41. package/dist-server/service/pipeline/stage4-binding.js.map +1 -0
  42. package/dist-server/service/registry/index.d.ts +27 -0
  43. package/dist-server/service/registry/index.js +108 -0
  44. package/dist-server/service/registry/index.js.map +1 -0
  45. package/dist-server/service/types/index.d.ts +117 -0
  46. package/dist-server/service/types/index.js +3 -0
  47. package/dist-server/service/types/index.js.map +1 -0
  48. package/dist-server/service/types/registry.d.ts +175 -0
  49. package/dist-server/service/types/registry.js +3 -0
  50. package/dist-server/service/types/registry.js.map +1 -0
  51. package/dist-server/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +34 -0
  53. package/server/index.ts +7 -0
  54. package/server/service/adapters/dxf-adapter.ts +217 -0
  55. package/server/service/ai/types.ts +284 -0
  56. package/server/service/converters/generic-to.ts +98 -0
  57. package/server/service/import-session/import-session-resolver.ts +113 -0
  58. package/server/service/import-session/import-session.ts +141 -0
  59. package/server/service/import-session/import-worker.ts +88 -0
  60. package/server/service/import-session/index.ts +8 -0
  61. package/server/service/index.ts +96 -0
  62. package/server/service/pipeline/index.ts +89 -0
  63. package/server/service/pipeline/stage2-mapping.ts +195 -0
  64. package/server/service/pipeline/stage3-board.ts +114 -0
  65. package/server/service/pipeline/stage4-binding.ts +31 -0
  66. package/server/service/registry/index.ts +127 -0
  67. package/server/service/types/index.ts +133 -0
  68. package/server/service/types/registry.ts +186 -0
  69. package/things-factory.config.js +1 -0
  70. package/tsconfig.json +9 -0
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerImportRules = registerImportRules;
4
+ exports.getImportRules = getImportRules;
5
+ exports.clearImportRules = clearImportRules;
6
+ exports.register3DPresets = register3DPresets;
7
+ exports.get3DPresets = get3DPresets;
8
+ exports.find3DPreset = find3DPreset;
9
+ exports.clear3DPresets = clear3DPresets;
10
+ exports.registerSubscriptionRules = registerSubscriptionRules;
11
+ exports.getSubscriptionRules = getSubscriptionRules;
12
+ exports.findSubscriptionRule = findSubscriptionRule;
13
+ exports.clearSubscriptionRules = clearSubscriptionRules;
14
+ exports.clearAllRegistries = clearAllRegistries;
15
+ /**
16
+ * 코어가 제공하는 카테고리별 기본 3D Preset.
17
+ * 앱이 자기 도메인 preset 을 등록 안 해도, 카테고리만 정의되면 합리적 3D 표현.
18
+ *
19
+ * 매칭 우선순위: app preset(componentType) → app preset(category) → 이 디폴트(category) → unknown 디폴트
20
+ */
21
+ const CORE_DEFAULT_PRESETS_BY_CATEGORY = {
22
+ container: { category: 'container', geometry: { kind: 'box', height: 3000 }, material: { color: '#888888', roughness: 0.7 }, origin: 'bottom' },
23
+ vehicle: { category: 'vehicle', geometry: { kind: 'box', height: 800 }, material: { color: '#3a8c5e' }, origin: 'bottom' },
24
+ path: { category: 'path', geometry: { kind: 'extrude', depth: 100 }, material: { color: '#bccfe0' } },
25
+ region: { category: 'region', geometry: { kind: 'extrude', depth: 10 }, material: { color: '#a3d9ff', opacity: 0.4 } },
26
+ equipment: { category: 'equipment', geometry: { kind: 'box', height: 2000 }, material: { color: '#777777' }, origin: 'bottom' },
27
+ io: { category: 'io', geometry: { kind: 'box', height: 200 }, material: { color: '#ffcc66' }, origin: 'bottom' },
28
+ marker: { category: 'marker', geometry: { kind: 'box', height: 100 }, material: { color: '#ffffff' } },
29
+ structure: { category: 'structure', geometry: { kind: 'extrude', depth: 4000 }, material: { color: '#dddddd', opacity: 0.3 }, origin: 'bottom' },
30
+ unknown: { category: 'unknown', geometry: { kind: 'box', height: 500 }, material: { color: '#aaaaaa', opacity: 0.7 }, origin: 'bottom' }
31
+ };
32
+ const importRulesByScope = {};
33
+ const threeDPresetsByScope = {};
34
+ const subscriptionRulesByScope = {};
35
+ // ── ImportRule ─────────────────────────────────────────────────────
36
+ function registerImportRules(scope, rules) {
37
+ importRulesByScope[scope] = (importRulesByScope[scope] || []).concat(rules);
38
+ }
39
+ function getImportRules(scopes) {
40
+ if (!scopes || scopes.length === 0) {
41
+ return Object.values(importRulesByScope).flat();
42
+ }
43
+ return scopes.flatMap(s => importRulesByScope[s] || []);
44
+ }
45
+ function clearImportRules(scope) {
46
+ delete importRulesByScope[scope];
47
+ }
48
+ // ── 3D Preset ──────────────────────────────────────────────────────
49
+ function register3DPresets(scope, presets) {
50
+ threeDPresetsByScope[scope] = (threeDPresetsByScope[scope] || []).concat(presets);
51
+ }
52
+ function get3DPresets(scopes) {
53
+ if (!scopes || scopes.length === 0) {
54
+ return Object.values(threeDPresetsByScope).flat();
55
+ }
56
+ return scopes.flatMap(s => threeDPresetsByScope[s] || []);
57
+ }
58
+ /**
59
+ * 컴포넌트 type + category 로 3D preset 을 찾는다.
60
+ * 우선순위:
61
+ * 1) 앱 등록 preset 중 componentType 정확 일치
62
+ * 2) 앱 등록 preset 중 category 일치
63
+ * 3) 코어 디폴트 카테고리 preset
64
+ * 4) 코어 디폴트 'unknown' preset
65
+ */
66
+ function find3DPreset(componentType, category, scopes) {
67
+ const appPresets = get3DPresets(scopes);
68
+ const exact = appPresets.find(p => p.componentType === componentType);
69
+ if (exact)
70
+ return exact;
71
+ if (category) {
72
+ const byCategory = appPresets.find(p => p.category === category && !p.componentType);
73
+ if (byCategory)
74
+ return byCategory;
75
+ if (CORE_DEFAULT_PRESETS_BY_CATEGORY[category]) {
76
+ return CORE_DEFAULT_PRESETS_BY_CATEGORY[category];
77
+ }
78
+ }
79
+ return CORE_DEFAULT_PRESETS_BY_CATEGORY.unknown;
80
+ }
81
+ function clear3DPresets(scope) {
82
+ delete threeDPresetsByScope[scope];
83
+ }
84
+ // ── Subscription Rule ──────────────────────────────────────────────
85
+ function registerSubscriptionRules(scope, rules) {
86
+ subscriptionRulesByScope[scope] = (subscriptionRulesByScope[scope] || []).concat(rules);
87
+ }
88
+ function getSubscriptionRules(scopes) {
89
+ if (!scopes || scopes.length === 0) {
90
+ return Object.values(subscriptionRulesByScope).flat();
91
+ }
92
+ return scopes.flatMap(s => subscriptionRulesByScope[s] || []);
93
+ }
94
+ function findSubscriptionRule(componentType, scopes) {
95
+ return getSubscriptionRules(scopes).find(r => r.componentType === componentType);
96
+ }
97
+ function clearSubscriptionRules(scope) {
98
+ delete subscriptionRulesByScope[scope];
99
+ }
100
+ function clearAllRegistries() {
101
+ for (const k of Object.keys(importRulesByScope))
102
+ delete importRulesByScope[k];
103
+ for (const k of Object.keys(threeDPresetsByScope))
104
+ delete threeDPresetsByScope[k];
105
+ for (const k of Object.keys(subscriptionRulesByScope))
106
+ delete subscriptionRulesByScope[k];
107
+ }
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/registry/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 앱이 자기 도메인 매핑을 등록하는 Registry.\n * scope 별로 분리 보관 (예: 'fmsim', 'mms', 'wms').\n *\n * 백엔드 단일 프로세스 안에서 in-memory 로 유지된다. 모듈 부트스트랩 시 등록.\n */\nimport type { ComponentCategory, ImportRule, SubscriptionRule, ThreeDPreset } from '../types/registry.js'\n\n/**\n * 코어가 제공하는 카테고리별 기본 3D Preset.\n * 앱이 자기 도메인 preset 을 등록 안 해도, 카테고리만 정의되면 합리적 3D 표현.\n *\n * 매칭 우선순위: app preset(componentType) → app preset(category) → 이 디폴트(category) → unknown 디폴트\n */\nconst CORE_DEFAULT_PRESETS_BY_CATEGORY: Record<ComponentCategory, ThreeDPreset> = {\n container: { category: 'container', geometry: { kind: 'box', height: 3000 }, material: { color: '#888888', roughness: 0.7 }, origin: 'bottom' },\n vehicle: { category: 'vehicle', geometry: { kind: 'box', height: 800 }, material: { color: '#3a8c5e' }, origin: 'bottom' },\n path: { category: 'path', geometry: { kind: 'extrude', depth: 100 }, material: { color: '#bccfe0' } },\n region: { category: 'region', geometry: { kind: 'extrude', depth: 10 }, material: { color: '#a3d9ff', opacity: 0.4 } },\n equipment: { category: 'equipment', geometry: { kind: 'box', height: 2000 }, material: { color: '#777777' }, origin: 'bottom' },\n io: { category: 'io', geometry: { kind: 'box', height: 200 }, material: { color: '#ffcc66' }, origin: 'bottom' },\n marker: { category: 'marker', geometry: { kind: 'box', height: 100 }, material: { color: '#ffffff' } },\n structure: { category: 'structure', geometry: { kind: 'extrude', depth: 4000 }, material: { color: '#dddddd', opacity: 0.3 }, origin: 'bottom' },\n unknown: { category: 'unknown', geometry: { kind: 'box', height: 500 }, material: { color: '#aaaaaa', opacity: 0.7 }, origin: 'bottom' }\n}\n\ninterface ScopedRegistry<T> {\n [scope: string]: T[]\n}\n\nconst importRulesByScope: ScopedRegistry<ImportRule> = {}\nconst threeDPresetsByScope: ScopedRegistry<ThreeDPreset> = {}\nconst subscriptionRulesByScope: ScopedRegistry<SubscriptionRule> = {}\n\n// ── ImportRule ─────────────────────────────────────────────────────\n\nexport function registerImportRules(scope: string, rules: ImportRule[]): void {\n importRulesByScope[scope] = (importRulesByScope[scope] || []).concat(rules)\n}\n\nexport function getImportRules(scopes?: string[]): ImportRule[] {\n if (!scopes || scopes.length === 0) {\n return Object.values(importRulesByScope).flat()\n }\n return scopes.flatMap(s => importRulesByScope[s] || [])\n}\n\nexport function clearImportRules(scope: string): void {\n delete importRulesByScope[scope]\n}\n\n// ── 3D Preset ──────────────────────────────────────────────────────\n\nexport function register3DPresets(scope: string, presets: ThreeDPreset[]): void {\n threeDPresetsByScope[scope] = (threeDPresetsByScope[scope] || []).concat(presets)\n}\n\nexport function get3DPresets(scopes?: string[]): ThreeDPreset[] {\n if (!scopes || scopes.length === 0) {\n return Object.values(threeDPresetsByScope).flat()\n }\n return scopes.flatMap(s => threeDPresetsByScope[s] || [])\n}\n\n/**\n * 컴포넌트 type + category 로 3D preset 을 찾는다.\n * 우선순위:\n * 1) 앱 등록 preset 중 componentType 정확 일치\n * 2) 앱 등록 preset 중 category 일치\n * 3) 코어 디폴트 카테고리 preset\n * 4) 코어 디폴트 'unknown' preset\n */\nexport function find3DPreset(\n componentType: string,\n category?: ComponentCategory,\n scopes?: string[]\n): ThreeDPreset {\n const appPresets = get3DPresets(scopes)\n\n const exact = appPresets.find(p => p.componentType === componentType)\n if (exact) return exact\n\n if (category) {\n const byCategory = appPresets.find(p => p.category === category && !p.componentType)\n if (byCategory) return byCategory\n\n if (CORE_DEFAULT_PRESETS_BY_CATEGORY[category]) {\n return CORE_DEFAULT_PRESETS_BY_CATEGORY[category]\n }\n }\n\n return CORE_DEFAULT_PRESETS_BY_CATEGORY.unknown\n}\n\nexport function clear3DPresets(scope: string): void {\n delete threeDPresetsByScope[scope]\n}\n\n// ── Subscription Rule ──────────────────────────────────────────────\n\nexport function registerSubscriptionRules(scope: string, rules: SubscriptionRule[]): void {\n subscriptionRulesByScope[scope] = (subscriptionRulesByScope[scope] || []).concat(rules)\n}\n\nexport function getSubscriptionRules(scopes?: string[]): SubscriptionRule[] {\n if (!scopes || scopes.length === 0) {\n return Object.values(subscriptionRulesByScope).flat()\n }\n return scopes.flatMap(s => subscriptionRulesByScope[s] || [])\n}\n\nexport function findSubscriptionRule(\n componentType: string,\n scopes?: string[]\n): SubscriptionRule | undefined {\n return getSubscriptionRules(scopes).find(r => r.componentType === componentType)\n}\n\nexport function clearSubscriptionRules(scope: string): void {\n delete subscriptionRulesByScope[scope]\n}\n\nexport function clearAllRegistries(): void {\n for (const k of Object.keys(importRulesByScope)) delete importRulesByScope[k]\n for (const k of Object.keys(threeDPresetsByScope)) delete threeDPresetsByScope[k]\n for (const k of Object.keys(subscriptionRulesByScope)) delete subscriptionRulesByScope[k]\n}\n"]}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Universal Entity — 도면 형식(DXF/IFC/이미지/SVG/glTF)에 무관한 통일 모델.
3
+ * 모든 Format Adapter 가 이 타입으로 정규화한다.
4
+ */
5
+ export type EntityKind = 'group' | 'shape' | 'path' | 'text' | 'mesh3d' | 'image-region';
6
+ export interface Vec2 {
7
+ x: number;
8
+ y: number;
9
+ }
10
+ export interface Vec3 {
11
+ x: number;
12
+ y: number;
13
+ z: number;
14
+ }
15
+ export interface Size2 {
16
+ width: number;
17
+ height: number;
18
+ }
19
+ export interface Size3 {
20
+ width: number;
21
+ height: number;
22
+ depth: number;
23
+ }
24
+ export interface BoundingBox {
25
+ min: Vec2;
26
+ max: Vec2;
27
+ }
28
+ export interface UniversalEntity {
29
+ /** 도면 내 고유 ID */
30
+ uid: string;
31
+ /** 엔티티 종류 */
32
+ kind: EntityKind;
33
+ /** 위치 (좌상단 또는 중심 — 어댑터별로 정규화 정책 명시) */
34
+ position: Vec2;
35
+ /** 크기 (없을 수도 있음, e.g. text) */
36
+ size?: Size2;
37
+ /** 회전 (도수, 시계방향) */
38
+ rotation?: number;
39
+ /** 이 엔티티의 bounding box */
40
+ bbox: BoundingBox;
41
+ /** 의미적 단서 — 가능한 만큼 채움 */
42
+ attribs: EntityAttribs;
43
+ /** 공간 관계 (선택) */
44
+ neighbors?: EntityRelation[];
45
+ /** 3D 자산 참조 (3D 형식만) */
46
+ meshRef?: string;
47
+ }
48
+ export interface EntityAttribs {
49
+ /** 블록 이름, IFC GlobalId, 식별자 */
50
+ name?: string;
51
+ /** CAD 레이어 이름 */
52
+ layer?: string;
53
+ /** IFC 의미 클래스 (예: 'IfcStockerStorage') */
54
+ semanticType?: string;
55
+ /** 텍스트 내용 (text 종류일 때) */
56
+ text?: string;
57
+ /** 도면 모양 종류 (rect/circle/polyline 등) */
58
+ shape?: 'rect' | 'circle' | 'polyline' | 'polygon';
59
+ /** 임의 추가 속성 */
60
+ [k: string]: any;
61
+ }
62
+ export interface EntityRelation {
63
+ uid: string;
64
+ relation: 'adjacent' | 'inside' | 'connected' | 'labeled-by';
65
+ distance?: number;
66
+ }
67
+ /**
68
+ * Adapter 결과 — 정규화된 entity 목록 + 메타데이터.
69
+ */
70
+ export interface ParseResult {
71
+ entities: UniversalEntity[];
72
+ metadata: AdapterMetadata;
73
+ bbox: BoundingBox;
74
+ /** 도면 단위 (mm/m/inch 등). 명시되지 않으면 'unknown' */
75
+ units?: 'mm' | 'cm' | 'm' | 'inch' | 'unknown';
76
+ }
77
+ export interface AdapterMetadata {
78
+ format: string;
79
+ formatVersion?: string;
80
+ source?: string;
81
+ /** 어댑터가 알아낸 추가 메타 (예: DXF $INSUNITS) */
82
+ [k: string]: any;
83
+ }
84
+ export interface ParseOptions {
85
+ /** 최대 entity 수 (성능 가드) */
86
+ maxEntities?: number;
87
+ /** 무시할 레이어 이름들 */
88
+ excludeLayers?: string[];
89
+ /** 추가 옵션 */
90
+ [k: string]: any;
91
+ }
92
+ /**
93
+ * Adapter 입력 — 백엔드/프론트엔드 양쪽에서 사용 가능하도록 최대한 폭넓게 수용.
94
+ * - string: DXF 등 텍스트 포맷의 본문
95
+ * - Buffer: Node 환경의 파일 바이너리
96
+ * - ArrayBuffer / Uint8Array: 바이너리 페이로드
97
+ */
98
+ export type AdapterInput = string | ArrayBuffer | Uint8Array | Buffer;
99
+ /**
100
+ * Format Adapter — 형식별 파서. 모든 어댑터는 동일 인터페이스 구현.
101
+ */
102
+ export interface FormatAdapter {
103
+ readonly format: string;
104
+ readonly supportedExtensions: string[];
105
+ readonly capabilities: AdapterCapabilities;
106
+ /** 파일이 이 어댑터로 처리 가능한지 빠른 검사 (확장자 + 시그니처) */
107
+ detect(file: AdapterInput, filename?: string): Promise<boolean>;
108
+ /** 실제 파싱 → UniversalEntity 목록 */
109
+ parse(file: AdapterInput, options?: ParseOptions): Promise<ParseResult>;
110
+ }
111
+ export interface AdapterCapabilities {
112
+ has2D: boolean;
113
+ has3D: boolean;
114
+ hasSemanticLabels: boolean;
115
+ hasTextLabels: boolean;
116
+ hasAttributes: boolean;
117
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/types/index.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Universal Entity — 도면 형식(DXF/IFC/이미지/SVG/glTF)에 무관한 통일 모델.\n * 모든 Format Adapter 가 이 타입으로 정규화한다.\n */\nexport type EntityKind = 'group' | 'shape' | 'path' | 'text' | 'mesh3d' | 'image-region'\n\nexport interface Vec2 {\n x: number\n y: number\n}\n\nexport interface Vec3 {\n x: number\n y: number\n z: number\n}\n\nexport interface Size2 {\n width: number\n height: number\n}\n\nexport interface Size3 {\n width: number\n height: number\n depth: number\n}\n\nexport interface BoundingBox {\n min: Vec2\n max: Vec2\n}\n\nexport interface UniversalEntity {\n /** 도면 내 고유 ID */\n uid: string\n /** 엔티티 종류 */\n kind: EntityKind\n /** 위치 (좌상단 또는 중심 — 어댑터별로 정규화 정책 명시) */\n position: Vec2\n /** 크기 (없을 수도 있음, e.g. text) */\n size?: Size2\n /** 회전 (도수, 시계방향) */\n rotation?: number\n /** 이 엔티티의 bounding box */\n bbox: BoundingBox\n /** 의미적 단서 — 가능한 만큼 채움 */\n attribs: EntityAttribs\n /** 공간 관계 (선택) */\n neighbors?: EntityRelation[]\n /** 3D 자산 참조 (3D 형식만) */\n meshRef?: string\n}\n\nexport interface EntityAttribs {\n /** 블록 이름, IFC GlobalId, 식별자 */\n name?: string\n /** CAD 레이어 이름 */\n layer?: string\n /** IFC 의미 클래스 (예: 'IfcStockerStorage') */\n semanticType?: string\n /** 텍스트 내용 (text 종류일 때) */\n text?: string\n /** 도면 모양 종류 (rect/circle/polyline 등) */\n shape?: 'rect' | 'circle' | 'polyline' | 'polygon'\n /** 임의 추가 속성 */\n [k: string]: any\n}\n\nexport interface EntityRelation {\n uid: string\n relation: 'adjacent' | 'inside' | 'connected' | 'labeled-by'\n distance?: number\n}\n\n/**\n * Adapter 결과 — 정규화된 entity 목록 + 메타데이터.\n */\nexport interface ParseResult {\n entities: UniversalEntity[]\n metadata: AdapterMetadata\n bbox: BoundingBox\n /** 도면 단위 (mm/m/inch 등). 명시되지 않으면 'unknown' */\n units?: 'mm' | 'cm' | 'm' | 'inch' | 'unknown'\n}\n\nexport interface AdapterMetadata {\n format: string\n formatVersion?: string\n source?: string\n /** 어댑터가 알아낸 추가 메타 (예: DXF $INSUNITS) */\n [k: string]: any\n}\n\nexport interface ParseOptions {\n /** 최대 entity 수 (성능 가드) */\n maxEntities?: number\n /** 무시할 레이어 이름들 */\n excludeLayers?: string[]\n /** 추가 옵션 */\n [k: string]: any\n}\n\n/**\n * Adapter 입력 — 백엔드/프론트엔드 양쪽에서 사용 가능하도록 최대한 폭넓게 수용.\n * - string: DXF 등 텍스트 포맷의 본문\n * - Buffer: Node 환경의 파일 바이너리\n * - ArrayBuffer / Uint8Array: 바이너리 페이로드\n */\nexport type AdapterInput = string | ArrayBuffer | Uint8Array | Buffer\n\n/**\n * Format Adapter — 형식별 파서. 모든 어댑터는 동일 인터페이스 구현.\n */\nexport interface FormatAdapter {\n readonly format: string\n readonly supportedExtensions: string[]\n readonly capabilities: AdapterCapabilities\n\n /** 파일이 이 어댑터로 처리 가능한지 빠른 검사 (확장자 + 시그니처) */\n detect(file: AdapterInput, filename?: string): Promise<boolean>\n\n /** 실제 파싱 → UniversalEntity 목록 */\n parse(file: AdapterInput, options?: ParseOptions): Promise<ParseResult>\n}\n\nexport interface AdapterCapabilities {\n has2D: boolean\n has3D: boolean\n hasSemanticLabels: boolean\n hasTextLabels: boolean\n hasAttributes: boolean\n}\n"]}
@@ -0,0 +1,175 @@
1
+ import type { EntityKind, UniversalEntity } from './index';
2
+ /**
3
+ * Universal Component Category — 도메인 비종속 분류 체계.
4
+ * 코어가 정의하며, 모든 앱이 공유. 새 카테고리 추가는 코어 변경 필요.
5
+ *
6
+ * 도메인 type ('Stocker', 'AGV') 과 별개로, 모든 컴포넌트는 카테고리에 속함.
7
+ * AI 추론 / 기본 3D Preset / Cross-app 호환성의 기반.
8
+ */
9
+ export type ComponentCategory = 'container' | 'vehicle' | 'path' | 'region' | 'equipment' | 'io' | 'marker' | 'structure' | 'unknown';
10
+ /**
11
+ * ImportRule — 앱이 등록하는 매핑 규칙.
12
+ * 한 entity 가 어떤 컴포넌트 type 으로 변환될지 결정.
13
+ */
14
+ export interface ImportRule {
15
+ /** 결과 컴포넌트 type (예: 'Stocker', 'AGV', 'Conveyor') */
16
+ componentType: string;
17
+ /**
18
+ * Universal Category. 권장값. 누락 시 'unknown'.
19
+ * AI 추론 / 기본 3D preset 폴백의 기반.
20
+ */
21
+ category?: ComponentCategory;
22
+ /** 매칭 조건 — 하나라도 해당되면 매칭. 모두 비어있으면 항상 매칭 안 됨. */
23
+ match: ImportMatcher;
24
+ /** 결과 변환 정책 */
25
+ build: ImportBuildSpec;
26
+ /** 충돌 시 우선순위 (높을수록 먼저). 기본 0 */
27
+ priority?: number;
28
+ }
29
+ export interface ImportMatcher {
30
+ /** CAD (DXF/SVG) 매칭 */
31
+ cad?: {
32
+ /** INSERT 블록 이름 패턴 */
33
+ blockNamePattern?: RegExp;
34
+ /** 레이어 이름 (정확 일치 또는 정규식) */
35
+ layer?: string | RegExp;
36
+ /** entity 종류 화이트리스트 */
37
+ entityKinds?: EntityKind[];
38
+ /** 인접 텍스트 패턴 (라벨 매칭) */
39
+ textAdjacent?: RegExp;
40
+ };
41
+ /** IFC 매칭 */
42
+ ifc?: {
43
+ /** IFC 클래스명 (예: 'IfcStockerStorage', 또는 정규식) */
44
+ ifcClass?: string | RegExp;
45
+ /** PropertySet 의 특정 속성 */
46
+ propertySet?: {
47
+ name: string;
48
+ value?: any;
49
+ };
50
+ };
51
+ /** 이미지(VLM) 매칭 — VLM 응답의 라벨 후보 */
52
+ image?: {
53
+ labels: string[];
54
+ };
55
+ /** 모양/크기 기반 매칭 (모든 형식 공통) */
56
+ geometry?: {
57
+ shape?: 'rect' | 'circle' | 'polyline' | 'polygon';
58
+ sizeRange?: {
59
+ minArea?: number;
60
+ maxArea?: number;
61
+ };
62
+ aspectRatio?: {
63
+ min?: number;
64
+ max?: number;
65
+ };
66
+ };
67
+ }
68
+ export interface ImportBuildSpec {
69
+ /** 컴포넌트 기본 상태 */
70
+ defaultState?: Record<string, any>;
71
+ /** ID 부여 전략 */
72
+ idStrategy?: IdStrategy;
73
+ /** 사용자 정의 변환 (있으면 우선) */
74
+ transform?: (entity: UniversalEntity, draft: ComponentDraft) => ComponentDraft;
75
+ }
76
+ export type IdStrategy = 'fromAttribute' | 'fromAdjacentText' | 'fromIfcGlobalId' | 'auto' | {
77
+ custom: (entity: UniversalEntity) => string;
78
+ };
79
+ /**
80
+ * Stage 2/3 의 중간 결과. 보드 모델 컴포넌트로 변환되기 직전 상태.
81
+ */
82
+ export interface ComponentDraft {
83
+ /** Universal category. 항상 채워짐 (rule 누락 시 'unknown') */
84
+ category: ComponentCategory;
85
+ /** 도메인 type. 'unknown' 카테고리면 'GenericObject' */
86
+ type: string;
87
+ id?: string;
88
+ state: Record<string, any>;
89
+ /** 매칭된 ImportRule (디버깅/검수용). 'unknown' 이면 undefined */
90
+ matchedBy?: ImportRule;
91
+ /** 원본 entity (재참조 / 매핑 검토용 / 변환 시 단서) */
92
+ source?: UniversalEntity;
93
+ }
94
+ /**
95
+ * 3D Preset — 컴포넌트 type 또는 카테고리에 대한 3D 기본 표현.
96
+ *
97
+ * 매칭 우선순위:
98
+ * 1) componentType 정확 일치
99
+ * 2) componentType 없으면 category 폴백
100
+ * 3) 둘 다 없으면 코어 기본 'unknown' preset
101
+ */
102
+ export interface ThreeDPreset {
103
+ /** 정확 매칭용 도메인 type. category 와 둘 중 하나는 필수. */
104
+ componentType?: string;
105
+ /** 카테고리 단위 폴백 매칭 */
106
+ category?: ComponentCategory;
107
+ geometry: ThreeDGeometry;
108
+ material?: ThreeDMaterial;
109
+ /** bbox 기준점 — bottom: 바닥 닿음, center: 중심 */
110
+ origin?: 'bottom' | 'center';
111
+ /** 후처리 훅 */
112
+ postProcess?: (componentState: Record<string, any>) => void;
113
+ }
114
+ export type ThreeDGeometry = {
115
+ kind: 'box';
116
+ height: number;
117
+ } | {
118
+ kind: 'cylinder';
119
+ height: number;
120
+ radius: number;
121
+ } | {
122
+ kind: 'gltf';
123
+ url: string;
124
+ scale?: number;
125
+ } | {
126
+ kind: 'extrude';
127
+ depth: number;
128
+ bevel?: boolean;
129
+ };
130
+ export interface ThreeDMaterial {
131
+ color?: string;
132
+ roughness?: number;
133
+ metalness?: number;
134
+ texture?: string;
135
+ opacity?: number;
136
+ }
137
+ /**
138
+ * Subscription Rule — 컴포넌트 type ↔ 데이터 소스 태그 매핑.
139
+ */
140
+ export interface SubscriptionRule {
141
+ componentType: string;
142
+ /** 데이터 소스 태그 (예: 'MCS_LV_VEHICLE') */
143
+ tag: string;
144
+ /** 응답 record 의 ID 필드 (default 'NAME') */
145
+ idField?: string;
146
+ /** 추가 필드 매핑 (서버 필드 → 컴포넌트 상태) */
147
+ fieldMappings?: Record<string, string>;
148
+ }
149
+ /**
150
+ * 매핑 결과 — Stage 2 의 출력.
151
+ *
152
+ * 모든 entity 가 ComponentDraft 가 됨:
153
+ * - 매칭 성공: 도메인 type (e.g. 'Stocker', category='container')
154
+ * - 매칭 실패: 'GenericObject' (category='unknown') — 좌표/원본 attribs 보존
155
+ *
156
+ * 사용자가 'GenericObject' 를 사후에 도메인 type 으로 변환 가능 (`convertGenericTo`).
157
+ */
158
+ export interface MappingResult {
159
+ /** 모든 컴포넌트 draft (도메인 + GenericObject) */
160
+ components: ComponentDraft[];
161
+ /** 매핑 통계 */
162
+ stats: {
163
+ total: number;
164
+ matched: number;
165
+ generic: number;
166
+ skipped: number;
167
+ };
168
+ /** 경고 / 충돌 정보 */
169
+ warnings: MappingWarning[];
170
+ }
171
+ export interface MappingWarning {
172
+ level: 'info' | 'warn' | 'error';
173
+ entity?: UniversalEntity;
174
+ message: string;
175
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../server/service/types/registry.ts"],"names":[],"mappings":"","sourcesContent":["import type { EntityKind, UniversalEntity } from './index'\n\n/**\n * Universal Component Category — 도메인 비종속 분류 체계.\n * 코어가 정의하며, 모든 앱이 공유. 새 카테고리 추가는 코어 변경 필요.\n *\n * 도메인 type ('Stocker', 'AGV') 과 별개로, 모든 컴포넌트는 카테고리에 속함.\n * AI 추론 / 기본 3D Preset / Cross-app 호환성의 기반.\n */\nexport type ComponentCategory =\n | 'container' // Stocker, Buffer, Shelf, Rack — 정적 보관소\n | 'vehicle' // AGV, OHT, Forklift, AMR — 이동체\n | 'path' // Conveyor, AGVLine, OHTLine — 이동 경로\n | 'region' // Zone, Area, Floor — 영역\n | 'equipment' // Machine, Workstation, Robot — 작업 설비\n | 'io' // Port, Dock, Gate — 입출력 지점\n | 'marker' // Label, Sign, Gauge — 표시·계측\n | 'structure' // Wall, Pillar, Door — 건축 구조\n | 'unknown' // 분류 미상 (GenericObject 로 변환됨)\n\n/**\n * ImportRule — 앱이 등록하는 매핑 규칙.\n * 한 entity 가 어떤 컴포넌트 type 으로 변환될지 결정.\n */\nexport interface ImportRule {\n /** 결과 컴포넌트 type (예: 'Stocker', 'AGV', 'Conveyor') */\n componentType: string\n\n /**\n * Universal Category. 권장값. 누락 시 'unknown'.\n * AI 추론 / 기본 3D preset 폴백의 기반.\n */\n category?: ComponentCategory\n\n /** 매칭 조건 — 하나라도 해당되면 매칭. 모두 비어있으면 항상 매칭 안 됨. */\n match: ImportMatcher\n\n /** 결과 변환 정책 */\n build: ImportBuildSpec\n\n /** 충돌 시 우선순위 (높을수록 먼저). 기본 0 */\n priority?: number\n}\n\nexport interface ImportMatcher {\n /** CAD (DXF/SVG) 매칭 */\n cad?: {\n /** INSERT 블록 이름 패턴 */\n blockNamePattern?: RegExp\n /** 레이어 이름 (정확 일치 또는 정규식) */\n layer?: string | RegExp\n /** entity 종류 화이트리스트 */\n entityKinds?: EntityKind[]\n /** 인접 텍스트 패턴 (라벨 매칭) */\n textAdjacent?: RegExp\n }\n\n /** IFC 매칭 */\n ifc?: {\n /** IFC 클래스명 (예: 'IfcStockerStorage', 또는 정규식) */\n ifcClass?: string | RegExp\n /** PropertySet 의 특정 속성 */\n propertySet?: { name: string; value?: any }\n }\n\n /** 이미지(VLM) 매칭 — VLM 응답의 라벨 후보 */\n image?: {\n labels: string[]\n }\n\n /** 모양/크기 기반 매칭 (모든 형식 공통) */\n geometry?: {\n shape?: 'rect' | 'circle' | 'polyline' | 'polygon'\n sizeRange?: { minArea?: number; maxArea?: number }\n aspectRatio?: { min?: number; max?: number }\n }\n}\n\nexport interface ImportBuildSpec {\n /** 컴포넌트 기본 상태 */\n defaultState?: Record<string, any>\n /** ID 부여 전략 */\n idStrategy?: IdStrategy\n /** 사용자 정의 변환 (있으면 우선) */\n transform?: (entity: UniversalEntity, draft: ComponentDraft) => ComponentDraft\n}\n\nexport type IdStrategy =\n | 'fromAttribute' // attribs.name 사용\n | 'fromAdjacentText' // 인접 텍스트 라벨\n | 'fromIfcGlobalId'\n | 'auto' // 'TYPE_001' 자동 생성\n | { custom: (entity: UniversalEntity) => string }\n\n/**\n * Stage 2/3 의 중간 결과. 보드 모델 컴포넌트로 변환되기 직전 상태.\n */\nexport interface ComponentDraft {\n /** Universal category. 항상 채워짐 (rule 누락 시 'unknown') */\n category: ComponentCategory\n /** 도메인 type. 'unknown' 카테고리면 'GenericObject' */\n type: string\n id?: string\n state: Record<string, any>\n /** 매칭된 ImportRule (디버깅/검수용). 'unknown' 이면 undefined */\n matchedBy?: ImportRule\n /** 원본 entity (재참조 / 매핑 검토용 / 변환 시 단서) */\n source?: UniversalEntity\n}\n\n/**\n * 3D Preset — 컴포넌트 type 또는 카테고리에 대한 3D 기본 표현.\n *\n * 매칭 우선순위:\n * 1) componentType 정확 일치\n * 2) componentType 없으면 category 폴백\n * 3) 둘 다 없으면 코어 기본 'unknown' preset\n */\nexport interface ThreeDPreset {\n /** 정확 매칭용 도메인 type. category 와 둘 중 하나는 필수. */\n componentType?: string\n /** 카테고리 단위 폴백 매칭 */\n category?: ComponentCategory\n geometry: ThreeDGeometry\n material?: ThreeDMaterial\n /** bbox 기준점 — bottom: 바닥 닿음, center: 중심 */\n origin?: 'bottom' | 'center'\n /** 후처리 훅 */\n postProcess?: (componentState: Record<string, any>) => void\n}\n\nexport type ThreeDGeometry =\n | { kind: 'box'; height: number }\n | { kind: 'cylinder'; height: number; radius: number }\n | { kind: 'gltf'; url: string; scale?: number }\n | { kind: 'extrude'; depth: number; bevel?: boolean }\n\nexport interface ThreeDMaterial {\n color?: string\n roughness?: number\n metalness?: number\n texture?: string\n opacity?: number\n}\n\n/**\n * Subscription Rule — 컴포넌트 type ↔ 데이터 소스 태그 매핑.\n */\nexport interface SubscriptionRule {\n componentType: string\n /** 데이터 소스 태그 (예: 'MCS_LV_VEHICLE') */\n tag: string\n /** 응답 record 의 ID 필드 (default 'NAME') */\n idField?: string\n /** 추가 필드 매핑 (서버 필드 → 컴포넌트 상태) */\n fieldMappings?: Record<string, string>\n}\n\n/**\n * 매핑 결과 — Stage 2 의 출력.\n *\n * 모든 entity 가 ComponentDraft 가 됨:\n * - 매칭 성공: 도메인 type (e.g. 'Stocker', category='container')\n * - 매칭 실패: 'GenericObject' (category='unknown') — 좌표/원본 attribs 보존\n *\n * 사용자가 'GenericObject' 를 사후에 도메인 type 으로 변환 가능 (`convertGenericTo`).\n */\nexport interface MappingResult {\n /** 모든 컴포넌트 draft (도메인 + GenericObject) */\n components: ComponentDraft[]\n /** 매핑 통계 */\n stats: {\n total: number\n matched: number // 도메인 type 으로 분류\n generic: number // GenericObject 로 폴백\n skipped: number // 무시 (예: 레이어 제외 등)\n }\n /** 경고 / 충돌 정보 */\n warnings: MappingWarning[]\n}\n\nexport interface MappingWarning {\n level: 'info' | 'warn' | 'error'\n entity?: UniversalEntity\n message: string\n}\n"]}