@things-factory/figure-service 10.1.15 → 10.1.16

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/figure-service",
3
- "version": "10.1.15",
3
+ "version": "10.1.16",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -26,12 +26,12 @@
26
26
  "@hatiolab/figure-model": "^0.1.17",
27
27
  "@operato/typeorm-history": "^10.0.0",
28
28
  "@things-factory/ai-client-base": "^10.1.8",
29
- "@things-factory/auth-base": "^10.1.7",
29
+ "@things-factory/auth-base": "^10.1.16",
30
30
  "@things-factory/env": "^10.1.3",
31
- "@things-factory/shell": "^10.1.7",
31
+ "@things-factory/shell": "^10.1.16",
32
32
  "graphql-scalars": "^1.22.4",
33
33
  "graphql-upload": "^15.0.2",
34
34
  "koa-router": "^7.4.0"
35
35
  },
36
- "gitHead": "0186eb7ec008781fd9f80b6f59379a91e0908820"
36
+ "gitHead": "aff86bc162b42c1d933711f7bc85a073f1ddcd29"
37
37
  }
@@ -175,4 +175,33 @@ describe('figureBlueprint 질의 — 소비처(board, twin, things-scene)를 위
175
175
  expect(result.groups[0].members[0].name).toBe('box-1')
176
176
  expect(result.groups[0].members[0].transform.size.x).toBe(100)
177
177
  })
178
+
179
+ /*
180
+ Every test above calls the resolver method directly, so none of them passes through the
181
+ `@privilege` directive — that is attached when the schema is assembled. They stay green
182
+ whether the declaration is there or not.
183
+
184
+ Without a declaration this field is open to callers who have not even signed in
185
+ (`auth-rebuild.md` §1), so that gap is the thing worth guarding. The check reads the source
186
+ rather than the compiled output because the declaration is authored there, and it pins the
187
+ category: `figure` is the authoring permission, and requiring it here would leave board and
188
+ twin users with an empty palette. Someone who hits that wall will be tempted to delete this
189
+ line; the failure message is for them.
190
+ */
191
+ it('declares figure-published on the consumer door, not the authoring permission', () => {
192
+ const { readFileSync } = require('fs')
193
+ const { join } = require('path')
194
+ const source = readFileSync(join(__dirname, 'figure-query.ts'), 'utf-8')
195
+
196
+ const at = source.indexOf('async figureBlueprint')
197
+ expect(at).toBeGreaterThan(-1)
198
+
199
+ const preceding = source.slice(0, at)
200
+ const directive = preceding.lastIndexOf("@Directive('@privilege(")
201
+ const declaration = preceding.slice(directive, preceding.indexOf('\n', directive))
202
+
203
+ expect(declaration).toContain('category: "figure-published"')
204
+ expect(declaration).toContain('privilege: "query"')
205
+ expect(declaration).toContain('domainOwnerGranted: true')
206
+ })
178
207
  })
@@ -140,9 +140,25 @@ export class FigureQuery {
140
140
  * 4. 한 번도 발행된 적 없는 초안 자산이거나 존재하지 않는 타입인 경우 null을 반환하여
141
141
  * 미완성 자산이 씬 화면에 유출되는 것을 엄격히 차단한다.
142
142
  * 5. 도메인 상속 시 자식 도메인의 타입이 부모 도메인의 동일 타입을 항상 오버라이드한다.
143
- * 6. 소비처(board, twin, things-scene) 사용자에게 저작 권한(figure:query)이 없어도
144
- * 발행된 청사진을 열람할 수 있도록 도메인 인증 상태에서 열람을 허용한다.
143
+ * 6. 권한은 저작(`figure`)과 **다른 category** 인 `figure-published` 다. 소비처 사용자에게
144
+ * 저작 권한을 요구하면 board·twin 의 팔레트가 안 뜨고, 그렇다고 선언을 비우면 이 집에서는
145
+ * 로그인조차 안 한 사람에게 열린다(`auth-rebuild.md` §1). `<module>-published` 는
146
+ * 「그 모듈이 발행한 것을 남이 읽는 문」이라는 규약이다 — 아키텍트 판정(2026-09-15).
147
+ *
148
+ * ## 열거는 막혀 있다 — 지금은 우연이다
149
+ *
150
+ * 이 문의 열쇠는 `type` 하나뿐이라 이름을 모르면 못 꺼낸다. 그런데 그것이 **설계가 아니다** —
151
+ * 목록을 내는 `figures`·`figureTypeNames` 가 `figure` category 로 잠겨 있어서 결과적으로
152
+ * 그런 것뿐이다. 나중에 누가 소비처용 목록 문을 하나 더 열고 `figure-published` 를 붙이면
153
+ * 열거가 함께 열리고, 「이름을 알아야 한다」에 기대고 있던 가정이 조용히 사라진다.
154
+ * 그 문을 낼 사람이 이 단락을 읽고 그때 정하면 된다.
145
155
  */
156
+ /*
157
+ This directive is attached by schema assembly, so a test that calls the resolver method
158
+ directly never exercises it. `figure-blueprint-query.test.ts` reads this source for the
159
+ declaration instead — see the test named for it there.
160
+ */
161
+ @Directive('@privilege(category: "figure-published", privilege: "query", domainOwnerGranted: true)')
146
162
  @Query(returns => GraphQLJSON, {
147
163
  nullable: true,
148
164
  description: 'Compiled FigureBlueprint for external consumers (board, twin, things-scene). Only returns released figures.'