@things-factory/figure-service 10.1.157 → 10.1.159

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.
@@ -1,4 +1,5 @@
1
1
  import './routes.js';
2
+ import './service/figure/figure-subject.js';
2
3
  import './service/figure/figure-thumbnail-reader.js';
3
4
  export * from './service/index.js';
4
5
  export * from './migrations/index.js';
@@ -12,6 +12,8 @@ require("./routes.js");
12
12
  */
13
13
  const figure_tools_js_1 = require("./service/figure/figure-tools.js");
14
14
  (0, figure_tools_js_1.registerFigureToolCategories)();
15
+ /* The figure the modeller has open, said to the AI each turn (design/ai/screen-pattern.md §2-1). */
16
+ require("./service/figure/figure-subject.js");
15
17
  /*
16
18
  * The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,
17
19
  * said here rather than found by a person as a missing template.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;;AAAA,uBAAoB;AAEpB;;;;;;GAMG;AACH,sEAA+E;AAC/E,IAAA,8CAA4B,GAAE,CAAA;AAE9B;;;GAGG;AACH,mGAAoF;AACpF,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,GAAG,EAAE;IAC/C,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAA,4CAAe,GAAE,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,+CAA+C,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAC7I,CAAC,CAAC,CAAA;AAEF;;;GAGG;AACH,iDAAiE;AACjE,8FAAmF;AACnF,+GAA+G;AAC/G,uDAAoD;AACpD,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,GAAG,EAAE;IAC/C,IAAA,kBAAU,EAAC,GAAG,EAAE,CAAC,IAAA,kDAAoB,EAAC,IAAA,qBAAa,GAAE,CAAC,CAAC;SACpD,IAAI,CAAC,KAAK,CAAC,EAAE;QACZ,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,gDAAgD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChL,CAAC,CAAC;SACD,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,mCAAmC,KAAK,EAAE,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAA;AAChG,CAAC,CAAC,CAAA;AAEF,6DAAkC;AAClC,gEAAqC;AACrC,oEAA+E;AAAtE,+HAAA,4BAA4B,OAAA","sourcesContent":["import './routes.js'\n\n/*\n * 저작 도구를 어시스턴트 레지스트리에 등록한다.\n *\n * 이 패키지가 적재되면 대화면(도크)이 저작 중인 Figure 의 정본·비용·발행 판정을 읽고 후보를\n * 요청할 수 있다. 능력을 복제하지 않고 figure-service 가 이미 가진 진입점을 투영한다 —\n * 노출 범위는 대화면이 `toolCategories` 로 고른다(twin-ai · board-import 와 같은 방식).\n */\nimport { registerFigureToolCategories } from './service/figure/figure-tools.js'\nregisterFigureToolCategories()\n\n/*\n * The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,\n * said here rather than found by a person as a missing template.\n */\nimport { recipeTemplates } from './service/figure-recipe/figure-recipe-templates.js'\nprocess.on('bootstrap-module-start' as any, () => {\n const { templates, refused } = recipeTemplates()\n console.log(`[figure-service:bootstrap] recipe templates ${templates.length}${refused.length ? `, left out: ${refused.join(' | ')}` : ''}`)\n})\n\n/*\n * The pictures an install kept as base64 move into attachment-base by their content, once, at start (ADR-0104 decision\n * 6). In the boot's one line: it writes, and sqlite holds one runner. A failure is said and the server goes on.\n */\nimport { getDataSource, inBootTurn } from '@things-factory/shell'\nimport { moveBase64Thumbnails } from './service/figure/figure-thumbnail-upgrade.js'\n/* Who may read a figure picture by its attachment path (ADR-0106): registered with attachment-base at load. */\nimport './service/figure/figure-thumbnail-reader.js'\nprocess.on('bootstrap-module-start' as any, () => {\n inBootTurn(() => moveBase64Thumbnails(getDataSource()))\n .then(moved => {\n const any = moved.filter(m => m.moved || m.kept)\n if (any.length) console.log(`[figure-service:bootstrap] thumbnails moved: ${any.map(m => `${m.table} ${m.moved}${m.kept ? ` (kept as base64: ${m.kept})` : ''}`).join(', ')}`)\n })\n .catch(error => console.error(`[figure-service] 썸네일을 옮기지 못했다 — ${error?.message ?? error}`))\n})\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\nexport { registerFigureToolCategories } from './service/figure/figure-tools.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;;AAAA,uBAAoB;AAEpB;;;;;;GAMG;AACH,sEAA+E;AAC/E,IAAA,8CAA4B,GAAE,CAAA;AAC9B,oGAAoG;AACpG,8CAA2C;AAE3C;;;GAGG;AACH,mGAAoF;AACpF,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,GAAG,EAAE;IAC/C,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAA,4CAAe,GAAE,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,+CAA+C,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAC7I,CAAC,CAAC,CAAA;AAEF;;;GAGG;AACH,iDAAiE;AACjE,8FAAmF;AACnF,+GAA+G;AAC/G,uDAAoD;AACpD,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,GAAG,EAAE;IAC/C,IAAA,kBAAU,EAAC,GAAG,EAAE,CAAC,IAAA,kDAAoB,EAAC,IAAA,qBAAa,GAAE,CAAC,CAAC;SACpD,IAAI,CAAC,KAAK,CAAC,EAAE;QACZ,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,GAAG,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,gDAAgD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChL,CAAC,CAAC;SACD,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,mCAAmC,KAAK,EAAE,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAA;AAChG,CAAC,CAAC,CAAA;AAEF,6DAAkC;AAClC,gEAAqC;AACrC,oEAA+E;AAAtE,+HAAA,4BAA4B,OAAA","sourcesContent":["import './routes.js'\n\n/*\n * 저작 도구를 어시스턴트 레지스트리에 등록한다.\n *\n * 이 패키지가 적재되면 대화면(도크)이 저작 중인 Figure 의 정본·비용·발행 판정을 읽고 후보를\n * 요청할 수 있다. 능력을 복제하지 않고 figure-service 가 이미 가진 진입점을 투영한다 —\n * 노출 범위는 대화면이 `toolCategories` 로 고른다(twin-ai · board-import 와 같은 방식).\n */\nimport { registerFigureToolCategories } from './service/figure/figure-tools.js'\nregisterFigureToolCategories()\n/* The figure the modeller has open, said to the AI each turn (design/ai/screen-pattern.md §2-1). */\nimport './service/figure/figure-subject.js'\n\n/*\n * The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,\n * said here rather than found by a person as a missing template.\n */\nimport { recipeTemplates } from './service/figure-recipe/figure-recipe-templates.js'\nprocess.on('bootstrap-module-start' as any, () => {\n const { templates, refused } = recipeTemplates()\n console.log(`[figure-service:bootstrap] recipe templates ${templates.length}${refused.length ? `, left out: ${refused.join(' | ')}` : ''}`)\n})\n\n/*\n * The pictures an install kept as base64 move into attachment-base by their content, once, at start (ADR-0104 decision\n * 6). In the boot's one line: it writes, and sqlite holds one runner. A failure is said and the server goes on.\n */\nimport { getDataSource, inBootTurn } from '@things-factory/shell'\nimport { moveBase64Thumbnails } from './service/figure/figure-thumbnail-upgrade.js'\n/* Who may read a figure picture by its attachment path (ADR-0106): registered with attachment-base at load. */\nimport './service/figure/figure-thumbnail-reader.js'\nprocess.on('bootstrap-module-start' as any, () => {\n inBootTurn(() => moveBase64Thumbnails(getDataSource()))\n .then(moved => {\n const any = moved.filter(m => m.moved || m.kept)\n if (any.length) console.log(`[figure-service:bootstrap] thumbnails moved: ${any.map(m => `${m.table} ${m.moved}${m.kept ? ` (kept as base64: ${m.kept})` : ''}`).join(', ')}`)\n })\n .catch(error => console.error(`[figure-service] 썸네일을 옮기지 못했다 — ${error?.message ?? error}`))\n})\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\nexport { registerFigureToolCategories } from './service/figure/figure-tools.js'\n"]}
