@things-factory/figure-library 10.1.206 → 10.1.208

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.
Files changed (60) hide show
  1. package/dist-client/figure-palette-group.d.ts +52 -0
  2. package/dist-client/figure-palette-group.js +41 -0
  3. package/dist-client/figure-palette-group.js.map +1 -0
  4. package/dist-client/figure-palette.d.ts +7 -0
  5. package/dist-client/figure-palette.js +49 -0
  6. package/dist-client/figure-palette.js.map +1 -0
  7. package/dist-client/item-types/index.d.ts +10 -0
  8. package/dist-client/item-types/index.js +48 -0
  9. package/dist-client/item-types/index.js.map +1 -0
  10. package/dist-client/item-types/item-types-editor.d.ts +9 -0
  11. package/dist-client/item-types/item-types-editor.js +49 -0
  12. package/dist-client/item-types/item-types-editor.js.map +1 -0
  13. package/dist-client/item-types/item-types-renderer.d.ts +2 -0
  14. package/dist-client/item-types/item-types-renderer.js +8 -0
  15. package/dist-client/item-types/item-types-renderer.js.map +1 -0
  16. package/dist-client/item-types/item-types-rows.d.ts +16 -0
  17. package/dist-client/item-types/item-types-rows.js +58 -0
  18. package/dist-client/item-types/item-types-rows.js.map +1 -0
  19. package/dist-client/item-types/ox-item-types-input.d.ts +24 -0
  20. package/dist-client/item-types/ox-item-types-input.js +284 -0
  21. package/dist-client/item-types/ox-item-types-input.js.map +1 -0
  22. package/dist-client/pages/figure-library-page.d.ts +3 -3
  23. package/dist-client/pages/figure-library-page.js +34 -5
  24. package/dist-client/pages/figure-library-page.js.map +1 -1
  25. package/dist-client/tsconfig.tsbuildinfo +1 -1
  26. package/dist-client/viewparts/figure-releases-popup.d.ts +10 -0
  27. package/dist-client/viewparts/figure-releases-popup.js +68 -0
  28. package/dist-client/viewparts/figure-releases-popup.js.map +1 -1
  29. package/dist-server/service/figure-library-release/figure-library-release.js +2 -2
  30. package/dist-server/service/figure-library-release/figure-library-release.js.map +1 -1
  31. package/dist-server/service/figure-library-resolver.d.ts +3 -0
  32. package/dist-server/service/figure-library-resolver.js +48 -2
  33. package/dist-server/service/figure-library-resolver.js.map +1 -1
  34. package/dist-server/service/figure-library-types.d.ts +2 -0
  35. package/dist-server/service/figure-library-types.js +8 -0
  36. package/dist-server/service/figure-library-types.js.map +1 -1
  37. package/dist-server/service/figure-type-map/figure-type-map-entry.d.ts +3 -0
  38. package/dist-server/service/figure-type-map/figure-type-map-entry.js +21 -0
  39. package/dist-server/service/figure-type-map/figure-type-map-entry.js.map +1 -1
  40. package/dist-server/service/library/blueprints.js +4 -0
  41. package/dist-server/service/library/blueprints.js.map +1 -1
  42. package/dist-server/service/library/extensions.js +3 -0
  43. package/dist-server/service/library/extensions.js.map +1 -1
  44. package/dist-server/service/library/figures.d.ts +8 -0
  45. package/dist-server/service/library/figures.js +5 -1
  46. package/dist-server/service/library/figures.js.map +1 -1
  47. package/dist-server/service/library/inspection.d.ts +1 -1
  48. package/dist-server/service/library/inspection.js +2 -0
  49. package/dist-server/service/library/inspection.js.map +1 -1
  50. package/dist-server/service/library/type-map.d.ts +15 -0
  51. package/dist-server/service/library/type-map.js +59 -0
  52. package/dist-server/service/library/type-map.js.map +1 -1
  53. package/dist-server/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +10 -8
  55. package/translations/en.json +29 -3
  56. package/translations/es.json +29 -3
  57. package/translations/ja.json +29 -3
  58. package/translations/ko.json +34 -8
  59. package/translations/ms.json +29 -3
  60. package/translations/zh.json +29 -3
