@things-factory/board-import 10.1.28 → 10.1.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/board-import",
3
- "version": "10.1.28",
3
+ "version": "10.1.31",
4
4
  "main": "dist-server/index.js",
5
5
  "things-factory": true,
6
6
  "author": "heartyoh",
@@ -23,13 +23,13 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@operato/graphql": "^10.0.0",
26
- "@things-factory/ai-client-base": "^10.1.28",
27
- "@things-factory/attachment-base": "^10.1.28",
28
- "@things-factory/auth-base": "^10.1.28",
29
- "@things-factory/board-service": "^10.1.28",
26
+ "@things-factory/ai-client-base": "^10.1.31",
27
+ "@things-factory/attachment-base": "^10.1.31",
28
+ "@things-factory/auth-base": "^10.1.31",
29
+ "@things-factory/board-service": "^10.1.31",
30
30
  "@things-factory/env": "^10.1.20",
31
- "@things-factory/shell": "^10.1.28",
31
+ "@things-factory/shell": "^10.1.31",
32
32
  "dxf-parser": "^1.1.2"
33
33
  },
34
- "gitHead": "3a260a0f79bc6b41a692a103955bfcc541aff60e"
34
+ "gitHead": "5213e6f9913b40ea2618a2111d9884b792dbaa01"
35
35
  }
@@ -42,6 +42,12 @@ const ARGS: Record<keyof typeof IMPORT_TOOL_PRIVILEGE, any> = {
42
42
  getImportSession: { sessionId: 's1' },
43
43
  materializeImportSession: { sessionId: 's1', name: 'New' }
44
44
  }
