@things-factory/figure-service 10.1.109 → 10.1.110

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.
@@ -113,7 +113,7 @@ tslib_1.__decorate([
113
113
  (0, typeorm_1.Column)({ nullable: true, ...(0, shell_1.longTextColumn)() }),
114
114
  (0, type_graphql_1.Field)({
115
115
  nullable: true,
116
- description: 'What changed against the previous release, as JSON: [{ code, blocking, subject?, values, detail }]. [] for the first release; null when the previous release could not be read.'
116
+ description: 'What changed against the previous release, as JSON: [{ code, blocking, subject?, values, detail }]. [] for the first release. null when it was not compared: the previous release could not be read, or this release was made before releases recorded it (figure-service 10.1.109).'
117
117
  }),
118
118
  tslib_1.__metadata("design:type", String)
119
119
  ], FigureHistory.prototype, "changes", void 0);
@@ -1 +1 @@
1
- {"version":3,"file":"figure-history.js","sourceRoot":"","sources":["../../../server/service/figure/figure-history.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, ObjectType, Float, Int } from 'type-graphql'\nimport { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\n\nimport {\n HistoryActionColumn,\n HistoryActionType,\n HistoryEntityInterface,\n HistoryOriginalIdColumn\n} from '@operato/typeorm-history'\nimport { User } from '@things-factory/auth-base'\nimport { Domain, longTextColumn } from '@things-factory/shell'\n\nimport { Figure } from './figure.js'\n\n/**\n * 발행된 판 하나 — **발행할 때만 남는다.**\n *\n * 저작 중 저장은 여기 오지 않는다. 스냅샷을 뜨는 subscriber 가 `state === 'released'` 인 저장에만\n * 움직인다(`event-subscriber.ts`) — board 가 같은 규칙으로 돈다.\n *\n * ## 왜 통째로 베끼나\n *\n * 판을 되세우려면 그때의 정본만으로는 모자란다. 이름·분류·그림·비용까지 함께 있어야 「그 판이\n * 무엇이었나」를 보여 주고 되돌릴 수 있다. 파생값(점수·삼각형·묶음)도 같이 둔다 — 다시 계산하면\n * 형식이 바뀐 오늘의 값이 나오고, 그것은 그 판의 값이 아니다.\n */\n@Entity()\n@Index('ix_figure_history_0', (history: FigureHistory) => [history.originalId, history.version], { unique: true })\n@ObjectType({ description: 'A released version of a figure — one row per release.' })\nexport class FigureHistory implements HistoryEntityInterface<Figure> {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for this history record.' })\n readonly id: string\n\n @Column({ nullable: true, default: 1 })\n @Field(type => Int, { nullable: true, description: 'The published version number this record holds.' })\n version?: number = 1\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the figure belonged to.' })\n domain?: Domain\n\n @RelationId((history: FigureHistory) => history.domain)\n domainId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The stored identifier of the figure. It never changes.' })\n type?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The display name at this version.' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The description at this version.' })\n description?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The classification code at this version.' })\n category?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The lifecycle state recorded with this version.' })\n state?: 'draft' | 'released'\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The figure asset JSON (figure-asset-1) at this version.' })\n source?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The master-data field definition at this version.' })\n properties?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The thumbnail as it looked at this version.' })\n thumbnail?: string\n\n /*\n * 그때의 비용이다. 오늘 다시 계산하면 오늘의 형식으로 나온다 — 그 판을 설명하지 못한다.\n */\n @Column({ type: 'float', nullable: true })\n @Field(type => Float, { nullable: true, description: 'Reuse score as computed at this version.' })\n score?: number\n\n @Column({ nullable: true })\n @Field(type => Int, { nullable: true, description: 'Triangle count at this version.' })\n triangles?: number\n\n @Column({ nullable: true })\n @Field(type => Int, { nullable: true, description: 'Material group count at this version.' })\n groups?: number\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The note left when this version was released.' })\n comment?: string\n\n /*\n * 이 판의 정본 형식(`figure-asset-1`)과, 직전 판에 견준 변경(ADR-0104 결정 5). 발행할 때 한 번 정하고 다시\n * 계산하지 않는다 — 판을 고정해 쓰는 쪽은 다음 판으로 옮기기 전에 이것을 읽는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The asset format this version was written in (figure-asset-1).' })\n assetFormat?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({\n nullable: true,\n description:\n 'What changed against the previous release, as JSON: [{ code, blocking, subject?, values, detail }]. [] for the first release; null when the previous release could not be read.'\n })\n changes?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the original figure was created.' })\n createdAt?: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the figure was saved at this version.' })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who created the original figure.' })\n creator?: User\n\n @RelationId((history: FigureHistory) => history.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who released this version.' })\n updater?: User\n\n @RelationId((history: FigureHistory) => history.updater)\n updaterId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the original figure was deleted, if it was.' })\n deletedAt?: Date\n\n @HistoryOriginalIdColumn()\n @Field({ description: 'The id of the figure this version belongs to.' })\n public originalId!: string\n\n @HistoryActionColumn({ nullable: false, type: 'varchar', length: 10 })\n @Field(type => String, { description: 'What produced this record (created · updated · deleted).' })\n public action!: HistoryActionType\n}\n"]}
1
+ {"version":3,"file":"figure-history.js","sourceRoot":"","sources":["../../../server/service/figure/figure-history.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, ID, ObjectType, Float, Int } from 'type-graphql'\nimport { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\n\nimport {\n HistoryActionColumn,\n HistoryActionType,\n HistoryEntityInterface,\n HistoryOriginalIdColumn\n} from '@operato/typeorm-history'\nimport { User } from '@things-factory/auth-base'\nimport { Domain, longTextColumn } from '@things-factory/shell'\n\nimport { Figure } from './figure.js'\n\n/**\n * 발행된 판 하나 — **발행할 때만 남는다.**\n *\n * 저작 중 저장은 여기 오지 않는다. 스냅샷을 뜨는 subscriber 가 `state === 'released'` 인 저장에만\n * 움직인다(`event-subscriber.ts`) — board 가 같은 규칙으로 돈다.\n *\n * ## 왜 통째로 베끼나\n *\n * 판을 되세우려면 그때의 정본만으로는 모자란다. 이름·분류·그림·비용까지 함께 있어야 「그 판이\n * 무엇이었나」를 보여 주고 되돌릴 수 있다. 파생값(점수·삼각형·묶음)도 같이 둔다 — 다시 계산하면\n * 형식이 바뀐 오늘의 값이 나오고, 그것은 그 판의 값이 아니다.\n */\n@Entity()\n@Index('ix_figure_history_0', (history: FigureHistory) => [history.originalId, history.version], { unique: true })\n@ObjectType({ description: 'A released version of a figure — one row per release.' })\nexport class FigureHistory implements HistoryEntityInterface<Figure> {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier for this history record.' })\n readonly id: string\n\n @Column({ nullable: true, default: 1 })\n @Field(type => Int, { nullable: true, description: 'The published version number this record holds.' })\n version?: number = 1\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the figure belonged to.' })\n domain?: Domain\n\n @RelationId((history: FigureHistory) => history.domain)\n domainId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The stored identifier of the figure. It never changes.' })\n type?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The display name at this version.' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The description at this version.' })\n description?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The classification code at this version.' })\n category?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The lifecycle state recorded with this version.' })\n state?: 'draft' | 'released'\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The figure asset JSON (figure-asset-1) at this version.' })\n source?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The master-data field definition at this version.' })\n properties?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The thumbnail as it looked at this version.' })\n thumbnail?: string\n\n /*\n * 그때의 비용이다. 오늘 다시 계산하면 오늘의 형식으로 나온다 — 그 판을 설명하지 못한다.\n */\n @Column({ type: 'float', nullable: true })\n @Field(type => Float, { nullable: true, description: 'Reuse score as computed at this version.' })\n score?: number\n\n @Column({ nullable: true })\n @Field(type => Int, { nullable: true, description: 'Triangle count at this version.' })\n triangles?: number\n\n @Column({ nullable: true })\n @Field(type => Int, { nullable: true, description: 'Material group count at this version.' })\n groups?: number\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({ nullable: true, description: 'The note left when this version was released.' })\n comment?: string\n\n /*\n * 이 판의 정본 형식(`figure-asset-1`)과, 직전 판에 견준 변경(ADR-0104 결정 5). 발행할 때 한 번 정하고 다시\n * 계산하지 않는다 — 판을 고정해 쓰는 쪽은 다음 판으로 옮기기 전에 이것을 읽는다.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'The asset format this version was written in (figure-asset-1).' })\n assetFormat?: string\n\n @Column({ nullable: true, ...longTextColumn() })\n @Field({\n nullable: true,\n description:\n 'What changed against the previous release, as JSON: [{ code, blocking, subject?, values, detail }]. [] for the first release. null when it was not compared: the previous release could not be read, or this release was made before releases recorded it (figure-service 10.1.109).'\n })\n changes?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the original figure was created.' })\n createdAt?: Date\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the figure was saved at this version.' })\n updatedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who created the original figure.' })\n creator?: User\n\n @RelationId((history: FigureHistory) => history.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'Who released this version.' })\n updater?: User\n\n @RelationId((history: FigureHistory) => history.updater)\n updaterId?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the original figure was deleted, if it was.' })\n deletedAt?: Date\n\n @HistoryOriginalIdColumn()\n @Field({ description: 'The id of the figure this version belongs to.' })\n public originalId!: string\n\n @HistoryActionColumn({ nullable: false, type: 'varchar', length: 10 })\n @Field(type => String, { description: 'What produced this record (created · updated · deleted).' })\n public action!: HistoryActionType\n}\n"]}
@@ -77,5 +77,5 @@ export declare class FigureQuery {
77
77
  * 열거가 함께 열리고, 「이름을 알아야 한다」에 기대고 있던 가정이 조용히 사라진다.
78
78
  * 그 문을 낼 사람이 이 단락을 읽고 그때 정하면 된다.
79
79
  */
80
- figureBlueprint(type: string, context: ResolverContext): Promise<any>;
80
+ figureBlueprint(type: string, context: ResolverContext, pinned?: number): Promise<any>;
81
81
  }
