@things-factory/figure-library 10.1.153 → 10.1.155
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/pages/figure-library-page.d.ts +20 -58
- package/dist-client/pages/figure-library-page.js +189 -672
- package/dist-client/pages/figure-library-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/figure-card-meta.d.ts +19 -0
- package/dist-client/viewparts/figure-card-meta.js +122 -0
- package/dist-client/viewparts/figure-card-meta.js.map +1 -0
- package/dist-client/viewparts/figure-releases-popup.d.ts +34 -0
- package/dist-client/viewparts/figure-releases-popup.js +400 -0
- package/dist-client/viewparts/figure-releases-popup.js.map +1 -0
- package/dist-client/viewparts/release-pair.d.ts +9 -0
- package/dist-client/viewparts/release-pair.js +15 -0
- package/dist-client/viewparts/release-pair.js.map +1 -0
- package/dist-server/service/figure-library-release/figure-library-release.d.ts +5 -3
- package/dist-server/service/figure-library-release/figure-library-release.js +13 -3
- package/dist-server/service/figure-library-release/figure-library-release.js.map +1 -1
- package/dist-server/service/figure-library-resolver.d.ts +9 -9
- package/dist-server/service/figure-library-resolver.js +66 -69
- package/dist-server/service/figure-library-resolver.js.map +1 -1
- package/dist-server/service/figure-library-types.d.ts +17 -4
- package/dist-server/service/figure-library-types.js +63 -15
- package/dist-server/service/figure-library-types.js.map +1 -1
- package/dist-server/service/figure-type-map/figure-type-map-entry.d.ts +5 -4
- package/dist-server/service/figure-type-map/figure-type-map-entry.js +8 -14
- package/dist-server/service/figure-type-map/figure-type-map-entry.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -3
- package/dist-server/service/index.js +2 -3
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/library/blueprints.d.ts +3 -8
- package/dist-server/service/library/blueprints.js +5 -6
- package/dist-server/service/library/blueprints.js.map +1 -1
- package/dist-server/service/library/carriers.d.ts +8 -11
- package/dist-server/service/library/carriers.js +8 -14
- package/dist-server/service/library/carriers.js.map +1 -1
- package/dist-server/service/library/derived.d.ts +5 -3
- package/dist-server/service/library/derived.js +4 -3
- package/dist-server/service/library/derived.js.map +1 -1
- package/dist-server/service/library/figures.d.ts +46 -0
- package/dist-server/service/library/figures.js +91 -0
- package/dist-server/service/library/figures.js.map +1 -0
- package/dist-server/service/library/import.d.ts +1 -1
- package/dist-server/service/library/import.js +5 -3
- package/dist-server/service/library/import.js.map +1 -1
- package/dist-server/service/library/inspection.d.ts +1 -48
- package/dist-server/service/library/inspection.js +3 -80
- package/dist-server/service/library/inspection.js.map +1 -1
- package/dist-server/service/library/type-map.d.ts +10 -22
- package/dist-server/service/library/type-map.js +44 -108
- package/dist-server/service/library/type-map.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -10
- package/translations/en.json +26 -47
- package/translations/es.json +26 -47
- package/translations/ja.json +26 -47
- package/translations/ko.json +26 -47
- package/translations/ms.json +26 -47
- package/translations/zh.json +26 -47
- package/dist-server/service/figure-type-map/figure-type-map.d.ts +0 -34
- package/dist-server/service/figure-type-map/figure-type-map.js +0 -120
- package/dist-server/service/figure-type-map/figure-type-map.js.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EntityManager } from 'typeorm';
|
|
2
|
+
import { FigureLibraryRelease } from '../figure-library-release/figure-library-release';
|
|
3
|
+
type Domain = {
|
|
4
|
+
id: string;
|
|
5
|
+
};
|
|
6
|
+
/** The filter value for a figure with no category, or one that declares no capability. */
|
|
7
|
+
export declare const FIGURE_FILTER_NONE = "-";
|
|
8
|
+
export interface FiguresQuery {
|
|
9
|
+
/** A category, or FIGURE_FILTER_NONE for figures without one. */
|
|
10
|
+
category?: string;
|
|
11
|
+
/** A place role (seat, grip), or FIGURE_FILTER_NONE for figures that declare no place. */
|
|
12
|
+
capability?: string;
|
|
13
|
+
/** Part of the full name or the display name. */
|
|
14
|
+
search?: string;
|
|
15
|
+
page?: number;
|
|
16
|
+
limit?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface FigureOfLibrary {
|
|
19
|
+
name: string;
|
|
20
|
+
/** The release in use; null when none of its releases can be drawn here. */
|
|
21
|
+
release: FigureLibraryRelease | null;
|
|
22
|
+
/** The newest release imported, drawable or not. */
|
|
23
|
+
latest: FigureLibraryRelease;
|
|
24
|
+
latestVersion: number;
|
|
25
|
+
releaseCount: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The library by figure (ADR-0114, amended 2026-10-12): one item per full name with the release it draws with, its
|
|
29
|
+
* latest release and how many releases are imported. Filters and paging run in the database, so a library of thousands
|
|
30
|
+
* pages like any list. Category and capability are those of the release that draws; a figure with none in use has
|
|
31
|
+
* neither.
|
|
32
|
+
*/
|
|
33
|
+
export declare function figuresOf(tx: EntityManager, domain: Domain, query: FiguresQuery): Promise<{
|
|
34
|
+
items: FigureOfLibrary[];
|
|
35
|
+
total: number;
|
|
36
|
+
}>;
|
|
37
|
+
/** What the filters offer: the categories and place roles of the releases that draw, and whether any has none. */
|
|
38
|
+
export declare function figureFacetsOf(tx: EntityManager, domain: Domain): Promise<{
|
|
39
|
+
categories: string[];
|
|
40
|
+
uncategorized: boolean;
|
|
41
|
+
capabilities: string[];
|
|
42
|
+
incapable: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
/** Every imported release of one full name, newest first. */
|
|
45
|
+
export declare function releasesOfName(tx: EntityManager, domain: Domain, name: string): Promise<FigureLibraryRelease[]>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIGURE_FILTER_NONE = void 0;
|
|
4
|
+
exports.figuresOf = figuresOf;
|
|
5
|
+
exports.figureFacetsOf = figureFacetsOf;
|
|
6
|
+
exports.releasesOfName = releasesOfName;
|
|
7
|
+
const typeorm_1 = require("typeorm");
|
|
8
|
+
const figure_library_release_1 = require("../figure-library-release/figure-library-release");
|
|
9
|
+
const figure_type_map_entry_1 = require("../figure-type-map/figure-type-map-entry");
|
|
10
|
+
const type_map_1 = require("./type-map");
|
|
11
|
+
/** The filter value for a figure with no category, or one that declares no capability. */
|
|
12
|
+
exports.FIGURE_FILTER_NONE = '-';
|
|
13
|
+
/**
|
|
14
|
+
* The library by figure (ADR-0114, amended 2026-10-12): one item per full name with the release it draws with, its
|
|
15
|
+
* latest release and how many releases are imported. Filters and paging run in the database, so a library of thousands
|
|
16
|
+
* pages like any list. Category and capability are those of the release that draws; a figure with none in use has
|
|
17
|
+
* neither.
|
|
18
|
+
*/
|
|
19
|
+
async function figuresOf(tx, domain, query) {
|
|
20
|
+
const builder = tx
|
|
21
|
+
.getRepository(figure_type_map_entry_1.FigureTypeMapEntry)
|
|
22
|
+
.createQueryBuilder('line')
|
|
23
|
+
.leftJoinAndSelect('line.release', 'release')
|
|
24
|
+
.where('line.domain_id = :domain', { domain: domain.id });
|
|
25
|
+
if (query.category === exports.FIGURE_FILTER_NONE)
|
|
26
|
+
builder.andWhere(`(release.category IS NULL OR release.category = '')`);
|
|
27
|
+
else if (query.category)
|
|
28
|
+
builder.andWhere('release.category = :category', { category: query.category });
|
|
29
|
+
/* `capabilities` is a JSON list of roles; a role is matched with its quotes so one never matches inside another. */
|
|
30
|
+
if (query.capability === exports.FIGURE_FILTER_NONE)
|
|
31
|
+
builder.andWhere(`release.capabilities = '[]'`);
|
|
32
|
+
else if (query.capability)
|
|
33
|
+
builder.andWhere('release.capabilities LIKE :capability', { capability: `%"${query.capability}"%` });
|
|
34
|
+
if (query.search) {
|
|
35
|
+
const search = `%${query.search.toLowerCase()}%`;
|
|
36
|
+
builder.andWhere(new typeorm_1.Brackets(where => where.where('LOWER(line.name) LIKE :search', { search }).orWhere('LOWER(release.name) LIKE :search', { search })));
|
|
37
|
+
}
|
|
38
|
+
const limit = Math.max(1, Math.min(query.limit ?? 50, 500));
|
|
39
|
+
const page = Math.max(1, query.page ?? 1);
|
|
40
|
+
const [lines, total] = await builder
|
|
41
|
+
.orderBy('line.name', 'ASC')
|
|
42
|
+
.skip((page - 1) * limit)
|
|
43
|
+
.take(limit)
|
|
44
|
+
.getManyAndCount();
|
|
45
|
+
const releases = lines.length
|
|
46
|
+
? await tx.getRepository(figure_library_release_1.FigureLibraryRelease).find({
|
|
47
|
+
where: lines.map(line => {
|
|
48
|
+
const slash = line.name.indexOf('/');
|
|
49
|
+
return { domain: { id: domain.id }, namespace: line.name.slice(0, slash), type: line.name.slice(slash + 1) };
|
|
50
|
+
}),
|
|
51
|
+
order: { version: 'DESC' }
|
|
52
|
+
})
|
|
53
|
+
: [];
|
|
54
|
+
const byName = new Map();
|
|
55
|
+
for (const release of releases)
|
|
56
|
+
byName.set((0, type_map_1.fullNameOf)(release), [...(byName.get((0, type_map_1.fullNameOf)(release)) ?? []), release]);
|
|
57
|
+
return {
|
|
58
|
+
total,
|
|
59
|
+
items: lines.map(line => {
|
|
60
|
+
const all = byName.get(line.name) ?? [];
|
|
61
|
+
const latest = all[0] ?? line.release;
|
|
62
|
+
return { name: line.name, release: line.release ?? null, latest, latestVersion: latest.version, releaseCount: all.length };
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** What the filters offer: the categories and place roles of the releases that draw, and whether any has none. */
|
|
67
|
+
async function figureFacetsOf(tx, domain) {
|
|
68
|
+
const lines = await tx.getRepository(figure_type_map_entry_1.FigureTypeMapEntry).find({
|
|
69
|
+
where: { domain: { id: domain.id } },
|
|
70
|
+
relations: { release: true },
|
|
71
|
+
select: { id: true, release: { id: true, category: true, capabilities: true } }
|
|
72
|
+
});
|
|
73
|
+
const drawn = lines.map(line => line.release).filter(Boolean);
|
|
74
|
+
return {
|
|
75
|
+
categories: [...new Set(drawn.map(release => release.category).filter(Boolean))].sort(),
|
|
76
|
+
uncategorized: drawn.some(release => !release.category),
|
|
77
|
+
capabilities: [...new Set(drawn.flatMap(release => release.capabilities ?? []))].sort(),
|
|
78
|
+
incapable: drawn.some(release => Array.isArray(release.capabilities) && release.capabilities.length === 0)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Every imported release of one full name, newest first. */
|
|
82
|
+
async function releasesOfName(tx, domain, name) {
|
|
83
|
+
const slash = name.indexOf('/');
|
|
84
|
+
if (slash < 0)
|
|
85
|
+
return [];
|
|
86
|
+
return await tx.getRepository(figure_library_release_1.FigureLibraryRelease).find({
|
|
87
|
+
where: { domain: { id: domain.id }, namespace: name.slice(0, slash), type: name.slice(slash + 1) },
|
|
88
|
+
order: { version: 'DESC' }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=figures.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -20,7 +20,7 @@ export interface ImportResult {
|
|
|
20
20
|
* Imports a figure package into this domain's library (ADR-0114 결정 5 · 16).
|
|
21
21
|
*
|
|
22
22
|
* A release is known by its reference and the hash of its source, nothing else. Not here: added, its dependency files
|
|
23
|
-
* kept, and put into the
|
|
23
|
+
* kept, and put into the type map when it is the latest of its name. The same source: skipped, the row left as it is, the fields that differ named.
|
|
24
24
|
* Another source under the same reference: refused. A format this server cannot read is still imported.
|
|
25
25
|
*/
|
|
26
26
|
export declare function importFigurePackage(tx: EntityManager, domain: {
|
|
@@ -15,7 +15,7 @@ const json = (value) => (value == null ? null : JSON.stringify(value));
|
|
|
15
15
|
* Imports a figure package into this domain's library (ADR-0114 결정 5 · 16).
|
|
16
16
|
*
|
|
17
17
|
* A release is known by its reference and the hash of its source, nothing else. Not here: added, its dependency files
|
|
18
|
-
* kept, and put into the
|
|
18
|
+
* kept, and put into the type map when it is the latest of its name. The same source: skipped, the row left as it is, the fields that differ named.
|
|
19
19
|
* Another source under the same reference: refused. A format this server cannot read is still imported.
|
|
20
20
|
*/
|
|
21
21
|
async function importFigurePackage(tx, domain, user, bytes, packageName, files = (0, library_files_1.libraryFiles)()) {
|
|
@@ -79,10 +79,11 @@ async function importFigurePackage(tx, domain, user, bytes, packageName, files =
|
|
|
79
79
|
sourceDomain: read.manifest.source.domain,
|
|
80
80
|
packageName,
|
|
81
81
|
releasedAt: release.releasedAt ? new Date(release.releasedAt) : undefined,
|
|
82
|
+
comment: release.comment ?? undefined,
|
|
82
83
|
creator: user,
|
|
83
84
|
updater: user
|
|
84
85
|
});
|
|
85
|
-
await (0, type_map_1.
|
|
86
|
+
await (0, type_map_1.putIntoTypeMap)(tx, domain, user, saved);
|
|
86
87
|
outcomes.push({ ref: release.ref, outcome: 'added' });
|
|
87
88
|
}
|
|
88
89
|
return { problems: [], outcomes };
|
|
@@ -103,7 +104,8 @@ function differingFields(row, release, blobs) {
|
|
|
103
104
|
['changes', parsed(row.changes), release.changes],
|
|
104
105
|
['dependencies', (row.dependencies ?? []).map(d => d.hash), blobs.map(b => b.hash)],
|
|
105
106
|
['deprecated', !!row.deprecated, !!release.deprecated],
|
|
106
|
-
['supersededBy', row.supersededBy, release.supersededBy]
|
|
107
|
+
['supersededBy', row.supersededBy, release.supersededBy],
|
|
108
|
+
['comment', row.comment, release.comment]
|
|
107
109
|
];
|
|
108
110
|
return pairs.filter(([, a, b]) => !same(a, b)).map(([field]) => field);
|
|
109
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../../server/service/library/import.ts"],"names":[],"mappings":"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","sourcesContent":["import JSZip from 'jszip'\nimport { EntityManager } from 'typeorm'\n\nimport { parseFigureRef, readFigurePackage, type FigurePackageRelease } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { derivedOfSource } from './derived'\nimport { keepLibraryFile, libraryFiles, sha256Of, type LibraryFiles } from './library-files'\nimport { putIntoCandidate } from './type-map'\n\nexport type ImportOutcomeKind = 'added' | 'skipped' | 'refused'\n\nexport interface ImportOutcome {\n ref: string\n outcome: ImportOutcomeKind\n code?: string\n /** For a skipped release: the fields the package gave differently from the row already here. */\n differs?: string[]\n}\n\nexport interface ImportResult {\n /** Problems that refuse the whole package; nothing is imported when there is one. */\n problems: { code: string; path?: string }[]\n outcomes: ImportOutcome[]\n}\n\nconst json = (value: unknown) => (value == null ? null : JSON.stringify(value))\n\n/**\n * Imports a figure package into this domain's library (ADR-0114 결정 5 · 16).\n *\n * A release is known by its reference and the hash of its source, nothing else. Not here: added, its dependency files\n * kept, and put into the candidate map. The same source: skipped, the row left as it is, the fields that differ named.\n * Another source under the same reference: refused. A format this server cannot read is still imported.\n */\nexport async function importFigurePackage(\n tx: EntityManager,\n domain: { id: string },\n user: unknown,\n bytes: Buffer,\n packageName: string,\n files: LibraryFiles = libraryFiles()\n): Promise<ImportResult> {\n let zip: JSZip\n try {\n zip = await JSZip.loadAsync(bytes)\n } catch {\n return { problems: [{ code: 'package-unreadable' }], outcomes: [] }\n }\n const packed = new Map<string, Uint8Array>()\n for (const [path, entry] of Object.entries(zip.files)) if (!entry.dir) packed.set(path, await entry.async('uint8array'))\n\n const read = readFigurePackage(packed, sha256Of)\n if (read.problems.length) return { problems: read.problems.map(p => ({ code: p.code, path: p.path })), outcomes: [] }\n\n const outcomes: ImportOutcome[] = read.refused.map(p => ({ ref: p.ref ?? '', outcome: 'refused', code: p.code }))\n const entries = new Map(read.manifest!.releases.map(entry => [entry.ref, entry]))\n const repository = tx.getRepository(FigureLibraryRelease)\n\n for (const release of read.releases) {\n const entry = entries.get(release.ref)!\n const contentHash = entry.contentHash\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, ref: release.ref } })\n if (existing) {\n if (existing.contentHash !== contentHash) {\n outcomes.push({ ref: release.ref, outcome: 'refused', code: 'figure-import-conflict' })\n continue\n }\n const differs = differingFields(existing, release, entry.blobs)\n outcomes.push({ ref: release.ref, outcome: 'skipped', ...(differs.length ? { code: 'skipped-metadata-differs', differs } : {}) })\n continue\n }\n\n for (const blob of entry.blobs) {\n const file = read.blobs.get(blob.hash)!\n await keepLibraryFile(tx, domain, user, { hash: blob.hash, bytes: file.bytes, contentType: file.contentType }, files)\n }\n const ref = parseFigureRef(release.ref)!\n const saved = await repository.save({\n domain,\n name: release.name,\n description: release.description ?? undefined,\n ref: release.ref,\n namespace: ref.namespace,\n type: ref.type,\n version: ref.version,\n names: release.names ?? undefined,\n category: release.category ?? undefined,\n source: release.source,\n contentHash,\n assetFormat: release.assetFormat,\n kernelVersion: release.kernelVersion ?? undefined,\n metrics: json(release.metrics) ?? undefined,\n topView: json(release.topView) ?? undefined,\n changes: json(release.changes) ?? undefined,\n ...derivedOfSource(release.source),\n dependencies: entry.blobs.map(blob => ({ ...blob })),\n deprecated: !!release.deprecated,\n supersededBy: release.supersededBy ?? undefined,\n sourceUrl: read.manifest!.source.url,\n sourceDomain: read.manifest!.source.domain,\n packageName,\n releasedAt: release.releasedAt ? new Date(release.releasedAt) : undefined,\n creator: user,\n updater: user\n } as Partial<FigureLibraryRelease>)\n await putIntoCandidate(tx, domain, user, saved as FigureLibraryRelease)\n outcomes.push({ ref: release.ref, outcome: 'added' })\n }\n\n return { problems: [], outcomes }\n}\n\n/** The fields a package gives for an existing release that differ from the row: reported, never written. */\nfunction differingFields(row: FigureLibraryRelease, release: FigurePackageRelease, blobs: readonly { hash: string }[]): string[] {\n const same = (a: unknown, b: unknown) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null)\n const parsed = (text?: string | null) => (text ? JSON.parse(text) : null)\n const pairs: [string, unknown, unknown][] = [\n ['name', row.name, release.name],\n ['names', row.names, release.names],\n ['description', row.description, release.description],\n ['category', row.category, release.category],\n ['assetFormat', row.assetFormat, release.assetFormat],\n ['kernelVersion', row.kernelVersion, release.kernelVersion],\n ['metrics', parsed(row.metrics), release.metrics],\n ['topView', parsed(row.topView), release.topView],\n ['changes', parsed(row.changes), release.changes],\n ['dependencies', (row.dependencies ?? []).map(d => d.hash), blobs.map(b => b.hash)],\n ['deprecated', !!row.deprecated, !!release.deprecated],\n ['supersededBy', row.supersededBy, release.supersededBy]\n ]\n return pairs.filter(([, a, b]) => !same(a, b)).map(([field]) => field)\n}\n\n/** A file the import was given is not a package at all. */\nexport function assertPackageName(name: string): void {\n if (!/\\.(figpkg|zip)$/i.test(name)) {\n throw new Refusal('figure-package-invalid', { name }, '{name} is not a figure package (.figpkg).')\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../../server/service/library/import.ts"],"names":[],"mappings":"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","sourcesContent":["import JSZip from 'jszip'\nimport { EntityManager } from 'typeorm'\n\nimport { parseFigureRef, readFigurePackage, type FigurePackageRelease } from '@hatiolab/figure-model'\nimport { Refusal } from '@things-factory/shell'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { derivedOfSource } from './derived'\nimport { keepLibraryFile, libraryFiles, sha256Of, type LibraryFiles } from './library-files'\nimport { putIntoTypeMap } from './type-map'\n\nexport type ImportOutcomeKind = 'added' | 'skipped' | 'refused'\n\nexport interface ImportOutcome {\n ref: string\n outcome: ImportOutcomeKind\n code?: string\n /** For a skipped release: the fields the package gave differently from the row already here. */\n differs?: string[]\n}\n\nexport interface ImportResult {\n /** Problems that refuse the whole package; nothing is imported when there is one. */\n problems: { code: string; path?: string }[]\n outcomes: ImportOutcome[]\n}\n\nconst json = (value: unknown) => (value == null ? null : JSON.stringify(value))\n\n/**\n * Imports a figure package into this domain's library (ADR-0114 결정 5 · 16).\n *\n * A release is known by its reference and the hash of its source, nothing else. Not here: added, its dependency files\n * kept, and put into the type map when it is the latest of its name. The same source: skipped, the row left as it is, the fields that differ named.\n * Another source under the same reference: refused. A format this server cannot read is still imported.\n */\nexport async function importFigurePackage(\n tx: EntityManager,\n domain: { id: string },\n user: unknown,\n bytes: Buffer,\n packageName: string,\n files: LibraryFiles = libraryFiles()\n): Promise<ImportResult> {\n let zip: JSZip\n try {\n zip = await JSZip.loadAsync(bytes)\n } catch {\n return { problems: [{ code: 'package-unreadable' }], outcomes: [] }\n }\n const packed = new Map<string, Uint8Array>()\n for (const [path, entry] of Object.entries(zip.files)) if (!entry.dir) packed.set(path, await entry.async('uint8array'))\n\n const read = readFigurePackage(packed, sha256Of)\n if (read.problems.length) return { problems: read.problems.map(p => ({ code: p.code, path: p.path })), outcomes: [] }\n\n const outcomes: ImportOutcome[] = read.refused.map(p => ({ ref: p.ref ?? '', outcome: 'refused', code: p.code }))\n const entries = new Map(read.manifest!.releases.map(entry => [entry.ref, entry]))\n const repository = tx.getRepository(FigureLibraryRelease)\n\n for (const release of read.releases) {\n const entry = entries.get(release.ref)!\n const contentHash = entry.contentHash\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, ref: release.ref } })\n if (existing) {\n if (existing.contentHash !== contentHash) {\n outcomes.push({ ref: release.ref, outcome: 'refused', code: 'figure-import-conflict' })\n continue\n }\n const differs = differingFields(existing, release, entry.blobs)\n outcomes.push({ ref: release.ref, outcome: 'skipped', ...(differs.length ? { code: 'skipped-metadata-differs', differs } : {}) })\n continue\n }\n\n for (const blob of entry.blobs) {\n const file = read.blobs.get(blob.hash)!\n await keepLibraryFile(tx, domain, user, { hash: blob.hash, bytes: file.bytes, contentType: file.contentType }, files)\n }\n const ref = parseFigureRef(release.ref)!\n const saved = await repository.save({\n domain,\n name: release.name,\n description: release.description ?? undefined,\n ref: release.ref,\n namespace: ref.namespace,\n type: ref.type,\n version: ref.version,\n names: release.names ?? undefined,\n category: release.category ?? undefined,\n source: release.source,\n contentHash,\n assetFormat: release.assetFormat,\n kernelVersion: release.kernelVersion ?? undefined,\n metrics: json(release.metrics) ?? undefined,\n topView: json(release.topView) ?? undefined,\n changes: json(release.changes) ?? undefined,\n ...derivedOfSource(release.source),\n dependencies: entry.blobs.map(blob => ({ ...blob })),\n deprecated: !!release.deprecated,\n supersededBy: release.supersededBy ?? undefined,\n sourceUrl: read.manifest!.source.url,\n sourceDomain: read.manifest!.source.domain,\n packageName,\n releasedAt: release.releasedAt ? new Date(release.releasedAt) : undefined,\n comment: release.comment ?? undefined,\n creator: user,\n updater: user\n } as Partial<FigureLibraryRelease>)\n await putIntoTypeMap(tx, domain, user, saved as FigureLibraryRelease)\n outcomes.push({ ref: release.ref, outcome: 'added' })\n }\n\n return { problems: [], outcomes }\n}\n\n/** The fields a package gives for an existing release that differ from the row: reported, never written. */\nfunction differingFields(row: FigureLibraryRelease, release: FigurePackageRelease, blobs: readonly { hash: string }[]): string[] {\n const same = (a: unknown, b: unknown) => JSON.stringify(a ?? null) === JSON.stringify(b ?? null)\n const parsed = (text?: string | null) => (text ? JSON.parse(text) : null)\n const pairs: [string, unknown, unknown][] = [\n ['name', row.name, release.name],\n ['names', row.names, release.names],\n ['description', row.description, release.description],\n ['category', row.category, release.category],\n ['assetFormat', row.assetFormat, release.assetFormat],\n ['kernelVersion', row.kernelVersion, release.kernelVersion],\n ['metrics', parsed(row.metrics), release.metrics],\n ['topView', parsed(row.topView), release.topView],\n ['changes', parsed(row.changes), release.changes],\n ['dependencies', (row.dependencies ?? []).map(d => d.hash), blobs.map(b => b.hash)],\n ['deprecated', !!row.deprecated, !!release.deprecated],\n ['supersededBy', row.supersededBy, release.supersededBy],\n ['comment', row.comment, release.comment]\n ]\n return pairs.filter(([, a, b]) => !same(a, b)).map(([field]) => field)\n}\n\n/** A file the import was given is not a package at all. */\nexport function assertPackageName(name: string): void {\n if (!/\\.(figpkg|zip)$/i.test(name)) {\n throw new Refusal('figure-package-invalid', { name }, '{name} is not a figure package (.figpkg).')\n }\n}\n"]}
|
|
@@ -4,11 +4,6 @@ import { type LibraryFiles } from './library-files';
|
|
|
4
4
|
type Domain = {
|
|
5
5
|
id: string;
|
|
6
6
|
};
|
|
7
|
-
/** How much of the domain's boards a line touches. */
|
|
8
|
-
export interface Reach {
|
|
9
|
-
boards: number;
|
|
10
|
-
nodes: number;
|
|
11
|
-
}
|
|
12
7
|
/** Brings a row's derived values up to this server's kernel, writing them back when they changed. */
|
|
13
8
|
export declare function freshen(tx: EntityManager, release: FigureLibraryRelease): Promise<FigureLibraryRelease>;
|
|
14
9
|
/** The dependency files of a release that are missing or whose bytes no longer hash to their name. */
|
|
@@ -16,55 +11,13 @@ export declare function dependencyFaults(tx: EntityManager, release: FigureLibra
|
|
|
16
11
|
hash: string;
|
|
17
12
|
fault: 'missing' | 'corrupt';
|
|
18
13
|
}[]>;
|
|
19
|
-
/** Figure nodes across the domain's boards: per name, how many boards and nodes use it. */
|
|
20
|
-
export declare function reachOf(tx: EntityManager, domain: Domain): Promise<Map<string, Reach>>;
|
|
21
|
-
/**
|
|
22
|
-
* The check before activation (ADR-0114 결정 8): the candidate against the active map and every board of the domain.
|
|
23
|
-
* It reports and never blocks; the activation screen shows it and asks to confirm.
|
|
24
|
-
*/
|
|
25
|
-
export declare function typeMapCheck(tx: EntityManager, domain: Domain, files?: LibraryFiles): Promise<{
|
|
26
|
-
candidate: {
|
|
27
|
-
version: number;
|
|
28
|
-
revision: number;
|
|
29
|
-
};
|
|
30
|
-
active: {
|
|
31
|
-
version: number;
|
|
32
|
-
revision: number;
|
|
33
|
-
};
|
|
34
|
-
missing: {
|
|
35
|
-
boards: number;
|
|
36
|
-
nodes: number;
|
|
37
|
-
name: string;
|
|
38
|
-
}[];
|
|
39
|
-
added: {
|
|
40
|
-
boards: number;
|
|
41
|
-
nodes: number;
|
|
42
|
-
name: string;
|
|
43
|
-
ref: string;
|
|
44
|
-
}[];
|
|
45
|
-
changed: any[];
|
|
46
|
-
deprecated: any[];
|
|
47
|
-
unsupported: any[];
|
|
48
|
-
dependencies: any[];
|
|
49
|
-
carrierConflicts: {
|
|
50
|
-
carrierType: string;
|
|
51
|
-
lines: {
|
|
52
|
-
name: string;
|
|
53
|
-
ref: string;
|
|
54
|
-
}[];
|
|
55
|
-
}[];
|
|
56
|
-
}>;
|
|
57
14
|
export type RequirementStatus = 'drawn' | 'not-in-type-map' | 'release-unavailable' | 'dependency-missing' | 'format-unsupported';
|
|
58
15
|
/**
|
|
59
|
-
* What one board needs from the
|
|
16
|
+
* What one board needs from the type map (ADR-0114 결정 8): each figure it holds, its status and node count. A
|
|
60
17
|
* figure not in the library has dependencies this server cannot know.
|
|
61
18
|
*/
|
|
62
19
|
export declare function boardRequirements(tx: EntityManager, domain: Domain, boardId: string, files?: LibraryFiles): Promise<{
|
|
63
20
|
board: string;
|
|
64
|
-
typeMap: {
|
|
65
|
-
version: number;
|
|
66
|
-
revision: number;
|
|
67
|
-
};
|
|
68
21
|
figures: any[];
|
|
69
22
|
}>;
|
|
70
23
|
export {};
|
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.freshen = freshen;
|
|
4
4
|
exports.dependencyFaults = dependencyFaults;
|
|
5
|
-
exports.reachOf = reachOf;
|
|
6
|
-
exports.typeMapCheck = typeMapCheck;
|
|
7
5
|
exports.boardRequirements = boardRequirements;
|
|
8
|
-
const figure_model_1 = require("@hatiolab/figure-model");
|
|
9
6
|
const board_service_1 = require("@things-factory/board-service");
|
|
10
7
|
const figure_library_release_1 = require("../figure-library-release/figure-library-release");
|
|
11
|
-
const figure_type_map_1 = require("../figure-type-map/figure-type-map");
|
|
12
8
|
const derived_1 = require("./derived");
|
|
13
9
|
const board_walk_1 = require("./board-walk");
|
|
14
10
|
const library_files_1 = require("./library-files");
|
|
@@ -41,88 +37,15 @@ async function dependencyFaults(tx, release, files = (0, library_files_1.library
|
|
|
41
37
|
}
|
|
42
38
|
return faults;
|
|
43
39
|
}
|
|
44
|
-
/** Figure nodes across the domain's boards: per name, how many boards and nodes use it. */
|
|
45
|
-
async function reachOf(tx, domain) {
|
|
46
|
-
const reach = new Map();
|
|
47
|
-
const boards = await tx.getRepository(board_service_1.Board).find({ where: { domain: { id: domain.id } }, select: { id: true, model: true } });
|
|
48
|
-
for (const board of boards) {
|
|
49
|
-
for (const [name, nodes] of (0, board_walk_1.figureNodesOf)(board.model)) {
|
|
50
|
-
const now = reach.get(name) ?? { boards: 0, nodes: 0 };
|
|
51
|
-
reach.set(name, { boards: now.boards + 1, nodes: now.nodes + nodes });
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return reach;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* The check before activation (ADR-0114 결정 8): the candidate against the active map and every board of the domain.
|
|
58
|
-
* It reports and never blocks; the activation screen shows it and asks to confirm.
|
|
59
|
-
*/
|
|
60
|
-
async function typeMapCheck(tx, domain, files) {
|
|
61
|
-
const candidate = await (0, type_map_1.mapOfState)(tx, domain, figure_type_map_1.FigureTypeMapState.CANDIDATE);
|
|
62
|
-
if (!candidate)
|
|
63
|
-
return null;
|
|
64
|
-
const active = await (0, type_map_1.mapOfState)(tx, domain, figure_type_map_1.FigureTypeMapState.ACTIVE);
|
|
65
|
-
const next = new Map((await (0, type_map_1.entriesOf)(tx, candidate)).map(line => [line.name, line.release]));
|
|
66
|
-
const now = new Map(active ? (await (0, type_map_1.entriesOf)(tx, active)).map(line => [line.name, line.release]) : []);
|
|
67
|
-
const reach = await reachOf(tx, domain);
|
|
68
|
-
const at = (name) => reach.get(name) ?? { boards: 0, nodes: 0 };
|
|
69
|
-
const missing = [...reach.keys()].filter(name => !next.has(name)).map(name => ({ name, ...at(name) }));
|
|
70
|
-
const added = [...next.keys()].filter(name => !now.has(name)).map(name => ({ name, ref: next.get(name).ref, ...at(name) }));
|
|
71
|
-
const changed = [];
|
|
72
|
-
for (const [name, release] of next) {
|
|
73
|
-
const before = now.get(name);
|
|
74
|
-
if (!before || before.id === release.id)
|
|
75
|
-
continue;
|
|
76
|
-
changed.push({ name, from: before.ref, to: release.ref, contract: contractOf(before, release), ...at(name) });
|
|
77
|
-
}
|
|
78
|
-
const deprecated = [];
|
|
79
|
-
const unsupported = [];
|
|
80
|
-
const dependencies = [];
|
|
81
|
-
const carriers = new Map();
|
|
82
|
-
for (const [name, release] of next) {
|
|
83
|
-
const fresh = await freshen(tx, release);
|
|
84
|
-
if (fresh.carrierType)
|
|
85
|
-
carriers.set(fresh.carrierType, [...(carriers.get(fresh.carrierType) ?? []), { name, ref: fresh.ref }]);
|
|
86
|
-
if (fresh.deprecated)
|
|
87
|
-
deprecated.push({ name, ref: fresh.ref, supersededBy: fresh.supersededBy ?? null, ...at(name) });
|
|
88
|
-
if (!fresh.formatSupported)
|
|
89
|
-
unsupported.push({ name, ref: fresh.ref, assetFormat: fresh.assetFormat, ...at(name) });
|
|
90
|
-
const faults = await dependencyFaults(tx, fresh, files);
|
|
91
|
-
if (faults.length)
|
|
92
|
-
dependencies.push({ name, ref: fresh.ref, faults, ...at(name) });
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
candidate: { version: candidate.version, revision: candidate.revision },
|
|
96
|
-
active: active ? { version: active.version, revision: active.revision } : null,
|
|
97
|
-
missing,
|
|
98
|
-
added,
|
|
99
|
-
changed,
|
|
100
|
-
deprecated,
|
|
101
|
-
unsupported,
|
|
102
|
-
dependencies,
|
|
103
|
-
/* A CARRIERTYPE code two figures of the candidate declare: live data with it finds no carrier (ADR-0123). Reported, as the rest. */
|
|
104
|
-
carrierConflicts: [...carriers].filter(([, lines]) => lines.length > 1).map(([carrierType, lines]) => ({ carrierType, lines }))
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/** The contract changes between two releases, or `uncheckable` when this server cannot read either. */
|
|
108
|
-
function contractOf(before, after) {
|
|
109
|
-
try {
|
|
110
|
-
return { checked: true, changes: (0, figure_model_1.figureContractChanges)((0, figure_model_1.parseFigureAsset)(before.source), (0, figure_model_1.parseFigureAsset)(after.source)) };
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
return { checked: false, changes: [] };
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
40
|
/**
|
|
117
|
-
* What one board needs from the
|
|
41
|
+
* What one board needs from the type map (ADR-0114 결정 8): each figure it holds, its status and node count. A
|
|
118
42
|
* figure not in the library has dependencies this server cannot know.
|
|
119
43
|
*/
|
|
120
44
|
async function boardRequirements(tx, domain, boardId, files) {
|
|
121
45
|
const board = await tx.getRepository(board_service_1.Board).findOne({ where: { id: boardId, domain: { id: domain.id } }, select: { id: true, model: true } });
|
|
122
46
|
if (!board)
|
|
123
47
|
return null;
|
|
124
|
-
const
|
|
125
|
-
const lines = new Map(active ? (await (0, type_map_1.entriesOf)(tx, active)).map(line => [line.name, line]) : []);
|
|
48
|
+
const lines = new Map((await (0, type_map_1.linesOf)(tx, domain)).map(line => [line.name, line]));
|
|
126
49
|
const figures = [];
|
|
127
50
|
for (const [name, nodes] of (0, board_walk_1.figureNodesOf)(board.model)) {
|
|
128
51
|
const line = lines.get(name);
|
|
@@ -144,6 +67,6 @@ async function boardRequirements(tx, domain, boardId, files) {
|
|
|
144
67
|
}
|
|
145
68
|
figures.push({ name, status, ref, nodes, dependencies });
|
|
146
69
|
}
|
|
147
|
-
return { board: board.id,
|
|
70
|
+
return { board: board.id, figures };
|
|
148
71
|
}
|
|
149
72
|
//# sourceMappingURL=inspection.js.map
|
|
@@ -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 { figureContractChanges, parseFigureAsset } from '@hatiolab/figure-model'\nimport { Board } from '@things-factory/board-service'\n\nimport { FigureLibraryRelease } from '../figure-library-release/figure-library-release'\nimport { FigureTypeMapState } from '../figure-type-map/figure-type-map'\nimport { derivedIsStale, derivedOfSource } from './derived'\nimport { figureNodesOf } from './board-walk'\nimport { libraryFileOf, libraryFiles, sha256Of, type LibraryFiles } from './library-files'\nimport { entriesOf, mapOfState } from './type-map'\n\ntype Domain = { id: string }\n\n/** How much of the domain's boards a line touches. */\nexport interface Reach {\n boards: number\n nodes: number\n}\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\n/** Figure nodes across the domain's boards: per name, how many boards and nodes use it. */\nexport async function reachOf(tx: EntityManager, domain: Domain): Promise<Map<string, Reach>> {\n const reach = new Map<string, Reach>()\n const boards = await tx.getRepository(Board).find({ where: { domain: { id: domain.id } }, select: { id: true, model: true } })\n for (const board of boards) {\n for (const [name, nodes] of figureNodesOf(board.model)) {\n const now = reach.get(name) ?? { boards: 0, nodes: 0 }\n reach.set(name, { boards: now.boards + 1, nodes: now.nodes + nodes })\n }\n }\n return reach\n}\n\n/**\n * The check before activation (ADR-0114 결정 8): the candidate against the active map and every board of the domain.\n * It reports and never blocks; the activation screen shows it and asks to confirm.\n */\nexport async function typeMapCheck(tx: EntityManager, domain: Domain, files?: LibraryFiles) {\n const candidate = await mapOfState(tx, domain, FigureTypeMapState.CANDIDATE)\n if (!candidate) return null\n const active = await mapOfState(tx, domain, FigureTypeMapState.ACTIVE)\n\n const next = new Map((await entriesOf(tx, candidate)).map(line => [line.name, line.release!]))\n const now = new Map(active ? (await entriesOf(tx, active)).map(line => [line.name, line.release!]) : [])\n const reach = await reachOf(tx, domain)\n const at = (name: string): Reach => reach.get(name) ?? { boards: 0, nodes: 0 }\n\n const missing = [...reach.keys()].filter(name => !next.has(name)).map(name => ({ name, ...at(name) }))\n const added = [...next.keys()].filter(name => !now.has(name)).map(name => ({ name, ref: next.get(name)!.ref, ...at(name) }))\n const changed = []\n for (const [name, release] of next) {\n const before = now.get(name)\n if (!before || before.id === release.id) continue\n changed.push({ name, from: before.ref, to: release.ref, contract: contractOf(before, release), ...at(name) })\n }\n const deprecated = []\n const unsupported = []\n const dependencies = []\n const carriers = new Map<string, { name: string; ref: string }[]>()\n for (const [name, release] of next) {\n const fresh = await freshen(tx, release)\n if (fresh.carrierType) carriers.set(fresh.carrierType, [...(carriers.get(fresh.carrierType) ?? []), { name, ref: fresh.ref }])\n if (fresh.deprecated) deprecated.push({ name, ref: fresh.ref, supersededBy: fresh.supersededBy ?? null, ...at(name) })\n if (!fresh.formatSupported) unsupported.push({ name, ref: fresh.ref, assetFormat: fresh.assetFormat, ...at(name) })\n const faults = await dependencyFaults(tx, fresh, files)\n if (faults.length) dependencies.push({ name, ref: fresh.ref, faults, ...at(name) })\n }\n\n return {\n candidate: { version: candidate.version, revision: candidate.revision },\n active: active ? { version: active.version, revision: active.revision } : null,\n missing,\n added,\n changed,\n deprecated,\n unsupported,\n dependencies,\n /* A CARRIERTYPE code two figures of the candidate declare: live data with it finds no carrier (ADR-0123). Reported, as the rest. */\n carrierConflicts: [...carriers].filter(([, lines]) => lines.length > 1).map(([carrierType, lines]) => ({ carrierType, lines }))\n }\n}\n\n/** The contract changes between two releases, or `uncheckable` when this server cannot read either. */\nfunction contractOf(before: FigureLibraryRelease, after: FigureLibraryRelease) {\n try {\n return { checked: true, changes: figureContractChanges(parseFigureAsset(before.source), parseFigureAsset(after.source)) }\n } catch {\n return { checked: false, changes: [] }\n }\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 active 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 active = await mapOfState(tx, domain, FigureTypeMapState.ACTIVE)\n const lines = new Map(active ? (await entriesOf(tx, active)).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, typeMap: active ? { version: active.version, revision: active.revision } : null, 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' | '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,35 +1,23 @@
|
|
|
1
1
|
import { EntityManager } from 'typeorm';
|
|
2
2
|
import { FigureLibraryRelease } from '../figure-library-release/figure-library-release';
|
|
3
|
-
import { FigureTypeMap, FigureTypeMapState } from '../figure-type-map/figure-type-map';
|
|
4
3
|
import { FigureTypeMapEntry } from '../figure-type-map/figure-type-map-entry';
|
|
5
4
|
type Domain = {
|
|
6
5
|
id: string;
|
|
7
6
|
};
|
|
8
7
|
/** The full name a release is the release of. */
|
|
9
8
|
export declare const fullNameOf: (release: Pick<FigureLibraryRelease, "namespace" | "type">) => string;
|
|
10
|
-
|
|
11
|
-
export declare function
|
|
9
|
+
/** The domain's type map: one line per full name, with the release it draws with. */
|
|
10
|
+
export declare function linesOf(tx: EntityManager, domain: Domain): Promise<FigureTypeMapEntry[]>;
|
|
12
11
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* Makes `releaseId` the release `name` draws with (ADR-0114 결정 15): a release of this domain's library, and the
|
|
13
|
+
* release of that very name. Refused by the first fault, named.
|
|
15
14
|
*/
|
|
16
|
-
export declare function
|
|
17
|
-
export interface CandidateLine {
|
|
18
|
-
name: string;
|
|
19
|
-
releaseId: string;
|
|
20
|
-
}
|
|
15
|
+
export declare function chooseRelease(tx: EntityManager, domain: Domain, user: unknown, name: string, releaseId: string): Promise<FigureTypeMapEntry>;
|
|
21
16
|
/**
|
|
22
|
-
*
|
|
23
|
-
* release of
|
|
17
|
+
* What an import does to the type map (ADR-0114 결정 7, amended 2026-10-12): the newest release this server can draw is
|
|
18
|
+
* 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
|
|
19
|
+
* draws with moves the line to it; an older one leaves it. A release in a format this server cannot read never takes
|
|
20
|
+
* over: a name that has only such releases keeps a line with no release, so it is listed and boards are told why.
|
|
24
21
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
/** Replaces the candidate's lines; each save raises the revision an open preview compares (결정 12). */
|
|
27
|
-
export declare function saveCandidate(tx: EntityManager, domain: Domain, user: unknown, lines: readonly CandidateLine[], description?: string): Promise<FigureTypeMap>;
|
|
28
|
-
/** Puts an imported release into the candidate: a new line, or its figure's line moved to it (결정 7). */
|
|
29
|
-
export declare function putIntoCandidate(tx: EntityManager, domain: Domain, user: unknown, release: FigureLibraryRelease): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Makes a release ACTIVE in one transaction: the candidate after a check, or an ARCHIVED one to roll back. The active
|
|
32
|
-
* one before becomes ARCHIVED; a candidate left aside by a roll back stays a candidate (ADR-0114 결정 6).
|
|
33
|
-
*/
|
|
34
|
-
export declare function activate(tx: EntityManager, domain: Domain, user: unknown, version: number): Promise<FigureTypeMap>;
|
|
22
|
+
export declare function putIntoTypeMap(tx: EntityManager, domain: Domain, user: unknown, release: FigureLibraryRelease): Promise<void>;
|
|
35
23
|
export {};
|