@things-factory/board-import 10.0.0-beta.71 → 10.0.0-beta.73

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 (83) hide show
  1. package/dist-server/index.d.ts +1 -1
  2. package/dist-server/index.js +1 -1
  3. package/dist-server/index.js.map +1 -1
  4. package/dist-server/service/adapters/dxf-adapter.d.ts +1 -1
  5. package/dist-server/service/adapters/dxf-adapter.js +18 -4
  6. package/dist-server/service/adapters/dxf-adapter.js.map +1 -1
  7. package/dist-server/service/adapters/image-adapter.d.ts +32 -0
  8. package/dist-server/service/adapters/image-adapter.js +420 -0
  9. package/dist-server/service/adapters/image-adapter.js.map +1 -0
  10. package/dist-server/service/ai/types.d.ts +96 -4
  11. package/dist-server/service/ai/types.js +221 -12
  12. package/dist-server/service/ai/types.js.map +1 -1
  13. package/dist-server/service/converters/generic-to.d.ts +17 -8
  14. package/dist-server/service/converters/generic-to.js +44 -44
  15. package/dist-server/service/converters/generic-to.js.map +1 -1
  16. package/dist-server/service/import-session/import-actions.d.ts +79 -0
  17. package/dist-server/service/import-session/import-actions.js +98 -0
  18. package/dist-server/service/import-session/import-actions.js.map +1 -0
  19. package/dist-server/service/import-session/import-session-resolver.d.ts +22 -1
  20. package/dist-server/service/import-session/import-session-resolver.js +166 -38
  21. package/dist-server/service/import-session/import-session-resolver.js.map +1 -1
  22. package/dist-server/service/import-session/import-worker.js +55 -13
  23. package/dist-server/service/import-session/import-worker.js.map +1 -1
  24. package/dist-server/service/import-session/index.d.ts +5 -3
  25. package/dist-server/service/import-session/index.js +16 -9
  26. package/dist-server/service/import-session/index.js.map +1 -1
  27. package/dist-server/service/import-session/materialize-from-session.d.ts +66 -0
  28. package/dist-server/service/import-session/materialize-from-session.js +104 -0
  29. package/dist-server/service/import-session/materialize-from-session.js.map +1 -0
  30. package/dist-server/service/import-session/suggest-board-name.d.ts +80 -0
  31. package/dist-server/service/import-session/suggest-board-name.js +195 -0
  32. package/dist-server/service/import-session/suggest-board-name.js.map +1 -0
  33. package/dist-server/service/import-tools.d.ts +10 -0
  34. package/dist-server/service/import-tools.js +227 -0
  35. package/dist-server/service/import-tools.js.map +1 -0
  36. package/dist-server/service/index.d.ts +22 -19
  37. package/dist-server/service/index.js +46 -36
  38. package/dist-server/service/index.js.map +1 -1
  39. package/dist-server/service/pipeline/index.d.ts +42 -9
  40. package/dist-server/service/pipeline/index.js +92 -23
  41. package/dist-server/service/pipeline/index.js.map +1 -1
  42. package/dist-server/service/pipeline/stage2-mapping.d.ts +20 -5
  43. package/dist-server/service/pipeline/stage2-mapping.js +84 -26
  44. package/dist-server/service/pipeline/stage2-mapping.js.map +1 -1
  45. package/dist-server/service/pipeline/stage3-board.d.ts +2 -2
  46. package/dist-server/service/pipeline/stage3-board.js +65 -10
  47. package/dist-server/service/pipeline/stage3-board.js.map +1 -1
  48. package/dist-server/service/pipeline/stage4-binding.d.ts +1 -1
  49. package/dist-server/service/pipeline/stage4-binding.js +2 -2
  50. package/dist-server/service/pipeline/stage4-binding.js.map +1 -1
  51. package/dist-server/service/registry/index.d.ts +1 -1
  52. package/dist-server/service/registry/index.js.map +1 -1
  53. package/dist-server/service/types/index.d.ts +35 -0
  54. package/dist-server/service/types/index.js.map +1 -1
  55. package/dist-server/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +7 -6
  57. package/server/index.ts +1 -1
  58. package/server/service/adapters/dxf-adapter.ts +17 -4
  59. package/server/service/adapters/image-adapter.test.ts +545 -0
  60. package/server/service/adapters/image-adapter.ts +464 -0
  61. package/server/service/ai/types.ts +354 -19
  62. package/server/service/converters/generic-to.test.ts +91 -0
  63. package/server/service/converters/generic-to.ts +66 -49
  64. package/server/service/import-session/import-actions.test.ts +185 -0
  65. package/server/service/import-session/import-actions.ts +164 -0
  66. package/server/service/import-session/import-session-resolver.ts +171 -39
  67. package/server/service/import-session/import-worker.ts +56 -12
  68. package/server/service/import-session/index.ts +19 -3
  69. package/server/service/import-session/materialize-from-session.test.ts +274 -0
  70. package/server/service/import-session/materialize-from-session.ts +158 -0
  71. package/server/service/import-session/suggest-board-name.test.ts +271 -0
  72. package/server/service/import-session/suggest-board-name.ts +279 -0
  73. package/server/service/import-tools.test.ts +137 -0
  74. package/server/service/import-tools.ts +255 -0
  75. package/server/service/index.ts +35 -18
  76. package/server/service/pipeline/index.ts +118 -23
  77. package/server/service/pipeline/stage2-mapping.test.ts +204 -0
  78. package/server/service/pipeline/stage2-mapping.ts +102 -27
  79. package/server/service/pipeline/stage3-board.test.ts +133 -0
  80. package/server/service/pipeline/stage3-board.ts +76 -11
  81. package/server/service/pipeline/stage4-binding.ts +2 -2
  82. package/server/service/registry/index.ts +1 -1
  83. package/server/service/types/index.ts +36 -0