@@ -14,6 +14,9 @@ export declare class FigureTypeMapEntry {
14
14
  description?: string;
15
15
  release?: FigureLibraryRelease;
16
16
  releaseId?: string;
17
+ archivedAt?: Date;
18
+ archivedBy?: User;
19
+ item?: boolean;
17
20
  createdAt?: Date;
18
21
  updatedAt?: Date;
19
22
  deletedAt?: Date;
@@ -48,6 +48,27 @@ tslib_1.__decorate([
48
48
  (0, typeorm_1.RelationId)((entry) => entry.release),
49
49
  tslib_1.__metadata("design:type", String)
50
50
  ], FigureTypeMapEntry.prototype, "releaseId", void 0);
51
+ tslib_1.__decorate([
52
+ (0, typeorm_1.Column)({ nullable: true }),
53
+ (0, type_graphql_1.Field)({ nullable: true, description: 'When the name was archived: out of the tray and the batch door. Null while in use.' }),
54
+ tslib_1.__metadata("design:type", Date)
55
+ ], FigureTypeMapEntry.prototype, "archivedAt", void 0);
56
+ tslib_1.__decorate([
57
+ (0, typeorm_1.ManyToOne)(type => auth_base_1.User, { nullable: true }),
58
+ (0, type_graphql_1.Field)(type => auth_base_1.User, { nullable: true, description: 'The user who archived it.' }),
59
+ tslib_1.__metadata("design:type", auth_base_1.User
60
+ /*
61
+ Item (ADR-0155 개정 1 E, 2026-10-23): in this domain the name is drawn as an item a holder holds -- a carrier in
62
+ fmsim. Set on the library screen; the item type table offers only these names for a look, and a modeller's palette
63
+ leaves them out. Kept on the line, so a newer release of the name keeps it.
64
+ */
65
+ )
66
+ ], FigureTypeMapEntry.prototype, "archivedBy", void 0);
67
+ tslib_1.__decorate([
68
+ (0, typeorm_1.Column)({ default: false }),
69
+ (0, type_graphql_1.Field)({ description: 'Whether this domain draws the name as an item a holder holds (fmsim: a carrier).' }),
70
+ tslib_1.__metadata("design:type", Boolean)
71
+ ], FigureTypeMapEntry.prototype, "item", void 0);
51
72
  tslib_1.__decorate([
52
73
  (0, typeorm_1.CreateDateColumn)(),
53
74
  (0, type_graphql_1.Field)({ nullable: true, description: 'Creation timestamp.' }),
@@ -1 +1 @@
1
- {"version":3,"file":"figure-type-map-entry.js","sourceRoot":"","sources":["../../../server/service/figure-type-map/figure-type-map-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\n\n/**\n * One line of a domain's type map: a full name and the imported release it draws with (ADR-0114 결정 6, amended\n * 2026-10-12). The type map is these lines, one per name; it has no releases of its own. Importing a release of a new\n * name adds its line; a newer release of a name moves the line to it; the operator may choose another imported release.\n */\n@Entity()\n@Index('ix_figure_type_map_entry_1', (entry: FigureTypeMapEntry) => [entry.domain, entry.name], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@ObjectType({ description: 'A line of the domain type map: full name {namespace}/{type} to the imported release it draws with.' })\nexport class FigureTypeMapEntry {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier.' })\n readonly id?: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the line belongs to.' })\n domain?: Domain\n\n @RelationId((entry: FigureTypeMapEntry) => entry.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'Full figure name {namespace}/{type} boards store.' })\n name: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Note on the line.' })\n description?: string\n\n @ManyToOne(type => FigureLibraryRelease)\n @Field(type => FigureLibraryRelease, { nullable: true, description: 'The imported release this name draws with.' })\n release?: FigureLibraryRelease\n\n @RelationId((entry: FigureTypeMapEntry) => entry.release)\n releaseId?: string\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Creation timestamp.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Last update timestamp.' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'Soft-delete timestamp.' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who created it.' })\n creator?: User\n\n @RelationId((entry: FigureTypeMapEntry) => entry.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who last updated it.' })\n updater?: User\n\n @RelationId((entry: FigureTypeMapEntry) => entry.updater)\n updaterId?: string\n}\n"]}
1
+ {"version":3,"file":"figure-type-map-entry.js","sourceRoot":"","sources":["../../../server/service/figure-type-map/figure-type-map-entry.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Column,\n CreateDateColumn,\n DeleteDateColumn,\n Entity,\n Index,\n ManyToOne,\n PrimaryGeneratedColumn,\n RelationId,\n UpdateDateColumn\n} from 'typeorm'\nimport { Field, ID, ObjectType } from 'type-graphql'\n\nimport { User } from '@things-factory/auth-base'\nimport { Domain } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\n\n/**\n * One line of a domain's type map: a full name and the imported release it draws with (ADR-0114 결정 6, amended\n * 2026-10-12). The type map is these lines, one per name; it has no releases of its own. Importing a release of a new\n * name adds its line; a newer release of a name moves the line to it; the operator may choose another imported release.\n */\n@Entity()\n@Index('ix_figure_type_map_entry_1', (entry: FigureTypeMapEntry) => [entry.domain, entry.name], {\n unique: true,\n where: '\"deleted_at\" IS NULL'\n})\n@ObjectType({ description: 'A line of the domain type map: full name {namespace}/{type} to the imported release it draws with.' })\nexport class FigureTypeMapEntry {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID, { description: 'Unique identifier.' })\n readonly id?: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'The domain the line belongs to.' })\n domain?: Domain\n\n @RelationId((entry: FigureTypeMapEntry) => entry.domain)\n domainId?: string\n\n @Column()\n @Field({ description: 'Full figure name {namespace}/{type} boards store.' })\n name: string\n\n @Column({ type: 'text', nullable: true })\n @Field({ nullable: true, description: 'Note on the line.' })\n description?: string\n\n @ManyToOne(type => FigureLibraryRelease)\n @Field(type => FigureLibraryRelease, { nullable: true, description: 'The imported release this name draws with.' })\n release?: FigureLibraryRelease\n\n @RelationId((entry: FigureTypeMapEntry) => entry.release)\n releaseId?: string\n\n /*\n Archived (ADR-0114 결정 18, 2026-10-22): the way a figure leaves the library. The line and its releases stay -- a\n release is never changed or deleted (결정 3) -- but the name is out of the tray, the type map and the batch door.\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'When the name was archived: out of the tray and the batch door. Null while in use.' })\n archivedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who archived it.' })\n archivedBy?: User\n\n /*\n Item (ADR-0155 개정 1 E, 2026-10-23): in this domain the name is drawn as an item a holder holds -- a carrier in\n fmsim. Set on the library screen; the item type table offers only these names for a look, and a modeller's palette\n leaves them out. Kept on the line, so a newer release of the name keeps it.\n */\n @Column({ default: false })\n @Field({ description: 'Whether this domain draws the name as an item a holder holds (fmsim: a carrier).' })\n item?: boolean\n\n @CreateDateColumn()\n @Field({ nullable: true, description: 'Creation timestamp.' })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true, description: 'Last update timestamp.' })\n updatedAt?: Date\n\n @DeleteDateColumn()\n @Field({ nullable: true, description: 'Soft-delete timestamp.' })\n deletedAt?: Date\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who created it.' })\n creator?: User\n\n @RelationId((entry: FigureTypeMapEntry) => entry.creator)\n creatorId?: string\n\n @ManyToOne(type => User, { nullable: true })\n @Field(type => User, { nullable: true, description: 'The user who last updated it.' })\n updater?: User\n\n @RelationId((entry: FigureTypeMapEntry) => entry.updater)\n updaterId?: string\n}\n"]}
@@ -29,6 +29,10 @@ async function blueprintsOf(tx, domain, types, speak, files) {
29
29
  refuse(type, new shell_1.Refusal('figure-not-in-type-map', { type }, 'not in the type map of this domain.'));
30
30
  continue;
31
31
  }
32
+ if (line.archivedAt) {
33
+ refuse(type, new shell_1.Refusal('figure-archived', { type }, 'archived in this domain.'));
34
+ continue;
35
+ }
32
36
  if (!line.release || line.release.deletedAt) {
33
37
  refuse(type, new shell_1.Refusal('figure-release-unavailable', { type }, 'the release the type map chose is not in the library.'));
34
38
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"blueprints.js","sourceRoot":"","sources":["../../../server/service/library/blueprints.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { parseFigureName } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { dependencyFaults } from './inspection'\nimport { type LibraryFiles } from './library-files'\nimport { linesOf } from './type-map'\n\nexport const FIGURE_BLUEPRINTS_MAX = 500\n\nexport interface BlueprintsAnswer {\n items: { type: string; ref: string; asset: unknown }[]\n refused: { type: string; code: string; message: string }[]\n}\n\n/**\n * The batch door a board asks when it opens (ADR-0114 결정 9 · 13 · 14): every name answered from the domain's type\n * map, an item under the name asked or a refusal with its cause. One refusal never removes another item.\n */\nexport async function blueprintsOf(\n tx: EntityManager,\n domain: { id: string },\n types: readonly string[],\n speak: (refusal: Refusal) => string,\n files?: LibraryFiles\n): Promise<BlueprintsAnswer> {\n const names = [...new Set(types)]\n if (names.length > FIGURE_BLUEPRINTS_MAX) {\n throw new Refusal('figure-names-too-many', { count: names.length, max: FIGURE_BLUEPRINTS_MAX }, 'at most {max} figure names can be asked at once, not {count}.')\n }\n const lines = new Map((await linesOf(tx, domain)).map(line => [line.name, line]))\n\n const answer: BlueprintsAnswer = { items: [], refused: [] }\n const refuse = (type: string, refusal: Refusal) => answer.refused.push({ type, code: refusal.code, message: speak(refusal) })\n\n for (const type of names) {\n if (!parseFigureName(type)) {\n refuse(type, new Refusal('figure-name-malformed', { type }, '{type} is not a full figure name of the form namespace/type.'))\n continue\n }\n const line = lines.get(type)\n if (!line) {\n refuse(type, new Refusal('figure-not-in-type-map', { type }, 'not in the type map of this domain.'))\n continue\n }\n if (!line.release || line.release.deletedAt) {\n refuse(type, new Refusal('figure-release-unavailable', { type }, 'the release the type map chose is not in the library.'))\n continue\n }\n if ((await dependencyFaults(tx, line.release, files)).length) {\n refuse(type, new Refusal('figure-dependency-missing', { type, ref: line.release.ref }, 'a file {ref} depends on is missing or damaged.'))\n continue\n }\n let asset: unknown\n try {\n asset = JSON.parse(line.release.source)\n } catch {\n refuse(type, new Refusal('figure-format-unsupported', { type, ref: line.release.ref }, '{ref} cannot be read here.'))\n continue\n }\n answer.items.push({ type, ref: line.release.ref, asset })\n }\n return answer\n}\n"]}
1
+ {"version":3,"file":"blueprints.js","sourceRoot":"","sources":["../../../server/service/library/blueprints.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { parseFigureName } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { dependencyFaults } from './inspection'\nimport { type LibraryFiles } from './library-files'\nimport { linesOf } from './type-map'\n\nexport const FIGURE_BLUEPRINTS_MAX = 500\n\nexport interface BlueprintsAnswer {\n items: { type: string; ref: string; asset: unknown }[]\n refused: { type: string; code: string; message: string }[]\n}\n\n/**\n * The batch door a board asks when it opens (ADR-0114 결정 9 · 13 · 14): every name answered from the domain's type\n * map, an item under the name asked or a refusal with its cause. One refusal never removes another item.\n */\nexport async function blueprintsOf(\n tx: EntityManager,\n domain: { id: string },\n types: readonly string[],\n speak: (refusal: Refusal) => string,\n files?: LibraryFiles\n): Promise<BlueprintsAnswer> {\n const names = [...new Set(types)]\n if (names.length > FIGURE_BLUEPRINTS_MAX) {\n throw new Refusal('figure-names-too-many', { count: names.length, max: FIGURE_BLUEPRINTS_MAX }, 'at most {max} figure names can be asked at once, not {count}.')\n }\n const lines = new Map((await linesOf(tx, domain)).map(line => [line.name, line]))\n\n const answer: BlueprintsAnswer = { items: [], refused: [] }\n const refuse = (type: string, refusal: Refusal) => answer.refused.push({ type, code: refusal.code, message: speak(refusal) })\n\n for (const type of names) {\n if (!parseFigureName(type)) {\n refuse(type, new Refusal('figure-name-malformed', { type }, '{type} is not a full figure name of the form namespace/type.'))\n continue\n }\n const line = lines.get(type)\n if (!line) {\n refuse(type, new Refusal('figure-not-in-type-map', { type }, 'not in the type map of this domain.'))\n continue\n }\n if (line.archivedAt) {\n refuse(type, new Refusal('figure-archived', { type }, 'archived in this domain.'))\n continue\n }\n if (!line.release || line.release.deletedAt) {\n refuse(type, new Refusal('figure-release-unavailable', { type }, 'the release the type map chose is not in the library.'))\n continue\n }\n if ((await dependencyFaults(tx, line.release, files)).length) {\n refuse(type, new Refusal('figure-dependency-missing', { type, ref: line.release.ref }, 'a file {ref} depends on is missing or damaged.'))\n continue\n }\n let asset: unknown\n try {\n asset = JSON.parse(line.release.source)\n } catch {\n refuse(type, new Refusal('figure-format-unsupported', { type, ref: line.release.ref }, '{ref} cannot be read here.'))\n continue\n }\n answer.items.push({ type, ref: line.release.ref, asset })\n }\n return answer\n}\n"]}
@@ -15,6 +15,9 @@ async function extensionsOf(tx, domain, key) {
15
15
  }
16
16
  const answer = { items: [], unread: [] };
17
17
  for (const line of await (0, type_map_1.linesOf)(tx, domain)) {
18
+ /* An archived name is out of use (ADR-0114 결정 18): not answered, and not unread either. */
19
+ if (line.archivedAt)
20
+ continue;
18
21
  if (!line.release || line.release.deletedAt) {
19
22
  answer.unread.push({ name: line.name, ref: null });
20
23
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../../server/service/library/extensions.ts"],"names":[],"mappings":";;AAsBA,oCAqBC;AAzCD,yDAAgE;AAChE,iDAA+C;AAE/C,6CAAsC;AACtC,yCAAoC;AAepC,mHAAmH;AAC5G,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAsB,EAAE,GAAW;IACvF,IAAI,CAAC;QACH,IAAA,sCAAuB,EAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,eAAO,CAAC,sBAAsB,EAAE,EAAE,GAAG,EAAE,EAAE,+CAA+C,CAAC,CAAA;IACrG,CAAC;IACD,MAAM,MAAM,GAA2B,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IAChE,KAAK,MAAM,IAAI,IAAI,MAAM,IAAA,kBAAO,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;YAClD,SAAQ;QACV,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAA,oBAAO,EAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;YACzD,SAAQ;QACV,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAA;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { checkFigureExtensionKey } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { freshen } from './inspection'\nimport { linesOf } from './type-map'\n\n/**\n * The answer to one extension key (ADR-0123 결정 6): the type map lines whose release carries that key, with its value,\n * and the lines whose release this server's kernel cannot read, so whether they carry it is not known. A product builds\n * its own meaning on this answer (fmsim's carrier catalogue on `fmsim/carrier`); it does not read the releases itself.\n */\nexport interface FigureExtensionsAnswer {\n /** `name` is the line's canonical name {namespace}/{type}, the name a board draws it by. */\n /** `value` is the JSON value the release carries under the key, as it carries it. */\n items: { name: string; ref: string; value: unknown }[]\n /** `ref` is null for a line with no release this server can draw. */\n unread: { name: string; ref: string | null }[]\n}\n\n/** One key asked of the domain's type map. A key that is not `{owner}/{name}` is refused, by the kernel's rule. */\nexport async function extensionsOf(tx: EntityManager, domain: { id: string }, key: string): Promise<FigureExtensionsAnswer> {\n try {\n checkFigureExtensionKey(key)\n } catch {\n throw new Refusal('figure-extension-key', { key }, '{key} is not an extension key {owner}/{name}.')\n }\n const answer: FigureExtensionsAnswer = { items: [], unread: [] }\n for (const line of await linesOf(tx, domain)) {\n if (!line.release || line.release.deletedAt) {\n answer.unread.push({ name: line.name, ref: null })\n continue\n }\n const release = await freshen(tx, line.release)\n if (!release.formatSupported) {\n answer.unread.push({ name: line.name, ref: release.ref })\n continue\n }\n const value = release.extensions?.[key]\n if (value !== undefined) answer.items.push({ name: line.name, ref: release.ref, value })\n }\n return answer\n}\n"]}
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../../server/service/library/extensions.ts"],"names":[],"mappings":";;AAsBA,oCAuBC;AA3CD,yDAAgE;AAChE,iDAA+C;AAE/C,6CAAsC;AACtC,yCAAoC;AAepC,mHAAmH;AAC5G,KAAK,UAAU,YAAY,CAAC,EAAiB,EAAE,MAAsB,EAAE,GAAW;IACvF,IAAI,CAAC;QACH,IAAA,sCAAuB,EAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,eAAO,CAAC,sBAAsB,EAAE,EAAE,GAAG,EAAE,EAAE,+CAA+C,CAAC,CAAA;IACrG,CAAC;IACD,MAAM,MAAM,GAA2B,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;IAChE,KAAK,MAAM,IAAI,IAAI,MAAM,IAAA,kBAAO,EAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;QAC7C,2FAA2F;QAC3F,IAAI,IAAI,CAAC,UAAU;YAAE,SAAQ;QAC7B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;YAClD,SAAQ;QACV,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,IAAA,oBAAO,EAAC,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;YAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;YACzD,SAAQ;QACV,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAA;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;IAC1F,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { checkFigureExtensionKey } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { freshen } from './inspection'\nimport { linesOf } from './type-map'\n\n/**\n * The answer to one extension key (ADR-0123 결정 6): the type map lines whose release carries that key, with its value,\n * and the lines whose release this server's kernel cannot read, so whether they carry it is not known. A product builds\n * its own meaning on this answer (fmsim's carrier catalogue on `fmsim/carrier`); it does not read the releases itself.\n */\nexport interface FigureExtensionsAnswer {\n /** `name` is the line's canonical name {namespace}/{type}, the name a board draws it by. */\n /** `value` is the JSON value the release carries under the key, as it carries it. */\n items: { name: string; ref: string; value: unknown }[]\n /** `ref` is null for a line with no release this server can draw. */\n unread: { name: string; ref: string | null }[]\n}\n\n/** One key asked of the domain's type map. A key that is not `{owner}/{name}` is refused, by the kernel's rule. */\nexport async function extensionsOf(tx: EntityManager, domain: { id: string }, key: string): Promise<FigureExtensionsAnswer> {\n try {\n checkFigureExtensionKey(key)\n } catch {\n throw new Refusal('figure-extension-key', { key }, '{key} is not an extension key {owner}/{name}.')\n }\n const answer: FigureExtensionsAnswer = { items: [], unread: [] }\n for (const line of await linesOf(tx, domain)) {\n /* An archived name is out of use (ADR-0114 결정 18): not answered, and not unread either. */\n if (line.archivedAt) continue\n if (!line.release || line.release.deletedAt) {\n answer.unread.push({ name: line.name, ref: null })\n continue\n }\n const release = await freshen(tx, line.release)\n if (!release.formatSupported) {\n answer.unread.push({ name: line.name, ref: release.ref })\n continue\n }\n const value = release.extensions?.[key]\n if (value !== undefined) answer.items.push({ name: line.name, ref: release.ref, value })\n }\n return answer\n}\n"]}
@@ -12,6 +12,10 @@ export interface FiguresQuery {
12
12
  capability?: string;
13
13
  /** Part of the full name or the display name. */
14
14
  search?: string;
15
+ /** Archived names (ADR-0114 결정 18) are left out unless this is true. */
16
+ includeArchived?: boolean;
17
+ /** Only the names this domain draws as items (ADR-0155 개정 1 E). */
18
+ onlyItems?: boolean;
15
19
  page?: number;
16
20
  limit?: number;
17
21
  }
@@ -23,6 +27,10 @@ export interface FigureOfLibrary {
23
27
  latest: FigureLibraryRelease;
24
28
  latestVersion: number;
25
29
  releaseCount: number;
30
+ /** When the name was archived; null while in use. */
31
+ archivedAt: Date | null;
32
+ /** Whether this domain draws the name as an item a holder holds. */
33
+ item: boolean;
26
34
  }
27
35
  /**
28
36
  * The library by figure (ADR-0114, amended 2026-10-12): one item per full name with the release it draws with, its
@@ -22,6 +22,10 @@ async function figuresOf(tx, domain, query) {
22
22
  .createQueryBuilder('line')
23
23
  .leftJoinAndSelect('line.release', 'release')
24
24
  .where('line.domain_id = :domain', { domain: domain.id });
25
+ if (!query.includeArchived)
26
+ builder.andWhere('line.archived_at IS NULL');
27
+ if (query.onlyItems)
28
+ builder.andWhere('line.item = :item', { item: true });
25
29
  if (query.category === exports.FIGURE_FILTER_NONE)
26
30
  builder.andWhere(`(release.category IS NULL OR release.category = '')`);
27
31
  else if (query.category)
@@ -59,7 +63,7 @@ async function figuresOf(tx, domain, query) {
59
63
  items: lines.map(line => {
60
64
  const all = byName.get(line.name) ?? [];
61
65
  const latest = all[0] ?? line.release;
62
- return { name: line.name, release: line.release ?? null, latest, latestVersion: latest.version, releaseCount: all.length };
66
+ return { name: line.name, release: line.release ?? null, latest, latestVersion: latest.version, releaseCount: all.length, archivedAt: line.archivedAt ?? null, item: !!line.item };
63
67
  })
64
68
  };
65
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"figures.js","sourceRoot":"","sources":["../../../server/service/library/figures.ts"],"names":[],"mappings":"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","sourcesContent":["import { Brackets, EntityManager } from 'typeorm'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { FigureTypeMapEntry } from '../figure-type-map/figure-type-map-entry'\nimport { fullNameOf } from './type-map'\n\ntype Domain = { id: string }\n\n/** The filter value for a figure with no category, or one that declares no capability. */\nexport const FIGURE_FILTER_NONE = '-'\n\nexport interface FiguresQuery {\n /** A category, or FIGURE_FILTER_NONE for figures without one. */\n category?: string\n /** A place role (seat, grip), or FIGURE_FILTER_NONE for figures that declare no place. */\n capability?: string\n /** Part of the full name or the display name. */\n search?: string\n page?: number\n limit?: number\n}\n\nexport interface FigureOfLibrary {\n name: string\n /** The release in use; null when none of its releases can be drawn here. */\n release: FigureLibraryRelease | null\n /** The newest release imported, drawable or not. */\n latest: FigureLibraryRelease\n latestVersion: number\n releaseCount: number\n}\n\n/**\n * The library by figure (ADR-0114, amended 2026-10-12): one item per full name with the release it draws with, its\n * latest release and how many releases are imported. Filters and paging run in the database, so a library of thousands\n * pages like any list. Category and capability are those of the release that draws; a figure with none in use has\n * neither.\n */\nexport async function figuresOf(tx: EntityManager, domain: Domain, query: FiguresQuery): Promise<{ items: FigureOfLibrary[]; total: number }> {\n const builder = tx\n .getRepository(FigureTypeMapEntry)\n .createQueryBuilder('line')\n .leftJoinAndSelect('line.release', 'release')\n .where('line.domain_id = :domain', { domain: domain.id })\n\n if (query.category === FIGURE_FILTER_NONE) builder.andWhere(`(release.category IS NULL OR release.category = '')`)\n else if (query.category) builder.andWhere('release.category = :category', { category: query.category })\n\n /* `capabilities` is a JSON list of roles; a role is matched with its quotes so one never matches inside another. */\n if (query.capability === FIGURE_FILTER_NONE) builder.andWhere(`release.capabilities = '[]'`)\n else if (query.capability) builder.andWhere('release.capabilities LIKE :capability', { capability: `%\"${query.capability}\"%` })\n\n if (query.search) {\n const search = `%${query.search.toLowerCase()}%`\n builder.andWhere(new Brackets(where => where.where('LOWER(line.name) LIKE :search', { search }).orWhere('LOWER(release.name) LIKE :search', { search })))\n }\n\n const limit = Math.max(1, Math.min(query.limit ?? 50, 500))\n const page = Math.max(1, query.page ?? 1)\n const [lines, total] = await builder\n .orderBy('line.name', 'ASC')\n .skip((page - 1) * limit)\n .take(limit)\n .getManyAndCount()\n\n const releases = lines.length\n ? await tx.getRepository(FigureLibraryRelease).find({\n where: lines.map(line => {\n const slash = line.name.indexOf('/')\n return { domain: { id: domain.id }, namespace: line.name.slice(0, slash), type: line.name.slice(slash + 1) }\n }),\n order: { version: 'DESC' }\n })\n : []\n const byName = new Map<string, FigureLibraryRelease[]>()\n for (const release of releases) byName.set(fullNameOf(release), [...(byName.get(fullNameOf(release)) ?? []), release])\n\n return {\n total,\n items: lines.map(line => {\n const all = byName.get(line.name) ?? []\n const latest = all[0] ?? line.release!\n return { name: line.name, release: line.release ?? null, latest, latestVersion: latest.version, releaseCount: all.length }\n })\n }\n}\n\n/** What the filters offer: the categories and place roles of the releases that draw, and whether any has none. */\nexport async function figureFacetsOf(tx: EntityManager, domain: Domain) {\n const lines = await tx.getRepository(FigureTypeMapEntry).find({\n where: { domain: { id: domain.id } },\n relations: { release: true },\n select: { id: true, release: { id: true, category: true, capabilities: true } }\n })\n const drawn = lines.map(line => line.release).filter(Boolean) as FigureLibraryRelease[]\n return {\n categories: [...new Set(drawn.map(release => release.category).filter(Boolean) as string[])].sort(),\n uncategorized: drawn.some(release => !release.category),\n capabilities: [...new Set(drawn.flatMap(release => release.capabilities ?? []))].sort(),\n incapable: drawn.some(release => Array.isArray(release.capabilities) && release.capabilities.length === 0)\n }\n}\n\n/** Every imported release of one full name, newest first. */\nexport async function releasesOfName(tx: EntityManager, domain: Domain, name: string): Promise<FigureLibraryRelease[]> {\n const slash = name.indexOf('/')\n if (slash < 0) return []\n return await tx.getRepository(FigureLibraryRelease).find({\n where: { domain: { id: domain.id }, namespace: name.slice(0, slash), type: name.slice(slash + 1) },\n order: { version: 'DESC' }\n })\n}\n"]}
1
+ {"version":3,"file":"figures.js","sourceRoot":"","sources":["../../../server/service/library/figures.ts"],"names":[],"mappings":"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","sourcesContent":["import { Brackets, EntityManager } from 'typeorm'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { FigureTypeMapEntry } from '../figure-type-map/figure-type-map-entry'\nimport { fullNameOf } from './type-map'\n\ntype Domain = { id: string }\n\n/** The filter value for a figure with no category, or one that declares no capability. */\nexport const FIGURE_FILTER_NONE = '-'\n\nexport interface FiguresQuery {\n /** A category, or FIGURE_FILTER_NONE for figures without one. */\n category?: string\n /** A place role (seat, grip), or FIGURE_FILTER_NONE for figures that declare no place. */\n capability?: string\n /** Part of the full name or the display name. */\n search?: string\n /** Archived names (ADR-0114 결정 18) are left out unless this is true. */\n includeArchived?: boolean\n /** Only the names this domain draws as items (ADR-0155 개정 1 E). */\n onlyItems?: boolean\n page?: number\n limit?: number\n}\n\nexport interface FigureOfLibrary {\n name: string\n /** The release in use; null when none of its releases can be drawn here. */\n release: FigureLibraryRelease | null\n /** The newest release imported, drawable or not. */\n latest: FigureLibraryRelease\n latestVersion: number\n releaseCount: number\n /** When the name was archived; null while in use. */\n archivedAt: Date | null\n /** Whether this domain draws the name as an item a holder holds. */\n item: boolean\n}\n\n/**\n * The library by figure (ADR-0114, amended 2026-10-12): one item per full name with the release it draws with, its\n * latest release and how many releases are imported. Filters and paging run in the database, so a library of thousands\n * pages like any list. Category and capability are those of the release that draws; a figure with none in use has\n * neither.\n */\nexport async function figuresOf(tx: EntityManager, domain: Domain, query: FiguresQuery): Promise<{ items: FigureOfLibrary[]; total: number }> {\n const builder = tx\n .getRepository(FigureTypeMapEntry)\n .createQueryBuilder('line')\n .leftJoinAndSelect('line.release', 'release')\n .where('line.domain_id = :domain', { domain: domain.id })\n\n if (!query.includeArchived) builder.andWhere('line.archived_at IS NULL')\n if (query.onlyItems) builder.andWhere('line.item = :item', { item: true })\n\n if (query.category === FIGURE_FILTER_NONE) builder.andWhere(`(release.category IS NULL OR release.category = '')`)\n else if (query.category) builder.andWhere('release.category = :category', { category: query.category })\n\n /* `capabilities` is a JSON list of roles; a role is matched with its quotes so one never matches inside another. */\n if (query.capability === FIGURE_FILTER_NONE) builder.andWhere(`release.capabilities = '[]'`)\n else if (query.capability) builder.andWhere('release.capabilities LIKE :capability', { capability: `%\"${query.capability}\"%` })\n\n if (query.search) {\n const search = `%${query.search.toLowerCase()}%`\n builder.andWhere(new Brackets(where => where.where('LOWER(line.name) LIKE :search', { search }).orWhere('LOWER(release.name) LIKE :search', { search })))\n }\n\n const limit = Math.max(1, Math.min(query.limit ?? 50, 500))\n const page = Math.max(1, query.page ?? 1)\n const [lines, total] = await builder\n .orderBy('line.name', 'ASC')\n .skip((page - 1) * limit)\n .take(limit)\n .getManyAndCount()\n\n const releases = lines.length\n ? await tx.getRepository(FigureLibraryRelease).find({\n where: lines.map(line => {\n const slash = line.name.indexOf('/')\n return { domain: { id: domain.id }, namespace: line.name.slice(0, slash), type: line.name.slice(slash + 1) }\n }),\n order: { version: 'DESC' }\n })\n : []\n const byName = new Map<string, FigureLibraryRelease[]>()\n for (const release of releases) byName.set(fullNameOf(release), [...(byName.get(fullNameOf(release)) ?? []), release])\n\n return {\n total,\n items: lines.map(line => {\n const all = byName.get(line.name) ?? []\n const latest = all[0] ?? line.release!\n return { name: line.name, release: line.release ?? null, latest, latestVersion: latest.version, releaseCount: all.length, archivedAt: line.archivedAt ?? null, item: !!line.item }\n })\n }\n}\n\n/** What the filters offer: the categories and place roles of the releases that draw, and whether any has none. */\nexport async function figureFacetsOf(tx: EntityManager, domain: Domain) {\n const lines = await tx.getRepository(FigureTypeMapEntry).find({\n where: { domain: { id: domain.id } },\n relations: { release: true },\n select: { id: true, release: { id: true, category: true, capabilities: true } }\n })\n const drawn = lines.map(line => line.release).filter(Boolean) as FigureLibraryRelease[]\n return {\n categories: [...new Set(drawn.map(release => release.category).filter(Boolean) as string[])].sort(),\n uncategorized: drawn.some(release => !release.category),\n capabilities: [...new Set(drawn.flatMap(release => release.capabilities ?? []))].sort(),\n incapable: drawn.some(release => Array.isArray(release.capabilities) && release.capabilities.length === 0)\n }\n}\n\n/** Every imported release of one full name, newest first. */\nexport async function releasesOfName(tx: EntityManager, domain: Domain, name: string): Promise<FigureLibraryRelease[]> {\n const slash = name.indexOf('/')\n if (slash < 0) return []\n return await tx.getRepository(FigureLibraryRelease).find({\n where: { domain: { id: domain.id }, namespace: name.slice(0, slash), type: name.slice(slash + 1) },\n order: { version: 'DESC' }\n })\n}\n"]}
@@ -11,7 +11,7 @@ export declare function dependencyFaults(tx: EntityManager, release: FigureLibra
11
11
  hash: string;
12
12
  fault: 'missing' | 'corrupt';
13
13
  }[]>;
14
- export type RequirementStatus = 'drawn' | 'not-in-type-map' | 'release-unavailable' | 'dependency-missing' | 'format-unsupported';
14
+ export type RequirementStatus = 'drawn' | 'not-in-type-map' | 'archived' | 'release-unavailable' | 'dependency-missing' | 'format-unsupported';
15
15
  /**
16
16
  * What one board needs from the type map (ADR-0114 결정 8): each figure it holds, its status and node count. A
17
17
  * figure not in the library has dependencies this server cannot know.
@@ -54,6 +54,8 @@ async function boardRequirements(tx, domain, boardId, files) {
54
54
  let dependencies = 'unknown';
55
55
  if (!line)
56
56
  status = 'not-in-type-map';
57
+ else if (line.archivedAt)
58
+ status = 'archived';
57
59
  else if (!line.release)
58
60
  status = 'release-unavailable';
59
61
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"inspection.js","sourceRoot":"","sources":["../../../server/service/library/inspection.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { Board } from '@things-factory/board-service'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { derivedIsStale, derivedOfSource } from './derived'\nimport { figureNodesOf } from './board-walk'\nimport { libraryFileOf, libraryFiles, sha256Of, type LibraryFiles } from './library-files'\nimport { linesOf } from './type-map'\n\ntype Domain = { id: string }\n\n/** Brings a row's derived values up to this server's kernel, writing them back when they changed. */\nexport async function freshen(tx: EntityManager, release: FigureLibraryRelease): Promise<FigureLibraryRelease> {\n if (!derivedIsStale(release)) return release\n const derived = derivedOfSource(release.source)\n await tx.getRepository(FigureLibraryRelease).update({ id: release.id }, derived)\n return Object.assign(release, derived)\n}\n\n/** The dependency files of a release that are missing or whose bytes no longer hash to their name. */\nexport async function dependencyFaults(\n tx: EntityManager,\n release: FigureLibraryRelease,\n files: LibraryFiles = libraryFiles()\n): Promise<{ hash: string; fault: 'missing' | 'corrupt' }[]> {\n const faults: { hash: string; fault: 'missing' | 'corrupt' }[] = []\n for (const dependency of release.dependencies ?? []) {\n const attachment = await libraryFileOf(tx, release.domainId!, dependency.hash)\n if (!attachment) {\n faults.push({ hash: dependency.hash, fault: 'missing' })\n continue\n }\n try {\n const bytes = await files.read(attachment.path)\n if (sha256Of(bytes) !== dependency.hash) faults.push({ hash: dependency.hash, fault: 'corrupt' })\n } catch {\n faults.push({ hash: dependency.hash, fault: 'missing' })\n }\n }\n return faults\n}\n\nexport type RequirementStatus = 'drawn' | 'not-in-type-map' | 'release-unavailable' | 'dependency-missing' | 'format-unsupported'\n\n/**\n * What one board needs from the type map (ADR-0114 결정 8): each figure it holds, its status and node count. A\n * figure not in the library has dependencies this server cannot know.\n */\nexport async function boardRequirements(tx: EntityManager, domain: Domain, boardId: string, files?: LibraryFiles) {\n const board = await tx.getRepository(Board).findOne({ where: { id: boardId, domain: { id: domain.id } }, select: { id: true, model: true } })\n if (!board) return null\n const lines = new Map((await linesOf(tx, domain)).map(line => [line.name, line]))\n\n const figures = []\n for (const [name, nodes] of figureNodesOf(board.model)) {\n const line = lines.get(name)\n let status: RequirementStatus = 'drawn'\n let ref: string | null = null\n let dependencies: unknown = 'unknown'\n if (!line) status = 'not-in-type-map'\n else if (!line.release) status = 'release-unavailable'\n else {\n const release = await freshen(tx, line.release)\n ref = release.ref\n dependencies = release.dependencies ?? []\n if ((await dependencyFaults(tx, release, files)).length) status = 'dependency-missing'\n else if (!release.formatSupported) status = 'format-unsupported'\n }\n figures.push({ name, status, ref, nodes, dependencies })\n }\n return { board: board.id, figures }\n}\n"]}
1
+ {"version":3,"file":"inspection.js","sourceRoot":"","sources":["../../../server/service/library/inspection.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { Board } from '@things-factory/board-service'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { derivedIsStale, derivedOfSource } from './derived'\nimport { figureNodesOf } from './board-walk'\nimport { libraryFileOf, libraryFiles, sha256Of, type LibraryFiles } from './library-files'\nimport { linesOf } from './type-map'\n\ntype Domain = { id: string }\n\n/** Brings a row's derived values up to this server's kernel, writing them back when they changed. */\nexport async function freshen(tx: EntityManager, release: FigureLibraryRelease): Promise<FigureLibraryRelease> {\n if (!derivedIsStale(release)) return release\n const derived = derivedOfSource(release.source)\n await tx.getRepository(FigureLibraryRelease).update({ id: release.id }, derived)\n return Object.assign(release, derived)\n}\n\n/** The dependency files of a release that are missing or whose bytes no longer hash to their name. */\nexport async function dependencyFaults(\n tx: EntityManager,\n release: FigureLibraryRelease,\n files: LibraryFiles = libraryFiles()\n): Promise<{ hash: string; fault: 'missing' | 'corrupt' }[]> {\n const faults: { hash: string; fault: 'missing' | 'corrupt' }[] = []\n for (const dependency of release.dependencies ?? []) {\n const attachment = await libraryFileOf(tx, release.domainId!, dependency.hash)\n if (!attachment) {\n faults.push({ hash: dependency.hash, fault: 'missing' })\n continue\n }\n try {\n const bytes = await files.read(attachment.path)\n if (sha256Of(bytes) !== dependency.hash) faults.push({ hash: dependency.hash, fault: 'corrupt' })\n } catch {\n faults.push({ hash: dependency.hash, fault: 'missing' })\n }\n }\n return faults\n}\n\nexport type RequirementStatus = 'drawn' | 'not-in-type-map' | 'archived' | 'release-unavailable' | 'dependency-missing' | 'format-unsupported'\n\n/**\n * What one board needs from the type map (ADR-0114 결정 8): each figure it holds, its status and node count. A\n * figure not in the library has dependencies this server cannot know.\n */\nexport async function boardRequirements(tx: EntityManager, domain: Domain, boardId: string, files?: LibraryFiles) {\n const board = await tx.getRepository(Board).findOne({ where: { id: boardId, domain: { id: domain.id } }, select: { id: true, model: true } })\n if (!board) return null\n const lines = new Map((await linesOf(tx, domain)).map(line => [line.name, line]))\n\n const figures = []\n for (const [name, nodes] of figureNodesOf(board.model)) {\n const line = lines.get(name)\n let status: RequirementStatus = 'drawn'\n let ref: string | null = null\n let dependencies: unknown = 'unknown'\n if (!line) status = 'not-in-type-map'\n else if (line.archivedAt) status = 'archived'\n else if (!line.release) status = 'release-unavailable'\n else {\n const release = await freshen(tx, line.release)\n ref = release.ref\n dependencies = release.dependencies ?? []\n if ((await dependencyFaults(tx, release, files)).length) status = 'dependency-missing'\n else if (!release.formatSupported) status = 'format-unsupported'\n }\n figures.push({ name, status, ref, nodes, dependencies })\n }\n return { board: board.id, figures }\n}\n"]}
@@ -8,6 +8,8 @@ type Domain = {
8
8
  export declare const fullNameOf: (release: Pick<FigureLibraryRelease, "namespace" | "type">) => string;
9
9
  /** The domain's type map: one line per full name, with the release it draws with. */
10
10
  export declare function linesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]>;
11
+ /** The lines a board may draw from: the type map without its archived names (ADR-0114 결정 18) -- what the tray offers. */
12
+ export declare function inUseLinesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]>;
11
13
  /**
12
14
  * Makes `releaseId` the release `name` draws with (ADR-0114 결정 15): a release of this domain's library, and the
13
15
  * release of that very name. Refused by the first fault, named.
@@ -20,4 +22,17 @@ export declare function chooseRelease(tx: EntityManager, domain: Domain, user: u
20
22
  * over: a name that has only such releases keeps a line with no release, so it is listed and boards are told why.
21
23
  */
22
24
  export declare function putIntoTypeMap(tx: EntityManager, domain: Domain, user: unknown, release: FigureLibraryRelease): Promise<void>;
25
+ /**
26
+ * Archives `name` (ADR-0114 결정 18 · 19, 2026-10-22): the line and its releases stay, the name leaves the tray, the type
27
+ * map and the batch door. Refused while a board of this domain holds a node of it, naming those boards. Archiving a
28
+ * archived name changes nothing.
29
+ */
30
+ export declare function archiveFigure(tx: EntityManager, domain: Domain, user: unknown, name: string): Promise<FigureTypeMapEntry>;
31
+ /**
32
+ * Marks `name` as an item this domain draws -- a carrier in fmsim -- or takes the mark away (ADR-0155 개정 1 E). The
33
+ * mark is the line's: a newer release of the name keeps it. Marking it as it is changes nothing.
34
+ */
35
+ export declare function markFigureItem(tx: EntityManager, domain: Domain, user: unknown, name: string, item: boolean): Promise<FigureTypeMapEntry>;
36
+ /** Brings an archived name back (ADR-0114 결정 20), drawing with the release its line kept. Unarchiving one in use changes nothing. */
37
+ export declare function unarchiveFigure(tx: EntityManager, domain: Domain, user: unknown, name: string): Promise<FigureTypeMapEntry>;
23
38
  export {};
@@ -2,12 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fullNameOf = void 0;
4
4
  exports.linesOf = linesOf;
5
+ exports.inUseLinesOf = inUseLinesOf;
5
6
  exports.chooseRelease = chooseRelease;
6
7
  exports.putIntoTypeMap = putIntoTypeMap;
8
+ exports.archiveFigure = archiveFigure;
9
+ exports.markFigureItem = markFigureItem;
10
+ exports.unarchiveFigure = unarchiveFigure;
7
11
  const figure_model_1 = require("@hatiolab/figure-model");
12
+ const board_service_1 = require("@things-factory/board-service");
8
13
  const shell_1 = require("@things-factory/shell");
9
14
  const figure_library_release_1 = require("../figure-library-release/figure-library-release");
10
15
  const figure_type_map_entry_1 = require("../figure-type-map/figure-type-map-entry");
16
+ const board_walk_1 = require("./board-walk");
11
17
  /** The full name a release is the release of. */
12
18
  const fullNameOf = (release) => (0, figure_model_1.formatFigureName)({ namespace: release.namespace, type: release.type });
13
19
  exports.fullNameOf = fullNameOf;
@@ -15,6 +21,10 @@ exports.fullNameOf = fullNameOf;
15
21
  function linesOf(tx, domain) {
16
22
  return tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).find({ where: { domain: { id: domain.id } }, relations: { release: true }, order: { name: 'ASC' } });
17
23
  }
24
+ /** The lines a board may draw from: the type map without its archived names (ADR-0114 결정 18) -- what the tray offers. */
25
+ async function inUseLinesOf(tx, domain) {
26
+ return (await linesOf(tx, domain)).filter(line => !line.archivedAt);
27
+ }
18
28
  function lineOf(tx, domain, name) {
19
29
  return tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).findOne({ where: { domain: { id: domain.id }, name }, relations: { release: true } });
20
30
  }
@@ -66,4 +76,53 @@ async function setLine(tx, domain, user, name, release) {
66
76
  await repository.save({ domain, name, release: target, creator: user, updater: user });
67
77
  return (await lineOf(tx, domain, name));
68
78
  }
79
+ /** How many boards a refusal names before it says how many more there are. */
80
+ const BOARDS_NAMED = 5;
81
+ /** The boards of this domain that hold a node of `name`, by board name. */
82
+ async function boardsUsing(tx, domain, name) {
83
+ const boards = await tx.getRepository(board_service_1.Board).find({ where: { domain: { id: domain.id } }, select: { id: true, name: true, model: true } });
84
+ return boards.filter(board => (0, board_walk_1.figureNodesOf)(board.model).has(name)).map(board => board.name ?? board.id);
85
+ }
86
+ /**
87
+ * Archives `name` (ADR-0114 결정 18 · 19, 2026-10-22): the line and its releases stay, the name leaves the tray, the type
88
+ * map and the batch door. Refused while a board of this domain holds a node of it, naming those boards. Archiving a
89
+ * archived name changes nothing.
90
+ */
91
+ async function archiveFigure(tx, domain, user, name) {
92
+ const line = await lineOf(tx, domain, name);
93
+ if (!line)
94
+ throw new shell_1.Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.');
95
+ if (line.archivedAt)
96
+ return line;
97
+ const boards = await boardsUsing(tx, domain, name);
98
+ if (boards.length) {
99
+ const shown = boards.slice(0, BOARDS_NAMED).join(', ') + (boards.length > BOARDS_NAMED ? ` +${boards.length - BOARDS_NAMED}` : '');
100
+ throw new shell_1.Refusal('figure-in-use', { name, count: boards.length, boards: shown }, '{name} is on {count} board(s): {boards}. Take it off them first.');
101
+ }
102
+ await tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).update({ id: line.id }, { archivedAt: new Date(), archivedBy: user, updater: user });
103
+ return (await lineOf(tx, domain, name));
104
+ }
105
+ /**
106
+ * Marks `name` as an item this domain draws -- a carrier in fmsim -- or takes the mark away (ADR-0155 개정 1 E). The
107
+ * mark is the line's: a newer release of the name keeps it. Marking it as it is changes nothing.
108
+ */
109
+ async function markFigureItem(tx, domain, user, name, item) {
110
+ const line = await lineOf(tx, domain, name);
111
+ if (!line)
112
+ throw new shell_1.Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.');
113
+ if (!!line.item === item)
114
+ return line;
115
+ await tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).update({ id: line.id }, { item, updater: user });
116
+ return (await lineOf(tx, domain, name));
117
+ }
118
+ /** Brings an archived name back (ADR-0114 결정 20), drawing with the release its line kept. Unarchiving one in use changes nothing. */
119
+ async function unarchiveFigure(tx, domain, user, name) {
120
+ const line = await lineOf(tx, domain, name);
121
+ if (!line)
122
+ throw new shell_1.Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.');
123
+ if (!line.archivedAt)
124
+ return line;
125
+ await tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).update({ id: line.id }, { archivedAt: null, archivedBy: null, updater: user });
126
+ return (await lineOf(tx, domain, name));
127
+ }
69
128
  //# sourceMappingURL=type-map.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"type-map.js","sourceRoot":"","sources":["../../../server/service/library/type-map.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { formatFigureName } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { FigureTypeMapEntry } from '../figure-type-map/figure-type-map-entry'\n\ntype Domain = { id: string }\n\n/** The full name a release is the release of. */\nexport const fullNameOf = (release: Pick<FigureLibraryRelease, 'namespace' | 'type'>) =>\n formatFigureName({ namespace: release.namespace, type: release.type })\n\n/** The domain's type map: one line per full name, with the release it draws with. */\nexport function linesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]> {\n return tx.getRepository(FigureTypeMapEntry).find({ where: { domain: { id: domain.id } }, relations: { release: true }, order: { name: 'ASC' } })\n}\n\nfunction lineOf(tx: EntityManager, domain: Domain, name: string): Promise<FigureTypeMapEntry | null> {\n return tx.getRepository(FigureTypeMapEntry).findOne({ where: { domain: { id: domain.id }, name }, relations: { release: true } })\n}\n\n/**\n * Makes `releaseId` the release `name` draws with (ADR-0114 결정 15): a release of this domain's library, and the\n * release of that very name. Refused by the first fault, named.\n */\nexport async function chooseRelease(tx: EntityManager, domain: Domain, user: unknown, name: string, releaseId: string): Promise<FigureTypeMapEntry> {\n const release = await tx.getRepository(FigureLibraryRelease).findOne({ where: { id: releaseId } })\n if (!release) {\n throw new Refusal('figure-type-map-missing-release', { name }, 'the release chosen for {name} is not in the library.')\n }\n if (release.domainId !== domain.id) {\n throw new Refusal('figure-type-map-foreign-release', { name }, 'the release chosen for {name} belongs to another domain.')\n }\n if (fullNameOf(release) !== name) {\n throw new Refusal('figure-type-map-name-mismatch', { name, ref: release.ref }, '{ref} is not a release of {name}.')\n }\n if (!release.formatSupported) {\n throw new Refusal('figure-type-map-format-unsupported', { name, ref: release.ref }, '{ref} is in a format this server cannot draw.')\n }\n return await setLine(tx, domain, user, name, release)\n}\n\n/**\n * What an import does to the type map (ADR-0114 결정 7, amended 2026-10-12): the newest release this server can draw is\n * the one in use. A release of a name the map does not have adds its line; a drawable release newer than the one a line\n * draws with moves the line to it; an older one leaves it. A release in a format this server cannot read never takes\n * over: a name that has only such releases keeps a line with no release, so it is listed and boards are told why.\n */\nexport async function putIntoTypeMap(tx: EntityManager, domain: Domain, user: unknown, release: FigureLibraryRelease): Promise<void> {\n const name = fullNameOf(release)\n const line = await lineOf(tx, domain, name)\n if (!release.formatSupported) {\n if (!line) await setLine(tx, domain, user, name, null)\n return\n }\n if (line?.release && line.release.version >= release.version) return\n await setLine(tx, domain, user, name, release)\n}\n\nasync function setLine(tx: EntityManager, domain: Domain, user: unknown, name: string, release: FigureLibraryRelease | null): Promise<FigureTypeMapEntry> {\n const repository = tx.getRepository(FigureTypeMapEntry)\n const line = await lineOf(tx, domain, name)\n const target = release ? { id: release.id } : null\n if (line) await repository.update({ id: line.id }, { release: target, updater: user } as any)\n else await repository.save({ domain, name, release: target, creator: user, updater: user } as Partial<FigureTypeMapEntry>)\n return (await lineOf(tx, domain, name))!\n}\n"]}
