@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,39 @@
|
|
|
1
|
+
import '@things-factory/auth-base';
|
|
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
|
+
}
|
|
18
|
+
declare class ImportBoardAsyncInput {
|
|
19
|
+
attachmentId: string;
|
|
20
|
+
chatSessionId?: string;
|
|
21
|
+
scopes?: string[];
|
|
22
|
+
normalizeOrigin?: boolean;
|
|
23
|
+
flipY?: boolean;
|
|
24
|
+
scale?: number;
|
|
25
|
+
applyBinding?: boolean;
|
|
26
|
+
parseOptions?: any;
|
|
27
|
+
userPrompt?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class ImportSessionResolver {
|
|
30
|
+
importSession(id: string, context: ResolverContext): Promise<ImportSession | null>;
|
|
31
|
+
importSessionsByChatSession(chatSessionId: string, context: ResolverContext): Promise<ImportSession[]>;
|
|
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>;
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImportSessionResolver = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/**
|
|
6
|
+
* ImportSession resolver + 비동기 worker.
|
|
7
|
+
*
|
|
8
|
+
* Mutation:
|
|
9
|
+
* - importBoardAsync(attachmentId, options): ImportSession (즉시 반환, 백그라운드에서 처리)
|
|
10
|
+
*
|
|
11
|
+
* Query:
|
|
12
|
+
* - importSession(id): ImportSession (진행 상황 polling)
|
|
13
|
+
*
|
|
14
|
+
* 비동기 worker 는 단일 프로세스 내 setImmediate 기반. 추후 큐(Redis/BullMQ) 로 이전 가능.
|
|
15
|
+
*/
|
|
16
|
+
const type_graphql_1 = require("type-graphql");
|
|
17
|
+
const graphql_scalars_1 = require("graphql-scalars");
|
|
18
|
+
const typeorm_1 = require("typeorm");
|
|
19
|
+
require("@things-factory/auth-base");
|
|
20
|
+
const shell_1 = require("@things-factory/shell");
|
|
21
|
+
const board_service_1 = require("@things-factory/board-service");
|
|
22
|
+
const attachment_base_1 = require("@things-factory/attachment-base");
|
|
23
|
+
const import_session_1 = require("./import-session");
|
|
24
|
+
const materialize_from_session_1 = require("./materialize-from-session");
|
|
25
|
+
const suggest_board_name_1 = require("./suggest-board-name");
|
|
26
|
+
const import_actions_1 = require("./import-actions");
|
|
27
|
+
let MaterializeImportSessionInput = class MaterializeImportSessionInput {
|
|
28
|
+
};
|
|
29
|
+
tslib_1.__decorate([
|
|
30
|
+
(0, type_graphql_1.Field)(() => type_graphql_1.ID, { description: 'ImportSession id (must be in completed state).' }),
|
|
31
|
+
tslib_1.__metadata("design:type", String)
|
|
32
|
+
], MaterializeImportSessionInput.prototype, "sessionId", void 0);
|
|
33
|
+
tslib_1.__decorate([
|
|
34
|
+
(0, type_graphql_1.Field)({ description: 'New Board name.' }),
|
|
35
|
+
tslib_1.__metadata("design:type", String)
|
|
36
|
+
], MaterializeImportSessionInput.prototype, "name", void 0);
|
|
37
|
+
tslib_1.__decorate([
|
|
38
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Optional Board description.' }),
|
|
39
|
+
tslib_1.__metadata("design:type", String)
|
|
40
|
+
], MaterializeImportSessionInput.prototype, "description", void 0);
|
|
41
|
+
tslib_1.__decorate([
|
|
42
|
+
(0, type_graphql_1.Field)(() => type_graphql_1.ID, { nullable: true, description: 'Optional Group id to attach the Board to.' }),
|
|
43
|
+
tslib_1.__metadata("design:type", String)
|
|
44
|
+
], MaterializeImportSessionInput.prototype, "groupId", void 0);
|
|
45
|
+
tslib_1.__decorate([
|
|
46
|
+
(0, type_graphql_1.Field)({ nullable: true, description: "Board type — 'main' | 'sub' | 'popup'. Default 'main'." }),
|
|
47
|
+
tslib_1.__metadata("design:type", String)
|
|
48
|
+
], MaterializeImportSessionInput.prototype, "type", void 0);
|
|
49
|
+
tslib_1.__decorate([
|
|
50
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Base64 thumbnail. If omitted, board-service default empty thumbnail is used.' }),
|
|
51
|
+
tslib_1.__metadata("design:type", String)
|
|
52
|
+
], MaterializeImportSessionInput.prototype, "thumbnail", void 0);
|
|
53
|
+
tslib_1.__decorate([
|
|
54
|
+
(0, type_graphql_1.Field)({
|
|
55
|
+
nullable: true,
|
|
56
|
+
description: "ImportSession 의 결과 시안 중 하나의 id. 'as-is' / 'scene' / 'auto-fit'. " +
|
|
57
|
+
'미지정 시 default variant (보통 \'scene\') 사용.'
|
|
58
|
+
}),
|
|
59
|
+
tslib_1.__metadata("design:type", String)
|
|
60
|
+
], MaterializeImportSessionInput.prototype, "variantId", void 0);
|
|
61
|
+
MaterializeImportSessionInput = tslib_1.__decorate([
|
|
62
|
+
(0, type_graphql_1.InputType)()
|
|
63
|
+
], MaterializeImportSessionInput);
|
|
64
|
+
let BoardMetaSuggestion = class BoardMetaSuggestion {
|
|
65
|
+
};
|
|
66
|
+
tslib_1.__decorate([
|
|
67
|
+
(0, type_graphql_1.Field)({ description: 'Suggested short board name (collision-free with current domain).' }),
|
|
68
|
+
tslib_1.__metadata("design:type", String)
|
|
69
|
+
], BoardMetaSuggestion.prototype, "name", void 0);
|
|
70
|
+
tslib_1.__decorate([
|
|
71
|
+
(0, type_graphql_1.Field)({
|
|
72
|
+
description: 'Suggested detailed description — combines user prompt, AI importStrategy narrative, and category distribution stats.'
|
|
73
|
+
}),
|
|
74
|
+
tslib_1.__metadata("design:type", String)
|
|
75
|
+
], BoardMetaSuggestion.prototype, "description", void 0);
|
|
76
|
+
BoardMetaSuggestion = tslib_1.__decorate([
|
|
77
|
+
(0, type_graphql_1.ObjectType)()
|
|
78
|
+
], BoardMetaSuggestion);
|
|
79
|
+
let SuggestBoardNameInput = class SuggestBoardNameInput {
|
|
80
|
+
};
|
|
81
|
+
tslib_1.__decorate([
|
|
82
|
+
(0, type_graphql_1.Field)(() => type_graphql_1.ID, {
|
|
83
|
+
nullable: true,
|
|
84
|
+
description: 'ImportSession id — 있으면 거기서 userPrompt / VLM reasoning / attachment.name 추출.'
|
|
85
|
+
}),
|
|
86
|
+
tslib_1.__metadata("design:type", String)
|
|
87
|
+
], SuggestBoardNameInput.prototype, "sessionId", void 0);
|
|
88
|
+
tslib_1.__decorate([
|
|
89
|
+
(0, type_graphql_1.Field)({
|
|
90
|
+
nullable: true,
|
|
91
|
+
description: '명시 hint. session 정보보다 우선. 사용자가 직접 적은 메모를 기반으로 추천받고 싶을 때.'
|
|
92
|
+
}),
|
|
93
|
+
tslib_1.__metadata("design:type", String)
|
|
94
|
+
], SuggestBoardNameInput.prototype, "hint", void 0);
|
|
95
|
+
SuggestBoardNameInput = tslib_1.__decorate([
|
|
96
|
+
(0, type_graphql_1.InputType)()
|
|
97
|
+
], SuggestBoardNameInput);
|
|
98
|
+
let ImportBoardAsyncInput = class ImportBoardAsyncInput {
|
|
99
|
+
};
|
|
100
|
+
tslib_1.__decorate([
|
|
101
|
+
(0, type_graphql_1.Field)({ description: 'Attachment id of the source drawing file (uploaded via attachment-base).' }),
|
|
102
|
+
tslib_1.__metadata("design:type", String)
|
|
103
|
+
], ImportBoardAsyncInput.prototype, "attachmentId", void 0);
|
|
104
|
+
tslib_1.__decorate([
|
|
105
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'ChatSession id when triggered from board-ai chat.' }),
|
|
106
|
+
tslib_1.__metadata("design:type", String)
|
|
107
|
+
], ImportBoardAsyncInput.prototype, "chatSessionId", void 0);
|
|
108
|
+
tslib_1.__decorate([
|
|
109
|
+
(0, type_graphql_1.Field)(() => [String], { nullable: true, description: 'Registry scopes to use (board-import).' }),
|
|
110
|
+
tslib_1.__metadata("design:type", Array)
|
|
111
|
+
], ImportBoardAsyncInput.prototype, "scopes", void 0);
|
|
112
|
+
tslib_1.__decorate([
|
|
113
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Normalize coordinates so minX,minY=0' }),
|
|
114
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
115
|
+
], ImportBoardAsyncInput.prototype, "normalizeOrigin", void 0);
|
|
116
|
+
tslib_1.__decorate([
|
|
117
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Flip Y axis (CAD Y-up → scene Y-down).' }),
|
|
118
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
119
|
+
], ImportBoardAsyncInput.prototype, "flipY", void 0);
|
|
120
|
+
tslib_1.__decorate([
|
|
121
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Scale factor (e.g. 1 for mm:1unit).' }),
|
|
122
|
+
tslib_1.__metadata("design:type", Number)
|
|
123
|
+
], ImportBoardAsyncInput.prototype, "scale", void 0);
|
|
124
|
+
tslib_1.__decorate([
|
|
125
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'Apply Stage 4 (data binding).' }),
|
|
126
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
127
|
+
], ImportBoardAsyncInput.prototype, "applyBinding", void 0);
|
|
128
|
+
tslib_1.__decorate([
|
|
129
|
+
(0, type_graphql_1.Field)(() => graphql_scalars_1.GraphQLJSON, { nullable: true, description: 'Adapter parse options (excludeLayers, maxEntities, ...).' }),
|
|
130
|
+
tslib_1.__metadata("design:type", Object)
|
|
131
|
+
], ImportBoardAsyncInput.prototype, "parseOptions", void 0);
|
|
132
|
+
tslib_1.__decorate([
|
|
133
|
+
(0, type_graphql_1.Field)({
|
|
134
|
+
nullable: true,
|
|
135
|
+
description: 'User-provided context / hints about the drawing — passed to VLM as additional guidance. ' +
|
|
136
|
+
'e.g. "fab lithography zone, central rectangles are stockers, thin lines are OHT rails."'
|
|
137
|
+
}),
|
|
138
|
+
tslib_1.__metadata("design:type", String)
|
|
139
|
+
], ImportBoardAsyncInput.prototype, "userPrompt", void 0);
|
|
140
|
+
ImportBoardAsyncInput = tslib_1.__decorate([
|
|
141
|
+
(0, type_graphql_1.InputType)()
|
|
142
|
+
], ImportBoardAsyncInput);
|
|
143
|
+
let ImportSessionResolver = class ImportSessionResolver {
|
|
144
|
+
async importSession(id, context) {
|
|
145
|
+
const { domain } = context.state;
|
|
146
|
+
return await (0, import_actions_1.fetchImportSession)(id, { domain, user: context.state.user }, (0, shell_1.getRepository)(import_session_1.ImportSession));
|
|
147
|
+
}
|
|
148
|
+
async importSessionsByChatSession(chatSessionId, context) {
|
|
149
|
+
const { domain, user } = context.state;
|
|
150
|
+
return await (0, import_actions_1.listImportSessionsByChatSession)(chatSessionId, { domain, user }, (0, shell_1.getRepository)(import_session_1.ImportSession));
|
|
151
|
+
}
|
|
152
|
+
async importBoardAsync(input, context) {
|
|
153
|
+
const { domain, user } = context.state;
|
|
154
|
+
return await (0, import_actions_1.startImportJobAsync)(input, { domain, user }, (0, shell_1.getRepository)(import_session_1.ImportSession));
|
|
155
|
+
}
|
|
156
|
+
async suggestBoardName(input, context) {
|
|
157
|
+
const { domain } = context.state;
|
|
158
|
+
const deps = this._buildSuggestDeps();
|
|
159
|
+
return await (0, suggest_board_name_1.suggestBoardName)(input, { domain }, deps);
|
|
160
|
+
}
|
|
161
|
+
async suggestBoardMeta(input, context) {
|
|
162
|
+
const { domain } = context.state;
|
|
163
|
+
const deps = this._buildSuggestDeps();
|
|
164
|
+
return await (0, suggest_board_name_1.suggestBoardMeta)(input, { domain }, deps);
|
|
165
|
+
}
|
|
166
|
+
/** 두 query 가 공유하는 deps wiring — typeorm find idiomatic 패턴 (distinctAlias 회귀 회피). */
|
|
167
|
+
_buildSuggestDeps() {
|
|
168
|
+
const sessionRepo = (0, shell_1.getRepository)(import_session_1.ImportSession);
|
|
169
|
+
const attachmentRepo = (0, shell_1.getRepository)(attachment_base_1.Attachment);
|
|
170
|
+
const boardRepo = {
|
|
171
|
+
findExistingNames: async ({ domainId, basePrefix }) => {
|
|
172
|
+
// 도메인 격리 + 'basePrefix' 로 시작하는 name 후보 한 번에 조회.
|
|
173
|
+
// 단순 find({ where, select }) — take 없음 → DISTINCT 서브쿼리 미발생.
|
|
174
|
+
const escaped = basePrefix.replace(/[%_\\]/g, ch => '\\' + ch);
|
|
175
|
+
const items = await (0, shell_1.getRepository)(board_service_1.Board).find({
|
|
176
|
+
select: ['name'],
|
|
177
|
+
where: { domain: { id: domainId }, name: (0, typeorm_1.Like)(`${escaped}%`) }
|
|
178
|
+
});
|
|
179
|
+
return items.map(b => b.name);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
return { sessionRepo, attachmentRepo, boardRepo };
|
|
183
|
+
}
|
|
184
|
+
async materializeImportSession(input, context) {
|
|
185
|
+
const { domain, user } = context.state;
|
|
186
|
+
const sessionRepo = (0, shell_1.getRepository)(import_session_1.ImportSession);
|
|
187
|
+
const boardRepo = (0, shell_1.getRepository)(board_service_1.Board);
|
|
188
|
+
const groupRepo = (0, shell_1.getRepository)(board_service_1.Group);
|
|
189
|
+
const session = await sessionRepo.findOneBy({ id: input.sessionId, domain: { id: domain.id } });
|
|
190
|
+
return (await (0, materialize_from_session_1.materializeFromSession)(session, input, { domain, user }, { boardRepo: boardRepo, groupRepo: groupRepo }));
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
exports.ImportSessionResolver = ImportSessionResolver;
|
|
194
|
+
tslib_1.__decorate([
|
|
195
|
+
(0, type_graphql_1.Query)(() => import_session_1.ImportSession, { nullable: true, description: 'Get import session progress.' }),
|
|
196
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "query")'),
|
|
197
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('id')),
|
|
198
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
199
|
+
tslib_1.__metadata("design:type", Function),
|
|
200
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
201
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
202
|
+
], ImportSessionResolver.prototype, "importSession", null);
|
|
203
|
+
tslib_1.__decorate([
|
|
204
|
+
(0, type_graphql_1.Query)(() => [import_session_1.ImportSession], { description: 'List recent import sessions for a chat session.' }),
|
|
205
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "query")'),
|
|
206
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('chatSessionId')),
|
|
207
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
208
|
+
tslib_1.__metadata("design:type", Function),
|
|
209
|
+
tslib_1.__metadata("design:paramtypes", [String, Object]),
|
|
210
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
211
|
+
], ImportSessionResolver.prototype, "importSessionsByChatSession", null);
|
|
212
|
+
tslib_1.__decorate([
|
|
213
|
+
(0, type_graphql_1.Mutation)(() => import_session_1.ImportSession, {
|
|
214
|
+
description: '도면 → 보드 변환을 비동기로 시작. 즉시 ImportSession 반환, 백그라운드에서 처리.'
|
|
215
|
+
}),
|
|
216
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "mutation")'),
|
|
217
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
218
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
219
|
+
tslib_1.__metadata("design:type", Function),
|
|
220
|
+
tslib_1.__metadata("design:paramtypes", [ImportBoardAsyncInput, Object]),
|
|
221
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
222
|
+
], ImportSessionResolver.prototype, "importBoardAsync", null);
|
|
223
|
+
tslib_1.__decorate([
|
|
224
|
+
(0, type_graphql_1.Query)(() => String, {
|
|
225
|
+
description: '@deprecated suggestBoardMeta 사용 권장. 단순 string name 만 반환하는 구버전 query.'
|
|
226
|
+
}),
|
|
227
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "query")'),
|
|
228
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
229
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
230
|
+
tslib_1.__metadata("design:type", Function),
|
|
231
|
+
tslib_1.__metadata("design:paramtypes", [SuggestBoardNameInput, Object]),
|
|
232
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
233
|
+
], ImportSessionResolver.prototype, "suggestBoardName", null);
|
|
234
|
+
tslib_1.__decorate([
|
|
235
|
+
(0, type_graphql_1.Query)(() => BoardMetaSuggestion, {
|
|
236
|
+
description: '도메인 안에서 충돌하지 않는 Board name + 상세 description 제안. ' +
|
|
237
|
+
'name 은 짧고 심플 (30자 이내, 충돌 회피 (n) suffix), description 은 자세히 ' +
|
|
238
|
+
'(사용자 prompt + AI importStrategy + 카테고리 분포 통계 합성).'
|
|
239
|
+
}),
|
|
240
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "query")'),
|
|
241
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
242
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
243
|
+
tslib_1.__metadata("design:type", Function),
|
|
244
|
+
tslib_1.__metadata("design:paramtypes", [SuggestBoardNameInput, Object]),
|
|
245
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
246
|
+
], ImportSessionResolver.prototype, "suggestBoardMeta", null);
|
|
247
|
+
tslib_1.__decorate([
|
|
248
|
+
(0, type_graphql_1.Mutation)(() => board_service_1.Board, {
|
|
249
|
+
description: '완료된 ImportSession 의 결과 boardModel 을 새 Board entity 로 영속화한다. ' +
|
|
250
|
+
'검수 단계 (사용자/AI 가 import 결과를 확인 후 명시적으로 호출) 에서 사용. ' +
|
|
251
|
+
'Board.state="draft" 로 생성되므로 release mutation 으로 별도 발행 필요.'
|
|
252
|
+
}),
|
|
253
|
+
(0, type_graphql_1.Directive)('@privilege(category: "board-import", privilege: "mutation")'),
|
|
254
|
+
tslib_1.__param(0, (0, type_graphql_1.Arg)('input')),
|
|
255
|
+
tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
|
|
256
|
+
tslib_1.__metadata("design:type", Function),
|
|
257
|
+
tslib_1.__metadata("design:paramtypes", [MaterializeImportSessionInput, Object]),
|
|
258
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
259
|
+
], ImportSessionResolver.prototype, "materializeImportSession", null);
|
|
260
|
+
exports.ImportSessionResolver = ImportSessionResolver = tslib_1.__decorate([
|
|
261
|
+
(0, type_graphql_1.Resolver)()
|
|
262
|
+
], ImportSessionResolver);
|
|
263
|
+
//# sourceMappingURL=import-session-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-session-resolver.js","sourceRoot":"","sources":["../../../server/service/import-session/import-session-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ImportSession resolver + 비동기 worker.\n *\n * Mutation:\n * - importBoardAsync(attachmentId, options): ImportSession (즉시 반환, 백그라운드에서 처리)\n *\n * Query:\n * - importSession(id): ImportSession (진행 상황 polling)\n *\n * 비동기 worker 는 단일 프로세스 내 setImmediate 기반. 추후 큐(Redis/BullMQ) 로 이전 가능.\n */\nimport {\n Resolver,\n Query,\n Mutation,\n Arg,\n Ctx,\n Directive,\n Field,\n ID,\n InputType,\n ObjectType\n} from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\nimport { Like } from 'typeorm'\nimport '@things-factory/auth-base'\n\nimport { getRepository } from '@things-factory/shell'\nimport { Board, Group } from '@things-factory/board-service'\nimport { Attachment } from '@things-factory/attachment-base'\n\nimport { ImportSession } from './import-session'\nimport { materializeFromSession } from './materialize-from-session'\nimport {\n suggestBoardName,\n suggestBoardMeta,\n type SuggestBoardMetaResult,\n type SuggestBoardRepoLike\n} from './suggest-board-name'\nimport {\n startImportJobAsync,\n fetchImportSession,\n listImportSessionsByChatSession\n} from './import-actions'\n\n@InputType()\nclass MaterializeImportSessionInput {\n @Field(() => ID, { description: 'ImportSession id (must be in completed state).' })\n sessionId!: string\n\n @Field({ description: 'New Board name.' })\n name!: string\n\n @Field({ nullable: true, description: 'Optional Board description.' })\n description?: string\n\n @Field(() => ID, { nullable: true, description: 'Optional Group id to attach the Board to.' })\n groupId?: string\n\n @Field({ nullable: true, description: \"Board type — 'main' | 'sub' | 'popup'. Default 'main'.\" })\n type?: 'main' | 'sub' | 'popup'\n\n @Field({ nullable: true, description: 'Base64 thumbnail. If omitted, board-service default empty thumbnail is used.' })\n thumbnail?: string\n\n @Field({\n nullable: true,\n description:\n \"ImportSession 의 결과 시안 중 하나의 id. 'as-is' / 'scene' / 'auto-fit'. \" +\n '미지정 시 default variant (보통 \\'scene\\') 사용.'\n })\n variantId?: string\n}\n\n@ObjectType()\nclass BoardMetaSuggestion {\n @Field({ description: 'Suggested short board name (collision-free with current domain).' })\n name!: string\n\n @Field({\n description:\n 'Suggested detailed description — combines user prompt, AI importStrategy narrative, and category distribution stats.'\n })\n description!: string\n}\n\n@InputType()\nclass SuggestBoardNameInput {\n @Field(() => ID, {\n nullable: true,\n description: 'ImportSession id — 있으면 거기서 userPrompt / VLM reasoning / attachment.name 추출.'\n })\n sessionId?: string\n\n @Field({\n nullable: true,\n description: '명시 hint. session 정보보다 우선. 사용자가 직접 적은 메모를 기반으로 추천받고 싶을 때.'\n })\n hint?: string\n}\n\n@InputType()\nclass ImportBoardAsyncInput {\n @Field({ description: 'Attachment id of the source drawing file (uploaded via attachment-base).' })\n attachmentId!: string\n\n @Field({ nullable: true, description: 'ChatSession id when triggered from board-ai chat.' })\n chatSessionId?: string\n\n @Field(() => [String], { nullable: true, description: 'Registry scopes to use (board-import).' })\n scopes?: string[]\n\n @Field({ nullable: true, description: 'Normalize coordinates so minX,minY=0' })\n normalizeOrigin?: boolean\n\n @Field({ nullable: true, description: 'Flip Y axis (CAD Y-up → scene Y-down).' })\n flipY?: boolean\n\n @Field({ nullable: true, description: 'Scale factor (e.g. 1 for mm:1unit).' })\n scale?: number\n\n @Field({ nullable: true, description: 'Apply Stage 4 (data binding).' })\n applyBinding?: boolean\n\n @Field(() => GraphQLJSON, { nullable: true, description: 'Adapter parse options (excludeLayers, maxEntities, ...).' })\n parseOptions?: any\n\n @Field({\n nullable: true,\n description:\n 'User-provided context / hints about the drawing — passed to VLM as additional guidance. ' +\n 'e.g. \"fab lithography zone, central rectangles are stockers, thin lines are OHT rails.\"'\n })\n userPrompt?: string\n}\n\n@Resolver()\nexport class ImportSessionResolver {\n @Query(() => ImportSession, { nullable: true, description: 'Get import session progress.' })\n @Directive('@privilege(category: \"board-import\", privilege: \"query\")')\n async importSession(\n @Arg('id') id: string,\n @Ctx() context: ResolverContext\n ): Promise<ImportSession | null> {\n const { domain } = context.state\n return await fetchImportSession(id, { domain, user: context.state.user }, getRepository(ImportSession) as any)\n }\n\n @Query(() => [ImportSession], { description: 'List recent import sessions for a chat session.' })\n @Directive('@privilege(category: \"board-import\", privilege: \"query\")')\n async importSessionsByChatSession(\n @Arg('chatSessionId') chatSessionId: string,\n @Ctx() context: ResolverContext\n ): Promise<ImportSession[]> {\n const { domain, user } = context.state\n return await listImportSessionsByChatSession(chatSessionId, { domain, user }, getRepository(ImportSession) as any)\n }\n\n @Mutation(() => ImportSession, {\n description: '도면 → 보드 변환을 비동기로 시작. 즉시 ImportSession 반환, 백그라운드에서 처리.'\n })\n @Directive('@privilege(category: \"board-import\", privilege: \"mutation\")')\n async importBoardAsync(\n @Arg('input') input: ImportBoardAsyncInput,\n @Ctx() context: ResolverContext\n ): Promise<ImportSession> {\n const { domain, user } = context.state\n return await startImportJobAsync(input, { domain, user }, getRepository(ImportSession) as any)\n }\n\n @Query(() => String, {\n description:\n '@deprecated suggestBoardMeta 사용 권장. 단순 string name 만 반환하는 구버전 query.'\n })\n @Directive('@privilege(category: \"board-import\", privilege: \"query\")')\n async suggestBoardName(\n @Arg('input') input: SuggestBoardNameInput,\n @Ctx() context: ResolverContext\n ): Promise<string> {\n const { domain } = context.state\n const deps = this._buildSuggestDeps()\n return await suggestBoardName(input, { domain }, deps)\n }\n\n @Query(() => BoardMetaSuggestion, {\n description:\n '도메인 안에서 충돌하지 않는 Board name + 상세 description 제안. ' +\n 'name 은 짧고 심플 (30자 이내, 충돌 회피 (n) suffix), description 은 자세히 ' +\n '(사용자 prompt + AI importStrategy + 카테고리 분포 통계 합성).'\n })\n @Directive('@privilege(category: \"board-import\", privilege: \"query\")')\n async suggestBoardMeta(\n @Arg('input') input: SuggestBoardNameInput,\n @Ctx() context: ResolverContext\n ): Promise<SuggestBoardMetaResult> {\n const { domain } = context.state\n const deps = this._buildSuggestDeps()\n return await suggestBoardMeta(input, { domain }, deps)\n }\n\n /** 두 query 가 공유하는 deps wiring — typeorm find idiomatic 패턴 (distinctAlias 회귀 회피). */\n private _buildSuggestDeps() {\n const sessionRepo = getRepository(ImportSession) as any\n const attachmentRepo = getRepository(Attachment) as any\n const boardRepo: SuggestBoardRepoLike = {\n findExistingNames: async ({ domainId, basePrefix }) => {\n // 도메인 격리 + 'basePrefix' 로 시작하는 name 후보 한 번에 조회.\n // 단순 find({ where, select }) — take 없음 → DISTINCT 서브쿼리 미발생.\n const escaped = basePrefix.replace(/[%_\\\\]/g, ch => '\\\\' + ch)\n const items = await getRepository(Board).find({\n select: ['name'],\n where: { domain: { id: domainId }, name: Like(`${escaped}%`) }\n })\n return items.map(b => b.name as string)\n }\n }\n return { sessionRepo, attachmentRepo, boardRepo }\n }\n\n @Mutation(() => Board, {\n description:\n '완료된 ImportSession 의 결과 boardModel 을 새 Board entity 로 영속화한다. ' +\n '검수 단계 (사용자/AI 가 import 결과를 확인 후 명시적으로 호출) 에서 사용. ' +\n 'Board.state=\"draft\" 로 생성되므로 release mutation 으로 별도 발행 필요.'\n })\n @Directive('@privilege(category: \"board-import\", privilege: \"mutation\")')\n async materializeImportSession(\n @Arg('input') input: MaterializeImportSessionInput,\n @Ctx() context: ResolverContext\n ): Promise<Board> {\n const { domain, user } = context.state\n const sessionRepo = getRepository(ImportSession)\n const boardRepo = getRepository(Board)\n const groupRepo = getRepository(Group)\n\n const session = await sessionRepo.findOneBy({ id: input.sessionId, domain: { id: domain.id } })\n\n return (await materializeFromSession(\n session as any,\n input,\n { domain, user },\n { boardRepo: boardRepo as any, groupRepo: groupRepo as any }\n )) as Board\n }\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Domain } from '@things-factory/shell';
|
|
2
|
+
import { User } from '@things-factory/auth-base';
|
|
3
|
+
export declare class ImportSession {
|
|
4
|
+
readonly id?: string;
|
|
5
|
+
domain?: Domain;
|
|
6
|
+
domainId?: string;
|
|
7
|
+
/** Attachment.id (도면 파일). attachment-base 의존이지만 직접 ManyToOne 대신 ID 만 보유. */
|
|
8
|
+
attachmentId: string;
|
|
9
|
+
/** 어느 ChatSession 에서 발동됐는지 (board-ai 와 연동). 없으면 직접 호출. */
|
|
10
|
+
chatSessionId?: string;
|
|
11
|
+
/** 'queued' | 'parsing' | 'mapping' | 'assembling' | 'binding' | 'completed' | 'failed' */
|
|
12
|
+
status: string;
|
|
13
|
+
/** 0..100 */
|
|
14
|
+
progress: number;
|
|
15
|
+
/** 현재 단계 설명 또는 에러 메시지 */
|
|
16
|
+
message?: string;
|
|
17
|
+
/** import 옵션 (scopes, parseOptions, normalizeOrigin 등) */
|
|
18
|
+
optionsJson?: string;
|
|
19
|
+
get options(): any;
|
|
20
|
+
/** 결과: { boardModel, mapping: { stats, warnings } } */
|
|
21
|
+
resultJson?: string;
|
|
22
|
+
get result(): any;
|
|
23
|
+
totalEntities?: number;
|
|
24
|
+
creator?: User;
|
|
25
|
+
createdAt?: Date;
|
|
26
|
+
updatedAt?: Date;
|
|
27
|
+
completedAt?: Date;
|
|
28
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImportSession = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/**
|
|
6
|
+
* ImportSession — 도면(Attachment) → BoardModel 변환 작업 단위.
|
|
7
|
+
*
|
|
8
|
+
* 비동기 워크플로우:
|
|
9
|
+
* 1) 클라이언트가 importBoardAsync(attachmentId) 호출 → ImportSession 즉시 반환 (status:'queued')
|
|
10
|
+
* 2) 백그라운드 worker 가 attachment 본문 → importBoard() 실행 → status 갱신
|
|
11
|
+
* 3) 클라이언트는 polling 또는 subscription 으로 진행 상황 추적
|
|
12
|
+
* 4) status='completed' 시 result 에 BoardModel + stats + warnings 포함
|
|
13
|
+
*
|
|
14
|
+
* board-ai 의 ChatSession 과 연결 가능 (chatSessionId) — chat 안에서 자동 발동되면 진행 메시지가
|
|
15
|
+
* 자동으로 ChatMessage(role:'system') 으로 기록되어 AI 컨텍스트에 흐름.
|
|
16
|
+
*/
|
|
17
|
+
const typeorm_1 = require("typeorm");
|
|
18
|
+
const type_graphql_1 = require("type-graphql");
|
|
19
|
+
const graphql_scalars_1 = require("graphql-scalars");
|
|
20
|
+
const shell_1 = require("@things-factory/shell");
|
|
21
|
+
const auth_base_1 = require("@things-factory/auth-base");
|
|
22
|
+
const env_1 = require("@things-factory/env");
|
|
23
|
+
const ORMCONFIG = env_1.config.get('ormconfig', {});
|
|
24
|
+
const DATABASE_TYPE = ORMCONFIG.type;
|
|
25
|
+
let ImportSession = class ImportSession {
|
|
26
|
+
get options() {
|
|
27
|
+
try {
|
|
28
|
+
return this.optionsJson ? JSON.parse(this.optionsJson) : null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get result() {
|
|
35
|
+
try {
|
|
36
|
+
return this.resultJson ? JSON.parse(this.resultJson) : null;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
exports.ImportSession = ImportSession;
|
|
44
|
+
tslib_1.__decorate([
|
|
45
|
+
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
|
46
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.ID, { nullable: true }),
|
|
47
|
+
tslib_1.__metadata("design:type", String)
|
|
48
|
+
], ImportSession.prototype, "id", void 0);
|
|
49
|
+
tslib_1.__decorate([
|
|
50
|
+
(0, typeorm_1.ManyToOne)(type => shell_1.Domain),
|
|
51
|
+
(0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true }),
|
|
52
|
+
tslib_1.__metadata("design:type", shell_1.Domain)
|
|
53
|
+
], ImportSession.prototype, "domain", void 0);
|
|
54
|
+
tslib_1.__decorate([
|
|
55
|
+
(0, typeorm_1.RelationId)((session) => session.domain),
|
|
56
|
+
tslib_1.__metadata("design:type", String)
|
|
57
|
+
], ImportSession.prototype, "domainId", void 0);
|
|
58
|
+
tslib_1.__decorate([
|
|
59
|
+
(0, typeorm_1.Column)(),
|
|
60
|
+
(0, type_graphql_1.Field)({ description: 'Attachment id (the source drawing file)' }),
|
|
61
|
+
tslib_1.__metadata("design:type", String)
|
|
62
|
+
], ImportSession.prototype, "attachmentId", void 0);
|
|
63
|
+
tslib_1.__decorate([
|
|
64
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
65
|
+
(0, type_graphql_1.Field)({ nullable: true, description: 'ChatSession id when triggered from chat (board-ai).' }),
|
|
66
|
+
tslib_1.__metadata("design:type", String)
|
|
67
|
+
], ImportSession.prototype, "chatSessionId", void 0);
|
|
68
|
+
tslib_1.__decorate([
|
|
69
|
+
(0, typeorm_1.Column)({ type: 'varchar', length: 32, default: 'queued' }),
|
|
70
|
+
(0, type_graphql_1.Field)({ description: 'queued | parsing | mapping | assembling | binding | completed | failed' }),
|
|
71
|
+
tslib_1.__metadata("design:type", String)
|
|
72
|
+
], ImportSession.prototype, "status", void 0);
|
|
73
|
+
tslib_1.__decorate([
|
|
74
|
+
(0, typeorm_1.Column)({ type: 'float', default: 0 }),
|
|
75
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Float, { description: 'Progress percentage 0..100' }),
|
|
76
|
+
tslib_1.__metadata("design:type", Number)
|
|
77
|
+
], ImportSession.prototype, "progress", void 0);
|
|
78
|
+
tslib_1.__decorate([
|
|
79
|
+
(0, typeorm_1.Column)({ nullable: true, type: 'text' }),
|
|
80
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
81
|
+
tslib_1.__metadata("design:type", String)
|
|
82
|
+
], ImportSession.prototype, "message", void 0);
|
|
83
|
+
tslib_1.__decorate([
|
|
84
|
+
(0, typeorm_1.Column)({
|
|
85
|
+
nullable: true,
|
|
86
|
+
type: DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
|
|
87
|
+
? 'longtext'
|
|
88
|
+
: DATABASE_TYPE == 'oracle'
|
|
89
|
+
? 'clob'
|
|
90
|
+
: DATABASE_TYPE == 'mssql'
|
|
91
|
+
? 'nvarchar'
|
|
92
|
+
: 'text',
|
|
93
|
+
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
|
94
|
+
}),
|
|
95
|
+
tslib_1.__metadata("design:type", String)
|
|
96
|
+
], ImportSession.prototype, "optionsJson", void 0);
|
|
97
|
+
tslib_1.__decorate([
|
|
98
|
+
(0, type_graphql_1.Field)(type => graphql_scalars_1.GraphQLJSON, { nullable: true, description: 'Options used (parsed JSON).' }),
|
|
99
|
+
tslib_1.__metadata("design:type", Object),
|
|
100
|
+
tslib_1.__metadata("design:paramtypes", [])
|
|
101
|
+
], ImportSession.prototype, "options", null);
|
|
102
|
+
tslib_1.__decorate([
|
|
103
|
+
(0, typeorm_1.Column)({
|
|
104
|
+
nullable: true,
|
|
105
|
+
type: DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
|
|
106
|
+
? 'longtext'
|
|
107
|
+
: DATABASE_TYPE == 'oracle'
|
|
108
|
+
? 'clob'
|
|
109
|
+
: DATABASE_TYPE == 'mssql'
|
|
110
|
+
? 'nvarchar'
|
|
111
|
+
: 'text',
|
|
112
|
+
length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
|
|
113
|
+
}),
|
|
114
|
+
tslib_1.__metadata("design:type", String)
|
|
115
|
+
], ImportSession.prototype, "resultJson", void 0);
|
|
116
|
+
tslib_1.__decorate([
|
|
117
|
+
(0, type_graphql_1.Field)(type => graphql_scalars_1.GraphQLJSON, { nullable: true, description: 'Result (parsed JSON): { boardModel, stats, warnings }.' }),
|
|
118
|
+
tslib_1.__metadata("design:type", Object),
|
|
119
|
+
tslib_1.__metadata("design:paramtypes", [])
|
|
120
|
+
], ImportSession.prototype, "result", null);
|
|
121
|
+
tslib_1.__decorate([
|
|
122
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
123
|
+
(0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Total entities counted (when known).' }),
|
|
124
|
+
tslib_1.__metadata("design:type", Number)
|
|
125
|
+
], ImportSession.prototype, "totalEntities", void 0);
|
|
126
|
+
tslib_1.__decorate([
|
|
127
|
+
(0, typeorm_1.ManyToOne)(type => auth_base_1.User),
|
|
128
|
+
(0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true }),
|
|
129
|
+
tslib_1.__metadata("design:type", auth_base_1.User)
|
|
130
|
+
], ImportSession.prototype, "creator", void 0);
|
|
131
|
+
tslib_1.__decorate([
|
|
132
|
+
(0, typeorm_1.CreateDateColumn)(),
|
|
133
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
134
|
+
tslib_1.__metadata("design:type", Date)
|
|
135
|
+
], ImportSession.prototype, "createdAt", void 0);
|
|
136
|
+
tslib_1.__decorate([
|
|
137
|
+
(0, typeorm_1.UpdateDateColumn)(),
|
|
138
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
139
|
+
tslib_1.__metadata("design:type", Date)
|
|
140
|
+
], ImportSession.prototype, "updatedAt", void 0);
|
|
141
|
+
tslib_1.__decorate([
|
|
142
|
+
(0, typeorm_1.Column)({ nullable: true }),
|
|
143
|
+
(0, type_graphql_1.Field)({ nullable: true }),
|
|
144
|
+
tslib_1.__metadata("design:type", Date)
|
|
145
|
+
], ImportSession.prototype, "completedAt", void 0);
|
|
146
|
+
exports.ImportSession = ImportSession = tslib_1.__decorate([
|
|
147
|
+
(0, typeorm_1.Entity)(),
|
|
148
|
+
(0, typeorm_1.Index)('ix_import_session_1', (session) => [session.domain, session.createdAt]),
|
|
149
|
+
(0, typeorm_1.Index)('ix_import_session_2', (session) => [session.chatSessionId]),
|
|
150
|
+
(0, type_graphql_1.ObjectType)({ description: '도면 → 보드 변환 작업의 영속 단위 (비동기 진행상태 추적).' })
|
|
151
|
+
], ImportSession);
|
|
152
|
+
//# sourceMappingURL=import-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-session.js","sourceRoot":"","sources":["../../../server/service/import-session/import-session.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ImportSession — 도면(Attachment) → BoardModel 변환 작업 단위.\n *\n * 비동기 워크플로우:\n * 1) 클라이언트가 importBoardAsync(attachmentId) 호출 → ImportSession 즉시 반환 (status:'queued')\n * 2) 백그라운드 worker 가 attachment 본문 → importBoard() 실행 → status 갱신\n * 3) 클라이언트는 polling 또는 subscription 으로 진행 상황 추적\n * 4) status='completed' 시 result 에 BoardModel + stats + warnings 포함\n *\n * board-ai 의 ChatSession 과 연결 가능 (chatSessionId) — chat 안에서 자동 발동되면 진행 메시지가\n * 자동으로 ChatMessage(role:'system') 으로 기록되어 AI 컨텍스트에 흐름.\n */\nimport {\n Column,\n CreateDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, Float, ID, Int, ObjectType } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\n\nimport { Domain } from '@things-factory/shell'\nimport { User } from '@things-factory/auth-base'\nimport { config } from '@things-factory/env'\n\nconst ORMCONFIG = config.get('ormconfig', {})\nconst DATABASE_TYPE = ORMCONFIG.type\n\n@Entity()\n@Index('ix_import_session_1', (session: ImportSession) => [session.domain, session.createdAt])\n@Index('ix_import_session_2', (session: ImportSession) => [session.chatSessionId])\n@ObjectType({ description: '도면 → 보드 변환 작업의 영속 단위 (비동기 진행상태 추적).' })\nexport class ImportSession {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { nullable: true })\n readonly id?: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true })\n domain?: Domain\n\n @RelationId((session: ImportSession) => session.domain)\n domainId?: string\n\n /** Attachment.id (도면 파일). attachment-base 의존이지만 직접 ManyToOne 대신 ID 만 보유. */\n @Column()\n @Field({ description: 'Attachment id (the source drawing file)' })\n attachmentId!: string\n\n /** 어느 ChatSession 에서 발동됐는지 (board-ai 와 연동). 없으면 직접 호출. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'ChatSession id when triggered from chat (board-ai).' })\n chatSessionId?: string\n\n /** 'queued' | 'parsing' | 'mapping' | 'assembling' | 'binding' | 'completed' | 'failed' */\n @Column({ type: 'varchar', length: 32, default: 'queued' })\n @Field({ description: 'queued | parsing | mapping | assembling | binding | completed | failed' })\n status!: string\n\n /** 0..100 */\n @Column({ type: 'float', default: 0 })\n @Field(type => Float, { description: 'Progress percentage 0..100' })\n progress!: number\n\n /** 현재 단계 설명 또는 에러 메시지 */\n @Column({ nullable: true, type: 'text' })\n @Field({ nullable: true })\n message?: string\n\n /** import 옵션 (scopes, parseOptions, normalizeOrigin 등) */\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'text',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n optionsJson?: string\n\n @Field(type => GraphQLJSON, { nullable: true, description: 'Options used (parsed JSON).' })\n get options(): any {\n try {\n return this.optionsJson ? JSON.parse(this.optionsJson) : null\n } catch {\n return null\n }\n }\n\n /** 결과: { boardModel, mapping: { stats, warnings } } */\n @Column({\n nullable: true,\n type:\n DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'\n ? 'longtext'\n : DATABASE_TYPE == 'oracle'\n ? 'clob'\n : DATABASE_TYPE == 'mssql'\n ? 'nvarchar'\n : 'text',\n length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined\n })\n resultJson?: string\n\n @Field(type => GraphQLJSON, { nullable: true, description: 'Result (parsed JSON): { boardModel, stats, warnings }.' })\n get result(): any {\n try {\n return this.resultJson ? JSON.parse(this.resultJson) : null\n } catch {\n return null\n }\n }\n\n @Column({ nullable: true })\n @Field(type => Int, { nullable: true, description: 'Total entities counted (when known).' })\n totalEntities?: number\n\n @ManyToOne(type => User)\n @Field(type => User, { nullable: true })\n creator?: User\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true })\n completedAt?: Date\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runImportJob(sessionId: string): Promise<void>;
|