@@ -1,16 +1,25 @@
1
- import type { BoardComponent } from '../pipeline/stage3-board.js';
1
+ import type { BoardComponent } from '../pipeline/stage3-board';
2
2
  export interface ConvertOptions {
3
3
  scopes?: string[];
4
- /** 새 컴포넌트의 ID. 미지정 시 ImportRule 의 idStrategy 적용 또는 자동 생성 */
4
+ /** 새 컴포넌트의 ID. 미지정 시 ImportRule 의 idStrategy 적용 또는 자동 생성. */
5
5
  id?: string;
6
+ /**
7
+ * 코어 placeholder type ('placeholder') 외에 인정할 도메인 추상 type list.
8
+ * 예: ['GenericTransport', 'GenericFacility'].
9
+ * 호출자가 자기 도메인 패키지의 추상까지 변환 대상으로 포함하도록 명시.
10
+ */
11
+ extraPlaceholderTypes?: ReadonlyArray<string>;
6
12
  }
13
+ /** things-scene 코어가 등록한 도메인 비종속 placeholder type. */
14
+ export declare const CORE_PLACEHOLDER_TYPES: readonly ["placeholder"];
15
+ export declare function isPlaceholderType(type: string | undefined, extraTypes?: ReadonlyArray<string>): boolean;
7
16
  /**
8
- * GenericObject 를 지정된 도메인 type 으로 변환한다.
9
- * 기존 좌표/크기/회전/source 보존.
17
+ * 추상 placeholder 를 지정된 도메인 type 으로 변환. 호환되는 모든 generic-* 수용 (코어 + 도메인).
18
+ *
19
+ * 기존 좌표/크기/회전/source 보존. placeholder 시각 메타 (placeholderLabel/Hint, fillStyle 등) 는 제거.
10
20
  */
11
- export declare function convertGenericTo(generic: BoardComponent, toType: string, options?: ConvertOptions): BoardComponent;
21
+ export declare function convertPlaceholderTo(placeholder: BoardComponent, toType: string, options?: ConvertOptions): BoardComponent;
12
22
  /**
13
- * 변환된 컴포넌트를 다시 GenericObject 로 되돌린다.
14
- * convertedFrom 정보가 있어야 가능.
23
+ * @deprecated Use `convertPlaceholderTo`. Placeholder 한정 — 새 코드는 일반화된 helper.
15
24
  */
16
- export declare function revertToGeneric(component: BoardComponent, options?: ConvertOptions): BoardComponent;
25
+ export declare function convertGenericTo(generic: BoardComponent, toType: string, options?: ConvertOptions): BoardComponent;
@@ -1,78 +1,78 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CORE_PLACEHOLDER_TYPES = void 0;
4
+ exports.isPlaceholderType = isPlaceholderType;
5
+ exports.convertPlaceholderTo = convertPlaceholderTo;
3
6
  exports.convertGenericTo = convertGenericTo;
4
- exports.revertToGeneric = revertToGeneric;
5
7
  /**
6
- * GenericObject ↔ 도메인 컴포넌트 변환.
8
+ * Generic placeholder → 도메인 컴포넌트 변환.
7
9
  *
8
- * Import 시 매칭되지 않은 entity 는 GenericObject 로 보드에 배치된다.
9
- * 사용자가 사후에 모델러 UI 에서 "Convert to Stocker" 등 액션으로 호출.
10
+ * Import 시 매칭되지 않은 entity 는 `placeholder` 타입으로 보드에 배치된다.
11
+ * 사용자가 사후에 모델러 UI 에서 "Convert to ..." 액션으로 도메인 컴포넌트로 변환.
12
+ *
13
+ * 아키텍처 원칙:
14
+ * - `placeholder` — 도메인 비종속 임시 추상. 다른 type 으로 변환하는 기능만.
15
+ * GLB 지원 없음 (GltfComponent mixin 제거됨).
16
+ * - 도메인 generic 추상 (GenericTransport, GenericFacility 등) — 영구 도메인 추상.
17
+ * GLB 바디를 직접 채우는 방식으로 사용. 변환 대상 아님.
10
18
  *
11
19
  * 변환 시:
12
20
  * - 좌표/크기/회전 보존
13
21
  * - source (원본 entity attribs) 보존
14
22
  * - 등록된 ImportRule.build 의 defaultState / idStrategy 적용
15
23
  */
16
- const index_js_1 = require("../registry/index.js");
17
- const stage2_mapping_js_1 = require("../pipeline/stage2-mapping.js");
24
+ const index_1 = require("../registry/index");
25
+ const stage2_mapping_1 = require("../pipeline/stage2-mapping");
26
+ /** things-scene 코어가 등록한 도메인 비종속 placeholder type. */
27
+ exports.CORE_PLACEHOLDER_TYPES = [stage2_mapping_1.GENERIC_OBJECT_TYPE];
28
+ function isPlaceholderType(type, extraTypes = []) {
29
+ if (!type)
30
+ return false;
31
+ if (exports.CORE_PLACEHOLDER_TYPES.includes(type))
32
+ return true;
33
+ return extraTypes.includes(type);
34
+ }
35
+ // ── 일반화된 변환 helper ─────────────────────────────────────────────
18
36
  /**
19
- * GenericObject 를 지정된 도메인 type 으로 변환한다.
20
- * 기존 좌표/크기/회전/source 보존.
37
+ * 추상 placeholder 를 지정된 도메인 type 으로 변환. 호환되는 모든 generic-* 수용 (코어 + 도메인).
38
+ *
39
+ * 기존 좌표/크기/회전/source 보존. placeholder 시각 메타 (placeholderLabel/Hint, fillStyle 등) 는 제거.
21
40
  */