1
+ {"version":3,"file":"type-map.js","sourceRoot":"","sources":["../../../server/service/library/type-map.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { formatFigureName } from '@hatiolab/figure-model'\nimport { Board } from '@things-factory/board-service'\nimport { Refusal } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { FigureTypeMapEntry } from '../figure-type-map/figure-type-map-entry'\nimport { figureNodesOf } from './board-walk'\n\ntype Domain = { id: string }\n\n/** The full name a release is the release of. */\nexport const fullNameOf = (release: Pick<FigureLibraryRelease, 'namespace' | 'type'>) =>\n formatFigureName({ namespace: release.namespace, type: release.type })\n\n/** The domain's type map: one line per full name, with the release it draws with. */\nexport function linesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]> {\n return tx.getRepository(FigureTypeMapEntry).find({ where: { domain: { id: domain.id } }, relations: { release: true }, order: { name: 'ASC' } })\n}\n\n/** The lines a board may draw from: the type map without its archived names (ADR-0114 결정 18) -- what the tray offers. */\nexport async function inUseLinesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]> {\n return (await linesOf(tx, domain)).filter(line => !line.archivedAt)\n}\n\nfunction lineOf(tx: EntityManager, domain: Domain, name: string): Promise<FigureTypeMapEntry | null> {\n return tx.getRepository(FigureTypeMapEntry).findOne({ where: { domain: { id: domain.id }, name }, relations: { release: true } })\n}\n\n/**\n * Makes `releaseId` the release `name` draws with (ADR-0114 결정 15): a release of this domain's library, and the\n * release of that very name. Refused by the first fault, named.\n */\nexport async function chooseRelease(tx: EntityManager, domain: Domain, user: unknown, name: string, releaseId: string): Promise<FigureTypeMapEntry> {\n const release = await tx.getRepository(FigureLibraryRelease).findOne({ where: { id: releaseId } })\n if (!release) {\n throw new Refusal('figure-type-map-missing-release', { name }, 'the release chosen for {name} is not in the library.')\n }\n if (release.domainId !== domain.id) {\n throw new Refusal('figure-type-map-foreign-release', { name }, 'the release chosen for {name} belongs to another domain.')\n }\n if (fullNameOf(release) !== name) {\n throw new Refusal('figure-type-map-name-mismatch', { name, ref: release.ref }, '{ref} is not a release of {name}.')\n }\n if (!release.formatSupported) {\n throw new Refusal('figure-type-map-format-unsupported', { name, ref: release.ref }, '{ref} is in a format this server cannot draw.')\n }\n return await setLine(tx, domain, user, name, release)\n}\n\n/**\n * What an import does to the type map (ADR-0114 결정 7, amended 2026-10-12): the newest release this server can draw is\n * the one in use. A release of a name the map does not have adds its line; a drawable release newer than the one a line\n * draws with moves the line to it; an older one leaves it. A release in a format this server cannot read never takes\n * over: a name that has only such releases keeps a line with no release, so it is listed and boards are told why.\n */\nexport async function putIntoTypeMap(tx: EntityManager, domain: Domain, user: unknown, release: FigureLibraryRelease): Promise<void> {\n const name = fullNameOf(release)\n const line = await lineOf(tx, domain, name)\n if (!release.formatSupported) {\n if (!line) await setLine(tx, domain, user, name, null)\n return\n }\n if (line?.release && line.release.version >= release.version) return\n await setLine(tx, domain, user, name, release)\n}\n\nasync function setLine(tx: EntityManager, domain: Domain, user: unknown, name: string, release: FigureLibraryRelease | null): Promise<FigureTypeMapEntry> {\n const repository = tx.getRepository(FigureTypeMapEntry)\n const line = await lineOf(tx, domain, name)\n const target = release ? { id: release.id } : null\n if (line) await repository.update({ id: line.id }, { release: target, updater: user } as any)\n else await repository.save({ domain, name, release: target, creator: user, updater: user } as Partial<FigureTypeMapEntry>)\n return (await lineOf(tx, domain, name))!\n}\n\n/** How many boards a refusal names before it says how many more there are. */\nconst BOARDS_NAMED = 5\n\n/** The boards of this domain that hold a node of `name`, by board name. */\nasync function boardsUsing(tx: EntityManager, domain: Domain, name: string): Promise<string[]> {\n const boards = await tx.getRepository(Board).find({ where: { domain: { id: domain.id } }, select: { id: true, name: true, model: true } })\n return boards.filter(board => figureNodesOf(board.model).has(name)).map(board => board.name ?? board.id)\n}\n\n/**\n * Archives `name` (ADR-0114 결정 18 · 19, 2026-10-22): the line and its releases stay, the name leaves the tray, the type\n * map and the batch door. Refused while a board of this domain holds a node of it, naming those boards. Archiving a\n * archived name changes nothing.\n */\nexport async function archiveFigure(tx: EntityManager, domain: Domain, user: unknown, name: string): Promise<FigureTypeMapEntry> {\n const line = await lineOf(tx, domain, name)\n if (!line) throw new Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.')\n if (line.archivedAt) return line\n const boards = await boardsUsing(tx, domain, name)\n if (boards.length) {\n const shown = boards.slice(0, BOARDS_NAMED).join(', ') + (boards.length > BOARDS_NAMED ? ` +${boards.length - BOARDS_NAMED}` : '')\n throw new Refusal('figure-in-use', { name, count: boards.length, boards: shown }, '{name} is on {count} board(s): {boards}. Take it off them first.')\n }\n await tx.getRepository(FigureTypeMapEntry).update({ id: line.id }, { archivedAt: new Date(), archivedBy: user, updater: user } as any)\n return (await lineOf(tx, domain, name))!\n}\n\n/**\n * Marks `name` as an item this domain draws -- a carrier in fmsim -- or takes the mark away (ADR-0155 개정 1 E). The\n * mark is the line's: a newer release of the name keeps it. Marking it as it is changes nothing.\n */\nexport async function markFigureItem(tx: EntityManager, domain: Domain, user: unknown, name: string, item: boolean): Promise<FigureTypeMapEntry> {\n const line = await lineOf(tx, domain, name)\n if (!line) throw new Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.')\n if (!!line.item === item) return line\n await tx.getRepository(FigureTypeMapEntry).update({ id: line.id }, { item, updater: user } as any)\n return (await lineOf(tx, domain, name))!\n}\n\n/** Brings an archived name back (ADR-0114 결정 20), drawing with the release its line kept. Unarchiving one in use changes nothing. */\nexport async function unarchiveFigure(tx: EntityManager, domain: Domain, user: unknown, name: string): Promise<FigureTypeMapEntry> {\n const line = await lineOf(tx, domain, name)\n if (!line) throw new Refusal('figure-not-in-type-map', { type: name }, 'not in the type map of this domain.')\n if (!line.archivedAt) return line\n await tx.getRepository(FigureTypeMapEntry).update({ id: line.id }, { archivedAt: null, archivedBy: null, updater: user } as any)\n return (await lineOf(tx, domain, name))!\n}\n"]}