45
+ /** 도구가 지나는 문 — 등록기의 `doors`, 그리고 아래 「표가 문과 같은가」가 읽는 필드 이름. */
46
+ const DOOR: Record<keyof typeof IMPORT_TOOL_PRIVILEGE, string> = {
47
+ importBoardAsync: 'Mutation.importBoardAsync',
48
+ getImportSession: 'Query.importSession',
49
+ materializeImportSession: 'Mutation.materializeImportSession'
50
+ }
45
51
  const EFFECT: Record<keyof typeof IMPORT_TOOL_PRIVILEGE, jest.Mock> = {
46
52
  importBoardAsync: mockStartImportJobAsync,
47
53
  getImportSession: mockFetchImportSession,
@@ -73,22 +79,24 @@ describe.each(Object.keys(IMPORT_TOOL_PRIVILEGE) as (keyof typeof IMPORT_TOOL_PR
73
79
  expect(EFFECT[tool]).toHaveBeenCalled()
74
80
  })
75
81
 
76
- test('declares the same privilege on the spec, for doors that decide what to offer', () => {
82
+ /*
83
+ * 무엇을 **내보낼지**는 2026-09-18 부터 문이 정한다(ADR-0071 결정 2). 명세가 권한을 베껴 적는 대신
84
+ * 닿는 문을 적고, 관문이 그 필드의 지시자를 AUTH 의 표에서 읽는다. 여기 적힌 문 이름이 틀리면 그
85
+ * 도구는 어느 대화에도 안 나간다 — 조용히.
86
+ */
87
+ test('names the door it reaches, so the gate can ask that door what it requires', () => {
77
88
  const spec: any = findToolSpec(tool)
78
- expect(spec.privilege).toBe(IMPORT_TOOL_PRIVILEGE[tool])
89
+ expect(spec.doors).toEqual([DOOR[tool]])
90
+ expect(spec.privilege).toBeUndefined()
79
91
  })
80
92
  })
81
93
 
82
94
  test('the table says what the GraphQL doors say', () => {
83
95
  const resolver = readFileSync(join(__dirname, 'import-session', 'import-session-resolver.ts'), 'utf8')
84
- const DOOR: Record<keyof typeof IMPORT_TOOL_PRIVILEGE, string> = {
85
- importBoardAsync: 'importBoardAsync',
86
- getImportSession: 'importSession',
87
- materializeImportSession: 'materializeImportSession'
88
- }
89
96
  for (const [tool, door] of Object.entries(DOOR) as [keyof typeof IMPORT_TOOL_PRIVILEGE, string][]) {
97
+ const field = door.split('.')[1]
90
98
  const m = resolver.match(
91
- new RegExp(`@Directive\\('@privilege\\(category: "([^"]+)", privilege: "([^"]+)"\\)'\\)\\s*async ${door}\\(`)
99
+ new RegExp(`@Directive\\('@privilege\\(category: "([^"]+)", privilege: "([^"]+)"\\)'\\)\\s*async ${field}\\(`)
92
100
  )
93
101
  expect(m).not.toBeNull()
94
102
  expect({ category: m![1], privilege: m![2] }).toEqual(IMPORT_TOOL_PRIVILEGE[tool])
@@ -84,9 +84,13 @@ function requireDomainContext(state: any): { domain: any; user: any } {
84
84
  * `ai-assistant:mutation` — could create a Board through it.
85
85
  *
86
86
  * The check lives in the builder rather than in whichever chat door dispatches the tool: there is
87
- * more than one such door, and the effect has to be guarded wherever it is reached from. The same
88
- * object is also declared on the spec as `privilege`, which is what a door reads to decide whether
89
- * to offer the tool at all; declaring and enforcing from one constant keeps the two from drifting.
87
+ * more than one such door, and the effect has to be guarded wherever it is reached from.
88
+ *
89
+ * Whether the tool is **offered** at all is a separate question, and since 2026-09-18 the spec does
90
+ * not answer it with a copy of this object (ADR-0071 decision 2). The spec names the GraphQL doors
91
+ * it reaches and the gate resolves each one against that field's own `@privilege` — the same
92
+ * declarations this constant mirrors (`import-session-resolver.ts`). This constant stays because it
93
+ * guards the **effect**, which is reached from more places than the offering.
90
94
  */
91
95
  export const IMPORT_TOOL_PRIVILEGE = {
92
96
  importBoardAsync: { category: 'board-import', privilege: 'mutation' },
@@ -134,6 +138,8 @@ const importToolsCategory: ToolCategory = {
134
138
  {
135
139
  kind: 'read',
136
140
  name: 'listImportAdapters',
141
+ /* 등록된 어댑터를 그 자리에서 셀 뿐이다 — 저장소에 닿지 않으므로 지나는 문이 없다. */
142
+ doors: [],
137
143
  description:
138
144
  '등록된 도면 import 어댑터 (DXF / Image / 향후 IFC, glTF 등) 의 능력 조회. ' +
139
145
  '사용자가 어떤 형식 import 가 가능한지 묻거나, LLM 이 적절한 어댑터 선택을 inspect 할 때 사용.',
@@ -149,7 +155,7 @@ const importToolsCategory: ToolCategory = {
149
155
  {
150
156
  kind: 'external',
151
157
  name: 'importBoardAsync',
152
- privilege: IMPORT_TOOL_PRIVILEGE.importBoardAsync,
158
+ doors: ['Mutation.importBoardAsync'],
153
159
  description:
154
160
  'Attachment(도면/조감도/사진) 을 비동기로 보드 모델로 변환 시작. 즉시 ImportSession 을 ' +
155
161
  '반환하고 백그라운드에서 진행. status 가 completed 가 될 때까지 getImportSession 으로 ' +
@@ -201,7 +207,7 @@ const importToolsCategory: ToolCategory = {
201
207
  {
202
208
  kind: 'read',
203
209
  name: 'getImportSession',
204
- privilege: IMPORT_TOOL_PRIVILEGE.getImportSession,
210
+ doors: ['Query.importSession'],
205
211
  description:
206
212
  'ImportSession 의 진행/결과 조회. status 가 queued/parsing/mapping/assembling/binding 인 ' +
207
213
  '동안은 progress 와 message 를 반환, completed 면 result.boardModel 까지 포함. failed 면 ' +
@@ -237,7 +243,7 @@ const importToolsCategory: ToolCategory = {
237
243
  {
238
244
  kind: 'external',
239
245
  name: 'materializeImportSession',
240
- privilege: IMPORT_TOOL_PRIVILEGE.materializeImportSession,
246
+ doors: ['Mutation.materializeImportSession'],
241
247
  description:
242
248
  '완료된 ImportSession 의 결과 boardModel 을 새 Board entity 로 영속화. status 가 ' +
243
249
  'completed 인 세션만 처리. 결과 Board 는 state="draft" 로 생성되므로 사용자가 검수 후 ' +