@@ -185,7 +185,7 @@ let FigureQuery = class FigureQuery {
185
185
  directly never exercises it. `figure-blueprint-query.test.ts` reads this source for the
186
186
  declaration instead — see the test named for it there.
187
187
  */
188
- async figureBlueprint(type, context) {
188
+ async figureBlueprint(type, context, pinned) {
189
189
  const { domain } = context.state;
190
190
  /*
191
191
  A child domain shadows the same type in its parent, which is how a tenant replaces the standard
@@ -194,14 +194,23 @@ let FigureQuery = class FigureQuery {
194
194
  architect ruling, 2026-09-17). This used to skip the fallback as soon as a child row existed and
195
195
  return null.
196
196
  */
197
- const found = (await releasedSourceOf(domain.id, type)) ?? (domain.parentId ? await releasedSourceOf(domain.parentId, type) : null);
198
- if (!found) {
197
+ const latest = (await releasedSourceOf(domain.id, type)) ?? (domain.parentId ? await releasedSourceOf(domain.parentId, type) : null);
198
+ if (!latest) {
199
199
  return null;
200
200
  }
201
+ /*
202
+ A pinned release is read from the same figure the latest comes from -- the shadowing rule decides whose
203
+ releases these are, then the number picks one. Releases are never deleted, so a number once handed out is
204
+ always there; a number that never was is refused by name rather than answered with another release.
205
+ */
206
+ const found = pinned === undefined || pinned === null || pinned === latest.version ? latest : await releaseNumbered(latest.figure, pinned);
207
+ if (!found) {
208
+ throw new shell_1.Refusal('figure-release-not-found', { type, version: pinned, latest: latest.version }, '{type} has no release {version}. its latest release is {latest}.');
209
+ }
201
210
  const { figure, source: sourceToCompile, version } = found;
202
211
  try {
203
212
  const text = typeof sourceToCompile === 'string' ? sourceToCompile : JSON.stringify(sourceToCompile);
204
- return { type, asset: (0, figure_format_js_1.consumablePayloadOf)(text).asset };
213
+ return { type, version, asset: (0, figure_format_js_1.consumablePayloadOf)(text).asset };
205
214
  }
206
215
  catch (err) {
207
216
  console.error(`[figureBlueprint] 타입 '${type}' (figure id: ${figure.id}) 자산 컴파일 실패:`, err);
@@ -306,18 +315,26 @@ tslib_1.__decorate([
306
315
  (0, type_graphql_1.Directive)('@privilege(category: "figure-published", privilege: "query", domainOwnerGranted: true)'),
307
316
  (0, type_graphql_1.Query)(returns => graphql_scalars_1.GraphQLJSON, {
308
317
  nullable: true,
309
- description: 'A released figure for its consumers (board, twin, things-scene): { type, asset } with the figure asset. Only returns released figures.'
318
+ description: 'A released figure for its consumers (board, twin, things-scene): { type, version, asset }. The latest release, or the one numbered `version` for a consumer that pins releases (ADR-0104). Only returns released figures.'
310
319
  }),
311
320
  tslib_1.__param(0, (0, type_graphql_1.Arg)('type')),
312
321
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
322
+ tslib_1.__param(2, (0, type_graphql_1.Arg)('version', type => type_graphql_1.Int, { nullable: true, description: 'A release number. Absent: the latest release.' })),
313
323
  tslib_1.__metadata("design:type", Function),
314
- tslib_1.__metadata("design:paramtypes", [String, Object]),
324
+ tslib_1.__metadata("design:paramtypes", [String, Object, Number]),
315
325
  tslib_1.__metadata("design:returntype", Promise)
316
326
  ], FigureQuery.prototype, "figureBlueprint", null);
317
327
  exports.FigureQuery = FigureQuery = tslib_1.__decorate([
318
328
  (0, type_graphql_1.Resolver)(figure_js_1.Figure),
319
329
  (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
320
330
  ], FigureQuery);
331
+ /** One numbered release of a figure, or null when it has none by that number. */
332
+ async function releaseNumbered(figure, version) {
333
+ const row = await (0, shell_1.getRepository)(figure_history_js_1.FigureHistory).findOne({
334
+ where: { originalId: figure.id, state: 'released', version }
335
+ });
336
+ return row?.source ? { figure, source: row.source, version: row.version } : null;
337
+ }
321
338
  /**
322
339
  * Finds the released source of a type in one domain, or null.
323
340
  *
@@ -1 +1 @@
1
- {"version":3,"file":"figure-query.js","sourceRoot":"","sources":["../../../server/service/figure/figure-query.ts"],"names":[],"mappings":";;;;AAAA,qCAA4B;AAC5B,+CAAwF;AACxF,qDAA6C;AAE7C,iDAO8B;AAE9B,2CAAoC;AACpC,2DAAmD;AACnD,yDAAuE;AACvE,qDAAsF;AACtF,yEAAgE;AAChE,yDAAqD;AACrD,6DAAwD;AAIjD,IAAM,WAAW,GAAjB,MAAM,WAAW;IAGhB,AAAN,KAAK,CAAC,MAAM,CAAY,EAAU,EAAS,OAAwB;QACjE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,6CAA6C;QAC7C,OAAO,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;YACzC,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE;SAChF,CAAC,CAAA;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,OAAO,CAAS,MAAiB,EAAS,OAAwB;QACtE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,YAAY,GAAG,IAAA,qCAA6B,EAAC;YACjD,UAAU,EAAE,IAAA,qBAAa,EAAC,kBAAM,CAAC;YACjC,MAAM;YACN,MAAM;YACN,KAAK,EAAE,QAAQ;YACf;;;;;;cAME;YACF,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,CAAC;SACjE,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,YAAY,CAAC,eAAe,EAAE,CAAA;QAE3D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IACzB,CAAC;IAED;;;;;;;OAOG;IACH;;;;;OAKG;IAGG,AAAN,KAAK,CAAC,cAAc,CAAY,EAAU,EAAS,OAAwB;QACzE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,OAAO,MAAM,IAAA,qBAAa,EAAC,iCAAa,CAAC,CAAC,IAAI,CAAC;YAC7C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;YAC3F,SAAS,EAAE,CAAC,SAAS,CAAC;YACtB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;YAC1B,IAAI,EAAE,EAAE;SACT,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACH,4FAA4F;IAKtF,AAAN,KAAK,CAAC,qBAAqB,CAAqB,WAAmB,EAAS,OAAwB;QAClG,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,OAAO,MAAM,IAAA,sCAAiB,EAAC,IAAA,qBAAa,EAAC,kBAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAA;IACxF,CAAC;IAED,8GAA8G;IAGxG,AAAN,KAAK,CAAC,oBAAoB,CAAY,EAAU,EAAS,OAAwB;QAC/E,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QACpC,OAAO,MAAM,IAAA,mCAAgB,EAAC,IAAA,qBAAa,EAAC,8CAAmB,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAA;IAC3F,CAAC;IAIK,AAAN,KAAK,CAAC,cAAc,CAAY,EAAU,EAAS,OAAwB;QACzE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,OAAO,MAAM,IAAA,qBAAa,EAAC,iCAAa,CAAC,CAAC,OAAO,CAAC;YAChD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;YAC3F,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;SAC3B,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;OAMG;IAGG,AAAN,KAAK,CAAC,aAAa,CAAY,EAAU,EAAS,OAAwB;QACxE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;YACjD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE;YAC/E;;;;cAIE;YACF,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;SAC/C,CAAC,CAAA;QAEF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QAC3C,CAAC;QAED;;;UAGE;QACF,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,iCAAa,CAAC;aAChD,kBAAkB,CAAC,SAAS,CAAC;aAC7B,KAAK,CAAC,0BAA0B,EAAE,EAAE,EAAE,EAAE,CAAC;aACzC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC;aAClC,KAAK,CAAC,CAAC,CAAC;aACR,MAAM,EAAE,CAAA;QAEX,OAAO,IAAA,gCAAa,EAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IAC/E,CAAC;IAED;;;;;;;OAOG;IAGG,AAAN,KAAK,CAAC,kBAAkB,CACP,MAAc,EACtB,OAAwB,EACA,EAAW;QAE1C,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,IAAI,CAAC,EAAE;YAAE,OAAO,IAAA,gCAAa,EAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAEtD,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;YACjD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE;YAC/E,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;SACjC,CAAC,CAAA;QACF,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;QAEtD,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,iCAAa,CAAC;aAChD,kBAAkB,CAAC,SAAS,CAAC;aAC7B,KAAK,CAAC,0BAA0B,EAAE,EAAE,EAAE,EAAE,CAAC;aACzC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC;aAClC,KAAK,CAAC,CAAC,CAAC;aACR,MAAM,EAAE,CAAA;QAEX,OAAO,IAAA,gCAAa,EAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;IACxE,CAAC;IAIK,AAAN,KAAK,CAAC,eAAe,CAAQ,OAAwB;QACnD,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,IAAI,GAAG,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,IAAI,CAAC;YAC5C,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE;YAC3E,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;SACvB,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC9C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH;;;;MAIE;IAMI,AAAN,KAAK,CAAC,eAAe,CACN,IAAY,EAClB,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC;;;;;;UAME;QACF,MAAM,KAAK,GACT,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,gBAAgB,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAEvH,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,KAAK,CAAA;QAE1D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,eAAe,KAAK,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;YACpG,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAA,sCAAmB,EAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;QACzD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,yBAAyB,IAAI,iBAAiB,MAAM,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,CAAA;YAEzF;;;;;;;;cAQE;YACF,MAAM,IAAI,eAAO,CACf,iCAAiC,EACjC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,EACtC,+FAA+F,CAChG,CAAA;QACH,CAAC;IACH,CAAC;CACF,CAAA;AA3QY,kCAAW;AAGhB;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,kBAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;IAChF,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yCAOzC;AAIK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,2BAAU,EAAE,EAAE,WAAW,EAAE,+DAA+D,EAAE,CAAC;IAChG,mBAAA,IAAA,mBAAI,GAAE,CAAA;IAAqB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;6CAAjB,iBAAS;;0CAqBtC;AAkBK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,iCAAa,CAAC,EAAE,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC;IAC7E,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDASjD;AAaK;IAJL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,sCAAqB,CAAC,EAAE;QACzC,WAAW,EAAE,yGAAyG;KACvH,CAAC;IAC2B,mBAAA,IAAA,kBAAG,EAAC,aAAa,CAAC,CAAA;IAAuB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wDAG1E;AAKK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,8CAAmB,CAAC,EAAE,EAAE,WAAW,EAAE,iFAAiF,EAAE,CAAC;IAChH,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;uDAGvD;AAIK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC;IAC9F,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAOjD;AAWK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAgB,EAAE,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;IACtF,mBAAA,IAAA,kBAAG,EAAC,IAAI,CAAC,CAAA;IAAc,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;gDA6BhD;AAYK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAgB,EAAE,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC;IAE1H,mBAAA,IAAA,kBAAG,EAAC,QAAQ,CAAC,CAAA;IACb,mBAAA,IAAA,kBAAG,GAAE,CAAA;IACL,mBAAA,IAAA,kBAAG,EAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;;;;qDAmB/B;AAIK;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,+EAA+E,EAAE,CAAC;IACtG,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kDAS3B;AAuCK;IALL,IAAA,wBAAS,EAAC,wFAAwF,CAAC;IACnG,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,6BAAW,EAAE;QAC7B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,wIAAwI;KACtJ,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,MAAM,CAAC,CAAA;IACX,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;kDAyCP;sBA1QU,WAAW;IAFvB,IAAA,uBAAQ,EAAC,kBAAM,CAAC;IAChB,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,WAAW,CA2QvB;AAED;;;;;GAKG;AACH,KAAK,UAAU,gBAAgB,CAC7B,QAAgB,EAChB,IAAY;IAEZ,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;IACjG,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,IAAI,MAAM,CAAC,KAAK,KAAK,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACjD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAA;IACnE,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,IAAA,qBAAa,EAAC,iCAAa,CAAC,CAAC,OAAO,CAAC;QAChE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;QAC7E,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;KAC3B,CAAC,CAAA;IAEF;wFACoF;IACpF,OAAO,cAAc,EAAE,MAAM;QAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,OAAO,EAAE;QAC5E,CAAC,CAAC,IAAI,CAAA;AACV,CAAC","sourcesContent":["import { In } from 'typeorm'\nimport { Args, Arg, Ctx, Directive, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\n\nimport {\n Domain,\n getQueryBuilderFromListParams,\n getRepository,\n ListParam,\n Refusal,\n SpeakRefusals\n} from '@things-factory/shell'\n\nimport { Figure } from './figure.js'\nimport { FigureHistory } from './figure-history.js'\nimport { consumablePayloadOf, inspectSource } from './figure-format.js'\nimport { FigureInspection, FigureList, FigureProposalOutcome } from './figure-type.js'\nimport { FigureReplacedDraft } from './figure-replaced-draft.js'\nimport { replacedDraftsOf } from './figure-revert.js'\nimport { proposalOutcomeOf } from './figure-proposal.js'\n\n@Resolver(Figure)\n@UseMiddleware(SpeakRefusals)\nexport class FigureQuery {\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Figure, { nullable: true, description: 'Finds a single figure by its ID.' })\n async figure(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Figure> {\n const { domain } = context.state\n\n // 부모 도메인의 Figure 도 보인다 — Board 와 같은 범위 규칙이다.\n return await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id }\n })\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureList, { description: 'Lists figures. Sortable by score, triangles, name, updatedAt.' })\n async figures(@Args() params: ListParam, @Ctx() context: ResolverContext): Promise<FigureList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n repository: getRepository(Figure),\n params,\n domain,\n alias: 'figure',\n /*\n `tags` 도 찾을 수 있어야 한다 — 태그의 값은 붙이는 데 있지 않고 **찾는 데** 있다.\n\n `simple-json` 이라 칸에는 `[\"반출\",\"자동\"]` 이 통째로 들어 있고, LIKE 는 그 글자를\n 본다. 「반출」로 찾으면 걸린다. 정확히 그 태그만 걸리는 것은 아니지만(이름에 「반출」이\n 든 도형도 걸린다) 찾기로는 그것이 오히려 낫다.\n */\n searchables: ['type', 'name', 'description', 'category', 'tags']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n /**\n * 등록된 타입 이름 목록.\n *\n * 저작 화면이 **새 타입 이름이 이미 쓰이는지** 물어보는 데 쓴다. `type` 은 저장되는\n * 식별자라 만든 뒤에 고칠 수 없으므로, 만들기 전에 알려 줘야 한다.\n *\n * `state` 로 걸러지지 않는다 — `draft` 인 Figure 의 이름도 이미 점유된 것이다.\n */\n /**\n * 발행된 판들 — 최근 것부터.\n *\n * 저작 중 저장은 여기 없다. 판본은 발행에서만 생기므로(`releaseFigure`), 이 목록이 곧\n * 「이 도형이 몇 번 약속되었나」다.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureHistory], { description: 'Released versions of a figure, newest first.' })\n async figureVersions(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureHistory[]> {\n const { domain } = context.state\n\n return await getRepository(FigureHistory).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },\n relations: ['updater'],\n order: { version: 'DESC' },\n take: 10\n })\n }\n\n /**\n * 지금 소비처가 쓰는 판.\n *\n * 도형의 현재 정본은 저작 중일 수 있다(초안). 배치된 것이 보는 것은 **마지막으로 발행된 판**이고,\n * 그것을 이 자리가 답한다.\n */\n /** What became of an AI proposal: the figures a save put it into (ADR-0097 §5.6 item 5). */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureProposalOutcome], {\n description: 'The figures an AI proposal was saved into, oldest first. None: it was not saved, or not in this domain.'\n })\n async figureProposalOutcome(@Arg('proposalKey') proposalKey: string, @Ctx() context: ResolverContext): Promise<FigureProposalOutcome[]> {\n const { domain, tx } = context.state\n return await proposalOutcomeOf(getRepository(Figure, tx).manager, domain, proposalKey)\n }\n\n /** The drafts reverts kept for a figure, newest first -- apart from its released versions (ADR-0097 §5.6). */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureReplacedDraft], { description: 'Drafts kept when a revert wrote over them, newest first. Not released versions.' })\n async figureReplacedDrafts(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureReplacedDraft[]> {\n const { domain, tx } = context.state\n return await replacedDraftsOf(getRepository(FigureReplacedDraft, tx).manager, domain, id)\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureHistory, { nullable: true, description: 'The most recently released version of a figure.' })\n async figureReleased(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureHistory> {\n const { domain } = context.state\n\n return await getRepository(FigureHistory).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },\n order: { version: 'DESC' }\n })\n }\n\n /**\n * 발행해도 되나 — **누르기 전에** 묻는 자리.\n *\n * 판정은 `figure-gate.ts` 가 하고 이 자리는 정본을 꺼내 넘긴다. 같은 판정을 `releaseFigure` 도\n * 부른다 — 화면이 물어 보고 통과했더라도 그 사이에 정본이 바뀌었을 수 있으므로, 막는 것은 언제나\n * 뮤테이션이다. 이 질의는 **이유를 미리 보여 주기 위한 것**이고 허가증이 아니다.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureInspection, { description: 'Whether a figure may be released, with the reasons.' })\n async inspectFigure(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureInspection> {\n const { domain } = context.state\n\n const figure = await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n /*\n `type` is read too. A asset carries no type, so the temporary-type refusal reads the row's; with only\n id and source selected it saw `undefined`, the panel enabled 「발행하기」, and the release mutation — which\n loads the whole row — refused on the press (2026-09-24).\n */\n select: { id: true, source: true, type: true }\n })\n\n if (!figure) {\n throw new Error(`Figure ${id} 를 찾을 수 없다`)\n }\n\n /*\n **화면이 발행 문과 같은 것을 본다.** 직전 발행판을 여기서도 함께 넘기지 않으면, 저작 화면은\n 조용한데 발행을 누른 순간 계약 위반으로 막힌다 — 그때는 이미 열 군데를 고친 뒤다.\n */\n const released = await getRepository(FigureHistory)\n .createQueryBuilder('history')\n .where('history.originalId = :id', { id })\n .orderBy('history.version', 'DESC')\n .limit(1)\n .getOne()\n\n return inspectSource(figure.source, released?.source, context.t, figure.type)\n }\n\n /**\n * The same judgement for a source that has not been saved — what the modeller shows **while** the author edits.\n *\n * Without it a author saw nothing until they saved and opened the release panel. The judgement runs here, not\n * in the browser, because the sentences are made from the request's language (`inspectAnySource` with `context.t`)\n * and a second copy on the screen would drift from the one the release refuses with. When `id` names a saved\n * figure, its row type and last release are read as `inspectFigure` does; a figure never saved has neither.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureInspection, { description: 'Whether an unsaved figure source would be released, with the reasons.' })\n async inspectFigureDraft(\n @Arg('source') source: string,\n @Ctx() context: ResolverContext,\n @Arg('id', { nullable: true }) id?: string\n ): Promise<FigureInspection> {\n const { domain } = context.state\n if (!id) return inspectSource(source, null, context.t)\n\n const figure = await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n select: { id: true, type: true }\n })\n if (!figure) throw new Error(`Figure ${id} 를 찾을 수 없다`)\n\n const released = await getRepository(FigureHistory)\n .createQueryBuilder('history')\n .where('history.originalId = :id', { id })\n .orderBy('history.version', 'DESC')\n .limit(1)\n .getOne()\n\n return inspectSource(source, released?.source, context.t, figure.type)\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [String], { description: 'All figure type names in scope — used to check name collisions before create.' })\n async figureTypeNames(@Ctx() context: ResolverContext): Promise<string[]> {\n const { domain } = context.state\n\n const rows = await getRepository(Figure).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) } },\n select: { type: true }\n })\n\n return rows.map(r => r.type).filter(Boolean)\n }\n\n /**\n * 소비처(operato-board, operato-twin, things-scene)가 발행된 도형을 받아 가는 문.\n *\n * 이름은 보드와의 계약이라 그대로 둔다(바꾸는 일은 따로 한다). 돌려주는 것은 자산이다 —\n * `{ type, asset }`. 그리는 쪽(`registerFigure`)이 커널로 컴파일한다.\n *\n * ## 규칙\n * 1. 소비처는 초안(draft)을 볼 수 없다 — **반드시 발행된(released) 판만 전달**한다.\n * 2. 현재 Figure가 released 상태이면 현재 source를 넘긴다.\n * 3. 현재 Figure가 저작 중(draft)이더라도 이전에 발행된 이력(FigureHistory)이 있으면\n * 가장 최근의 released 판본을 넘긴다.\n * 4. 한 번도 발행된 적 없는 초안 자산이거나 존재하지 않는 타입인 경우 null을 반환하여\n * 미완성 자산이 씬 화면에 유출되는 것을 엄격히 차단한다.\n * 5. 도메인 상속 시 자식 도메인의 타입이 부모 도메인의 동일 타입을 항상 오버라이드한다.\n * 6. 권한은 저작(`figure`)과 **다른 category** 인 `figure-published` 다. 소비처 사용자에게\n * 저작 권한을 요구하면 board·twin 의 팔레트가 안 뜨고, 그렇다고 선언을 비우면 이 집에서는\n * 로그인조차 안 한 사람에게 열린다(`auth-rebuild.md` §1). `<module>-published` 는\n * 「그 모듈이 발행한 것을 남이 읽는 문」이라는 규약이다 — 아키텍트 판정(2026-09-15).\n *\n * ## 열거는 막혀 있다 — 지금은 우연이다\n *\n * 이 문의 열쇠는 `type` 하나뿐이라 이름을 모르면 못 꺼낸다. 그런데 그것이 **설계가 아니다** —\n * 목록을 내는 `figures`·`figureTypeNames` 가 `figure` category 로 잠겨 있어서 결과적으로\n * 그런 것뿐이다. 나중에 누가 소비처용 목록 문을 하나 더 열고 `figure-published` 를 붙이면\n * 열거가 함께 열리고, 「이름을 알아야 한다」에 기대고 있던 가정이 조용히 사라진다.\n * 그 문을 낼 사람이 이 단락을 읽고 그때 정하면 된다.\n */\n /*\n This directive is attached by schema assembly, so a test that calls the resolver method\n directly never exercises it. `figure-blueprint-query.test.ts` reads this source for the\n declaration instead — see the test named for it there.\n */\n @Directive('@privilege(category: \"figure-published\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => GraphQLJSON, {\n nullable: true,\n description: 'A released figure for its consumers (board, twin, things-scene): { type, asset } with the figure asset. Only returns released figures.'\n })\n async figureBlueprint(\n @Arg('type') type: string,\n @Ctx() context: ResolverContext\n ): Promise<any> {\n const { domain } = context.state\n\n /*\n A child domain shadows the same type in its parent, which is how a tenant replaces the standard\n CONVEYOR with its own. Only a released version shadows. When the child has just a draft, the\n parent's release is used, so starting a draft never makes placed figures disappear (chief\n architect ruling, 2026-09-17). This used to skip the fallback as soon as a child row existed and\n return null.\n */\n const found =\n (await releasedSourceOf(domain.id, type)) ?? (domain.parentId ? await releasedSourceOf(domain.parentId, type) : null)\n\n if (!found) {\n return null\n }\n\n const { figure, source: sourceToCompile, version } = found\n\n try {\n const text = typeof sourceToCompile === 'string' ? sourceToCompile : JSON.stringify(sourceToCompile)\n return { type, asset: consumablePayloadOf(text).asset }\n } catch (err) {\n console.error(`[figureBlueprint] 타입 '${type}' (figure id: ${figure.id}) 자산 컴파일 실패:`, err)\n\n /*\n Null says one thing here: nobody published this type (ADR-0063 결정 2 보탬). A released figure\n whose stored source no longer compiles is a different fact, and answering null for it makes the\n board draw a stand-in with the reason nowhere on screen — the author published, the catalogue\n says released, and the only trace is the line above. That is the silence ADR-0053 refuses.\n\n The board asks per type inside a Promise.all and collects a rejection into `failed`, so this\n refusal costs that one figure its drawing and the scene still opens.\n */\n throw new Refusal(\n 'figure-release-does-not-compile',\n { type, figureId: figure.id, version },\n 'the released source of {type} does not compile. open it in the modeller and release it again.'\n )\n }\n }\n}\n\n/**\n * Finds the released source of a type in one domain, or null.\n *\n * A released row gives its own source; a draft row gives the source of its latest released history.\n * A figure that was never released gives null, because drafts never reach consumers.\n */\nasync function releasedSourceOf(\n domainId: string,\n type: string\n): Promise<{ figure: Figure; source: string; version: number } | null> {\n const figure = await getRepository(Figure).findOne({ where: { domain: { id: domainId }, type } })\n if (!figure) return null\n\n if (figure.state === 'released' && figure.source) {\n return { figure, source: figure.source, version: figure.version }\n }\n\n const latestReleased = await getRepository(FigureHistory).findOne({\n where: { domain: { id: domainId }, originalId: figure.id, state: 'released' },\n order: { version: 'DESC' }\n })\n\n /* The version travels with the source: a refusal names the edition that does not compile, and the\n row's own version is the draft being edited, not the one the board was handed. */\n return latestReleased?.source\n ? { figure, source: latestReleased.source, version: latestReleased.version }\n : null\n}\n"]}
1
+ {"version":3,"file":"figure-query.js","sourceRoot":"","sources":["../../../server/service/figure/figure-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { In } from 'typeorm'\nimport { Args, Arg, Ctx, Directive, Int, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { GraphQLJSON } from 'graphql-scalars'\n\nimport {\n Domain,\n getQueryBuilderFromListParams,\n getRepository,\n ListParam,\n Refusal,\n SpeakRefusals\n} from '@things-factory/shell'\n\nimport { Figure } from './figure.js'\nimport { FigureHistory } from './figure-history.js'\nimport { consumablePayloadOf, inspectSource } from './figure-format.js'\nimport { FigureInspection, FigureList, FigureProposalOutcome } from './figure-type.js'\nimport { FigureReplacedDraft } from './figure-replaced-draft.js'\nimport { replacedDraftsOf } from './figure-revert.js'\nimport { proposalOutcomeOf } from './figure-proposal.js'\n\n@Resolver(Figure)\n@UseMiddleware(SpeakRefusals)\nexport class FigureQuery {\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => Figure, { nullable: true, description: 'Finds a single figure by its ID.' })\n async figure(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Figure> {\n const { domain } = context.state\n\n // 부모 도메인의 Figure 도 보인다 — Board 와 같은 범위 규칙이다.\n return await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id }\n })\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureList, { description: 'Lists figures. Sortable by score, triangles, name, updatedAt.' })\n async figures(@Args() params: ListParam, @Ctx() context: ResolverContext): Promise<FigureList> {\n const { domain } = context.state\n\n const queryBuilder = getQueryBuilderFromListParams({\n repository: getRepository(Figure),\n params,\n domain,\n alias: 'figure',\n /*\n `tags` 도 찾을 수 있어야 한다 — 태그의 값은 붙이는 데 있지 않고 **찾는 데** 있다.\n\n `simple-json` 이라 칸에는 `[\"반출\",\"자동\"]` 이 통째로 들어 있고, LIKE 는 그 글자를\n 본다. 「반출」로 찾으면 걸린다. 정확히 그 태그만 걸리는 것은 아니지만(이름에 「반출」이\n 든 도형도 걸린다) 찾기로는 그것이 오히려 낫다.\n */\n searchables: ['type', 'name', 'description', 'category', 'tags']\n })\n\n const [items, total] = await queryBuilder.getManyAndCount()\n\n return { items, total }\n }\n\n /**\n * 등록된 타입 이름 목록.\n *\n * 저작 화면이 **새 타입 이름이 이미 쓰이는지** 물어보는 데 쓴다. `type` 은 저장되는\n * 식별자라 만든 뒤에 고칠 수 없으므로, 만들기 전에 알려 줘야 한다.\n *\n * `state` 로 걸러지지 않는다 — `draft` 인 Figure 의 이름도 이미 점유된 것이다.\n */\n /**\n * 발행된 판들 — 최근 것부터.\n *\n * 저작 중 저장은 여기 없다. 판본은 발행에서만 생기므로(`releaseFigure`), 이 목록이 곧\n * 「이 도형이 몇 번 약속되었나」다.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureHistory], { description: 'Released versions of a figure, newest first.' })\n async figureVersions(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureHistory[]> {\n const { domain } = context.state\n\n return await getRepository(FigureHistory).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },\n relations: ['updater'],\n order: { version: 'DESC' },\n take: 10\n })\n }\n\n /**\n * 지금 소비처가 쓰는 판.\n *\n * 도형의 현재 정본은 저작 중일 수 있다(초안). 배치된 것이 보는 것은 **마지막으로 발행된 판**이고,\n * 그것을 이 자리가 답한다.\n */\n /** What became of an AI proposal: the figures a save put it into (ADR-0097 §5.6 item 5). */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureProposalOutcome], {\n description: 'The figures an AI proposal was saved into, oldest first. None: it was not saved, or not in this domain.'\n })\n async figureProposalOutcome(@Arg('proposalKey') proposalKey: string, @Ctx() context: ResolverContext): Promise<FigureProposalOutcome[]> {\n const { domain, tx } = context.state\n return await proposalOutcomeOf(getRepository(Figure, tx).manager, domain, proposalKey)\n }\n\n /** The drafts reverts kept for a figure, newest first -- apart from its released versions (ADR-0097 §5.6). */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureReplacedDraft], { description: 'Drafts kept when a revert wrote over them, newest first. Not released versions.' })\n async figureReplacedDrafts(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureReplacedDraft[]> {\n const { domain, tx } = context.state\n return await replacedDraftsOf(getRepository(FigureReplacedDraft, tx).manager, domain, id)\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureHistory, { nullable: true, description: 'The most recently released version of a figure.' })\n async figureReleased(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureHistory> {\n const { domain } = context.state\n\n return await getRepository(FigureHistory).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, originalId: id },\n order: { version: 'DESC' }\n })\n }\n\n /**\n * 발행해도 되나 — **누르기 전에** 묻는 자리.\n *\n * 판정은 `figure-gate.ts` 가 하고 이 자리는 정본을 꺼내 넘긴다. 같은 판정을 `releaseFigure` 도\n * 부른다 — 화면이 물어 보고 통과했더라도 그 사이에 정본이 바뀌었을 수 있으므로, 막는 것은 언제나\n * 뮤테이션이다. 이 질의는 **이유를 미리 보여 주기 위한 것**이고 허가증이 아니다.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureInspection, { description: 'Whether a figure may be released, with the reasons.' })\n async inspectFigure(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<FigureInspection> {\n const { domain } = context.state\n\n const figure = await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n /*\n `type` is read too. A asset carries no type, so the temporary-type refusal reads the row's; with only\n id and source selected it saw `undefined`, the panel enabled 「발행하기」, and the release mutation — which\n loads the whole row — refused on the press (2026-09-24).\n */\n select: { id: true, source: true, type: true }\n })\n\n if (!figure) {\n throw new Error(`Figure ${id} 를 찾을 수 없다`)\n }\n\n /*\n **화면이 발행 문과 같은 것을 본다.** 직전 발행판을 여기서도 함께 넘기지 않으면, 저작 화면은\n 조용한데 발행을 누른 순간 계약 위반으로 막힌다 — 그때는 이미 열 군데를 고친 뒤다.\n */\n const released = await getRepository(FigureHistory)\n .createQueryBuilder('history')\n .where('history.originalId = :id', { id })\n .orderBy('history.version', 'DESC')\n .limit(1)\n .getOne()\n\n return inspectSource(figure.source, released?.source, context.t, figure.type)\n }\n\n /**\n * The same judgement for a source that has not been saved — what the modeller shows **while** the author edits.\n *\n * Without it a author saw nothing until they saved and opened the release panel. The judgement runs here, not\n * in the browser, because the sentences are made from the request's language (`inspectAnySource` with `context.t`)\n * and a second copy on the screen would drift from the one the release refuses with. When `id` names a saved\n * figure, its row type and last release are read as `inspectFigure` does; a figure never saved has neither.\n */\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureInspection, { description: 'Whether an unsaved figure source would be released, with the reasons.' })\n async inspectFigureDraft(\n @Arg('source') source: string,\n @Ctx() context: ResolverContext,\n @Arg('id', { nullable: true }) id?: string\n ): Promise<FigureInspection> {\n const { domain } = context.state\n if (!id) return inspectSource(source, null, context.t)\n\n const figure = await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n select: { id: true, type: true }\n })\n if (!figure) throw new Error(`Figure ${id} 를 찾을 수 없다`)\n\n const released = await getRepository(FigureHistory)\n .createQueryBuilder('history')\n .where('history.originalId = :id', { id })\n .orderBy('history.version', 'DESC')\n .limit(1)\n .getOne()\n\n return inspectSource(source, released?.source, context.t, figure.type)\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [String], { description: 'All figure type names in scope — used to check name collisions before create.' })\n async figureTypeNames(@Ctx() context: ResolverContext): Promise<string[]> {\n const { domain } = context.state\n\n const rows = await getRepository(Figure).find({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) } },\n select: { type: true }\n })\n\n return rows.map(r => r.type).filter(Boolean)\n }\n\n /**\n * 소비처(operato-board, operato-twin, things-scene)가 발행된 도형을 받아 가는 문.\n *\n * 이름은 보드와의 계약이라 그대로 둔다(바꾸는 일은 따로 한다). 돌려주는 것은 자산이다 —\n * `{ type, asset }`. 그리는 쪽(`registerFigure`)이 커널로 컴파일한다.\n *\n * ## 규칙\n * 1. 소비처는 초안(draft)을 볼 수 없다 — **반드시 발행된(released) 판만 전달**한다.\n * 2. 현재 Figure가 released 상태이면 현재 source를 넘긴다.\n * 3. 현재 Figure가 저작 중(draft)이더라도 이전에 발행된 이력(FigureHistory)이 있으면\n * 가장 최근의 released 판본을 넘긴다.\n * 4. 한 번도 발행된 적 없는 초안 자산이거나 존재하지 않는 타입인 경우 null을 반환하여\n * 미완성 자산이 씬 화면에 유출되는 것을 엄격히 차단한다.\n * 5. 도메인 상속 시 자식 도메인의 타입이 부모 도메인의 동일 타입을 항상 오버라이드한다.\n * 6. 권한은 저작(`figure`)과 **다른 category** 인 `figure-published` 다. 소비처 사용자에게\n * 저작 권한을 요구하면 board·twin 의 팔레트가 안 뜨고, 그렇다고 선언을 비우면 이 집에서는\n * 로그인조차 안 한 사람에게 열린다(`auth-rebuild.md` §1). `<module>-published` 는\n * 「그 모듈이 발행한 것을 남이 읽는 문」이라는 규약이다 — 아키텍트 판정(2026-09-15).\n *\n * ## 열거는 막혀 있다 — 지금은 우연이다\n *\n * 이 문의 열쇠는 `type` 하나뿐이라 이름을 모르면 못 꺼낸다. 그런데 그것이 **설계가 아니다** —\n * 목록을 내는 `figures`·`figureTypeNames` 가 `figure` category 로 잠겨 있어서 결과적으로\n * 그런 것뿐이다. 나중에 누가 소비처용 목록 문을 하나 더 열고 `figure-published` 를 붙이면\n * 열거가 함께 열리고, 「이름을 알아야 한다」에 기대고 있던 가정이 조용히 사라진다.\n * 그 문을 낼 사람이 이 단락을 읽고 그때 정하면 된다.\n */\n /*\n This directive is attached by schema assembly, so a test that calls the resolver method\n directly never exercises it. `figure-blueprint-query.test.ts` reads this source for the\n declaration instead — see the test named for it there.\n */\n @Directive('@privilege(category: \"figure-published\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => GraphQLJSON, {\n nullable: true,\n description:\n 'A released figure for its consumers (board, twin, things-scene): { type, version, asset }. The latest release, or the one numbered `version` for a consumer that pins releases (ADR-0104). Only returns released figures.'\n })\n async figureBlueprint(\n @Arg('type') type: string,\n @Ctx() context: ResolverContext,\n @Arg('version', type => Int, { nullable: true, description: 'A release number. Absent: the latest release.' }) pinned?: number\n ): Promise<any> {\n const { domain } = context.state\n\n /*\n A child domain shadows the same type in its parent, which is how a tenant replaces the standard\n CONVEYOR with its own. Only a released version shadows. When the child has just a draft, the\n parent's release is used, so starting a draft never makes placed figures disappear (chief\n architect ruling, 2026-09-17). This used to skip the fallback as soon as a child row existed and\n return null.\n */\n const latest =\n (await releasedSourceOf(domain.id, type)) ?? (domain.parentId ? await releasedSourceOf(domain.parentId, type) : null)\n\n if (!latest) {\n return null\n }\n\n /*\n A pinned release is read from the same figure the latest comes from -- the shadowing rule decides whose\n releases these are, then the number picks one. Releases are never deleted, so a number once handed out is\n always there; a number that never was is refused by name rather than answered with another release.\n */\n const found = pinned === undefined || pinned === null || pinned === latest.version ? latest : await releaseNumbered(latest.figure, pinned)\n if (!found) {\n throw new Refusal(\n 'figure-release-not-found',\n { type, version: pinned, latest: latest.version },\n '{type} has no release {version}. its latest release is {latest}.'\n )\n }\n\n const { figure, source: sourceToCompile, version } = found\n\n try {\n const text = typeof sourceToCompile === 'string' ? sourceToCompile : JSON.stringify(sourceToCompile)\n return { type, version, asset: consumablePayloadOf(text).asset }\n } catch (err) {\n console.error(`[figureBlueprint] 타입 '${type}' (figure id: ${figure.id}) 자산 컴파일 실패:`, err)\n\n /*\n Null says one thing here: nobody published this type (ADR-0063 결정 2 보탬). A released figure\n whose stored source no longer compiles is a different fact, and answering null for it makes the\n board draw a stand-in with the reason nowhere on screen — the author published, the catalogue\n says released, and the only trace is the line above. That is the silence ADR-0053 refuses.\n\n The board asks per type inside a Promise.all and collects a rejection into `failed`, so this\n refusal costs that one figure its drawing and the scene still opens.\n */\n throw new Refusal(\n 'figure-release-does-not-compile',\n { type, figureId: figure.id, version },\n 'the released source of {type} does not compile. open it in the modeller and release it again.'\n )\n }\n }\n}\n\n/** One numbered release of a figure, or null when it has none by that number. */\nasync function releaseNumbered(figure: Figure, version: number): Promise<{ figure: Figure; source: string; version: number } | null> {\n const row = await getRepository(FigureHistory).findOne({\n where: { originalId: figure.id, state: 'released', version }\n })\n return row?.source ? { figure, source: row.source, version: row.version } : null\n}\n\n/**\n * Finds the released source of a type in one domain, or null.\n *\n * A released row gives its own source; a draft row gives the source of its latest released history.\n * A figure that was never released gives null, because drafts never reach consumers.\n */\nasync function releasedSourceOf(\n domainId: string,\n type: string\n): Promise<{ figure: Figure; source: string; version: number } | null> {\n const figure = await getRepository(Figure).findOne({ where: { domain: { id: domainId }, type } })\n if (!figure) return null\n\n if (figure.state === 'released' && figure.source) {\n return { figure, source: figure.source, version: figure.version }\n }\n\n const latestReleased = await getRepository(FigureHistory).findOne({\n where: { domain: { id: domainId }, originalId: figure.id, state: 'released' },\n order: { version: 'DESC' }\n })\n\n /* The version travels with the source: a refusal names the edition that does not compile, and the\n row's own version is the draft being edited, not the one the board was handed. */\n return latestReleased?.source\n ? { figure, source: latestReleased.source, version: latestReleased.version }\n : null\n}\n"]}