22
- function convertGenericTo(generic, toType, options = {}) {
23
- if (generic.type !== stage2_mapping_js_1.GENERIC_OBJECT_TYPE) {
24
- throw new Error(`Source component is not GenericObject (type=${generic.type})`);
41
+ function convertPlaceholderTo(placeholder, toType, options = {}) {
42
+ const extra = options.extraPlaceholderTypes ?? [];
43
+ if (!isPlaceholderType(placeholder.type, extra)) {
44
+ throw new Error(`Source component is not a generic placeholder (type=${placeholder.type}). ` +
45
+ `Expected one of: ${[...exports.CORE_PLACEHOLDER_TYPES, ...extra].join(', ')}`);
25
46
  }
26
- const rules = (0, index_js_1.getImportRules)(options.scopes);
47
+ const rules = (0, index_1.getImportRules)(options.scopes);
27
48
  const rule = rules.find(r => r.componentType === toType);
28
49
  const category = rule?.category ?? 'unknown';
29
- const newId = options.id ?? generic.id;
30
- const preset = (0, index_js_1.find3DPreset)(toType, category, options.scopes);
50
+ const newId = options.id ?? placeholder.id;
51
+ const preset = (0, index_1.find3DPreset)(toType, category, options.scopes);
31
52
  const threeD = {
32
53
  geometry: preset.geometry,
33
54
  ...(preset.material && { material: preset.material }),
34
55
  origin: preset.origin ?? 'bottom'
35
56
  };
36
- const { strokeStyle, strokeDashArray, fillStyle, label, ...preservedState } = generic;
57
+ // placeholder 시각/스타일 메타는 새 type 으로 옮길 때 의미 없음 — 제거.
58
+ const { strokeStyle: _strokeStyle, strokeDashArray: _strokeDashArray, fillStyle: _fillStyle, label: _label, placeholderLabel: _placeholderLabel, placeholderHint: _placeholderHint, ...preservedState } = placeholder;
37
59
  return {
38
60
  ...preservedState,
39
61
  category,
40
62
  type: toType,
41
63
  id: newId,
42
64
  threeD,
43
- ...(rule?.build.defaultState || {}),
44
- /**
45
- * 변환 추적 — 되돌리기 가능하게 source 보존.
46
- * `convertedFrom` 키는 board-modeller UI 에서 "되돌리기" 액션 활성화 단서.
47
- */
48
- convertedFrom: {
49
- type: stage2_mapping_js_1.GENERIC_OBJECT_TYPE,
50
- previousState: { strokeStyle, strokeDashArray, fillStyle, label }
51
- }
65
+ ...(rule?.build.defaultState || {})
52
66
  };
53
67
  }
68
+ // ── Backward-compat alias — 기존 호출처 호환 ──────────────────────
54
69
  /**
55
- * 변환된 컴포넌트를 다시 GenericObject 로 되돌린다.
56
- * convertedFrom 정보가 있어야 가능.
70
+ * @deprecated Use `convertPlaceholderTo`. Placeholder 한정 — 새 코드는 일반화된 helper.
57
71
  */
58
- function revertToGeneric(component, options = {}) {
59
- const previous = component.convertedFrom?.previousState;
60
- if (!previous) {
61
- throw new Error('이 컴포넌트는 GenericObject 에서 변환된 것이 아닙니다 (convertedFrom 없음).');
72
+ function convertGenericTo(generic, toType, options = {}) {
73
+ if (generic.type !== stage2_mapping_1.GENERIC_OBJECT_TYPE) {
74
+ throw new Error(`Source component is not Placeholder (type=${generic.type})`);
62
75
  }
63
- const preset = (0, index_js_1.find3DPreset)(stage2_mapping_js_1.GENERIC_OBJECT_TYPE, 'unknown', options.scopes);
64
- const threeD = {
65
- geometry: preset.geometry,
66
- ...(preset.material && { material: preset.material }),
67
- origin: preset.origin ?? 'bottom'
68
- };
69
- const { convertedFrom, ...rest } = component;
70
- return {
71
- ...rest,
72
- category: 'unknown',
73
- type: stage2_mapping_js_1.GENERIC_OBJECT_TYPE,
74
- threeD,
75
- ...previous
76
- };
76
+ return convertPlaceholderTo(generic, toType, options);
77
77
  }
78
78
  //# sourceMappingURL=generic-to.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generic-to.js","sourceRoot":"","sources":["../../../server/service/converters/generic-to.ts"],"names":[],"mappings":";;AA0BA,4CAwCC;AAMD,0CAyBC;AAjGD;;;;;;;;;;GAUG;AACH,mDAAmE;AAGnE,qEAAmE;AAQnE;;;GAGG;AACH,SAAgB,gBAAgB,CAC9B,OAAuB,EACvB,MAAc,EACd,UAA0B,EAAE;IAE5B,IAAI,OAAO,CAAC,IAAI,KAAK,uCAAmB,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,+CAA+C,OAAO,CAAC,IAAI,GAAG,CAAC,CAAA;IACjF,CAAC;IAED,MAAM,KAAK,GAAG,IAAA,yBAAc,EAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,MAAM,CAAC,CAAA;IAExD,MAAM,QAAQ,GAAsB,IAAI,EAAE,QAAQ,IAAI,SAAS,CAAA;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,EAAE,CAAA;IAEtC,MAAM,MAAM,GAAG,IAAA,uBAAY,EAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7D,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,QAAQ;KAClC,CAAA;IAED,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,CAAA;IAErF,OAAO;QACL,GAAG,cAAc;QACjB,QAAQ;QACR,IAAI,EAAE,MAAM;QACZ,EAAE,EAAE,KAAK;QACT,MAAM;QACN,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;QACnC;;;WAGG;QACH,aAAa,EAAE;YACb,IAAI,EAAE,uCAAmB;YACzB,aAAa,EAAE,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE;SAClE;KACF,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAC7B,SAAyB,EACzB,UAA0B,EAAE;IAE5B,MAAM,QAAQ,GAAI,SAAiB,CAAC,aAAa,EAAE,aAAa,CAAA;IAChE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,uBAAY,EAAC,uCAAmB,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3E,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,QAAQ;KAClC,CAAA;IAED,MAAM,EAAE,aAAa,EAAE,GAAG,IAAI,EAAE,GAAG,SAAgB,CAAA;IAEnD,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,uCAAmB;QACzB,MAAM;QACN,GAAG,QAAQ;KACZ,CAAA;AACH,CAAC","sourcesContent":["/**\n * GenericObject ↔ 도메인 컴포넌트 변환.\n *\n * Import 시 매칭되지 않은 entity 는 GenericObject 로 보드에 배치된다.\n * 사용자가 사후에 모델러 UI 에서 \"Convert to Stocker\" 등 액션으로 호출.\n *\n * 변환 시:\n * - 좌표/크기/회전 보존\n * - source (원본 entity attribs) 보존\n * - 등록된 ImportRule.build 의 defaultState / idStrategy 적용\n */\nimport { find3DPreset, getImportRules } from '../registry/index.js'\nimport type { BoardComponent } from '../pipeline/stage3-board.js'\nimport type { ComponentCategory } from '../types/registry.js'\nimport { GENERIC_OBJECT_TYPE } from '../pipeline/stage2-mapping.js'\n\nexport interface ConvertOptions {\n scopes?: string[]\n /** 새 컴포넌트의 ID. 미지정 시 ImportRule 의 idStrategy 적용 또는 자동 생성 */\n id?: string\n}\n\n/**\n * GenericObject 를 지정된 도메인 type 으로 변환한다.\n * 기존 좌표/크기/회전/source 보존.\n */\nexport function convertGenericTo(\n generic: BoardComponent,\n toType: string,\n options: ConvertOptions = {}\n): BoardComponent {\n if (generic.type !== GENERIC_OBJECT_TYPE) {\n throw new Error(`Source component is not GenericObject (type=${generic.type})`)\n }\n\n const rules = getImportRules(options.scopes)\n const rule = rules.find(r => r.componentType === toType)\n\n const category: ComponentCategory = rule?.category ?? 'unknown'\n const newId = options.id ?? generic.id\n\n const preset = find3DPreset(toType, category, options.scopes)\n const threeD = {\n geometry: preset.geometry,\n ...(preset.material && { material: preset.material }),\n origin: preset.origin ?? 'bottom'\n }\n\n const { strokeStyle, strokeDashArray, fillStyle, label, ...preservedState } = generic\n\n return {\n ...preservedState,\n category,\n type: toType,\n id: newId,\n threeD,\n ...(rule?.build.defaultState || {}),\n /**\n * 변환 추적 — 되돌리기 가능하게 source 보존.\n * `convertedFrom` 키는 board-modeller UI 에서 \"되돌리기\" 액션 활성화 단서.\n */\n convertedFrom: {\n type: GENERIC_OBJECT_TYPE,\n previousState: { strokeStyle, strokeDashArray, fillStyle, label }\n }\n }\n}\n\n/**\n * 변환된 컴포넌트를 다시 GenericObject 로 되돌린다.\n * convertedFrom 정보가 있어야 가능.\n */\nexport function revertToGeneric(\n component: BoardComponent,\n options: ConvertOptions = {}\n): BoardComponent {\n const previous = (component as any).convertedFrom?.previousState\n if (!previous) {\n throw new Error('이 컴포넌트는 GenericObject 에서 변환된 것이 아닙니다 (convertedFrom 없음).')\n }\n\n const preset = find3DPreset(GENERIC_OBJECT_TYPE, 'unknown', options.scopes)\n const threeD = {\n geometry: preset.geometry,\n ...(preset.material && { material: preset.material }),\n origin: preset.origin ?? 'bottom'\n }\n\n const { convertedFrom, ...rest } = component as any\n\n return {\n ...rest,\n category: 'unknown',\n type: GENERIC_OBJECT_TYPE,\n threeD,\n ...previous\n }\n}\n"]}
1
+ {"version":3,"file":"generic-to.js","sourceRoot":"","sources":["../../../server/service/converters/generic-to.ts"],"names":[],"mappings":";;;AAqCA,8CAOC;AASD,oDA6CC;AAOD,4CASC;AAlHD;;;;;;;;;;;;;;;;GAgBG;AACH,6CAAgE;AAGhE,+DAAgE;AAchE,qDAAqD;AACxC,QAAA,sBAAsB,GAAG,CAAC,oCAAmB,CAAU,CAAA;AAEpE,SAAgB,iBAAiB,CAC/B,IAAwB,EACxB,aAAoC,EAAE;IAEtC,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IACvB,IAAK,8BAA4C,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC7E,OAAO,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC;AAED,kEAAkE;AAElE;;;;GAIG;AACH,SAAgB,oBAAoB,CAClC,WAA2B,EAC3B,MAAc,EACd,UAA0B,EAAE;IAE5B,MAAM,KAAK,GAAG,OAAO,CAAC,qBAAqB,IAAI,EAAE,CAAA;IACjD,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,uDAAuD,WAAW,CAAC,IAAI,KAAK;YAC1E,oBAAoB,CAAC,GAAG,8BAAsB,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAA;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAA,sBAAc,EAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa,KAAK,MAAM,CAAC,CAAA;IAExD,MAAM,QAAQ,GAAsB,IAAI,EAAE,QAAQ,IAAI,SAAS,CAAA;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,IAAI,WAAW,CAAC,EAAE,CAAA;IAE1C,MAAM,MAAM,GAAG,IAAA,oBAAY,EAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7D,MAAM,MAAM,GAAG;QACb,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,QAAQ;KAClC,CAAA;IAED,oDAAoD;IACpD,MAAM,EACJ,WAAW,EAAE,YAAY,EACzB,eAAe,EAAE,gBAAgB,EACjC,SAAS,EAAE,UAAU,EACrB,KAAK,EAAE,MAAM,EACb,gBAAgB,EAAE,iBAAiB,EACnC,eAAe,EAAE,gBAAgB,EACjC,GAAG,cAAc,EAClB,GAAG,WAAkB,CAAA;IAEtB,OAAO;QACL,GAAG,cAAc;QACjB,QAAQ;QACR,IAAI,EAAE,MAAM;QACZ,EAAE,EAAE,KAAK;QACT,MAAM;QACN,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC;KACpC,CAAA;AACH,CAAC;AAED,8DAA8D;AAE9D;;GAEG;AACH,SAAgB,gBAAgB,CAC9B,OAAuB,EACvB,MAAc,EACd,UAA0B,EAAE;IAE5B,IAAI,OAAO,CAAC,IAAI,KAAK,oCAAmB,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,6CAA6C,OAAO,CAAC,IAAI,GAAG,CAAC,CAAA;IAC/E,CAAC;IACD,OAAO,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;AACvD,CAAC","sourcesContent":["/**\n * Generic placeholder → 도메인 컴포넌트 변환.\n *\n * Import 시 매칭되지 않은 entity 는 `placeholder` 타입으로 보드에 배치된다.\n * 사용자가 사후에 모델러 UI 에서 \"Convert to ...\" 액션으로 도메인 컴포넌트로 변환.\n *\n * 아키텍처 원칙:\n * - `placeholder` — 도메인 비종속 임시 추상. 다른 type 으로 변환하는 기능만.\n * GLB 지원 없음 (GltfComponent mixin 제거됨).\n * - 도메인 generic 추상 (GenericTransport, GenericFacility 등) — 영구 도메인 추상.\n * GLB 바디를 직접 채우는 방식으로 사용. 변환 대상 아님.\n *\n * 변환 시:\n * - 좌표/크기/회전 보존\n * - source (원본 entity attribs) 보존\n * - 등록된 ImportRule.build 의 defaultState / idStrategy 적용\n */\nimport { find3DPreset, getImportRules } from '../registry/index'\nimport type { BoardComponent } from '../pipeline/stage3-board'\nimport type { ComponentCategory } from '../types/registry'\nimport { GENERIC_OBJECT_TYPE } from '../pipeline/stage2-mapping'\n\nexport interface ConvertOptions {\n scopes?: string[]\n /** 새 컴포넌트의 ID. 미지정 시 ImportRule 의 idStrategy 적용 또는 자동 생성. */\n id?: string\n /**\n * 코어 placeholder type ('placeholder') 외에 인정할 도메인 추상 type list.\n * 예: ['GenericTransport', 'GenericFacility'].\n * 호출자가 자기 도메인 패키지의 추상까지 변환 대상으로 포함하도록 명시.\n */\n extraPlaceholderTypes?: ReadonlyArray<string>\n}\n\n/** things-scene 코어가 등록한 도메인 비종속 placeholder type. */\nexport const CORE_PLACEHOLDER_TYPES = [GENERIC_OBJECT_TYPE] as const\n\nexport function isPlaceholderType(\n type: string | undefined,\n extraTypes: ReadonlyArray<string> = []\n): boolean {\n if (!type) return false\n if ((CORE_PLACEHOLDER_TYPES as readonly string[]).includes(type)) return true\n return extraTypes.includes(type)\n}\n\n// ── 일반화된 변환 helper ─────────────────────────────────────────────\n\n/**\n * 추상 placeholder 를 지정된 도메인 type 으로 변환. 호환되는 모든 generic-* 수용 (코어 + 도메인).\n *\n * 기존 좌표/크기/회전/source 보존. placeholder 시각 메타 (placeholderLabel/Hint, fillStyle 등) 는 제거.\n */\nexport function convertPlaceholderTo(\n placeholder: BoardComponent,\n toType: string,\n options: ConvertOptions = {}\n): BoardComponent {\n const extra = options.extraPlaceholderTypes ?? []\n if (!isPlaceholderType(placeholder.type, extra)) {\n throw new Error(\n `Source component is not a generic placeholder (type=${placeholder.type}). ` +\n `Expected one of: ${[...CORE_PLACEHOLDER_TYPES, ...extra].join(', ')}`\n )\n }\n\n const rules = getImportRules(options.scopes)\n const rule = rules.find(r => r.componentType === toType)\n\n const category: ComponentCategory = rule?.category ?? 'unknown'\n const newId = options.id ?? placeholder.id\n\n const preset = find3DPreset(toType, category, options.scopes)\n const threeD = {\n geometry: preset.geometry,\n ...(preset.material && { material: preset.material }),\n origin: preset.origin ?? 'bottom'\n }\n\n // placeholder 시각/스타일 메타는 새 type 으로 옮길 때 의미 없음 — 제거.\n const {\n strokeStyle: _strokeStyle,\n strokeDashArray: _strokeDashArray,\n fillStyle: _fillStyle,\n label: _label,\n placeholderLabel: _placeholderLabel,\n placeholderHint: _placeholderHint,\n ...preservedState\n } = placeholder as any\n\n return {\n ...preservedState,\n category,\n type: toType,\n id: newId,\n threeD,\n ...(rule?.build.defaultState || {})\n }\n}\n\n// ── Backward-compat alias — 기존 호출처 호환 ──────────────────────\n\n/**\n * @deprecated Use `convertPlaceholderTo`. Placeholder 한정 — 새 코드는 일반화된 helper.\n */\nexport function convertGenericTo(\n generic: BoardComponent,\n toType: string,\n options: ConvertOptions = {}\n): BoardComponent {\n if (generic.type !== GENERIC_OBJECT_TYPE) {\n throw new Error(`Source component is not Placeholder (type=${generic.type})`)\n }\n return convertPlaceholderTo(generic, toType, options)\n}\n"]}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * board-import 의 ResolverContext 무관 service-level 함수.
3
+ *
4
+ * import-session-resolver 의 mutation/query 핵심 로직을 typeorm getRepository /
5
+ * ResolverContext 외부 의존성을 인자로 분리해 단위 테스트 + LLM tool builder 에서
6
+ * 재사용 가능하도록 추출. materializeFromSession 의 패턴 동일.
7
+ *
8
+ * 호출자 책임:
9
+ * - resolver: getRepository / context.state 에서 도메인/유저 추출 후 deps 채워 호출
10
+ * - import-tools.ts: ToolDispatchHelpers 가 builder 호출 시 ctx 에서 추출
11
+ */
12
+ import type { ImportSession } from './import-session';
13
+ export interface SessionRepoLike {
14
+ findOneBy(criteria: any): Promise<ImportSession | null>;
15
+ save(entity: any): Promise<ImportSession>;
16
+ find(options: any): Promise<ImportSession[]>;
17
+ }
18
+ export interface ImportActionsContext {
19
+ domain: {
20
+ id: string;
21
+ [k: string]: any;
22
+ };
23
+ user: any;
24
+ }
25
+ export interface StartImportJobInput {
26
+ attachmentId: string;
27
+ chatSessionId?: string;
28
+ scopes?: string[];
29
+ normalizeOrigin?: boolean;
30
+ flipY?: boolean;
31
+ scale?: number;
32
+ applyBinding?: boolean;
33
+ parseOptions?: any;
34
+ /**
35
+ * 사용자 자유서술 — 도면 설명 / 강조 부분 / 분류 힌트 등.
36
+ * VLM 이 view type 분류 + entity 추출 양쪽 패스에 모두 추가 컨텍스트로 사용.
37
+ */
38
+ userPrompt?: string;
39
+ }
40
+ /**
41
+ * 비동기 import job 시작. ImportSession 즉시 반환, 백그라운드에서 worker 실행.
42
+ *
43
+ * resolver 와 LLM tool builder 양쪽이 공유하는 진입점. side-effect 인 worker 가동도
44
+ * 본 함수에 묶음 — 사용자 입장에선 "단일 호출 = import 시작" 의미.
45
+ *
46
+ * `startWorker` 는 의존성 주입 — default 는 setImmediate(runImportJob). 단위 테스트는
47
+ * noop 주입해 worker chain 이 jest 환경에서 평가되지 않도록 한다 (typeorm DataSource init
48
+ * 안 된 환경에서 entity decorator 평가 시 fail 회피).
49
+ */
50
+ export declare function startImportJobAsync(input: StartImportJobInput, context: ImportActionsContext, sessionRepo: SessionRepoLike, startWorker?: (sessionId: string) => void): Promise<ImportSession>;
51
+ /**
52
+ * 단일 ImportSession 조회 (도메인 격리). 미존재면 null.
53
+ */
54
+ export declare function fetchImportSession(sessionId: string, context: ImportActionsContext, sessionRepo: SessionRepoLike): Promise<ImportSession | null>;
55
+ /**
56
+ * 한 ChatSession 안에서 발동된 ImportSession 목록 (최신순 50개). board-ai 의 chat 흐름이
57
+ * "이 chat 의 import 들 보여줘" 같은 호출에서 사용.
58
+ */
59
+ export declare function listImportSessionsByChatSession(chatSessionId: string, context: ImportActionsContext, sessionRepo: SessionRepoLike): Promise<ImportSession[]>;
60
+ /**
61
+ * 등록된 FormatAdapter 들의 능력 정보. context 무관 (인스턴스만 inspect).
62
+ * 외부 RPC 의존성도 없어 LLM 이 안전하게 호출 가능한 read tool.
63
+ */
64
+ export interface AdapterInfo {
65
+ format: string;
66
+ supportedExtensions: string[];
67
+ capabilities: {
68
+ has2D: boolean;
69
+ has3D: boolean;
70
+ hasSemanticLabels: boolean;
71
+ hasTextLabels: boolean;
72
+ hasAttributes: boolean;
73
+ };
74
+ }
75
+ export declare function describeAdapter(adapter: {
76
+ format: string;
77
+ supportedExtensions: string[];
78
+ capabilities: any;
79
+ }): AdapterInfo;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * board-import 의 ResolverContext 무관 service-level 함수.
4
+ *
5
+ * import-session-resolver 의 mutation/query 핵심 로직을 typeorm getRepository /
6
+ * ResolverContext 외부 의존성을 인자로 분리해 단위 테스트 + LLM tool builder 에서
7
+ * 재사용 가능하도록 추출. materializeFromSession 의 패턴 동일.
8
+ *
9
+ * 호출자 책임:
10
+ * - resolver: getRepository / context.state 에서 도메인/유저 추출 후 deps 채워 호출
11
+ * - import-tools.ts: ToolDispatchHelpers 가 builder 호출 시 ctx 에서 추출
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.startImportJobAsync = startImportJobAsync;
15
+ exports.fetchImportSession = fetchImportSession;
16
+ exports.listImportSessionsByChatSession = listImportSessionsByChatSession;
17
+ exports.describeAdapter = describeAdapter;
18
+ // ── service 함수들 ────────────────────────────────────────────────────
19
+ /**
20
+ * 비동기 import job 시작. ImportSession 즉시 반환, 백그라운드에서 worker 실행.
21
+ *
22
+ * resolver 와 LLM tool builder 양쪽이 공유하는 진입점. side-effect 인 worker 가동도
23
+ * 본 함수에 묶음 — 사용자 입장에선 "단일 호출 = import 시작" 의미.
24
+ *
25
+ * `startWorker` 는 의존성 주입 — default 는 setImmediate(runImportJob). 단위 테스트는
26
+ * noop 주입해 worker chain 이 jest 환경에서 평가되지 않도록 한다 (typeorm DataSource init
27
+ * 안 된 환경에서 entity decorator 평가 시 fail 회피).
28
+ */
29
+ async function startImportJobAsync(input, context, sessionRepo, startWorker = defaultStartWorker) {
30
+ const session = await sessionRepo.save({
31
+ domain: context.domain,
32
+ attachmentId: input.attachmentId,
33
+ chatSessionId: input.chatSessionId,
34
+ status: 'queued',
35
+ progress: 0,
36
+ message: 'Queued',
37
+ optionsJson: JSON.stringify({
38
+ scopes: input.scopes,
39
+ normalizeOrigin: input.normalizeOrigin,
40
+ flipY: input.flipY,
41
+ scale: input.scale,
42
+ applyBinding: input.applyBinding,
43
+ parseOptions: input.parseOptions,
44
+ userPrompt: input.userPrompt
45
+ }),
46
+ creator: context.user
47
+ });
48
+ // 백그라운드 worker 시작 — 의존성 주입으로 분리 (테스트 격리 + lazy require).
49
+ startWorker(session.id);
50
+ return session;
51
+ }
52
+ /**
53
+ * Default startWorker — setImmediate(runImportJob). worker module 은 lazy require 로
54
+ * module-level 평가 chain (attachment-base STORAGE, pipeline 의 adapters 등) 을 회피.
55
+ */
56
+ function defaultStartWorker(sessionId) {
57
+ setImmediate(() => {
58
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
59
+ const { runImportJob } = require('./import-worker');
60
+ runImportJob(sessionId).catch((err) => {
61
+ console.error('[board-import] runImportJob unhandled:', err);
62
+ });
63
+ });
64
+ }
65
+ /**
66
+ * 단일 ImportSession 조회 (도메인 격리). 미존재면 null.
67
+ */
68
+ async function fetchImportSession(sessionId, context, sessionRepo) {
69
+ return await sessionRepo.findOneBy({
70
+ id: sessionId,
71
+ domain: { id: context.domain.id }
72
+ });
73
+ }
74
+ /**
75
+ * 한 ChatSession 안에서 발동된 ImportSession 목록 (최신순 50개). board-ai 의 chat 흐름이
76
+ * "이 chat 의 import 들 보여줘" 같은 호출에서 사용.
77
+ */
78
+ async function listImportSessionsByChatSession(chatSessionId, context, sessionRepo) {
79
+ return await sessionRepo.find({
80
+ where: { chatSessionId, domain: { id: context.domain.id } },
81
+ order: { createdAt: 'DESC' },
82
+ take: 50
83
+ });
84
+ }
85
+ function describeAdapter(adapter) {
86
+ return {
87
+ format: adapter.format,
88
+ supportedExtensions: [...adapter.supportedExtensions],
89
+ capabilities: {
90
+ has2D: !!adapter.capabilities.has2D,
91
+ has3D: !!adapter.capabilities.has3D,
92
+ hasSemanticLabels: !!adapter.capabilities.hasSemanticLabels,
93
+ hasTextLabels: !!adapter.capabilities.hasTextLabels,
94
+ hasAttributes: !!adapter.capabilities.hasAttributes
95
+ }
96
+ };
97
+ }
98
+ //# sourceMappingURL=import-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-actions.js","sourceRoot":"","sources":["../../../server/service/import-session/import-actions.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AA+CH,kDA4BC;AAmBD,gDASC;AAMD,0EAUC;AAkBD,0CAgBC;AAtHD,sEAAsE;AAEtE;;;;;;;;;GASG;AACI,KAAK,UAAU,mBAAmB,CACvC,KAA0B,EAC1B,OAA6B,EAC7B,WAA4B,EAC5B,cAA2C,kBAAkB;IAE7D,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC;QACrC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,CAAC;QACX,OAAO,EAAE,QAAQ;QACjB,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC;YAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,UAAU,EAAE,KAAK,CAAC,UAAU;SAC7B,CAAC;QACF,OAAO,EAAE,OAAO,CAAC,IAAI;KACf,CAAC,CAAA;IAET,yDAAyD;IACzD,WAAW,CAAC,OAAO,CAAC,EAAG,CAAC,CAAA;IACxB,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,SAAiB;IAC3C,YAAY,CAAC,GAAG,EAAE;QAChB,8DAA8D;QAC9D,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;QACnD,YAAY,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;YAC3C,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAA;QAC9D,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,OAA6B,EAC7B,WAA4B;IAE5B,OAAO,MAAM,WAAW,CAAC,SAAS,CAAC;QACjC,EAAE,EAAE,SAAS;QACb,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE;KAClC,CAAC,CAAA;AACJ,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,+BAA+B,CACnD,aAAqB,EACrB,OAA6B,EAC7B,WAA4B;IAE5B,OAAO,MAAM,WAAW,CAAC,IAAI,CAAC;QAC5B,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE;QAC3D,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;QAC5B,IAAI,EAAE,EAAE;KACT,CAAC,CAAA;AACJ,CAAC;AAkBD,SAAgB,eAAe,CAAC,OAI/B;IACC,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,mBAAmB,EAAE,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC;QACrD,YAAY,EAAE;YACZ,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK;YACnC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK;YACnC,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,iBAAiB;YAC3D,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,aAAa;YACnD,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,aAAa;SACpD;KACF,CAAA;AACH,CAAC","sourcesContent":["/**\n * board-import 의 ResolverContext 무관 service-level 함수.\n *\n * import-session-resolver 의 mutation/query 핵심 로직을 typeorm getRepository /\n * ResolverContext 외부 의존성을 인자로 분리해 단위 테스트 + LLM tool builder 에서\n * 재사용 가능하도록 추출. materializeFromSession 의 패턴 동일.\n *\n * 호출자 책임:\n * - resolver: getRepository / context.state 에서 도메인/유저 추출 후 deps 채워 호출\n * - import-tools.ts: ToolDispatchHelpers 가 builder 호출 시 ctx 에서 추출\n */\n\n// type-only import — module level 에서 typeorm decorator chain 을 평가하지 않는다.\n// 단위 테스트 환경이 typeorm DataSource init 전에도 service 함수 검증할 수 있게 함.\nimport type { ImportSession } from './import-session'\n\n// ── 입력/의존성 타입 ──────────────────────────────────────────────────\n\nexport interface SessionRepoLike {\n findOneBy(criteria: any): Promise<ImportSession | null>\n save(entity: any): Promise<ImportSession>\n find(options: any): Promise<ImportSession[]>\n}\n\nexport interface ImportActionsContext {\n domain: { id: string; [k: string]: any }\n user: any\n}\n\nexport interface StartImportJobInput {\n attachmentId: string\n chatSessionId?: string\n scopes?: string[]\n normalizeOrigin?: boolean\n flipY?: boolean\n scale?: number\n applyBinding?: boolean\n parseOptions?: any\n /**\n * 사용자 자유서술 — 도면 설명 / 강조 부분 / 분류 힌트 등.\n * VLM 이 view type 분류 + entity 추출 양쪽 패스에 모두 추가 컨텍스트로 사용.\n */\n userPrompt?: string\n}\n\n// ── service 함수들 ────────────────────────────────────────────────────\n\n/**\n * 비동기 import job 시작. ImportSession 즉시 반환, 백그라운드에서 worker 실행.\n *\n * resolver 와 LLM tool builder 양쪽이 공유하는 진입점. side-effect 인 worker 가동도\n * 본 함수에 묶음 — 사용자 입장에선 \"단일 호출 = import 시작\" 의미.\n *\n * `startWorker` 는 의존성 주입 — default 는 setImmediate(runImportJob). 단위 테스트는\n * noop 주입해 worker chain 이 jest 환경에서 평가되지 않도록 한다 (typeorm DataSource init\n * 안 된 환경에서 entity decorator 평가 시 fail 회피).\n */\nexport async function startImportJobAsync(\n input: StartImportJobInput,\n context: ImportActionsContext,\n sessionRepo: SessionRepoLike,\n startWorker: (sessionId: string) => void = defaultStartWorker\n): Promise<ImportSession> {\n const session = await sessionRepo.save({\n domain: context.domain,\n attachmentId: input.attachmentId,\n chatSessionId: input.chatSessionId,\n status: 'queued',\n progress: 0,\n message: 'Queued',\n optionsJson: JSON.stringify({\n scopes: input.scopes,\n normalizeOrigin: input.normalizeOrigin,\n flipY: input.flipY,\n scale: input.scale,\n applyBinding: input.applyBinding,\n parseOptions: input.parseOptions,\n userPrompt: input.userPrompt\n }),\n creator: context.user\n } as any)\n\n // 백그라운드 worker 시작 — 의존성 주입으로 분리 (테스트 격리 + lazy require).\n startWorker(session.id!)\n return session\n}\n\n/**\n * Default startWorker — setImmediate(runImportJob). worker module 은 lazy require 로\n * module-level 평가 chain (attachment-base STORAGE, pipeline 의 adapters 등) 을 회피.\n */\nfunction defaultStartWorker(sessionId: string): void {\n setImmediate(() => {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { runImportJob } = require('./import-worker')\n runImportJob(sessionId).catch((err: Error) => {\n console.error('[board-import] runImportJob unhandled:', err)\n })\n })\n}\n\n/**\n * 단일 ImportSession 조회 (도메인 격리). 미존재면 null.\n */\nexport async function fetchImportSession(\n sessionId: string,\n context: ImportActionsContext,\n sessionRepo: SessionRepoLike\n): Promise<ImportSession | null> {\n return await sessionRepo.findOneBy({\n id: sessionId,\n domain: { id: context.domain.id }\n })\n}\n\n/**\n * 한 ChatSession 안에서 발동된 ImportSession 목록 (최신순 50개). board-ai 의 chat 흐름이\n * \"이 chat 의 import 들 보여줘\" 같은 호출에서 사용.\n */\nexport async function listImportSessionsByChatSession(\n chatSessionId: string,\n context: ImportActionsContext,\n sessionRepo: SessionRepoLike\n): Promise<ImportSession[]> {\n return await sessionRepo.find({\n where: { chatSessionId, domain: { id: context.domain.id } },\n order: { createdAt: 'DESC' },\n take: 50\n })\n}\n\n/**\n * 등록된 FormatAdapter 들의 능력 정보. context 무관 (인스턴스만 inspect).\n * 외부 RPC 의존성도 없어 LLM 이 안전하게 호출 가능한 read tool.\n */\nexport interface AdapterInfo {\n format: string\n supportedExtensions: string[]\n capabilities: {\n has2D: boolean\n has3D: boolean\n hasSemanticLabels: boolean\n hasTextLabels: boolean\n hasAttributes: boolean\n }\n}\n\nexport function describeAdapter(adapter: {\n format: string\n supportedExtensions: string[]\n capabilities: any\n}): AdapterInfo {\n return {\n format: adapter.format,\n supportedExtensions: [...adapter.supportedExtensions],\n capabilities: {\n has2D: !!adapter.capabilities.has2D,\n has3D: !!adapter.capabilities.has3D,\n hasSemanticLabels: !!adapter.capabilities.hasSemanticLabels,\n hasTextLabels: !!adapter.capabilities.hasTextLabels,\n hasAttributes: !!adapter.capabilities.hasAttributes\n }\n }\n}\n"]}
@@ -1,5 +1,20 @@
1
1
  import '@things-factory/auth-base';
2
- import { ImportSession } from './import-session.js';
2
+ import { Board } from '@things-factory/board-service';
3
+ import { ImportSession } from './import-session';
4
+ import { type SuggestBoardMetaResult } from './suggest-board-name';
5
+ declare class MaterializeImportSessionInput {
6
+ sessionId: string;
7
+ name: string;
8
+ description?: string;
9
+ groupId?: string;
10
+ type?: 'main' | 'sub' | 'popup';
11
+ thumbnail?: string;
12
+ variantId?: string;
13
+ }
14
+ declare class SuggestBoardNameInput {
15
+ sessionId?: string;
16
+ hint?: string;
17
+ }
3
18
  declare class ImportBoardAsyncInput {
4
19
  attachmentId: string;
5
20
  chatSessionId?: string;
@@ -9,10 +24,16 @@ declare class ImportBoardAsyncInput {
9
24
  scale?: number;
10
25
  applyBinding?: boolean;
11
26
  parseOptions?: any;
27
+ userPrompt?: string;
12
28
  }
13
29
  export declare class ImportSessionResolver {
14
30
  importSession(id: string, context: ResolverContext): Promise<ImportSession | null>;
15
31
  importSessionsByChatSession(chatSessionId: string, context: ResolverContext): Promise<ImportSession[]>;
16
32
  importBoardAsync(input: ImportBoardAsyncInput, context: ResolverContext): Promise<ImportSession>;
33
+ suggestBoardName(input: SuggestBoardNameInput, context: ResolverContext): Promise<string>;
34
+ suggestBoardMeta(input: SuggestBoardNameInput, context: ResolverContext): Promise<SuggestBoardMetaResult>;
35
+ /** 두 query 가 공유하는 deps wiring — typeorm find idiomatic 패턴 (distinctAlias 회귀 회피). */
36
+ private _buildSuggestDeps;
37
+ materializeImportSession(input: MaterializeImportSessionInput, context: ResolverContext): Promise<Board>;
17
38
  }
18
39
  export {};