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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/config/config.development.js +44 -0
  2. package/config/config.production.js +18 -0
  3. package/dist-server/index.d.ts +7 -0
  4. package/dist-server/index.js +11 -0
  5. package/dist-server/index.js.map +1 -0
  6. package/dist-server/service/adapters/dxf-adapter.d.ts +8 -0
  7. package/dist-server/service/adapters/dxf-adapter.js +206 -0
  8. package/dist-server/service/adapters/dxf-adapter.js.map +1 -0
  9. package/dist-server/service/ai/types.d.ts +93 -0
  10. package/dist-server/service/ai/types.js +169 -0
  11. package/dist-server/service/ai/types.js.map +1 -0
  12. package/dist-server/service/converters/generic-to.d.ts +16 -0
  13. package/dist-server/service/converters/generic-to.js +78 -0
  14. package/dist-server/service/converters/generic-to.js.map +1 -0
  15. package/dist-server/service/import-session/import-session-resolver.d.ts +18 -0
  16. package/dist-server/service/import-session/import-session-resolver.js +135 -0
  17. package/dist-server/service/import-session/import-session-resolver.js.map +1 -0
  18. package/dist-server/service/import-session/import-session.d.ts +28 -0
  19. package/dist-server/service/import-session/import-session.js +152 -0
  20. package/dist-server/service/import-session/import-session.js.map +1 -0
  21. package/dist-server/service/import-session/import-worker.d.ts +1 -0
  22. package/dist-server/service/import-session/import-worker.js +79 -0
  23. package/dist-server/service/import-session/import-worker.js.map +1 -0
  24. package/dist-server/service/import-session/index.d.ts +6 -0
  25. package/dist-server/service/import-session/index.js +12 -0
  26. package/dist-server/service/import-session/index.js.map +1 -0
  27. package/dist-server/service/index.d.ts +22 -0
  28. package/dist-server/service/index.js +47 -0
  29. package/dist-server/service/index.js.map +1 -0
  30. package/dist-server/service/pipeline/index.d.ts +33 -0
  31. package/dist-server/service/pipeline/index.js +58 -0
  32. package/dist-server/service/pipeline/index.js.map +1 -0
  33. package/dist-server/service/pipeline/stage2-mapping.d.ts +14 -0
  34. package/dist-server/service/pipeline/stage2-mapping.js +173 -0
  35. package/dist-server/service/pipeline/stage2-mapping.js.map +1 -0
  36. package/dist-server/service/pipeline/stage3-board.d.ts +52 -0
  37. package/dist-server/service/pipeline/stage3-board.js +53 -0
  38. package/dist-server/service/pipeline/stage3-board.js.map +1 -0
  39. package/dist-server/service/pipeline/stage4-binding.d.ts +9 -0
  40. package/dist-server/service/pipeline/stage4-binding.js +28 -0
  41. package/dist-server/service/pipeline/stage4-binding.js.map +1 -0
  42. package/dist-server/service/registry/index.d.ts +27 -0
  43. package/dist-server/service/registry/index.js +108 -0
  44. package/dist-server/service/registry/index.js.map +1 -0
  45. package/dist-server/service/types/index.d.ts +117 -0
  46. package/dist-server/service/types/index.js +3 -0
  47. package/dist-server/service/types/index.js.map +1 -0
  48. package/dist-server/service/types/registry.d.ts +175 -0
  49. package/dist-server/service/types/registry.js +3 -0
  50. package/dist-server/service/types/registry.js.map +1 -0
  51. package/dist-server/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +34 -0
  53. package/server/index.ts +7 -0
  54. package/server/service/adapters/dxf-adapter.ts +217 -0
  55. package/server/service/ai/types.ts +284 -0
  56. package/server/service/converters/generic-to.ts +98 -0
  57. package/server/service/import-session/import-session-resolver.ts +113 -0
  58. package/server/service/import-session/import-session.ts +141 -0
  59. package/server/service/import-session/import-worker.ts +88 -0
  60. package/server/service/import-session/index.ts +8 -0
  61. package/server/service/index.ts +96 -0
  62. package/server/service/pipeline/index.ts +89 -0
  63. package/server/service/pipeline/stage2-mapping.ts +195 -0
  64. package/server/service/pipeline/stage3-board.ts +114 -0
  65. package/server/service/pipeline/stage4-binding.ts +31 -0
  66. package/server/service/registry/index.ts +127 -0
  67. package/server/service/types/index.ts +133 -0
  68. package/server/service/types/registry.ts +186 -0
  69. package/things-factory.config.js +1 -0
  70. package/tsconfig.json +9 -0
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ 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.DxfAdapter = exports.revertToGeneric = exports.convertGenericTo = exports.applyDataBinding = exports.assembleBoard = 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_js_1 = require("./import-session/index.js");
7
+ tslib_1.__exportStar(require("./import-session/index.js"), exports);
8
+ exports.entities = [...index_js_1.entities];
9
+ exports.schema = { resolverClasses: [...index_js_1.resolverClasses] };
10
+ // Pipeline (high-level)
11
+ var index_js_2 = require("./pipeline/index.js");
12
+ Object.defineProperty(exports, "importBoard", { enumerable: true, get: function () { return index_js_2.importBoard; } });
13
+ // Stage 별 (low-level)
14
+ var stage2_mapping_js_1 = require("./pipeline/stage2-mapping.js");
15
+ Object.defineProperty(exports, "applyMapping", { enumerable: true, get: function () { return stage2_mapping_js_1.applyMapping; } });
16
+ Object.defineProperty(exports, "GENERIC_OBJECT_TYPE", { enumerable: true, get: function () { return stage2_mapping_js_1.GENERIC_OBJECT_TYPE; } });
17
+ var stage3_board_js_1 = require("./pipeline/stage3-board.js");
18
+ Object.defineProperty(exports, "assembleBoard", { enumerable: true, get: function () { return stage3_board_js_1.assembleBoard; } });
19
+ var stage4_binding_js_1 = require("./pipeline/stage4-binding.js");
20
+ Object.defineProperty(exports, "applyDataBinding", { enumerable: true, get: function () { return stage4_binding_js_1.applyDataBinding; } });
21
+ // Converters
22
+ var generic_to_js_1 = require("./converters/generic-to.js");
23
+ Object.defineProperty(exports, "convertGenericTo", { enumerable: true, get: function () { return generic_to_js_1.convertGenericTo; } });
24
+ Object.defineProperty(exports, "revertToGeneric", { enumerable: true, get: function () { return generic_to_js_1.revertToGeneric; } });
25
+ // Adapters
26
+ var dxf_adapter_js_1 = require("./adapters/dxf-adapter.js");
27
+ Object.defineProperty(exports, "DxfAdapter", { enumerable: true, get: function () { return dxf_adapter_js_1.DxfAdapter; } });
28
+ // Registry
29
+ var index_js_3 = require("./registry/index.js");
30
+ Object.defineProperty(exports, "registerImportRules", { enumerable: true, get: function () { return index_js_3.registerImportRules; } });
31
+ Object.defineProperty(exports, "registerSubscriptionRules", { enumerable: true, get: function () { return index_js_3.registerSubscriptionRules; } });
32
+ Object.defineProperty(exports, "register3DPresets", { enumerable: true, get: function () { return index_js_3.register3DPresets; } });
33
+ Object.defineProperty(exports, "getImportRules", { enumerable: true, get: function () { return index_js_3.getImportRules; } });
34
+ Object.defineProperty(exports, "getSubscriptionRules", { enumerable: true, get: function () { return index_js_3.getSubscriptionRules; } });
35
+ Object.defineProperty(exports, "get3DPresets", { enumerable: true, get: function () { return index_js_3.get3DPresets; } });
36
+ Object.defineProperty(exports, "find3DPreset", { enumerable: true, get: function () { return index_js_3.find3DPreset; } });
37
+ Object.defineProperty(exports, "findSubscriptionRule", { enumerable: true, get: function () { return index_js_3.findSubscriptionRule; } });
38
+ Object.defineProperty(exports, "clearImportRules", { enumerable: true, get: function () { return index_js_3.clearImportRules; } });
39
+ Object.defineProperty(exports, "clearSubscriptionRules", { enumerable: true, get: function () { return index_js_3.clearSubscriptionRules; } });
40
+ Object.defineProperty(exports, "clear3DPresets", { enumerable: true, get: function () { return index_js_3.clear3DPresets; } });
41
+ Object.defineProperty(exports, "clearAllRegistries", { enumerable: true, get: function () { return index_js_3.clearAllRegistries; } });
42
+ // AI domain interface (Phase 3+) — provider 추상화는 @things-factory/ai-client-base
43
+ var types_js_1 = require("./ai/types.js");
44
+ Object.defineProperty(exports, "setAIClient", { enumerable: true, get: function () { return types_js_1.setAIClient; } });
45
+ Object.defineProperty(exports, "getAIClient", { enumerable: true, get: function () { return types_js_1.getAIClient; } });
46
+ Object.defineProperty(exports, "DefaultBoardImportAI", { enumerable: true, get: function () { return types_js_1.DefaultBoardImportAI; } });
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,qDAAqD;AACrD,wDAGkC;AAClC,oEAAyC;AAE5B,QAAA,QAAQ,GAAG,CAAC,GAAG,mBAAqB,CAAC,CAAA;AACrC,QAAA,MAAM,GAAG,EAAE,eAAe,EAAE,CAAC,GAAG,0BAA4B,CAAC,EAAE,CAAA;AAE5E,wBAAwB;AACxB,gDAAiD;AAAxC,uGAAA,WAAW,OAAA;AAGpB,sBAAsB;AACtB,kEAAgF;AAAvE,iHAAA,YAAY,OAAA;AAAE,wHAAA,mBAAmB,OAAA;AAC1C,8DAA0D;AAAjD,gHAAA,aAAa,OAAA;AACtB,kEAA+D;AAAtD,qHAAA,gBAAgB,OAAA;AAKzB,aAAa;AACb,4DAA8E;AAArE,iHAAA,gBAAgB,OAAA;AAAE,gHAAA,eAAe,OAAA;AAG1C,WAAW;AACX,4DAAsD;AAA7C,4GAAA,UAAU,OAAA;AAEnB,WAAW;AACX,gDAa4B;AAZ1B,+GAAA,mBAAmB,OAAA;AACnB,qHAAA,yBAAyB,OAAA;AACzB,6GAAA,iBAAiB,OAAA;AACjB,0GAAA,cAAc,OAAA;AACd,gHAAA,oBAAoB,OAAA;AACpB,wGAAA,YAAY,OAAA;AACZ,wGAAA,YAAY,OAAA;AACZ,gHAAA,oBAAoB,OAAA;AACpB,4GAAA,gBAAgB,OAAA;AAChB,kHAAA,sBAAsB,OAAA;AACtB,0GAAA,cAAc,OAAA;AACd,8GAAA,kBAAkB,OAAA;AAsCpB,gFAAgF;AAChF,0CAIsB;AAHpB,uGAAA,WAAW,OAAA;AACX,uGAAA,WAAW,OAAA;AACX,gHAAA,oBAAoB,OAAA","sourcesContent":["// ImportSession (비동기 import) — things-factory 모듈 통합용\nimport {\n entities as ImportSessionEntities,\n resolverClasses as ImportSessionResolverClasses\n} from './import-session/index.js'\nexport * from './import-session/index.js'\n\nexport const entities = [...ImportSessionEntities]\nexport const schema = { resolverClasses: [...ImportSessionResolverClasses] }\n\n// Pipeline (high-level)\nexport { importBoard } from './pipeline/index.js'\nexport type { ImportOptions, ImportResult } from './pipeline/index.js'\n\n// Stage 별 (low-level)\nexport { applyMapping, GENERIC_OBJECT_TYPE } from './pipeline/stage2-mapping.js'\nexport { assembleBoard } from './pipeline/stage3-board.js'\nexport { applyDataBinding } from './pipeline/stage4-binding.js'\nexport type { BoardModel, BoardComponent, AssembleOptions } from './pipeline/stage3-board.js'\nexport type { MappingOptions } from './pipeline/stage2-mapping.js'\nexport type { BindingOptions } from './pipeline/stage4-binding.js'\n\n// Converters\nexport { convertGenericTo, revertToGeneric } from './converters/generic-to.js'\nexport type { ConvertOptions } from './converters/generic-to.js'\n\n// Adapters\nexport { DxfAdapter } from './adapters/dxf-adapter.js'\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.js'\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.js'\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.js'\n\n// AI domain interface (Phase 3+) — provider 추상화는 @things-factory/ai-client-base\nexport {\n setAIClient,\n getAIClient,\n DefaultBoardImportAI\n} from './ai/types.js'\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.js'\n"]}
@@ -0,0 +1,33 @@
1
+ import type { AdapterInput, FormatAdapter, ParseOptions, ParseResult } from '../types/index.js';
2
+ import type { MappingResult } from '../types/registry.js';
3
+ import { type BoardModel } from './stage3-board.js';
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
+ export declare function importBoard(opts: ImportOptions): Promise<ImportResult>;
28
+ export { applyMapping } from './stage2-mapping.js';
29
+ export { assembleBoard } from './stage3-board.js';
30
+ export { applyDataBinding } from './stage4-binding.js';
31
+ export type { BoardModel, BoardComponent, AssembleOptions } from './stage3-board.js';
32
+ export type { MappingOptions } from './stage2-mapping.js';
33
+ export type { BindingOptions } from './stage4-binding.js';
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyDataBinding = exports.assembleBoard = exports.applyMapping = void 0;
4
+ exports.importBoard = importBoard;
5
+ /**
6
+ * High-level Pipeline — 도면 한 장 → 운영 가능한 Board Model.
7
+ *
8
+ * 백엔드에서 호출:
9
+ * ```ts
10
+ * import { importBoard } from '@things-factory/board-import'
11
+ *
12
+ * const buf = await fs.promises.readFile(path)
13
+ * const result = await importBoard({ file: buf, filename: 'plant.dxf', scopes: ['fmsim'] })
14
+ * ```
15
+ */
16
+ const dxf_adapter_js_1 = require("../adapters/dxf-adapter.js");
17
+ const stage2_mapping_js_1 = require("./stage2-mapping.js");
18
+ const stage3_board_js_1 = require("./stage3-board.js");
19
+ const stage4_binding_js_1 = require("./stage4-binding.js");
20
+ const DEFAULT_ADAPTERS = [new dxf_adapter_js_1.DxfAdapter()];
21
+ async function importBoard(opts) {
22
+ const adapter = opts.adapter ?? (await pickAdapter(opts.file, opts.filename));
23
+ if (!adapter)
24
+ throw new Error('지원되지 않는 도면 형식이거나 어댑터를 식별할 수 없습니다.');
25
+ const parsed = await adapter.parse(opts.file, opts.parseOptions);
26
+ const mapping = (0, stage2_mapping_js_1.applyMapping)(parsed, { scopes: opts.scopes });
27
+ const assembleOptions = {
28
+ scopes: opts.scopes,
29
+ normalizeOrigin: opts.normalizeOrigin ?? true,
30
+ flipY: opts.flipY ?? true,
31
+ scale: opts.scale ?? 1,
32
+ importMeta: {
33
+ format: adapter.format,
34
+ sourceBbox: parsed.bbox,
35
+ units: parsed.units,
36
+ importedAt: new Date().toISOString()
37
+ }
38
+ };
39
+ let boardModel = (0, stage3_board_js_1.assembleBoard)(mapping.components, parsed.bbox, assembleOptions);
40
+ if (opts.applyBinding !== false) {
41
+ boardModel = (0, stage4_binding_js_1.applyDataBinding)(boardModel, { scopes: opts.scopes });
42
+ }
43
+ return { boardModel, mapping, parse: parsed };
44
+ }
45
+ async function pickAdapter(file, filename) {
46
+ for (const a of DEFAULT_ADAPTERS) {
47
+ if (await a.detect(file, filename))
48
+ return a;
49
+ }
50
+ return undefined;
51
+ }
52
+ var stage2_mapping_js_2 = require("./stage2-mapping.js");
53
+ Object.defineProperty(exports, "applyMapping", { enumerable: true, get: function () { return stage2_mapping_js_2.applyMapping; } });
54
+ var stage3_board_js_2 = require("./stage3-board.js");
55
+ Object.defineProperty(exports, "assembleBoard", { enumerable: true, get: function () { return stage3_board_js_2.assembleBoard; } });
56
+ var stage4_binding_js_2 = require("./stage4-binding.js");
57
+ Object.defineProperty(exports, "applyDataBinding", { enumerable: true, get: function () { return stage4_binding_js_2.applyDataBinding; } });
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/pipeline/index.ts"],"names":[],"mappings":";;;AA6CA,kCA0BC;AAvED;;;;;;;;;;GAUG;AACH,+DAAuD;AAGvD,2DAAkD;AAClD,uDAAwF;AACxF,2DAAsD;AA2BtD,MAAM,gBAAgB,GAAoB,CAAC,IAAI,2BAAU,EAAE,CAAC,CAAA;AAErD,KAAK,UAAU,WAAW,CAAC,IAAmB;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC7E,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAElE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;IAChE,MAAM,OAAO,GAAG,IAAA,gCAAY,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IAE7D,MAAM,eAAe,GAAoB;QACvC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI;QAC7C,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI;QACzB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;QACtB,UAAU,EAAE;YACV,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC;KACF,CAAA;IACD,IAAI,UAAU,GAAG,IAAA,+BAAa,EAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;IAEhF,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;QAChC,UAAU,GAAG,IAAA,oCAAgB,EAAC,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACpE,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AAC/C,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,IAAkB,EAClB,QAAiB;IAEjB,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;QACjC,IAAI,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC;YAAE,OAAO,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,yDAAkD;AAAzC,iHAAA,YAAY,OAAA;AACrB,qDAAiD;AAAxC,gHAAA,aAAa,OAAA;AACtB,yDAAsD;AAA7C,qHAAA,gBAAgB,OAAA","sourcesContent":["/**\n * High-level Pipeline — 도면 한 장 → 운영 가능한 Board Model.\n *\n * 백엔드에서 호출:\n * ```ts\n * import { importBoard } from '@things-factory/board-import'\n *\n * const buf = await fs.promises.readFile(path)\n * const result = await importBoard({ file: buf, filename: 'plant.dxf', scopes: ['fmsim'] })\n * ```\n */\nimport { DxfAdapter } from '../adapters/dxf-adapter.js'\nimport type { AdapterInput, FormatAdapter, ParseOptions, ParseResult } from '../types/index.js'\nimport type { MappingResult } from '../types/registry.js'\nimport { applyMapping } from './stage2-mapping.js'\nimport { assembleBoard, type AssembleOptions, type BoardModel } from './stage3-board.js'\nimport { applyDataBinding } from './stage4-binding.js'\n\nexport interface ImportOptions {\n file: AdapterInput\n filename?: string\n /** 등록된 매핑 규칙의 어느 scope 를 사용할지 */\n scopes?: string[]\n /** 어댑터 옵션 (excludeLayers 등) */\n parseOptions?: ParseOptions\n /** 좌표 normalize (좌상단 0,0) */\n normalizeOrigin?: boolean\n /** Y 축 뒤집기 (CAD Y-up → scene Y-down) */\n flipY?: boolean\n /** scene unit 변환 배율 */\n scale?: number\n /** 데이터 바인딩 stage 적용 여부 */\n applyBinding?: boolean\n /** 사용자 정의 어댑터 (있으면 디폴트 검색 대신 사용) */\n adapter?: FormatAdapter\n}\n\nexport interface ImportResult {\n boardModel: BoardModel\n mapping: MappingResult\n parse: ParseResult\n}\n\nconst DEFAULT_ADAPTERS: FormatAdapter[] = [new DxfAdapter()]\n\nexport async function importBoard(opts: ImportOptions): Promise<ImportResult> {\n const adapter = opts.adapter ?? (await pickAdapter(opts.file, opts.filename))\n if (!adapter) throw new Error('지원되지 않는 도면 형식이거나 어댑터를 식별할 수 없습니다.')\n\n const parsed = await adapter.parse(opts.file, opts.parseOptions)\n const mapping = applyMapping(parsed, { scopes: opts.scopes })\n\n const assembleOptions: AssembleOptions = {\n scopes: opts.scopes,\n normalizeOrigin: opts.normalizeOrigin ?? true,\n flipY: opts.flipY ?? true,\n scale: opts.scale ?? 1,\n importMeta: {\n format: adapter.format,\n sourceBbox: parsed.bbox,\n units: parsed.units,\n importedAt: new Date().toISOString()\n }\n }\n let boardModel = assembleBoard(mapping.components, parsed.bbox, assembleOptions)\n\n if (opts.applyBinding !== false) {\n boardModel = applyDataBinding(boardModel, { scopes: opts.scopes })\n }\n\n return { boardModel, mapping, parse: parsed }\n}\n\nasync function pickAdapter(\n file: AdapterInput,\n filename?: string\n): Promise<FormatAdapter | undefined> {\n for (const a of DEFAULT_ADAPTERS) {\n if (await a.detect(file, filename)) return a\n }\n return undefined\n}\n\nexport { applyMapping } from './stage2-mapping.js'\nexport { assembleBoard } from './stage3-board.js'\nexport { applyDataBinding } from './stage4-binding.js'\nexport type { BoardModel, BoardComponent, AssembleOptions } from './stage3-board.js'\nexport type { MappingOptions } from './stage2-mapping.js'\nexport type { BindingOptions } from './stage4-binding.js'\n"]}
@@ -0,0 +1,14 @@
1
+ import type { ParseResult } from '../types/index.js';
2
+ import type { MappingResult } from '../types/registry.js';
3
+ /** 매핑되지 않은 entity 가 변환되는 placeholder 도메인 type. */
4
+ export declare const GENERIC_OBJECT_TYPE = "GenericObject";
5
+ export interface MappingOptions {
6
+ /** 어떤 scope 의 규칙을 사용할지 (생략 시 전부) */
7
+ scopes?: string[];
8
+ /**
9
+ * 매칭 안 된 entity 를 GenericObject 로 변환할지 여부 (default: true).
10
+ * false 면 buffer 에서 제외되고 stats.skipped 로 카운트.
11
+ */
12
+ emitGeneric?: boolean;
13
+ }
14
+ export declare function applyMapping(parsed: ParseResult, options?: MappingOptions): MappingResult;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GENERIC_OBJECT_TYPE = void 0;
4
+ exports.applyMapping = applyMapping;
5
+ /**
6
+ * Stage 2 — Universal Entities → Component Drafts.
7
+ * 등록된 ImportRule 들을 entity 마다 평가하여 컴포넌트 type 결정.
8
+ */
9
+ const index_js_1 = require("../registry/index.js");
10
+ /** 매핑되지 않은 entity 가 변환되는 placeholder 도메인 type. */
11
+ exports.GENERIC_OBJECT_TYPE = 'GenericObject';
12
+ function applyMapping(parsed, options = {}) {
13
+ const rules = (0, index_js_1.getImportRules)(options.scopes)
14
+ .slice()
15
+ .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
16
+ const components = [];
17
+ const warnings = [];
18
+ const stats = { total: parsed.entities.length, matched: 0, generic: 0, skipped: 0 };
19
+ const usedIds = new Map();
20
+ const emitGeneric = options.emitGeneric !== false;
21
+ for (const entity of parsed.entities) {
22
+ const rule = rules.find(r => matchRule(r.match, entity));
23
+ if (!rule) {
24
+ if (!emitGeneric) {
25
+ stats.skipped++;
26
+ continue;
27
+ }
28
+ const draft = makeGenericDraft(entity, usedIds);
29
+ components.push(draft);
30
+ stats.generic++;
31
+ continue;
32
+ }
33
+ const id = resolveId(rule.build.idStrategy ?? 'auto', entity, rule.componentType, usedIds);
34
+ let draft = {
35
+ category: rule.category ?? 'unknown',
36
+ type: rule.componentType,
37
+ id,
38
+ state: { ...(rule.build.defaultState || {}) },
39
+ matchedBy: rule,
40
+ source: entity
41
+ };
42
+ if (rule.build.transform) {
43
+ try {
44
+ draft = rule.build.transform(entity, draft);
45
+ }
46
+ catch (e) {
47
+ warnings.push({
48
+ level: 'warn',
49
+ entity,
50
+ message: `transform 실패 (${rule.componentType}): ${e.message}`
51
+ });
52
+ }
53
+ }
54
+ components.push(draft);
55
+ stats.matched++;
56
+ }
57
+ return { components, stats, warnings };
58
+ }
59
+ /**
60
+ * 매칭되지 않은 entity 를 GenericObject draft 로 변환.
61
+ * 좌표·크기·원본 attribs 모두 보존하여 사후 변환 가능하게 한다.
62
+ */
63
+ function makeGenericDraft(entity, usedIds) {
64
+ const id = resolveId('auto', entity, exports.GENERIC_OBJECT_TYPE, usedIds);
65
+ return {
66
+ category: 'unknown',
67
+ type: exports.GENERIC_OBJECT_TYPE,
68
+ id,
69
+ state: {
70
+ strokeStyle: '#888888',
71
+ strokeDashArray: '4 4',
72
+ fillStyle: 'rgba(128,128,128,0.15)',
73
+ label: entity.attribs.name || entity.attribs.layer || entity.attribs.text || ''
74
+ },
75
+ source: entity
76
+ };
77
+ }
78
+ // ── 매칭 로직 ──────────────────────────────────────────────────────
79
+ function matchRule(matcher, entity) {
80
+ if (matcher.cad) {
81
+ const m = matcher.cad;
82
+ let ok = true;
83
+ if (m.blockNamePattern && !m.blockNamePattern.test(entity.attribs.name || ''))
84
+ ok = false;
85
+ if (m.layer && !matchString(m.layer, entity.attribs.layer || ''))
86
+ ok = false;
87
+ if (m.entityKinds && !m.entityKinds.includes(entity.kind))
88
+ ok = false;
89
+ if (m.textAdjacent) {
90
+ const adjacentText = (entity.neighbors || [])
91
+ .filter(r => r.relation === 'labeled-by')
92
+ .map(r => r.uid);
93
+ if (!adjacentText.some(t => m.textAdjacent.test(t)))
94
+ ok = false;
95
+ }
96
+ if (ok)
97
+ return true;
98
+ }
99
+ if (matcher.ifc) {
100
+ const m = matcher.ifc;
101
+ let ok = true;
102
+ if (m.ifcClass && !matchString(m.ifcClass, entity.attribs.semanticType || ''))
103
+ ok = false;
104
+ if (ok)
105
+ return true;
106
+ }
107
+ if (matcher.geometry) {
108
+ const g = matcher.geometry;
109
+ let ok = true;
110
+ if (g.shape && entity.attribs.shape !== g.shape)
111
+ ok = false;
112
+ if (g.sizeRange && entity.size) {
113
+ const area = entity.size.width * entity.size.height;
114
+ if (g.sizeRange.minArea !== undefined && area < g.sizeRange.minArea)
115
+ ok = false;
116
+ if (g.sizeRange.maxArea !== undefined && area > g.sizeRange.maxArea)
117
+ ok = false;
118
+ }
119
+ if (g.aspectRatio && entity.size) {
120
+ const ratio = entity.size.height === 0 ? 1 : entity.size.width / entity.size.height;
121
+ if (g.aspectRatio.min !== undefined && ratio < g.aspectRatio.min)
122
+ ok = false;
123
+ if (g.aspectRatio.max !== undefined && ratio > g.aspectRatio.max)
124
+ ok = false;
125
+ }
126
+ if (ok)
127
+ return true;
128
+ }
129
+ if (matcher.image && matcher.image.labels.length > 0) {
130
+ const label = entity.attribs.vlmLabel;
131
+ if (label && matcher.image.labels.includes(label))
132
+ return true;
133
+ }
134
+ return false;
135
+ }
136
+ function matchString(pattern, value) {
137
+ if (typeof pattern === 'string')
138
+ return pattern === value;
139
+ return pattern.test(value);
140
+ }
141
+ // ── ID 부여 전략 ──────────────────────────────────────────────────
142
+ function resolveId(strategy, entity, type, usedIds) {
143
+ let id;
144
+ if (typeof strategy === 'object' && 'custom' in strategy) {
145
+ id = strategy.custom(entity);
146
+ }
147
+ else {
148
+ switch (strategy) {
149
+ case 'fromAttribute':
150
+ id = entity.attribs.name;
151
+ break;
152
+ case 'fromAdjacentText':
153
+ id = (entity.neighbors || []).find(r => r.relation === 'labeled-by')?.uid;
154
+ break;
155
+ case 'fromIfcGlobalId':
156
+ id = entity.attribs.ifcGlobalId;
157
+ break;
158
+ case 'auto':
159
+ default:
160
+ id = undefined;
161
+ }
162
+ }
163
+ if (!id) {
164
+ const seq = (usedIds.get(type) ?? 0) + 1;
165
+ usedIds.set(type, seq);
166
+ id = `${type.toUpperCase()}_${String(seq).padStart(3, '0')}`;
167
+ }
168
+ else {
169
+ usedIds.set(id, (usedIds.get(id) ?? 0) + 1);
170
+ }
171
+ return id;
172
+ }
173
+ //# sourceMappingURL=stage2-mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage2-mapping.js","sourceRoot":"","sources":["../../../server/service/pipeline/stage2-mapping.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stage 2 — Universal Entities → Component Drafts.\n * 등록된 ImportRule 들을 entity 마다 평가하여 컴포넌트 type 결정.\n */\nimport { getImportRules } from '../registry/index.js'\nimport type { ParseResult, UniversalEntity } from '../types/index.js'\nimport type {\n ComponentDraft,\n IdStrategy,\n ImportMatcher,\n MappingResult,\n MappingWarning\n} from '../types/registry.js'\n\n/** 매핑되지 않은 entity 가 변환되는 placeholder 도메인 type. */\nexport const GENERIC_OBJECT_TYPE = 'GenericObject'\n\nexport interface MappingOptions {\n /** 어떤 scope 의 규칙을 사용할지 (생략 시 전부) */\n scopes?: string[]\n /**\n * 매칭 안 된 entity 를 GenericObject 로 변환할지 여부 (default: true).\n * false 면 buffer 에서 제외되고 stats.skipped 로 카운트.\n */\n emitGeneric?: boolean\n}\n\nexport function applyMapping(parsed: ParseResult, options: MappingOptions = {}): MappingResult {\n const rules = getImportRules(options.scopes)\n .slice()\n .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))\n\n const components: ComponentDraft[] = []\n const warnings: MappingWarning[] = []\n const stats = { total: parsed.entities.length, matched: 0, generic: 0, skipped: 0 }\n\n const usedIds = new Map<string, number>()\n const emitGeneric = options.emitGeneric !== false\n\n for (const entity of parsed.entities) {\n const rule = rules.find(r => matchRule(r.match, entity))\n\n if (!rule) {\n if (!emitGeneric) {\n stats.skipped++\n continue\n }\n const draft = makeGenericDraft(entity, usedIds)\n components.push(draft)\n stats.generic++\n continue\n }\n\n const id = resolveId(rule.build.idStrategy ?? 'auto', entity, rule.componentType, usedIds)\n let draft: ComponentDraft = {\n category: rule.category ?? 'unknown',\n type: rule.componentType,\n id,\n state: { ...(rule.build.defaultState || {}) },\n matchedBy: rule,\n source: entity\n }\n\n if (rule.build.transform) {\n try {\n draft = rule.build.transform(entity, draft)\n } catch (e) {\n warnings.push({\n level: 'warn',\n entity,\n message: `transform 실패 (${rule.componentType}): ${(e as Error).message}`\n })\n }\n }\n\n components.push(draft)\n stats.matched++\n }\n\n return { components, stats, warnings }\n}\n\n/**\n * 매칭되지 않은 entity 를 GenericObject draft 로 변환.\n * 좌표·크기·원본 attribs 모두 보존하여 사후 변환 가능하게 한다.\n */\nfunction makeGenericDraft(entity: UniversalEntity, usedIds: Map<string, number>): ComponentDraft {\n const id = resolveId('auto', entity, GENERIC_OBJECT_TYPE, usedIds)\n return {\n category: 'unknown',\n type: GENERIC_OBJECT_TYPE,\n id,\n state: {\n strokeStyle: '#888888',\n strokeDashArray: '4 4',\n fillStyle: 'rgba(128,128,128,0.15)',\n label: entity.attribs.name || entity.attribs.layer || entity.attribs.text || ''\n },\n source: entity\n }\n}\n\n// ── 매칭 로직 ──────────────────────────────────────────────────────\n\nfunction matchRule(matcher: ImportMatcher, entity: UniversalEntity): boolean {\n if (matcher.cad) {\n const m = matcher.cad\n let ok = true\n if (m.blockNamePattern && !m.blockNamePattern.test(entity.attribs.name || '')) ok = false\n if (m.layer && !matchString(m.layer, entity.attribs.layer || '')) ok = false\n if (m.entityKinds && !m.entityKinds.includes(entity.kind)) ok = false\n if (m.textAdjacent) {\n const adjacentText = (entity.neighbors || [])\n .filter(r => r.relation === 'labeled-by')\n .map(r => r.uid)\n if (!adjacentText.some(t => m.textAdjacent!.test(t))) ok = false\n }\n if (ok) return true\n }\n\n if (matcher.ifc) {\n const m = matcher.ifc\n let ok = true\n if (m.ifcClass && !matchString(m.ifcClass, entity.attribs.semanticType || '')) ok = false\n if (ok) return true\n }\n\n if (matcher.geometry) {\n const g = matcher.geometry\n let ok = true\n if (g.shape && entity.attribs.shape !== g.shape) ok = false\n if (g.sizeRange && entity.size) {\n const area = entity.size.width * entity.size.height\n if (g.sizeRange.minArea !== undefined && area < g.sizeRange.minArea) ok = false\n if (g.sizeRange.maxArea !== undefined && area > g.sizeRange.maxArea) ok = false\n }\n if (g.aspectRatio && entity.size) {\n const ratio = entity.size.height === 0 ? 1 : entity.size.width / entity.size.height\n if (g.aspectRatio.min !== undefined && ratio < g.aspectRatio.min) ok = false\n if (g.aspectRatio.max !== undefined && ratio > g.aspectRatio.max) ok = false\n }\n if (ok) return true\n }\n\n if (matcher.image && matcher.image.labels.length > 0) {\n const label = entity.attribs.vlmLabel\n if (label && matcher.image.labels.includes(label)) return true\n }\n\n return false\n}\n\nfunction matchString(pattern: string | RegExp, value: string): boolean {\n if (typeof pattern === 'string') return pattern === value\n return pattern.test(value)\n}\n\n// ── ID 부여 전략 ──────────────────────────────────────────────────\n\nfunction resolveId(\n strategy: IdStrategy,\n entity: UniversalEntity,\n type: string,\n usedIds: Map<string, number>\n): string {\n let id: string | undefined\n\n if (typeof strategy === 'object' && 'custom' in strategy) {\n id = strategy.custom(entity)\n } else {\n switch (strategy) {\n case 'fromAttribute':\n id = entity.attribs.name\n break\n case 'fromAdjacentText':\n id = (entity.neighbors || []).find(r => r.relation === 'labeled-by')?.uid\n break\n case 'fromIfcGlobalId':\n id = entity.attribs.ifcGlobalId\n break\n case 'auto':\n default:\n id = undefined\n }\n }\n\n if (!id) {\n const seq = (usedIds.get(type) ?? 0) + 1\n usedIds.set(type, seq)\n id = `${type.toUpperCase()}_${String(seq).padStart(3, '0')}`\n } else {\n usedIds.set(id, (usedIds.get(id) ?? 0) + 1)\n }\n return id\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import type { BoundingBox } from '../types/index.js';
2
+ import type { ComponentCategory, ComponentDraft } from '../types/registry.js';
3
+ export interface BoardModel {
4
+ width?: number;
5
+ height?: number;
6
+ fillStyle?: string;
7
+ /**
8
+ * 컴포넌트 목록. things-scene 보드 모델 호환을 위해 optional.
9
+ * 빈 보드 / legacy 형식 / root container 가 직접 model 인 경우 누락 가능.
10
+ * 사용처는 항상 `(model.components ?? [])` 패턴으로 접근.
11
+ */
12
+ components?: BoardComponent[];
13
+ /** 메타: import 출처 */
14
+ importMeta?: {
15
+ format: string;
16
+ sourceBbox: BoundingBox;
17
+ units?: string;
18
+ importedAt: string;
19
+ };
20
+ /** things-scene / 사용자 도메인이 임의 추가하는 필드 */
21
+ [k: string]: any;
22
+ }
23
+ export interface BoardComponent {
24
+ /** Universal category — 모든 컴포넌트가 보유. 변환·렌더 정책의 기반. */
25
+ category: ComponentCategory;
26
+ /** 도메인 type. 'GenericObject' 면 변환 가능한 placeholder. */
27
+ type: string;
28
+ id?: string;
29
+ left: number;
30
+ top: number;
31
+ width: number;
32
+ height: number;
33
+ rotation?: number;
34
+ /** 3D Preset 해석 결과 (있으면) */
35
+ threeD?: Record<string, any>;
36
+ [k: string]: any;
37
+ }
38
+ export interface AssembleOptions {
39
+ /** scope 들 — 3D preset 조회 시 사용 */
40
+ scopes?: string[];
41
+ /** 좌표계 정규화: true 면 entities 의 minX,minY 가 0,0 이 되도록 이동 */
42
+ normalizeOrigin?: boolean;
43
+ /** Y 축 뒤집기: CAD (Y-up) → scene (Y-down) */
44
+ flipY?: boolean;
45
+ /** 단위 → scene unit 변환 비율. 1 이면 그대로. */
46
+ scale?: number;
47
+ /** 보드 배경색 */
48
+ backgroundColor?: string;
49
+ /** importMeta 를 모델에 첨부 */
50
+ importMeta?: BoardModel['importMeta'];
51
+ }
52
+ export declare function assembleBoard(drafts: ComponentDraft[], bbox: BoundingBox, options?: AssembleOptions): BoardModel;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assembleBoard = assembleBoard;
4
+ /**
5
+ * Stage 3 — ComponentDraft → Board Model JSON.
6
+ * 좌표/단위 변환 + 3D Preset 적용.
7
+ */
8
+ const index_js_1 = require("../registry/index.js");
9
+ function assembleBoard(drafts, bbox, options = {}) {
10
+ const scale = options.scale ?? 1;
11
+ const offsetX = options.normalizeOrigin ? -bbox.min.x : 0;
12
+ const offsetY = options.normalizeOrigin ? -bbox.min.y : 0;
13
+ const boardWidth = (bbox.max.x - bbox.min.x) * scale;
14
+ const boardHeight = (bbox.max.y - bbox.min.y) * scale;
15
+ const components = drafts.map(d => {
16
+ const src = d.source;
17
+ const left = src ? (src.position.x + offsetX) * scale : 0;
18
+ const rawTop = src ? (src.position.y + offsetY) * scale : 0;
19
+ const top = options.flipY ? boardHeight - rawTop - (src?.size?.height ?? 0) * scale : rawTop;
20
+ const width = (src?.size?.width ?? 1) * scale;
21
+ const height = (src?.size?.height ?? 1) * scale;
22
+ const rotation = src?.rotation;
23
+ const preset = (0, index_js_1.find3DPreset)(d.type, d.category, options.scopes);
24
+ const threeD = presetToState(preset);
25
+ return {
26
+ category: d.category,
27
+ type: d.type,
28
+ id: d.id,
29
+ left,
30
+ top,
31
+ width,
32
+ height,
33
+ ...(rotation !== undefined && { rotation: options.flipY ? -rotation : rotation }),
34
+ threeD,
35
+ ...d.state
36
+ };
37
+ });
38
+ return {
39
+ width: boardWidth || 1000,
40
+ height: boardHeight || 1000,
41
+ fillStyle: options.backgroundColor ?? '#ffffff',
42
+ components,
43
+ importMeta: options.importMeta
44
+ };
45
+ }
46
+ function presetToState(preset) {
47
+ return {
48
+ geometry: preset.geometry,
49
+ ...(preset.material && { material: preset.material }),
50
+ origin: preset.origin ?? 'bottom'
51
+ };
52
+ }
53
+ //# sourceMappingURL=stage3-board.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage3-board.js","sourceRoot":"","sources":["../../../server/service/pipeline/stage3-board.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stage 3 — ComponentDraft → Board Model JSON.\n * 좌표/단위 변환 + 3D Preset 적용.\n */\nimport { find3DPreset } from '../registry/index.js'\nimport type { BoundingBox } from '../types/index.js'\nimport type { ComponentCategory, ComponentDraft } from '../types/registry.js'\n\nexport interface BoardModel {\n width?: number\n height?: number\n fillStyle?: string\n /**\n * 컴포넌트 목록. things-scene 보드 모델 호환을 위해 optional.\n * 빈 보드 / legacy 형식 / root container 가 직접 model 인 경우 누락 가능.\n * 사용처는 항상 `(model.components ?? [])` 패턴으로 접근.\n */\n components?: BoardComponent[]\n /** 메타: import 출처 */\n importMeta?: {\n format: string\n sourceBbox: BoundingBox\n units?: string\n importedAt: string\n }\n /** things-scene / 사용자 도메인이 임의 추가하는 필드 */\n [k: string]: any\n}\n\nexport interface BoardComponent {\n /** Universal category — 모든 컴포넌트가 보유. 변환·렌더 정책의 기반. */\n category: ComponentCategory\n /** 도메인 type. 'GenericObject' 면 변환 가능한 placeholder. */\n type: string\n id?: string\n left: number\n top: number\n width: number\n height: number\n rotation?: number\n /** 3D Preset 해석 결과 (있으면) */\n threeD?: Record<string, any>\n [k: string]: any\n}\n\nexport interface AssembleOptions {\n /** scope 들 — 3D preset 조회 시 사용 */\n scopes?: string[]\n /** 좌표계 정규화: true 면 entities 의 minX,minY 가 0,0 이 되도록 이동 */\n normalizeOrigin?: boolean\n /** Y 축 뒤집기: CAD (Y-up) → scene (Y-down) */\n flipY?: boolean\n /** 단위 → scene unit 변환 비율. 1 이면 그대로. */\n scale?: number\n /** 보드 배경색 */\n backgroundColor?: string\n /** importMeta 를 모델에 첨부 */\n importMeta?: BoardModel['importMeta']\n}\n\nexport function assembleBoard(\n drafts: ComponentDraft[],\n bbox: BoundingBox,\n options: AssembleOptions = {}\n): BoardModel {\n const scale = options.scale ?? 1\n const offsetX = options.normalizeOrigin ? -bbox.min.x : 0\n const offsetY = options.normalizeOrigin ? -bbox.min.y : 0\n\n const boardWidth = (bbox.max.x - bbox.min.x) * scale\n const boardHeight = (bbox.max.y - bbox.min.y) * scale\n\n const components: BoardComponent[] = drafts.map(d => {\n const src = d.source\n const left = src ? (src.position.x + offsetX) * scale : 0\n const rawTop = src ? (src.position.y + offsetY) * scale : 0\n const top = options.flipY ? boardHeight - rawTop - (src?.size?.height ?? 0) * scale : rawTop\n const width = (src?.size?.width ?? 1) * scale\n const height = (src?.size?.height ?? 1) * scale\n const rotation = src?.rotation\n\n const preset = find3DPreset(d.type, d.category, options.scopes)\n const threeD = presetToState(preset)\n\n return {\n category: d.category,\n type: d.type,\n id: d.id,\n left,\n top,\n width,\n height,\n ...(rotation !== undefined && { rotation: options.flipY ? -rotation : rotation }),\n threeD,\n ...d.state\n }\n })\n\n return {\n width: boardWidth || 1000,\n height: boardHeight || 1000,\n fillStyle: options.backgroundColor ?? '#ffffff',\n components,\n importMeta: options.importMeta\n }\n}\n\nfunction presetToState(preset: import('../types/registry.js').ThreeDPreset): Record<string, any> {\n return {\n geometry: preset.geometry,\n ...(preset.material && { material: preset.material }),\n origin: preset.origin ?? 'bottom'\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { BoardModel } from './stage3-board.js';
2
+ export interface BindingOptions {
3
+ scopes?: string[];
4
+ }
5
+ /**
6
+ * 보드 모델의 모든 컴포넌트에 subscription 정보를 주입.
7
+ * things-factory 의 dataSubscriptionProvider 가 인식하는 형태로.
8
+ */
9
+ export declare function applyDataBinding(model: BoardModel, options?: BindingOptions): BoardModel;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyDataBinding = applyDataBinding;
4
+ /**
5
+ * Stage 4 — 보드 모델에 데이터 바인딩 자동 설정.
6
+ * 컴포넌트 type 에 등록된 SubscriptionRule 을 찾아 subscriptionTag 등 부여.
7
+ */
8
+ const index_js_1 = require("../registry/index.js");
9
+ /**
10
+ * 보드 모델의 모든 컴포넌트에 subscription 정보를 주입.
11
+ * things-factory 의 dataSubscriptionProvider 가 인식하는 형태로.
12
+ */
13
+ function applyDataBinding(model, options = {}) {
14
+ const components = model.components.map(c => bindComponent(c, options));
15
+ return { ...model, components };
16
+ }
17
+ function bindComponent(c, options) {
18
+ const rule = (0, index_js_1.findSubscriptionRule)(c.type, options.scopes);
19
+ if (!rule)
20
+ return c;
21
+ return {
22
+ ...c,
23
+ subscriptionTag: rule.tag,
24
+ subscriptionIdField: rule.idField ?? 'NAME',
25
+ ...(rule.fieldMappings && { subscriptionFieldMappings: rule.fieldMappings })
26
+ };
27
+ }
28
+ //# sourceMappingURL=stage4-binding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage4-binding.js","sourceRoot":"","sources":["../../../server/service/pipeline/stage4-binding.ts"],"names":[],"mappings":";;AAeA,4CAGC;AAlBD;;;GAGG;AACH,mDAA2D;AAO3D;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,KAAiB,EAAE,UAA0B,EAAE;IAC9E,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;IACvE,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,CAAA;AACjC,CAAC;AAED,SAAS,aAAa,CAAC,CAAiB,EAAE,OAAuB;IAC/D,MAAM,IAAI,GAAG,IAAA,+BAAoB,EAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAA;IAEnB,OAAO;QACL,GAAG,CAAC;QACJ,eAAe,EAAE,IAAI,CAAC,GAAG;QACzB,mBAAmB,EAAE,IAAI,CAAC,OAAO,IAAI,MAAM;QAC3C,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,yBAAyB,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;KAC7E,CAAA;AACH,CAAC","sourcesContent":["/**\n * Stage 4 — 보드 모델에 데이터 바인딩 자동 설정.\n * 컴포넌트 type 에 등록된 SubscriptionRule 을 찾아 subscriptionTag 등 부여.\n */\nimport { findSubscriptionRule } from '../registry/index.js'\nimport type { BoardComponent, BoardModel } from './stage3-board.js'\n\nexport interface BindingOptions {\n scopes?: string[]\n}\n\n/**\n * 보드 모델의 모든 컴포넌트에 subscription 정보를 주입.\n * things-factory 의 dataSubscriptionProvider 가 인식하는 형태로.\n */\nexport function applyDataBinding(model: BoardModel, options: BindingOptions = {}): BoardModel {\n const components = model.components.map(c => bindComponent(c, options))\n return { ...model, components }\n}\n\nfunction bindComponent(c: BoardComponent, options: BindingOptions): BoardComponent {\n const rule = findSubscriptionRule(c.type, options.scopes)\n if (!rule) return c\n\n return {\n ...c,\n subscriptionTag: rule.tag,\n subscriptionIdField: rule.idField ?? 'NAME',\n ...(rule.fieldMappings && { subscriptionFieldMappings: rule.fieldMappings })\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 앱이 자기 도메인 매핑을 등록하는 Registry.
3
+ * scope 별로 분리 보관 (예: 'fmsim', 'mms', 'wms').
4
+ *
5
+ * 백엔드 단일 프로세스 안에서 in-memory 로 유지된다. 모듈 부트스트랩 시 등록.
6
+ */
7
+ import type { ComponentCategory, ImportRule, SubscriptionRule, ThreeDPreset } from '../types/registry.js';
8
+ export declare function registerImportRules(scope: string, rules: ImportRule[]): void;
9
+ export declare function getImportRules(scopes?: string[]): ImportRule[];
10
+ export declare function clearImportRules(scope: string): void;
11
+ export declare function register3DPresets(scope: string, presets: ThreeDPreset[]): void;
12
+ export declare function get3DPresets(scopes?: string[]): ThreeDPreset[];
13
+ /**
14
+ * 컴포넌트 type + category 로 3D preset 을 찾는다.
15
+ * 우선순위:
16
+ * 1) 앱 등록 preset 중 componentType 정확 일치
17
+ * 2) 앱 등록 preset 중 category 일치
18
+ * 3) 코어 디폴트 카테고리 preset
19
+ * 4) 코어 디폴트 'unknown' preset
20
+ */
21
+ export declare function find3DPreset(componentType: string, category?: ComponentCategory, scopes?: string[]): ThreeDPreset;
22
+ export declare function clear3DPresets(scope: string): void;
23
+ export declare function registerSubscriptionRules(scope: string, rules: SubscriptionRule[]): void;
24
+ export declare function getSubscriptionRules(scopes?: string[]): SubscriptionRule[];
25
+ export declare function findSubscriptionRule(componentType: string, scopes?: string[]): SubscriptionRule | undefined;
26
+ export declare function clearSubscriptionRules(scope: string): void;
27
+ export declare function clearAllRegistries(): void;