@@ -7,8 +7,10 @@ export declare const FIGURE_NAMESPACE_SETTING = "figure-namespace";
7
7
  * The namespace a new figure is created under.
8
8
  *
9
9
  * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores
10
- * never moves. The setting is set once: a value that differs from the namespace this domain's figures already carry
11
- * is refused, so editing it on the settings screen cannot rename anything.
10
+ * never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는
11
+ * subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in
12
+ * the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs
13
+ * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.
12
14
  */
13
15
  export declare function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string>;
14
16
  /** Refuses when the domain's figures already carry another namespace than the setting now says. */
@@ -15,14 +15,17 @@ exports.FIGURE_NAMESPACE_SETTING = 'figure-namespace';
15
15
  * The namespace a new figure is created under.
16
16
  *
17
17
  * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores
18
- * never moves. The setting is set once: a value that differs from the namespace this domain's figures already carry
19
- * is refused, so editing it on the settings screen cannot rename anything.
18
+ * never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는
19
+ * subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in
20
+ * the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs
21
+ * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.
20
22
  */
21
23
  async function namespaceForNewFigure(tx, domain) {
22
24
  const setting = await tx.getRepository(setting_base_1.Setting).findOne({
23
25
  where: { domain: { id: domain.id }, name: exports.FIGURE_NAMESPACE_SETTING }
24
26
  });
25
- const namespace = setting?.value?.trim() ?? '';
27
+ const written = setting?.value?.trim() ?? '';
28
+ const namespace = written || (domain.subdomain ?? '').trim();
26
29
  if (!(0, figure_model_1.isFigureNamespace)(namespace)) {
27
30
  throw new shell_1.Refusal('figure-namespace-missing', { setting: exports.FIGURE_NAMESPACE_SETTING, value: namespace }, 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.');
28
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"figure-namespace.js","sourceRoot":"","sources":["../../../server/service/figure/figure-namespace.ts"],"names":[],"mappings":";;;AAkBA,sDAcC;AAGD,4DAWC;AAGD,kCASC;AA1DD,qCAA4C;AAE5C,yDAA0D;AAC1D,iDAAuD;AACvD,+DAAsD;AAEtD,2CAAoC;AAEpC,wGAAwG;AAC3F,QAAA,wBAAwB,GAAG,kBAAkB,CAAA;AAE1D;;;;;;GAMG;AACI,KAAK,UAAU,qBAAqB,CAAC,EAAiB,EAAE,MAAc;IAC3E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,sBAAO,CAAC,CAAC,OAAO,CAAC;QACtD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,gCAAwB,EAAE;KACrE,CAAC,CAAA;IACF,MAAM,SAAS,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC9C,IAAI,CAAC,IAAA,gCAAiB,EAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,EACvD,2FAA2F,CAC5F,CAAA;IACH,CAAC;IACD,MAAM,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACrD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,mGAAmG;AAC5F,KAAK,UAAU,wBAAwB,CAAC,EAAiB,EAAE,MAAc,EAAE,SAAiB;IACjG,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;QACnD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAA,aAAG,EAAC,SAAS,CAAC,EAAE;KAChE,CAAC,CAAA;IACF,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EACnF,0GAA0G,CAC3G,CAAA;IACH,CAAC;AACH,CAAC;AAED,0GAA0G;AAC1G,SAAgB,WAAW,CAAC,MAAc;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,EAAE,EAAE,EAChD,oEAAoE,CACrE,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager, Not } from 'typeorm'\n\nimport { isFigureNamespace } from '@hatiolab/figure-model'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { Setting } from '@things-factory/setting-base'\n\nimport { Figure } from './figure.js'\n\n/** The domain setting that names the namespace this domain's figures are published under (ADR-0114). */\nexport const FIGURE_NAMESPACE_SETTING = 'figure-namespace'\n\n/**\n * The namespace a new figure is created under.\n *\n * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores\n * never moves. The setting is set once: a value that differs from the namespace this domain's figures already carry\n * is refused, so editing it on the settings screen cannot rename anything.\n */\nexport async function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string> {\n const setting = await tx.getRepository(Setting).findOne({\n where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }\n })\n const namespace = setting?.value?.trim() ?? ''\n if (!isFigureNamespace(namespace)) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace },\n 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.'\n )\n }\n await assertNamespaceUnchanged(tx, domain, namespace)\n return namespace\n}\n\n/** Refuses when the domain's figures already carry another namespace than the setting now says. */\nexport async function assertNamespaceUnchanged(tx: EntityManager, domain: Domain, namespace: string): Promise<void> {\n const other = await tx.getRepository(Figure).findOne({\n where: { domain: { id: domain.id }, namespace: Not(namespace) }\n })\n if (other?.namespace) {\n throw new Refusal(\n 'figure-namespace-changed',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace, namespace: other.namespace },\n 'the figure namespace of this domain is {namespace} and cannot change. set {setting} back to {namespace}.'\n )\n }\n}\n\n/** The namespace a figure already carries, refused when it has none (a figure made before namespaces). */\nexport function namespaceOf(figure: Figure): string {\n if (!figure.namespace) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: '' },\n 'this figure has no namespace. run the figure name migration first.'\n )\n }\n return figure.namespace\n}\n"]}
1
+ {"version":3,"file":"figure-namespace.js","sourceRoot":"","sources":["../../../server/service/figure/figure-namespace.ts"],"names":[],"mappings":";;;AAoBA,sDAeC;AAGD,4DAWC;AAGD,kCASC;AA7DD,qCAA4C;AAE5C,yDAA0D;AAC1D,iDAAuD;AACvD,+DAAsD;AAEtD,2CAAoC;AAEpC,wGAAwG;AAC3F,QAAA,wBAAwB,GAAG,kBAAkB,CAAA;AAE1D;;;;;;;;GAQG;AACI,KAAK,UAAU,qBAAqB,CAAC,EAAiB,EAAE,MAAc;IAC3E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,sBAAO,CAAC,CAAC,OAAO,CAAC;QACtD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,gCAAwB,EAAE;KACrE,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC5C,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAC5D,IAAI,CAAC,IAAA,gCAAiB,EAAC,SAAS,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,EACvD,2FAA2F,CAC5F,CAAA;IACH,CAAC;IACD,MAAM,wBAAwB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACrD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,mGAAmG;AAC5F,KAAK,UAAU,wBAAwB,CAAC,EAAiB,EAAE,MAAc,EAAE,SAAiB;IACjG,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,aAAa,CAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;QACnD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAA,aAAG,EAAC,SAAS,CAAC,EAAE;KAChE,CAAC,CAAA;IACF,IAAI,KAAK,EAAE,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EACnF,0GAA0G,CAC3G,CAAA;IACH,CAAC;AACH,CAAC;AAED,0GAA0G;AAC1G,SAAgB,WAAW,CAAC,MAAc;IACxC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,eAAO,CACf,0BAA0B,EAC1B,EAAE,OAAO,EAAE,gCAAwB,EAAE,KAAK,EAAE,EAAE,EAAE,EAChD,oEAAoE,CACrE,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAA;AACzB,CAAC","sourcesContent":["import { EntityManager, Not } from 'typeorm'\n\nimport { isFigureNamespace } from '@hatiolab/figure-model'\nimport { Domain, Refusal } from '@things-factory/shell'\nimport { Setting } from '@things-factory/setting-base'\n\nimport { Figure } from './figure.js'\n\n/** The domain setting that names the namespace this domain's figures are published under (ADR-0114). */\nexport const FIGURE_NAMESPACE_SETTING = 'figure-namespace'\n\n/**\n * The namespace a new figure is created under.\n *\n * It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores\n * never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는\n * subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in\n * the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs\n * from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.\n */\nexport async function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string> {\n const setting = await tx.getRepository(Setting).findOne({\n where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }\n })\n const written = setting?.value?.trim() ?? ''\n const namespace = written || (domain.subdomain ?? '').trim()\n if (!isFigureNamespace(namespace)) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace },\n 'this domain has no valid figure namespace. set {setting} first, for example hatiolab.com.'\n )\n }\n await assertNamespaceUnchanged(tx, domain, namespace)\n return namespace\n}\n\n/** Refuses when the domain's figures already carry another namespace than the setting now says. */\nexport async function assertNamespaceUnchanged(tx: EntityManager, domain: Domain, namespace: string): Promise<void> {\n const other = await tx.getRepository(Figure).findOne({\n where: { domain: { id: domain.id }, namespace: Not(namespace) }\n })\n if (other?.namespace) {\n throw new Refusal(\n 'figure-namespace-changed',\n { setting: FIGURE_NAMESPACE_SETTING, value: namespace, namespace: other.namespace },\n 'the figure namespace of this domain is {namespace} and cannot change. set {setting} back to {namespace}.'\n )\n }\n}\n\n/** The namespace a figure already carries, refused when it has none (a figure made before namespaces). */\nexport function namespaceOf(figure: Figure): string {\n if (!figure.namespace) {\n throw new Refusal(\n 'figure-namespace-missing',\n { setting: FIGURE_NAMESPACE_SETTING, value: '' },\n 'this figure has no namespace. run the figure name migration first.'\n )\n }\n return figure.namespace\n}\n"]}
@@ -0,0 +1,12 @@
1
+ export declare const figureSubject: {
2
+ anchorType: string;
3
+ describe({ anchorId, domain }: {
4
+ anchorId: string;
5
+ domain: {
6
+ id: string;
7
+ };
8
+ }): Promise<{
9
+ noun: string;
10
+ name: any;
11
+ }>;
12
+ };
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.figureSubject = void 0;
4
+ /**
5
+ * The figure the modeller has open, named for the AI (operato-application design/ai/screen-pattern.md §2-1).
6
+ *
7
+ * A conversation in the modeller is anchored to the open figure. Each turn the door reads this to say which figure
8
+ * that is, so the model starts from "the figure 선풍기 is open" and passes its id to the tools. A figure of another
9
+ * domain, or one that is gone, is not named.
10
+ */
11
+ const ai_client_base_1 = require("@things-factory/ai-client-base");
12
+ const shell_1 = require("@things-factory/shell");
13
+ /* By entity name - the entity's decorators stay out of what a test of this has to load. */
14
+ exports.figureSubject = {
15
+ anchorType: 'figure',
16
+ async describe({ anchorId, domain }) {
17
+ const figure = await (0, shell_1.getRepository)('Figure').findOne({ where: { id: anchorId, domain: { id: domain.id } } });
18
+ if (!figure)
19
+ return undefined;
20
+ return { noun: 'figure', name: figure.name || figure.type };
21
+ }
22
+ };
23
+ (0, ai_client_base_1.registerAssistantSubject)(exports.figureSubject);
24
+ //# sourceMappingURL=figure-subject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-subject.js","sourceRoot":"","sources":["../../../server/service/figure/figure-subject.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACH,mEAAyE;AACzE,iDAAqD;AAErD,2FAA2F;AAC9E,QAAA,aAAa,GAAG;IAC3B,UAAU,EAAE,QAAQ;IACpB,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAgD;QAC/E,MAAM,MAAM,GAAQ,MAAM,IAAA,qBAAa,EAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAA;QACjH,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAA;QAC7B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAA;IAC7D,CAAC;CACF,CAAA;AAED,IAAA,yCAAwB,EAAC,qBAAa,CAAC,CAAA","sourcesContent":["/**\n * The figure the modeller has open, named for the AI (operato-application design/ai/screen-pattern.md §2-1).\n *\n * A conversation in the modeller is anchored to the open figure. Each turn the door reads this to say which figure\n * that is, so the model starts from \"the figure 선풍기 is open\" and passes its id to the tools. A figure of another\n * domain, or one that is gone, is not named.\n */\nimport { registerAssistantSubject } from '@things-factory/ai-client-base'\nimport { getRepository } from '@things-factory/shell'\n\n/* By entity name - the entity's decorators stay out of what a test of this has to load. */\nexport const figureSubject = {\n anchorType: 'figure',\n async describe({ anchorId, domain }: { anchorId: string; domain: { id: string } }) {\n const figure: any = await getRepository('Figure').findOne({ where: { id: anchorId, domain: { id: domain.id } } })\n if (!figure) return undefined\n return { noun: 'figure', name: figure.name || figure.type }\n }\n}\n\nregisterAssistantSubject(figureSubject)\n"]}