@things-factory/board-import 10.0.0-beta.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/config.development.js +44 -0
- package/config/config.production.js +18 -0
- package/dist-server/index.d.ts +7 -0
- package/dist-server/index.js +11 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/adapters/dxf-adapter.d.ts +8 -0
- package/dist-server/service/adapters/dxf-adapter.js +220 -0
- package/dist-server/service/adapters/dxf-adapter.js.map +1 -0
- package/dist-server/service/adapters/image-adapter.d.ts +32 -0
- package/dist-server/service/adapters/image-adapter.js +420 -0
- package/dist-server/service/adapters/image-adapter.js.map +1 -0
- package/dist-server/service/ai/types.d.ts +185 -0
- package/dist-server/service/ai/types.js +378 -0
- package/dist-server/service/ai/types.js.map +1 -0
- package/dist-server/service/converters/generic-to.d.ts +31 -0
- package/dist-server/service/converters/generic-to.js +84 -0
- package/dist-server/service/converters/generic-to.js.map +1 -0
- package/dist-server/service/extraction/contracts.d.ts +112 -0
- package/dist-server/service/extraction/contracts.js +3 -0
- package/dist-server/service/extraction/contracts.js.map +1 -0
- package/dist-server/service/extraction/index.d.ts +17 -0
- package/dist-server/service/extraction/index.js +62 -0
- package/dist-server/service/extraction/index.js.map +1 -0
- package/dist-server/service/extraction/label-scale-calibrator.d.ts +5 -0
- package/dist-server/service/extraction/label-scale-calibrator.js +111 -0
- package/dist-server/service/extraction/label-scale-calibrator.js.map +1 -0
- package/dist-server/service/extraction/layout-refiner.d.ts +16 -0
- package/dist-server/service/extraction/layout-refiner.js +88 -0
- package/dist-server/service/extraction/layout-refiner.js.map +1 -0
- package/dist-server/service/extraction/pipeline.d.ts +11 -0
- package/dist-server/service/extraction/pipeline.js +124 -0
- package/dist-server/service/extraction/pipeline.js.map +1 -0
- package/dist-server/service/extraction/source-router.d.ts +17 -0
- package/dist-server/service/extraction/source-router.js +40 -0
- package/dist-server/service/extraction/source-router.js.map +1 -0
- package/dist-server/service/extraction/stubs.d.ts +18 -0
- package/dist-server/service/extraction/stubs.js +38 -0
- package/dist-server/service/extraction/stubs.js.map +1 -0
- package/dist-server/service/extraction/vector-geometry.d.ts +7 -0
- package/dist-server/service/extraction/vector-geometry.js +21 -0
- package/dist-server/service/extraction/vector-geometry.js.map +1 -0
- package/dist-server/service/extraction/vlm-recognizer.d.ts +18 -0
- package/dist-server/service/extraction/vlm-recognizer.js +78 -0
- package/dist-server/service/extraction/vlm-recognizer.js.map +1 -0
- package/dist-server/service/import-session/import-actions.d.ts +79 -0
- package/dist-server/service/import-session/import-actions.js +98 -0
- package/dist-server/service/import-session/import-actions.js.map +1 -0
- package/dist-server/service/import-session/import-session-resolver.d.ts +39 -0
- package/dist-server/service/import-session/import-session-resolver.js +263 -0
- package/dist-server/service/import-session/import-session-resolver.js.map +1 -0
- package/dist-server/service/import-session/import-session.d.ts +28 -0
- package/dist-server/service/import-session/import-session.js +152 -0
- package/dist-server/service/import-session/import-session.js.map +1 -0
- package/dist-server/service/import-session/import-worker.d.ts +1 -0
- package/dist-server/service/import-session/import-worker.js +121 -0
- package/dist-server/service/import-session/import-worker.js.map +1 -0
- package/dist-server/service/import-session/index.d.ts +8 -0
- package/dist-server/service/import-session/index.js +19 -0
- package/dist-server/service/import-session/index.js.map +1 -0
- package/dist-server/service/import-session/materialize-from-session.d.ts +66 -0
- package/dist-server/service/import-session/materialize-from-session.js +104 -0
- package/dist-server/service/import-session/materialize-from-session.js.map +1 -0
- package/dist-server/service/import-session/suggest-board-name.d.ts +80 -0
- package/dist-server/service/import-session/suggest-board-name.js +195 -0
- package/dist-server/service/import-session/suggest-board-name.js.map +1 -0
- package/dist-server/service/import-tools.d.ts +10 -0
- package/dist-server/service/import-tools.js +227 -0
- package/dist-server/service/import-tools.js.map +1 -0
- package/dist-server/service/index.d.ts +26 -0
- package/dist-server/service/index.js +60 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/pipeline/index.d.ts +66 -0
- package/dist-server/service/pipeline/index.js +127 -0
- package/dist-server/service/pipeline/index.js.map +1 -0
- package/dist-server/service/pipeline/stage2-mapping.d.ts +29 -0
- package/dist-server/service/pipeline/stage2-mapping.js +231 -0
- package/dist-server/service/pipeline/stage2-mapping.js.map +1 -0
- package/dist-server/service/pipeline/stage3-board.d.ts +52 -0
- package/dist-server/service/pipeline/stage3-board.js +108 -0
- package/dist-server/service/pipeline/stage3-board.js.map +1 -0
- package/dist-server/service/pipeline/stage4-binding.d.ts +9 -0
- package/dist-server/service/pipeline/stage4-binding.js +28 -0
- package/dist-server/service/pipeline/stage4-binding.js.map +1 -0
- package/dist-server/service/registry/index.d.ts +27 -0
- package/dist-server/service/registry/index.js +108 -0
- package/dist-server/service/registry/index.js.map +1 -0
- package/dist-server/service/types/index.d.ts +152 -0
- package/dist-server/service/types/index.js +3 -0
- package/dist-server/service/types/index.js.map +1 -0
- package/dist-server/service/types/registry.d.ts +175 -0
- package/dist-server/service/types/registry.js +3 -0
- package/dist-server/service/types/registry.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +35 -0
- package/probe/gcp-vision-probe.mjs +114 -0
- package/probe/opencv-geometry-probe.py +98 -0
- package/server/index.ts +7 -0
- package/server/service/adapters/dxf-adapter.ts +230 -0
- package/server/service/adapters/image-adapter.test.ts +545 -0
- package/server/service/adapters/image-adapter.ts +464 -0
- package/server/service/ai/types.ts +619 -0
- package/server/service/converters/generic-to.test.ts +105 -0
- package/server/service/converters/generic-to.ts +121 -0
- package/server/service/extraction/contracts.ts +142 -0
- package/server/service/extraction/extraction.test.ts +374 -0
- package/server/service/extraction/index.ts +51 -0
- package/server/service/extraction/label-scale-calibrator.ts +124 -0
- package/server/service/extraction/layout-refiner.ts +104 -0
- package/server/service/extraction/pipeline.ts +163 -0
- package/server/service/extraction/source-router.ts +45 -0
- package/server/service/extraction/stubs.ts +46 -0
- package/server/service/extraction/vector-geometry.ts +26 -0
- package/server/service/extraction/vlm-recognizer.ts +87 -0
- package/server/service/import-session/import-actions.test.ts +185 -0
- package/server/service/import-session/import-actions.ts +164 -0
- package/server/service/import-session/import-session-resolver.ts +245 -0
- package/server/service/import-session/import-session.ts +141 -0
- package/server/service/import-session/import-worker.ts +132 -0
- package/server/service/import-session/index.ts +24 -0
- package/server/service/import-session/materialize-from-session.test.ts +274 -0
- package/server/service/import-session/materialize-from-session.ts +158 -0
- package/server/service/import-session/suggest-board-name.test.ts +271 -0
- package/server/service/import-session/suggest-board-name.ts +279 -0
- package/server/service/import-tools.test.ts +137 -0
- package/server/service/import-tools.ts +255 -0
- package/server/service/index.ts +117 -0
- package/server/service/pipeline/index.ts +184 -0
- package/server/service/pipeline/stage2-mapping.test.ts +204 -0
- package/server/service/pipeline/stage2-mapping.ts +270 -0
- package/server/service/pipeline/stage3-board.test.ts +133 -0
- package/server/service/pipeline/stage3-board.ts +179 -0
- package/server/service/pipeline/stage4-binding.ts +31 -0
- package/server/service/registry/index.ts +127 -0
- package/server/service/types/index.ts +169 -0
- package/server/service/types/registry.ts +186 -0
- package/things-factory.config.js +1 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest-board-name.js","sourceRoot":"","sources":["../../../server/service/import-session/suggest-board-name.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * suggestBoardName — ImportSession 으로부터 새 Board 의 후보 이름을 추천한다.\n *\n * 정책:\n * 1) base 후보 우선순위: input.hint → session.options.userPrompt → session.result.metadata.viewTypeReasoning\n * → attachment.name stem → 'Imported board'\n * 2) base 정규화: 확장자 제거, _-. → 공백, 다중 공백 단일화, 50자 절단, trim\n * 3) 도메인 격리 + 단일 LIKE query: `WHERE domain_id = ? AND (name = base OR name LIKE 'base (%)')`\n * 한 번에 충돌 후보 다 가져오고 메모리에서 사용 가능한 가장 작은 (n) 결정\n * 4) 결과: base 가 충돌 없으면 base, 있으면 'base (2)' / 'base (3)' / ...\n *\n * materializeFromSession 과 동일한 *RepoLike 패턴 — typeorm 의존성 주입으로 단위테스트 가능.\n */\n\nconst FALLBACK = 'Imported board'\n\nexport interface SuggestBoardNameInput {\n /** ImportSession id — 옵션. 있으면 거기서 hint / attachment 정보 추출. */\n sessionId?: string\n /**\n * 명시 hint. session 정보보다 우선. 사용자가 wizard 에서 직접 메모를 쳐서 그것 기반 추천을\n * 받고 싶을 때 사용.\n */\n hint?: string\n}\n\nexport interface SessionForSuggest {\n id: string\n attachmentId?: string\n optionsJson?: string | null\n resultJson?: string | null\n}\n\nexport interface AttachmentForSuggest {\n id: string\n name?: string\n}\n\nexport interface SuggestSessionRepoLike {\n findOneBy(criteria: any): Promise<SessionForSuggest | null>\n}\n\nexport interface SuggestAttachmentRepoLike {\n findOneBy(criteria: any): Promise<AttachmentForSuggest | null>\n}\n\nexport interface SuggestBoardRepoLike {\n /**\n * 도메인 격리된 LIKE 쿼리. find({ where: { name: Like('base%'), domain: { id } } }) 형태로\n * 호출자가 wiring. typeorm Like operator 노출을 피하기 위해 단순 string 으로 받음.\n */\n findExistingNames(opts: { domainId: string; basePrefix: string }): Promise<string[]>\n}\n\nexport interface SuggestContext {\n domain: { id: string; [k: string]: any }\n}\n\nexport interface SuggestDeps {\n sessionRepo: SuggestSessionRepoLike\n attachmentRepo: SuggestAttachmentRepoLike\n boardRepo: SuggestBoardRepoLike\n}\n\nexport async function suggestBoardName(\n input: SuggestBoardNameInput,\n context: SuggestContext,\n deps: SuggestDeps\n): Promise<string> {\n const base = await resolveBaseName(input, context, deps)\n const cleaned = sanitize(base) || FALLBACK\n\n const existingNames = await deps.boardRepo.findExistingNames({\n domainId: context.domain.id,\n basePrefix: cleaned\n })\n return pickAvailableSuffix(cleaned, existingNames)\n}\n\n/**\n * Board 이름 + 설명을 함께 추천. 이름은 짧고 심플 (운영 카드/리스트 표시용),\n * 설명은 자세히 (사용자 hint + AI importStrategy + 통계 요약).\n *\n * 정책:\n * - **name**: hint / userPrompt / VLM reasoning / attachment stem 에서 짧은 base 추출 후\n * 30자 이내로 자르고, 충돌 검사 + (n) suffix\n * - **description**: userPrompt + AI importStrategy + (있으면) reasoning + 카테고리 분포\n * 를 자연어 단락으로 합성. 없으면 빈 문자열\n */\nexport interface SuggestBoardMetaResult {\n name: string\n description: string\n}\n\nexport async function suggestBoardMeta(\n input: SuggestBoardNameInput,\n context: SuggestContext,\n deps: SuggestDeps\n): Promise<SuggestBoardMetaResult> {\n // 보드 정체성 = AI 가 도면을 보고 식별한 subject. import 처리 메타 (importStrategy /\n // categoryDist / userPrompt — AI 에게 한 지시) 와는 별 차원이라 영속 데이터에 섞지 않는다.\n const { subjectName, subjectDescription } = await resolveSubject(input, context, deps)\n\n // name: subjectName > input.hint > attachment.name stem > FALLBACK\n // 사용자가 명시 hint 를 줬으면 그것이 우선 (직접 의도). VLM subjectName 은 그 다음.\n const baseName =\n (input.hint && input.hint.trim() && firstSentence(input.hint)) ||\n subjectName ||\n (await resolveAttachmentStem(input, context, deps)) ||\n FALLBACK\n const cleanedName = shortenName(sanitize(baseName) || FALLBACK)\n\n const existingNames = await deps.boardRepo.findExistingNames({\n domainId: context.domain.id,\n basePrefix: cleanedName\n })\n const name = pickAvailableSuffix(cleanedName, existingNames)\n\n // description: subjectDescription > input.hint (full) > 빈 문자열\n // AI 가 도면을 본 자세한 묘사가 있으면 그것이 가장 가치 있음.\n const description =\n subjectDescription || (input.hint && input.hint.trim()) || ''\n\n return { name, description }\n}\n\n/** AI 가 도면을 보고 식별한 subject (보드 정체성) — VLM 응답의 subjectName / subjectDescription. */\nasync function resolveSubject(\n input: SuggestBoardNameInput,\n context: SuggestContext,\n deps: SuggestDeps\n): Promise<{ subjectName?: string; subjectDescription?: string }> {\n if (!input.sessionId) return {}\n const session = await deps.sessionRepo.findOneBy({\n id: input.sessionId,\n domain: { id: context.domain.id }\n })\n if (!session) return {}\n const result = parseJsonSafe(session.resultJson)\n const subjectName = typeof result?.metadata?.subjectName === 'string'\n ? result.metadata.subjectName.trim()\n : undefined\n const subjectDescription = typeof result?.metadata?.subjectDescription === 'string'\n ? result.metadata.subjectDescription.trim()\n : undefined\n return { subjectName, subjectDescription }\n}\n\n/** subject 미산출 시 fallback — attachment.name stem 만. */\nasync function resolveAttachmentStem(\n input: SuggestBoardNameInput,\n context: SuggestContext,\n deps: SuggestDeps\n): Promise<string | undefined> {\n if (!input.sessionId) return undefined\n const session = await deps.sessionRepo.findOneBy({\n id: input.sessionId,\n domain: { id: context.domain.id }\n })\n if (!session?.attachmentId) return undefined\n const att = await deps.attachmentRepo.findOneBy({\n id: session.attachmentId,\n domain: { id: context.domain.id }\n })\n return stemOf(att?.name) || undefined\n}\n\n/**\n * 짧은 이름 — 단순 30자 단위로 단어 경계에서 자르기. 너무 길면 그대로 자르되 단어 보존.\n */\nfunction shortenName(s: string): string {\n if (s.length <= 30) return s\n // 30자 이내 마지막 공백에서 자르기\n const truncated = s.slice(0, 30)\n const lastSpace = truncated.lastIndexOf(' ')\n if (lastSpace > 10) return truncated.slice(0, lastSpace).trim()\n return truncated.trim()\n}\n\n\n// ── helpers ──────────────────────────────────────────────────────────\n\nasync function resolveBaseName(\n input: SuggestBoardNameInput,\n context: SuggestContext,\n deps: SuggestDeps\n): Promise<string> {\n // 1) 명시 hint\n if (input.hint && input.hint.trim()) return firstSentence(input.hint)\n\n // 2) session 정보\n if (input.sessionId) {\n const session = await deps.sessionRepo.findOneBy({\n id: input.sessionId,\n domain: { id: context.domain.id }\n })\n if (session) {\n // 2a) userPrompt 우선\n const opts = parseJsonSafe(session.optionsJson)\n const userPrompt = typeof opts?.userPrompt === 'string' ? opts.userPrompt.trim() : ''\n if (userPrompt) return firstSentence(userPrompt)\n\n // 2b) VLM reasoning\n const result = parseJsonSafe(session.resultJson)\n const reasoning =\n typeof result?.boardModel?.importMeta?.viewTypeReasoning === 'string'\n ? result.boardModel.importMeta.viewTypeReasoning.trim()\n : typeof result?.metadata?.viewTypeReasoning === 'string'\n ? result.metadata.viewTypeReasoning.trim()\n : ''\n if (reasoning) return firstSentence(reasoning)\n\n // 2c) attachment name stem\n if (session.attachmentId) {\n const att = await deps.attachmentRepo.findOneBy({\n id: session.attachmentId,\n domain: { id: context.domain.id }\n })\n const stem = stemOf(att?.name)\n if (stem) return stem\n }\n }\n }\n\n return FALLBACK\n}\n\nfunction parseJsonSafe(s: string | null | undefined): any {\n if (!s) return undefined\n try {\n return JSON.parse(s)\n } catch {\n return undefined\n }\n}\n\nfunction firstSentence(text: string): string {\n // 첫 문장 종결 부호 또는 줄바꿈까지. 미발견 시 전체.\n const m = text.match(/^([^.\\n!?]{1,120})[.\\n!?]?/)\n return (m?.[1] ?? text).trim()\n}\n\nfunction stemOf(name: string | undefined | null): string {\n if (!name) return ''\n // 마지막 . 이후 (확장자) 제거. 점이 여러 번이면 가장 마지막만.\n const lastDot = name.lastIndexOf('.')\n if (lastDot > 0) return name.slice(0, lastDot)\n return name\n}\n\n/**\n * 정규화: _-. → 공백, 다중 공백 단일화, trim, 50자 절단.\n * 결과가 빈 문자열이면 '' 반환 (호출자가 fallback).\n */\nfunction sanitize(s: string): string {\n if (!s) return ''\n let cleaned = s.replace(/[_\\-.]+/g, ' ').replace(/\\s+/g, ' ').trim()\n if (cleaned.length > 50) cleaned = cleaned.slice(0, 50).trim()\n return cleaned\n}\n\n/**\n * existingNames 안에서 base 가 그대로 비어있으면 base 반환, 아니면 'base (2)', '(3)', ...\n * case-insensitive 비교 (postgres ilike 호환).\n */\nexport function pickAvailableSuffix(base: string, existingNames: string[]): string {\n const lower = base.toLowerCase()\n const taken = new Set<string>()\n for (const name of existingNames) {\n if (typeof name === 'string') taken.add(name.toLowerCase())\n }\n if (!taken.has(lower)) return base\n for (let n = 2; n < 10000; n++) {\n const candidate = `${base} (${n})`\n if (!taken.has(candidate.toLowerCase())) return candidate\n }\n // 9999개 충돌 이라는 비현실 시나리오 — 마지막 fallback\n return `${base} (${Date.now()})`\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ToolCategory } from '@things-factory/ai-client-base';
|
|
2
|
+
/**
|
|
3
|
+
* 명시적 register 함수 — 테스트나 lazy bootstrap 에서 import 만으로 발동되는 side-effect 가
|
|
4
|
+
* 부담스러울 때 import 후 명시 호출 가능.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerBoardImportTools(): void;
|
|
7
|
+
/**
|
|
8
|
+
* 테스트용 — registerToolCategory 의 입력으로 사용된 카테고리 객체. 외부에서 inspect 가능.
|
|
9
|
+
*/
|
|
10
|
+
export declare const BOARD_IMPORT_TOOL_CATEGORY: ToolCategory;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BOARD_IMPORT_TOOL_CATEGORY = void 0;
|
|
4
|
+
exports.registerBoardImportTools = registerBoardImportTools;
|
|
5
|
+
/**
|
|
6
|
+
* board-import 의 LLM tool 카테고리 등록.
|
|
7
|
+
*
|
|
8
|
+
* board-ai 의 chat 흐름 안에서 사용자가 "이 도면을 보드로 만들어줘" / "import 결과 검수
|
|
9
|
+
* 후 이 이름으로 저장" 같은 자연어를 발화하면 LLM 이 본 카테고리의 tool 들을 호출한다.
|
|
10
|
+
*
|
|
11
|
+
* 노출되는 tool:
|
|
12
|
+
* - listImportAdapters (read) : 등록된 FormatAdapter 능력 조회
|
|
13
|
+
* - importBoardAsync (external) : Attachment → ImportSession 비동기 시작
|
|
14
|
+
* - getImportSession (read) : ImportSession 진행/결과 조회
|
|
15
|
+
* - materializeImportSession (external) : ImportSession.result → 새 Board entity
|
|
16
|
+
*
|
|
17
|
+
* dispatch wiring:
|
|
18
|
+
* board-ai 의 chat 메서드가 ToolCallContext (resolver state — domain/user/tx) 를 closure 로
|
|
19
|
+
* 주입한다. builder 는 ctx.state 에서 typeorm getRepository / domain / user 를 추출해
|
|
20
|
+
* service-level 함수 (import-actions, materialize-from-session) 를 호출한다.
|
|
21
|
+
*
|
|
22
|
+
* 이 layer 가 없으면 builder 가 typeorm 에 직접 의존해야 했고, board-ai 가 typeorm 까지
|
|
23
|
+
* 알게 되는 leakage 가 생겼을 것 — 의도적으로 builder 가 ctx.state.typeorm 같은 식으로
|
|
24
|
+
* 접근하지 않고 things-factory 표준 getRepository 를 호출.
|
|
25
|
+
*/
|
|
26
|
+
const shell_1 = require("@things-factory/shell");
|
|
27
|
+
const ai_client_base_1 = require("@things-factory/ai-client-base");
|
|
28
|
+
// 모든 server-side 의존성 (entity / service 함수 / 외부 패키지) 은 module-level import
|
|
29
|
+
// 없이 lazy require — module load 만으로 typeorm decorator / 외부 controllers chain
|
|
30
|
+
// (예: board-service 의 puppeteer pool) 이 평가되어 jest 환경에서 깨지는 것을 회피.
|
|
31
|
+
// 운영에서는 builder 첫 호출 시 cache 채워져 이후 호출은 직접 dispatch.
|
|
32
|
+
//
|
|
33
|
+
// 이 패턴은 LLM tool layer 의 일반 정책으로 가져갈 수 있다 — registerToolCategory 가
|
|
34
|
+
// 평가되는 시점은 server bootstrap 의 매우 이른 단계라, 그때 typeorm DataSource init 이
|
|
35
|
+
// 아직 끝나지 않았을 가능성이 있다.
|
|
36
|
+
let _depsCache;
|
|
37
|
+
function getDeps() {
|
|
38
|
+
if (!_depsCache) {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
40
|
+
const isMod = require('./import-session/import-session');
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
42
|
+
const bsMod = require('@things-factory/board-service');
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
44
|
+
const actionsMod = require('./import-session/import-actions');
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
46
|
+
const materializeMod = require('./import-session/materialize-from-session');
|
|
47
|
+
_depsCache = {
|
|
48
|
+
ImportSession: isMod.ImportSession,
|
|
49
|
+
Board: bsMod.Board,
|
|
50
|
+
Group: bsMod.Group,
|
|
51
|
+
startImportJobAsync: actionsMod.startImportJobAsync,
|
|
52
|
+
fetchImportSession: actionsMod.fetchImportSession,
|
|
53
|
+
describeAdapter: actionsMod.describeAdapter,
|
|
54
|
+
materializeFromSession: materializeMod.materializeFromSession
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return _depsCache;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* tool builder 의 ctx.state 가 도메인 식별을 갖는지 검증. 미설정이면 명확한 에러로 실패 —
|
|
61
|
+
* board-ai 의 toolCallContext wiring 누락 / 비인증 호출 등을 빠르게 찾기 위해.
|
|
62
|
+
*/
|
|
63
|
+
function requireDomainContext(state) {
|
|
64
|
+
if (!state || !state.domain) {
|
|
65
|
+
throw new Error('[board-import] tool builder 의 ctx.state.domain 미설정. ' +
|
|
66
|
+
'board-ai 의 ChatOptions.toolCallContext 에 ResolverContext.state 가 전달됐는지 확인.');
|
|
67
|
+
}
|
|
68
|
+
return { domain: state.domain, user: state.user };
|
|
69
|
+
}
|
|
70
|
+
const importToolsCategory = {
|
|
71
|
+
name: 'board-import',
|
|
72
|
+
description: 'Drawing/CAD/IFC/image → Board 변환 파이프라인 (zero-to-twin).',
|
|
73
|
+
specs: [
|
|
74
|
+
{
|
|
75
|
+
kind: 'read',
|
|
76
|
+
name: 'listImportAdapters',
|
|
77
|
+
description: '등록된 도면 import 어댑터 (DXF / Image / 향후 IFC, glTF 등) 의 능력 조회. ' +
|
|
78
|
+
'사용자가 어떤 형식 import 가 가능한지 묻거나, LLM 이 적절한 어댑터 선택을 inspect 할 때 사용.',
|
|
79
|
+
schema: { type: 'object', properties: {} },
|
|
80
|
+
builder: async () => {
|
|
81
|
+
// 동적 import — pipeline 모듈의 DEFAULT_ADAPTERS 를 inspect.
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
83
|
+
const pipelineModule = require('./pipeline/index');
|
|
84
|
+
const adapters = pipelineModule.DEFAULT_ADAPTERS ?? [];
|
|
85
|
+
return adapters.map(getDeps().describeAdapter);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
kind: 'external',
|
|
90
|
+
name: 'importBoardAsync',
|
|
91
|
+
description: 'Attachment(도면/조감도/사진) 을 비동기로 보드 모델로 변환 시작. 즉시 ImportSession 을 ' +
|
|
92
|
+
'반환하고 백그라운드에서 진행. status 가 completed 가 될 때까지 getImportSession 으로 ' +
|
|
93
|
+
'polling 해서 결과를 확인. 형식은 자동 감지 — DXF / GLTF / image 등.',
|
|
94
|
+
schema: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
attachmentId: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: 'Attachment id (attachment-base 에 미리 업로드된 도면 파일).'
|
|
100
|
+
},
|
|
101
|
+
chatSessionId: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
description: 'board-ai chat session id (선택). 진행 메시지가 chat 흐름에 합류.'
|
|
104
|
+
},
|
|
105
|
+
scopes: {
|
|
106
|
+
type: 'array',
|
|
107
|
+
items: { type: 'string' },
|
|
108
|
+
description: 'ImportRule 의 scope (예: ["fmsim"]). 도메인별 매핑 규칙 활성화.'
|
|
109
|
+
},
|
|
110
|
+
parseOptions: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
description: 'Adapter 파싱 옵션 (excludeLayers / maxEntities / context 등).',
|
|
113
|
+
additionalProperties: true
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
required: ['attachmentId']
|
|
117
|
+
},
|
|
118
|
+
builder: async (args, ctx) => {
|
|
119
|
+
const { domain, user } = requireDomainContext(ctx?.state);
|
|
120
|
+
const session = await getDeps().startImportJobAsync({
|
|
121
|
+
attachmentId: args.attachmentId,
|
|
122
|
+
chatSessionId: args.chatSessionId,
|
|
123
|
+
scopes: args.scopes,
|
|
124
|
+
parseOptions: args.parseOptions
|
|
125
|
+
}, { domain, user }, (0, shell_1.getRepository)(getDeps().ImportSession));
|
|
126
|
+
return {
|
|
127
|
+
sessionId: session.id,
|
|
128
|
+
status: session.status,
|
|
129
|
+
progress: session.progress,
|
|
130
|
+
message: session.message
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
kind: 'read',
|
|
136
|
+
name: 'getImportSession',
|
|
137
|
+
description: 'ImportSession 의 진행/결과 조회. status 가 queued/parsing/mapping/assembling/binding 인 ' +
|
|
138
|
+
'동안은 progress 와 message 를 반환, completed 면 result.boardModel 까지 포함. failed 면 ' +
|
|
139
|
+
'message 에 에러 사유.',
|
|
140
|
+
schema: {
|
|
141
|
+
type: 'object',
|
|
142
|
+
properties: {
|
|
143
|
+
sessionId: { type: 'string', description: 'ImportSession id (importBoardAsync 가 반환).' }
|
|
144
|
+
},
|
|
145
|
+
required: ['sessionId']
|
|
146
|
+
},
|
|
147
|
+
builder: async (args, ctx) => {
|
|
148
|
+
const { domain, user } = requireDomainContext(ctx?.state);
|
|
149
|
+
const session = await getDeps().fetchImportSession(args.sessionId, { domain, user }, (0, shell_1.getRepository)(getDeps().ImportSession));
|
|
150
|
+
if (!session)
|
|
151
|
+
return { found: false };
|
|
152
|
+
return {
|
|
153
|
+
found: true,
|
|
154
|
+
sessionId: session.id,
|
|
155
|
+
status: session.status,
|
|
156
|
+
progress: session.progress,
|
|
157
|
+
message: session.message,
|
|
158
|
+
// result 는 큰 boardModel 을 포함할 수 있어 status 가 completed 일 때만.
|
|
159
|
+
result: session.status === 'completed' ? session.result : undefined,
|
|
160
|
+
totalEntities: session.totalEntities,
|
|
161
|
+
completedAt: session.completedAt
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
kind: 'external',
|
|
167
|
+
name: 'materializeImportSession',
|
|
168
|
+
description: '완료된 ImportSession 의 결과 boardModel 을 새 Board entity 로 영속화. status 가 ' +
|
|
169
|
+
'completed 인 세션만 처리. 결과 Board 는 state="draft" 로 생성되므로 사용자가 검수 후 ' +
|
|
170
|
+
'발행해야 한다.',
|
|
171
|
+
themeNote: '⚠ 사용자의 명시적 confirm 후에만 호출. import 결과 미리보기를 보여주고 "이 이름으로 ' +
|
|
172
|
+
'저장하시겠어요?" 같은 확인 받은 다음 발동.',
|
|
173
|
+
schema: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
sessionId: { type: 'string' },
|
|
177
|
+
name: { type: 'string', description: '새 Board 이름 — 같은 도메인 내 unique.' },
|
|
178
|
+
description: { type: 'string' },
|
|
179
|
+
groupId: { type: 'string' },
|
|
180
|
+
type: { type: 'string', enum: ['main', 'sub', 'popup'] },
|
|
181
|
+
thumbnail: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: 'Base64 thumbnail. 미지정 시 빈 placeholder 사용 (별도 thumbnail 생성 호출).'
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
required: ['sessionId', 'name']
|
|
187
|
+
},
|
|
188
|
+
builder: async (args, ctx) => {
|
|
189
|
+
const { domain, user } = requireDomainContext(ctx?.state);
|
|
190
|
+
const session = await getDeps().fetchImportSession(args.sessionId, { domain, user }, (0, shell_1.getRepository)(getDeps().ImportSession));
|
|
191
|
+
const { Board, Group, materializeFromSession } = getDeps();
|
|
192
|
+
const board = await materializeFromSession(session, {
|
|
193
|
+
sessionId: args.sessionId,
|
|
194
|
+
name: args.name,
|
|
195
|
+
description: args.description,
|
|
196
|
+
groupId: args.groupId,
|
|
197
|
+
type: args.type,
|
|
198
|
+
thumbnail: args.thumbnail
|
|
199
|
+
}, { domain, user }, {
|
|
200
|
+
boardRepo: (0, shell_1.getRepository)(Board),
|
|
201
|
+
groupRepo: (0, shell_1.getRepository)(Group)
|
|
202
|
+
});
|
|
203
|
+
// 큰 model 본문은 LLM 회신에서 제외 — id 와 메타만.
|
|
204
|
+
return {
|
|
205
|
+
boardId: board.id,
|
|
206
|
+
name: board.name,
|
|
207
|
+
state: board.state,
|
|
208
|
+
sourceImportSessionId: board.sourceImportSessionId
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
]
|
|
213
|
+
};
|
|
214
|
+
// 등록 — module 로드 시 side-effect.
|
|
215
|
+
(0, ai_client_base_1.registerToolCategory)(importToolsCategory);
|
|
216
|
+
/**
|
|
217
|
+
* 명시적 register 함수 — 테스트나 lazy bootstrap 에서 import 만으로 발동되는 side-effect 가
|
|
218
|
+
* 부담스러울 때 import 후 명시 호출 가능.
|
|
219
|
+
*/
|
|
220
|
+
function registerBoardImportTools() {
|
|
221
|
+
(0, ai_client_base_1.registerToolCategory)(importToolsCategory);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 테스트용 — registerToolCategory 의 입력으로 사용된 카테고리 객체. 외부에서 inspect 가능.
|
|
225
|
+
*/
|
|
226
|
+
exports.BOARD_IMPORT_TOOL_CATEGORY = importToolsCategory;
|
|
227
|
+
//# sourceMappingURL=import-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-tools.js","sourceRoot":"","sources":["../../server/service/import-tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * board-import 의 LLM tool 카테고리 등록.\n *\n * board-ai 의 chat 흐름 안에서 사용자가 \"이 도면을 보드로 만들어줘\" / \"import 결과 검수\n * 후 이 이름으로 저장\" 같은 자연어를 발화하면 LLM 이 본 카테고리의 tool 들을 호출한다.\n *\n * 노출되는 tool:\n * - listImportAdapters (read) : 등록된 FormatAdapter 능력 조회\n * - importBoardAsync (external) : Attachment → ImportSession 비동기 시작\n * - getImportSession (read) : ImportSession 진행/결과 조회\n * - materializeImportSession (external) : ImportSession.result → 새 Board entity\n *\n * dispatch wiring:\n * board-ai 의 chat 메서드가 ToolCallContext (resolver state — domain/user/tx) 를 closure 로\n * 주입한다. builder 는 ctx.state 에서 typeorm getRepository / domain / user 를 추출해\n * service-level 함수 (import-actions, materialize-from-session) 를 호출한다.\n *\n * 이 layer 가 없으면 builder 가 typeorm 에 직접 의존해야 했고, board-ai 가 typeorm 까지\n * 알게 되는 leakage 가 생겼을 것 — 의도적으로 builder 가 ctx.state.typeorm 같은 식으로\n * 접근하지 않고 things-factory 표준 getRepository 를 호출.\n */\nimport { getRepository } from '@things-factory/shell'\nimport {\n registerToolCategory,\n type ToolCategory,\n type ToolSpec\n} from '@things-factory/ai-client-base'\n\n// 모든 server-side 의존성 (entity / service 함수 / 외부 패키지) 은 module-level import\n// 없이 lazy require — module load 만으로 typeorm decorator / 외부 controllers chain\n// (예: board-service 의 puppeteer pool) 이 평가되어 jest 환경에서 깨지는 것을 회피.\n// 운영에서는 builder 첫 호출 시 cache 채워져 이후 호출은 직접 dispatch.\n//\n// 이 패턴은 LLM tool layer 의 일반 정책으로 가져갈 수 있다 — registerToolCategory 가\n// 평가되는 시점은 server bootstrap 의 매우 이른 단계라, 그때 typeorm DataSource init 이\n// 아직 끝나지 않았을 가능성이 있다.\nlet _depsCache: any | undefined\nfunction getDeps(): any {\n if (!_depsCache) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const isMod = require('./import-session/import-session')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const bsMod = require('@things-factory/board-service')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const actionsMod = require('./import-session/import-actions')\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const materializeMod = require('./import-session/materialize-from-session')\n _depsCache = {\n ImportSession: isMod.ImportSession,\n Board: bsMod.Board,\n Group: bsMod.Group,\n startImportJobAsync: actionsMod.startImportJobAsync,\n fetchImportSession: actionsMod.fetchImportSession,\n describeAdapter: actionsMod.describeAdapter,\n materializeFromSession: materializeMod.materializeFromSession\n }\n }\n return _depsCache\n}\n\n/**\n * tool builder 의 ctx.state 가 도메인 식별을 갖는지 검증. 미설정이면 명확한 에러로 실패 —\n * board-ai 의 toolCallContext wiring 누락 / 비인증 호출 등을 빠르게 찾기 위해.\n */\nfunction requireDomainContext(state: any): { domain: any; user: any } {\n if (!state || !state.domain) {\n throw new Error(\n '[board-import] tool builder 의 ctx.state.domain 미설정. ' +\n 'board-ai 의 ChatOptions.toolCallContext 에 ResolverContext.state 가 전달됐는지 확인.'\n )\n }\n return { domain: state.domain, user: state.user }\n}\n\nconst importToolsCategory: ToolCategory = {\n name: 'board-import',\n description: 'Drawing/CAD/IFC/image → Board 변환 파이프라인 (zero-to-twin).',\n specs: [\n {\n kind: 'read',\n name: 'listImportAdapters',\n description:\n '등록된 도면 import 어댑터 (DXF / Image / 향후 IFC, glTF 등) 의 능력 조회. ' +\n '사용자가 어떤 형식 import 가 가능한지 묻거나, LLM 이 적절한 어댑터 선택을 inspect 할 때 사용.',\n schema: { type: 'object', properties: {} },\n builder: async () => {\n // 동적 import — pipeline 모듈의 DEFAULT_ADAPTERS 를 inspect.\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const pipelineModule = require('./pipeline/index')\n const adapters: any[] = pipelineModule.DEFAULT_ADAPTERS ?? []\n return adapters.map(getDeps().describeAdapter)\n }\n },\n {\n kind: 'external',\n name: 'importBoardAsync',\n description:\n 'Attachment(도면/조감도/사진) 을 비동기로 보드 모델로 변환 시작. 즉시 ImportSession 을 ' +\n '반환하고 백그라운드에서 진행. status 가 completed 가 될 때까지 getImportSession 으로 ' +\n 'polling 해서 결과를 확인. 형식은 자동 감지 — DXF / GLTF / image 등.',\n schema: {\n type: 'object',\n properties: {\n attachmentId: {\n type: 'string',\n description: 'Attachment id (attachment-base 에 미리 업로드된 도면 파일).'\n },\n chatSessionId: {\n type: 'string',\n description: 'board-ai chat session id (선택). 진행 메시지가 chat 흐름에 합류.'\n },\n scopes: {\n type: 'array',\n items: { type: 'string' },\n description: 'ImportRule 의 scope (예: [\"fmsim\"]). 도메인별 매핑 규칙 활성화.'\n },\n parseOptions: {\n type: 'object',\n description: 'Adapter 파싱 옵션 (excludeLayers / maxEntities / context 등).',\n additionalProperties: true\n }\n },\n required: ['attachmentId']\n },\n builder: async (args, ctx) => {\n const { domain, user } = requireDomainContext(ctx?.state)\n const session = await getDeps().startImportJobAsync(\n {\n attachmentId: args.attachmentId,\n chatSessionId: args.chatSessionId,\n scopes: args.scopes,\n parseOptions: args.parseOptions\n },\n { domain, user },\n getRepository(getDeps().ImportSession) as any\n )\n return {\n sessionId: session.id,\n status: session.status,\n progress: session.progress,\n message: session.message\n }\n }\n },\n {\n kind: 'read',\n name: 'getImportSession',\n description:\n 'ImportSession 의 진행/결과 조회. status 가 queued/parsing/mapping/assembling/binding 인 ' +\n '동안은 progress 와 message 를 반환, completed 면 result.boardModel 까지 포함. failed 면 ' +\n 'message 에 에러 사유.',\n schema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string', description: 'ImportSession id (importBoardAsync 가 반환).' }\n },\n required: ['sessionId']\n },\n builder: async (args, ctx) => {\n const { domain, user } = requireDomainContext(ctx?.state)\n const session = await getDeps().fetchImportSession(\n args.sessionId,\n { domain, user },\n getRepository(getDeps().ImportSession) as any\n )\n if (!session) return { found: false }\n return {\n found: true,\n sessionId: session.id,\n status: session.status,\n progress: session.progress,\n message: session.message,\n // result 는 큰 boardModel 을 포함할 수 있어 status 가 completed 일 때만.\n result: session.status === 'completed' ? session.result : undefined,\n totalEntities: session.totalEntities,\n completedAt: session.completedAt\n }\n }\n },\n {\n kind: 'external',\n name: 'materializeImportSession',\n description:\n '완료된 ImportSession 의 결과 boardModel 을 새 Board entity 로 영속화. status 가 ' +\n 'completed 인 세션만 처리. 결과 Board 는 state=\"draft\" 로 생성되므로 사용자가 검수 후 ' +\n '발행해야 한다.',\n themeNote:\n '⚠ 사용자의 명시적 confirm 후에만 호출. import 결과 미리보기를 보여주고 \"이 이름으로 ' +\n '저장하시겠어요?\" 같은 확인 받은 다음 발동.',\n schema: {\n type: 'object',\n properties: {\n sessionId: { type: 'string' },\n name: { type: 'string', description: '새 Board 이름 — 같은 도메인 내 unique.' },\n description: { type: 'string' },\n groupId: { type: 'string' },\n type: { type: 'string', enum: ['main', 'sub', 'popup'] },\n thumbnail: {\n type: 'string',\n description: 'Base64 thumbnail. 미지정 시 빈 placeholder 사용 (별도 thumbnail 생성 호출).'\n }\n },\n required: ['sessionId', 'name']\n },\n builder: async (args, ctx) => {\n const { domain, user } = requireDomainContext(ctx?.state)\n const session = await getDeps().fetchImportSession(\n args.sessionId,\n { domain, user },\n getRepository(getDeps().ImportSession) as any\n )\n const { Board, Group, materializeFromSession } = getDeps()\n const board = await materializeFromSession(\n session as any,\n {\n sessionId: args.sessionId,\n name: args.name,\n description: args.description,\n groupId: args.groupId,\n type: args.type,\n thumbnail: args.thumbnail\n },\n { domain, user },\n {\n boardRepo: getRepository(Board) as any,\n groupRepo: getRepository(Group) as any\n }\n )\n // 큰 model 본문은 LLM 회신에서 제외 — id 와 메타만.\n return {\n boardId: (board as any).id,\n name: (board as any).name,\n state: (board as any).state,\n sourceImportSessionId: (board as any).sourceImportSessionId\n }\n }\n }\n ] as ToolSpec[]\n}\n\n// 등록 — module 로드 시 side-effect.\nregisterToolCategory(importToolsCategory)\n\n/**\n * 명시적 register 함수 — 테스트나 lazy bootstrap 에서 import 만으로 발동되는 side-effect 가\n * 부담스러울 때 import 후 명시 호출 가능.\n */\nexport function registerBoardImportTools(): void {\n registerToolCategory(importToolsCategory)\n}\n\n/**\n * 테스트용 — registerToolCategory 의 입력으로 사용된 카테고리 객체. 외부에서 inspect 가능.\n */\nexport const BOARD_IMPORT_TOOL_CATEGORY = importToolsCategory\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from './import-session/index';
|
|
2
|
+
export declare const entities: typeof import("./import-session/import-session").ImportSession[];
|
|
3
|
+
export declare const schema: {
|
|
4
|
+
resolverClasses: typeof import("./import-session/import-session-resolver").ImportSessionResolver[];
|
|
5
|
+
};
|
|
6
|
+
export { importBoard } from './pipeline/index';
|
|
7
|
+
export type { ImportOptions, ImportResult } from './pipeline/index';
|
|
8
|
+
export { applyMapping, GENERIC_OBJECT_TYPE, DEFAULT_CATEGORY_FALLBACK } from './pipeline/stage2-mapping';
|
|
9
|
+
export { assembleBoard } from './pipeline/stage3-board';
|
|
10
|
+
export { applyDataBinding } from './pipeline/stage4-binding';
|
|
11
|
+
export type { BoardModel, BoardComponent, AssembleOptions } from './pipeline/stage3-board';
|
|
12
|
+
export type { MappingOptions } from './pipeline/stage2-mapping';
|
|
13
|
+
export type { BindingOptions } from './pipeline/stage4-binding';
|
|
14
|
+
export { convertGenericTo, // deprecated alias — keep for backward-compat
|
|
15
|
+
convertPlaceholderTo, isPlaceholderType, CORE_PLACEHOLDER_TYPES } from './converters/generic-to';
|
|
16
|
+
export type { ConvertOptions } from './converters/generic-to';
|
|
17
|
+
export { DxfAdapter } from './adapters/dxf-adapter';
|
|
18
|
+
export { ImageAdapter } from './adapters/image-adapter';
|
|
19
|
+
export { registerImportRules, registerSubscriptionRules, register3DPresets, getImportRules, getSubscriptionRules, get3DPresets, find3DPreset, findSubscriptionRule, clearImportRules, clearSubscriptionRules, clear3DPresets, clearAllRegistries } from './registry/index';
|
|
20
|
+
export type { EntityKind, EntityAttribs, EntityRelation, Vec2, Vec3, Size2, Size3, BoundingBox, UniversalEntity, ParseResult, AdapterMetadata, ParseOptions, AdapterInput, FormatAdapter, AdapterCapabilities } from './types/index';
|
|
21
|
+
export type { ComponentCategory, ImportRule, ImportMatcher, ImportBuildSpec, IdStrategy, ComponentDraft, ThreeDPreset, ThreeDGeometry, ThreeDMaterial, SubscriptionRule, MappingResult, MappingWarning } from './types/registry';
|
|
22
|
+
export { setAIClient, getAIClient, DefaultBoardImportAI } from './ai/types';
|
|
23
|
+
export { registerBoardImportTools, BOARD_IMPORT_TOOL_CATEGORY } from './import-tools';
|
|
24
|
+
export type { BoardImportAI, AIClient, // deprecated alias for BoardImportAI
|
|
25
|
+
ConventionInferenceOptions, RuleSuggestion, ClassificationOptions, LabeledEntity, EntityClassification, ImageParseOptions } from './ai/types';
|
|
26
|
+
export * as extraction from './extraction';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extraction = exports.BOARD_IMPORT_TOOL_CATEGORY = exports.registerBoardImportTools = exports.DefaultBoardImportAI = exports.getAIClient = exports.setAIClient = exports.clearAllRegistries = exports.clear3DPresets = exports.clearSubscriptionRules = exports.clearImportRules = exports.findSubscriptionRule = exports.find3DPreset = exports.get3DPresets = exports.getSubscriptionRules = exports.getImportRules = exports.register3DPresets = exports.registerSubscriptionRules = exports.registerImportRules = exports.ImageAdapter = exports.DxfAdapter = exports.CORE_PLACEHOLDER_TYPES = exports.isPlaceholderType = exports.convertPlaceholderTo = exports.convertGenericTo = exports.applyDataBinding = exports.assembleBoard = exports.DEFAULT_CATEGORY_FALLBACK = exports.GENERIC_OBJECT_TYPE = exports.applyMapping = exports.importBoard = exports.schema = exports.entities = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
// ImportSession (비동기 import) — things-factory 모듈 통합용
|
|
6
|
+
const index_1 = require("./import-session/index");
|
|
7
|
+
tslib_1.__exportStar(require("./import-session/index"), exports);
|
|
8
|
+
exports.entities = [...index_1.entities];
|
|
9
|
+
exports.schema = { resolverClasses: [...index_1.resolverClasses] };
|
|
10
|
+
// Pipeline (high-level)
|
|
11
|
+
var index_2 = require("./pipeline/index");
|
|
12
|
+
Object.defineProperty(exports, "importBoard", { enumerable: true, get: function () { return index_2.importBoard; } });
|
|
13
|
+
// Stage 별 (low-level)
|
|
14
|
+
var stage2_mapping_1 = require("./pipeline/stage2-mapping");
|
|
15
|
+
Object.defineProperty(exports, "applyMapping", { enumerable: true, get: function () { return stage2_mapping_1.applyMapping; } });
|
|
16
|
+
Object.defineProperty(exports, "GENERIC_OBJECT_TYPE", { enumerable: true, get: function () { return stage2_mapping_1.GENERIC_OBJECT_TYPE; } });
|
|
17
|
+
Object.defineProperty(exports, "DEFAULT_CATEGORY_FALLBACK", { enumerable: true, get: function () { return stage2_mapping_1.DEFAULT_CATEGORY_FALLBACK; } });
|
|
18
|
+
var stage3_board_1 = require("./pipeline/stage3-board");
|
|
19
|
+
Object.defineProperty(exports, "assembleBoard", { enumerable: true, get: function () { return stage3_board_1.assembleBoard; } });
|
|
20
|
+
var stage4_binding_1 = require("./pipeline/stage4-binding");
|
|
21
|
+
Object.defineProperty(exports, "applyDataBinding", { enumerable: true, get: function () { return stage4_binding_1.applyDataBinding; } });
|
|
22
|
+
// Converters
|
|
23
|
+
var generic_to_1 = require("./converters/generic-to");
|
|
24
|
+
Object.defineProperty(exports, "convertGenericTo", { enumerable: true, get: function () { return generic_to_1.convertGenericTo; } });
|
|
25
|
+
Object.defineProperty(exports, "convertPlaceholderTo", { enumerable: true, get: function () { return generic_to_1.convertPlaceholderTo; } });
|
|
26
|
+
Object.defineProperty(exports, "isPlaceholderType", { enumerable: true, get: function () { return generic_to_1.isPlaceholderType; } });
|
|
27
|
+
Object.defineProperty(exports, "CORE_PLACEHOLDER_TYPES", { enumerable: true, get: function () { return generic_to_1.CORE_PLACEHOLDER_TYPES; } });
|
|
28
|
+
// Adapters
|
|
29
|
+
var dxf_adapter_1 = require("./adapters/dxf-adapter");
|
|
30
|
+
Object.defineProperty(exports, "DxfAdapter", { enumerable: true, get: function () { return dxf_adapter_1.DxfAdapter; } });
|
|
31
|
+
var image_adapter_1 = require("./adapters/image-adapter");
|
|
32
|
+
Object.defineProperty(exports, "ImageAdapter", { enumerable: true, get: function () { return image_adapter_1.ImageAdapter; } });
|
|
33
|
+
// Registry
|
|
34
|
+
var index_3 = require("./registry/index");
|
|
35
|
+
Object.defineProperty(exports, "registerImportRules", { enumerable: true, get: function () { return index_3.registerImportRules; } });
|
|
36
|
+
Object.defineProperty(exports, "registerSubscriptionRules", { enumerable: true, get: function () { return index_3.registerSubscriptionRules; } });
|
|
37
|
+
Object.defineProperty(exports, "register3DPresets", { enumerable: true, get: function () { return index_3.register3DPresets; } });
|
|
38
|
+
Object.defineProperty(exports, "getImportRules", { enumerable: true, get: function () { return index_3.getImportRules; } });
|
|
39
|
+
Object.defineProperty(exports, "getSubscriptionRules", { enumerable: true, get: function () { return index_3.getSubscriptionRules; } });
|
|
40
|
+
Object.defineProperty(exports, "get3DPresets", { enumerable: true, get: function () { return index_3.get3DPresets; } });
|
|
41
|
+
Object.defineProperty(exports, "find3DPreset", { enumerable: true, get: function () { return index_3.find3DPreset; } });
|
|
42
|
+
Object.defineProperty(exports, "findSubscriptionRule", { enumerable: true, get: function () { return index_3.findSubscriptionRule; } });
|
|
43
|
+
Object.defineProperty(exports, "clearImportRules", { enumerable: true, get: function () { return index_3.clearImportRules; } });
|
|
44
|
+
Object.defineProperty(exports, "clearSubscriptionRules", { enumerable: true, get: function () { return index_3.clearSubscriptionRules; } });
|
|
45
|
+
Object.defineProperty(exports, "clear3DPresets", { enumerable: true, get: function () { return index_3.clear3DPresets; } });
|
|
46
|
+
Object.defineProperty(exports, "clearAllRegistries", { enumerable: true, get: function () { return index_3.clearAllRegistries; } });
|
|
47
|
+
// AI domain interface (Phase 3+) — provider 추상화는 @things-factory/ai-client-base
|
|
48
|
+
var types_1 = require("./ai/types");
|
|
49
|
+
Object.defineProperty(exports, "setAIClient", { enumerable: true, get: function () { return types_1.setAIClient; } });
|
|
50
|
+
Object.defineProperty(exports, "getAIClient", { enumerable: true, get: function () { return types_1.getAIClient; } });
|
|
51
|
+
Object.defineProperty(exports, "DefaultBoardImportAI", { enumerable: true, get: function () { return types_1.DefaultBoardImportAI; } });
|
|
52
|
+
// LLM tool 노출 — board-ai 의 chat 흐름이 자동 dispatch 가능하도록 등록.
|
|
53
|
+
// import 시 module side-effect 로 registerToolCategory 발동. 명시 호출도 가능.
|
|
54
|
+
var import_tools_1 = require("./import-tools");
|
|
55
|
+
Object.defineProperty(exports, "registerBoardImportTools", { enumerable: true, get: function () { return import_tools_1.registerBoardImportTools; } });
|
|
56
|
+
Object.defineProperty(exports, "BOARD_IMPORT_TOOL_CATEGORY", { enumerable: true, get: function () { return import_tools_1.BOARD_IMPORT_TOOL_CATEGORY; } });
|
|
57
|
+
// 하이브리드 추출 모듈 (Phase Board-AI §3.8) — 순수 추가. 네임스페이스로 묶어 충돌 회피.
|
|
58
|
+
// 기존 어댑터/파이프라인(ImageAdapter, stage1-4) 무변경. 본격 테스트 단계에서 stub → 실 impl 교체.
|
|
59
|
+
exports.extraction = tslib_1.__importStar(require("./extraction"));
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,qDAAqD;AACrD,kDAG+B;AAC/B,iEAAsC;AAEzB,QAAA,QAAQ,GAAG,CAAC,GAAG,gBAAqB,CAAC,CAAA;AACrC,QAAA,MAAM,GAAG,EAAE,eAAe,EAAE,CAAC,GAAG,uBAA4B,CAAC,EAAE,CAAA;AAE5E,wBAAwB;AACxB,0CAA8C;AAArC,oGAAA,WAAW,OAAA;AAGpB,sBAAsB;AACtB,4DAIkC;AAHhC,8GAAA,YAAY,OAAA;AACZ,qHAAA,mBAAmB,OAAA;AACnB,2HAAA,yBAAyB,OAAA;AAE3B,wDAAuD;AAA9C,6GAAA,aAAa,OAAA;AACtB,4DAA4D;AAAnD,kHAAA,gBAAgB,OAAA;AAKzB,aAAa;AACb,sDAKgC;AAJ9B,8GAAA,gBAAgB,OAAA;AAChB,kHAAA,oBAAoB,OAAA;AACpB,+GAAA,iBAAiB,OAAA;AACjB,oHAAA,sBAAsB,OAAA;AAIxB,WAAW;AACX,sDAAmD;AAA1C,yGAAA,UAAU,OAAA;AACnB,0DAAuD;AAA9C,6GAAA,YAAY,OAAA;AAErB,WAAW;AACX,0CAayB;AAZvB,4GAAA,mBAAmB,OAAA;AACnB,kHAAA,yBAAyB,OAAA;AACzB,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AACd,6GAAA,oBAAoB,OAAA;AACpB,qGAAA,YAAY,OAAA;AACZ,qGAAA,YAAY,OAAA;AACZ,6GAAA,oBAAoB,OAAA;AACpB,yGAAA,gBAAgB,OAAA;AAChB,+GAAA,sBAAsB,OAAA;AACtB,uGAAA,cAAc,OAAA;AACd,2GAAA,kBAAkB,OAAA;AAsCpB,gFAAgF;AAChF,oCAImB;AAHjB,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AACX,6GAAA,oBAAoB,OAAA;AAGtB,0DAA0D;AAC1D,oEAAoE;AACpE,+CAGuB;AAFrB,wHAAA,wBAAwB,OAAA;AACxB,0HAAA,0BAA0B,OAAA;AAa5B,+DAA+D;AAC/D,0EAA0E;AAC1E,mEAA0C","sourcesContent":["// ImportSession (비동기 import) — things-factory 모듈 통합용\nimport {\n entities as ImportSessionEntities,\n resolverClasses as ImportSessionResolverClasses\n} from './import-session/index'\nexport * from './import-session/index'\n\nexport const entities = [...ImportSessionEntities]\nexport const schema = { resolverClasses: [...ImportSessionResolverClasses] }\n\n// Pipeline (high-level)\nexport { importBoard } from './pipeline/index'\nexport type { ImportOptions, ImportResult } from './pipeline/index'\n\n// Stage 별 (low-level)\nexport {\n applyMapping,\n GENERIC_OBJECT_TYPE,\n DEFAULT_CATEGORY_FALLBACK\n} from './pipeline/stage2-mapping'\nexport { assembleBoard } from './pipeline/stage3-board'\nexport { applyDataBinding } from './pipeline/stage4-binding'\nexport type { BoardModel, BoardComponent, AssembleOptions } from './pipeline/stage3-board'\nexport type { MappingOptions } from './pipeline/stage2-mapping'\nexport type { BindingOptions } from './pipeline/stage4-binding'\n\n// Converters\nexport {\n convertGenericTo, // deprecated alias — keep for backward-compat\n convertPlaceholderTo,\n isPlaceholderType,\n CORE_PLACEHOLDER_TYPES\n} from './converters/generic-to'\nexport type { ConvertOptions } from './converters/generic-to'\n\n// Adapters\nexport { DxfAdapter } from './adapters/dxf-adapter'\nexport { ImageAdapter } from './adapters/image-adapter'\n\n// Registry\nexport {\n registerImportRules,\n registerSubscriptionRules,\n register3DPresets,\n getImportRules,\n getSubscriptionRules,\n get3DPresets,\n find3DPreset,\n findSubscriptionRule,\n clearImportRules,\n clearSubscriptionRules,\n clear3DPresets,\n clearAllRegistries\n} from './registry/index'\n\n// Types — Universal Entity\nexport type {\n EntityKind,\n EntityAttribs,\n EntityRelation,\n Vec2,\n Vec3,\n Size2,\n Size3,\n BoundingBox,\n UniversalEntity,\n ParseResult,\n AdapterMetadata,\n ParseOptions,\n AdapterInput,\n FormatAdapter,\n AdapterCapabilities\n} from './types/index'\n\n// Types — Registry\nexport type {\n ComponentCategory,\n ImportRule,\n ImportMatcher,\n ImportBuildSpec,\n IdStrategy,\n ComponentDraft,\n ThreeDPreset,\n ThreeDGeometry,\n ThreeDMaterial,\n SubscriptionRule,\n MappingResult,\n MappingWarning\n} from './types/registry'\n\n// AI domain interface (Phase 3+) — provider 추상화는 @things-factory/ai-client-base\nexport {\n setAIClient,\n getAIClient,\n DefaultBoardImportAI\n} from './ai/types'\n\n// LLM tool 노출 — board-ai 의 chat 흐름이 자동 dispatch 가능하도록 등록.\n// import 시 module side-effect 로 registerToolCategory 발동. 명시 호출도 가능.\nexport {\n registerBoardImportTools,\n BOARD_IMPORT_TOOL_CATEGORY\n} from './import-tools'\nexport type {\n BoardImportAI,\n AIClient, // deprecated alias for BoardImportAI\n ConventionInferenceOptions,\n RuleSuggestion,\n ClassificationOptions,\n LabeledEntity,\n EntityClassification,\n ImageParseOptions\n} from './ai/types'\n\n// 하이브리드 추출 모듈 (Phase Board-AI §3.8) — 순수 추가. 네임스페이스로 묶어 충돌 회피.\n// 기존 어댑터/파이프라인(ImageAdapter, stage1-4) 무변경. 본격 테스트 단계에서 stub → 실 impl 교체.\nexport * as extraction from './extraction'\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { AdapterInput, FormatAdapter, ParseOptions, ParseResult } from '../types/index';
|
|
2
|
+
import type { MappingResult } from '../types/registry';
|
|
3
|
+
import { type BoardModel } from './stage3-board';
|
|
4
|
+
export interface ImportOptions {
|
|
5
|
+
file: AdapterInput;
|
|
6
|
+
filename?: string;
|
|
7
|
+
/** 등록된 매핑 규칙의 어느 scope 를 사용할지 */
|
|
8
|
+
scopes?: string[];
|
|
9
|
+
/** 어댑터 옵션 (excludeLayers 등) */
|
|
10
|
+
parseOptions?: ParseOptions;
|
|
11
|
+
/** 좌표 normalize (좌상단 0,0) */
|
|
12
|
+
normalizeOrigin?: boolean;
|
|
13
|
+
/** Y 축 뒤집기 (CAD Y-up → scene Y-down) */
|
|
14
|
+
flipY?: boolean;
|
|
15
|
+
/** scene unit 변환 배율 */
|
|
16
|
+
scale?: number;
|
|
17
|
+
/** 데이터 바인딩 stage 적용 여부 */
|
|
18
|
+
applyBinding?: boolean;
|
|
19
|
+
/** 사용자 정의 어댑터 (있으면 디폴트 검색 대신 사용) */
|
|
20
|
+
adapter?: FormatAdapter;
|
|
21
|
+
}
|
|
22
|
+
export interface ImportResult {
|
|
23
|
+
boardModel: BoardModel;
|
|
24
|
+
mapping: MappingResult;
|
|
25
|
+
parse: ParseResult;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 변환 시안 (variant) — AI 가 같은 도면을 다른 관점으로 해석한 결과. 사용자가 review 단계에서
|
|
29
|
+
* 시각 비교 후 선택. parse 가 multi-variant 응답하면 (현재 ImageAdapter), 각 variant 마다
|
|
30
|
+
* 독립 mapping + assemble. DXF 같은 single-variant adapter 는 1 boardModel 만.
|
|
31
|
+
*/
|
|
32
|
+
export interface VariantResult {
|
|
33
|
+
id: string;
|
|
34
|
+
label: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
/** 보드 정체성 — name 자동 추천 기반. */
|
|
37
|
+
subjectName?: string;
|
|
38
|
+
/** 보드 정체성 — description 자동 추천 기반. */
|
|
39
|
+
subjectDescription?: string;
|
|
40
|
+
/** AI 의 의도 narrative — review 카드 표시 (transient). */
|
|
41
|
+
importStrategy?: string;
|
|
42
|
+
boardModel: BoardModel;
|
|
43
|
+
/** mapping 통계 (시안별 — entities 가 다르므로 stats 도 다름). */
|
|
44
|
+
stats: MappingResult['stats'];
|
|
45
|
+
}
|
|
46
|
+
export interface ImportVariantsResult {
|
|
47
|
+
variants: VariantResult[];
|
|
48
|
+
/** 각 시안의 mapping 결과 묶음. variants[i] 와 같은 인덱스. */
|
|
49
|
+
mappings: MappingResult[];
|
|
50
|
+
parse: ParseResult;
|
|
51
|
+
}
|
|
52
|
+
export declare const DEFAULT_ADAPTERS: FormatAdapter[];
|
|
53
|
+
export declare function importBoard(opts: ImportOptions): Promise<ImportResult>;
|
|
54
|
+
/**
|
|
55
|
+
* 다중 시안 import — parse 1번 (비싼 VLM/DXF), 시안별 mapping + assemble.
|
|
56
|
+
*
|
|
57
|
+
* AI multi-variant adapter (ImageAdapter) 는 parse.variants 에 N 시안 반환 → 각각 mapping +
|
|
58
|
+
* assemble. Single-variant adapter (DxfAdapter) 는 parse.variants 미존재 → 단일 시안 1 boardModel.
|
|
59
|
+
*/
|
|
60
|
+
export declare function importBoardVariants(opts: ImportOptions): Promise<ImportVariantsResult>;
|
|
61
|
+
export { applyMapping } from './stage2-mapping';
|
|
62
|
+
export { assembleBoard } from './stage3-board';
|
|
63
|
+
export { applyDataBinding } from './stage4-binding';
|
|
64
|
+
export type { BoardModel, BoardComponent, AssembleOptions } from './stage3-board';
|
|
65
|
+
export type { MappingOptions } from './stage2-mapping';
|
|
66
|
+
export type { BindingOptions } from './stage4-binding';
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyDataBinding = exports.assembleBoard = exports.applyMapping = exports.DEFAULT_ADAPTERS = void 0;
|
|
4
|
+
exports.importBoard = importBoard;
|
|
5
|
+
exports.importBoardVariants = importBoardVariants;
|
|
6
|
+
/**
|
|
7
|
+
* High-level Pipeline — 도면 한 장 → 운영 가능한 Board Model.
|
|
8
|
+
*
|
|
9
|
+
* 백엔드에서 호출:
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { importBoard } from '@things-factory/board-import'
|
|
12
|
+
*
|
|
13
|
+
* const buf = await fs.promises.readFile(path)
|
|
14
|
+
* const result = await importBoard({ file: buf, filename: 'plant.dxf', scopes: ['fmsim'] })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
const dxf_adapter_1 = require("../adapters/dxf-adapter");
|
|
18
|
+
const image_adapter_1 = require("../adapters/image-adapter");
|
|
19
|
+
const stage2_mapping_1 = require("./stage2-mapping");
|
|
20
|
+
const stage3_board_1 = require("./stage3-board");
|
|
21
|
+
const stage4_binding_1 = require("./stage4-binding");
|
|
22
|
+
// Adapter detection 순서가 중요: 정밀 형식 (DXF) 먼저, 폴백 / 비정형 (Image) 나중.
|
|
23
|
+
// detect() 가 filename 확장자 우선이라 충돌은 거의 없지만, 시그니처 모호한 경우 (예:
|
|
24
|
+
// 확장자 없는 binary stream) 도 정밀 형식이 먼저 잡히는 게 안전.
|
|
25
|
+
//
|
|
26
|
+
// import-tools.ts 의 listImportAdapters builder 가 본 배열을 inspect — 외부 export.
|
|
27
|
+
exports.DEFAULT_ADAPTERS = [new dxf_adapter_1.DxfAdapter(), new image_adapter_1.ImageAdapter()];
|
|
28
|
+
async function importBoard(opts) {
|
|
29
|
+
// 단일 변형 — multi-variant adapter 라도 첫 시안만 반환. back-compat.
|
|
30
|
+
const result = await importBoardVariants(opts);
|
|
31
|
+
const first = result.variants[0];
|
|
32
|
+
if (!first) {
|
|
33
|
+
throw new Error('Import produced no variants.');
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
boardModel: first.boardModel,
|
|
37
|
+
mapping: result.mappings[0],
|
|
38
|
+
parse: result.parse
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 다중 시안 import — parse 1번 (비싼 VLM/DXF), 시안별 mapping + assemble.
|
|
43
|
+
*
|
|
44
|
+
* AI multi-variant adapter (ImageAdapter) 는 parse.variants 에 N 시안 반환 → 각각 mapping +
|
|
45
|
+
* assemble. Single-variant adapter (DxfAdapter) 는 parse.variants 미존재 → 단일 시안 1 boardModel.
|
|
46
|
+
*/
|
|
47
|
+
async function importBoardVariants(opts) {
|
|
48
|
+
const adapter = opts.adapter ?? (await pickAdapter(opts.file, opts.filename));
|
|
49
|
+
if (!adapter)
|
|
50
|
+
throw new Error('지원되지 않는 도면 형식이거나 어댑터를 식별할 수 없습니다.');
|
|
51
|
+
const parsed = await adapter.parse(opts.file, opts.parseOptions);
|
|
52
|
+
const importMeta = {
|
|
53
|
+
format: adapter.format,
|
|
54
|
+
sourceBbox: parsed.bbox,
|
|
55
|
+
units: parsed.units,
|
|
56
|
+
importedAt: new Date().toISOString()
|
|
57
|
+
};
|
|
58
|
+
const assembleOptions = {
|
|
59
|
+
scopes: opts.scopes,
|
|
60
|
+
normalizeOrigin: opts.normalizeOrigin ?? true,
|
|
61
|
+
flipY: opts.flipY ?? true,
|
|
62
|
+
scale: opts.scale ?? 1,
|
|
63
|
+
importMeta
|
|
64
|
+
};
|
|
65
|
+
// multi-variant adapter — parse.variants 가 존재
|
|
66
|
+
if (Array.isArray(parsed.variants) && parsed.variants.length > 0) {
|
|
67
|
+
const variants = [];
|
|
68
|
+
const mappings = [];
|
|
69
|
+
for (const v of parsed.variants) {
|
|
70
|
+
// 각 시안의 entities 로 mapping (시안별로 다름)
|
|
71
|
+
const variantParsed = {
|
|
72
|
+
...parsed,
|
|
73
|
+
entities: v.entities,
|
|
74
|
+
bbox: v.bbox ?? parsed.bbox
|
|
75
|
+
};
|
|
76
|
+
const mapping = (0, stage2_mapping_1.applyMapping)(variantParsed, { scopes: opts.scopes });
|
|
77
|
+
mappings.push(mapping);
|
|
78
|
+
let boardModel = (0, stage3_board_1.assembleBoard)(mapping.components, variantParsed.bbox, assembleOptions);
|
|
79
|
+
if (opts.applyBinding !== false) {
|
|
80
|
+
boardModel = (0, stage4_binding_1.applyDataBinding)(boardModel, { scopes: opts.scopes });
|
|
81
|
+
}
|
|
82
|
+
variants.push({
|
|
83
|
+
id: v.id,
|
|
84
|
+
label: v.label,
|
|
85
|
+
description: v.description,
|
|
86
|
+
subjectName: v.subjectName,
|
|
87
|
+
subjectDescription: v.subjectDescription,
|
|
88
|
+
importStrategy: v.importStrategy,
|
|
89
|
+
boardModel,
|
|
90
|
+
stats: mapping.stats
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return { variants, mappings, parse: parsed };
|
|
94
|
+
}
|
|
95
|
+
// single-variant adapter (DXF 등) — 단일 boardModel
|
|
96
|
+
const mapping = (0, stage2_mapping_1.applyMapping)(parsed, { scopes: opts.scopes });
|
|
97
|
+
let boardModel = (0, stage3_board_1.assembleBoard)(mapping.components, parsed.bbox, assembleOptions);
|
|
98
|
+
if (opts.applyBinding !== false) {
|
|
99
|
+
boardModel = (0, stage4_binding_1.applyDataBinding)(boardModel, { scopes: opts.scopes });
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
variants: [
|
|
103
|
+
{
|
|
104
|
+
id: 'default',
|
|
105
|
+
label: 'default',
|
|
106
|
+
boardModel,
|
|
107
|
+
stats: mapping.stats
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
mappings: [mapping],
|
|
111
|
+
parse: parsed
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function pickAdapter(file, filename) {
|
|
115
|
+
for (const a of exports.DEFAULT_ADAPTERS) {
|
|
116
|
+
if (await a.detect(file, filename))
|
|
117
|
+
return a;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
var stage2_mapping_2 = require("./stage2-mapping");
|
|
122
|
+
Object.defineProperty(exports, "applyMapping", { enumerable: true, get: function () { return stage2_mapping_2.applyMapping; } });
|
|
123
|
+
var stage3_board_2 = require("./stage3-board");
|
|
124
|
+
Object.defineProperty(exports, "assembleBoard", { enumerable: true, get: function () { return stage3_board_2.assembleBoard; } });
|
|
125
|
+
var stage4_binding_2 = require("./stage4-binding");
|
|
126
|
+
Object.defineProperty(exports, "applyDataBinding", { enumerable: true, get: function () { return stage4_binding_2.applyDataBinding; } });
|
|
127
|
+
//# sourceMappingURL=index.js.map
|