@chartcoach/catalog 0.1.3
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/README.md +71 -0
- package/dist/catalog/artifacts.d.ts +27 -0
- package/dist/catalog/artifacts.d.ts.map +1 -0
- package/dist/catalog/artifacts.js +90 -0
- package/dist/catalog/errors.d.ts +4 -0
- package/dist/catalog/errors.d.ts.map +1 -0
- package/dist/catalog/errors.js +6 -0
- package/dist/catalog/labels.d.ts +9 -0
- package/dist/catalog/labels.d.ts.map +1 -0
- package/dist/catalog/labels.js +23 -0
- package/dist/catalog/load-parquet-core.d.ts +15 -0
- package/dist/catalog/load-parquet-core.d.ts.map +1 -0
- package/dist/catalog/load-parquet-core.js +47 -0
- package/dist/catalog/manifest.d.ts +15 -0
- package/dist/catalog/manifest.d.ts.map +1 -0
- package/dist/catalog/manifest.js +144 -0
- package/dist/catalog/markdown.d.ts +3 -0
- package/dist/catalog/markdown.d.ts.map +1 -0
- package/dist/catalog/markdown.js +13 -0
- package/dist/catalog/model.d.ts +33 -0
- package/dist/catalog/model.d.ts.map +1 -0
- package/dist/catalog/model.js +67 -0
- package/dist/catalog/wire.d.ts +18 -0
- package/dist/catalog/wire.d.ts.map +1 -0
- package/dist/catalog/wire.js +78 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/package.json +49 -0
- package/src/catalog/artifacts.ts +127 -0
- package/src/catalog/errors.ts +6 -0
- package/src/catalog/labels.ts +32 -0
- package/src/catalog/load-parquet-core.ts +81 -0
- package/src/catalog/manifest.ts +186 -0
- package/src/catalog/markdown.ts +22 -0
- package/src/catalog/model.ts +105 -0
- package/src/catalog/wire.ts +93 -0
- package/src/index.ts +25 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { CatalogError } from "./errors";
|
|
2
|
+
import type { CatalogManifest } from "./manifest";
|
|
3
|
+
import { validateManifestCoverage } from "./manifest";
|
|
4
|
+
|
|
5
|
+
export type GuidelineSection = {
|
|
6
|
+
role: string;
|
|
7
|
+
title: string;
|
|
8
|
+
content: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type Guideline = {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
bibliography?: string;
|
|
15
|
+
description: string;
|
|
16
|
+
labels: readonly string[];
|
|
17
|
+
body: string;
|
|
18
|
+
sections: readonly GuidelineSection[];
|
|
19
|
+
references: readonly string[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type CatalogOptions = {
|
|
23
|
+
manifest?: CatalogManifest;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export class Catalog implements Iterable<Guideline> {
|
|
27
|
+
readonly guidelines: readonly Guideline[];
|
|
28
|
+
readonly manifest?: CatalogManifest;
|
|
29
|
+
private readonly byId: Map<string, Guideline>;
|
|
30
|
+
|
|
31
|
+
constructor(guidelines: Iterable<Guideline> = [], options: CatalogOptions = {}) {
|
|
32
|
+
const records = Array.from(guidelines, copyGuideline);
|
|
33
|
+
const byId = new Map<string, Guideline>();
|
|
34
|
+
for (const guideline of records) {
|
|
35
|
+
if (byId.has(guideline.id)) {
|
|
36
|
+
throw new CatalogError(`Catalog contains duplicate guideline id: ${guideline.id}.`);
|
|
37
|
+
}
|
|
38
|
+
byId.set(guideline.id, guideline);
|
|
39
|
+
}
|
|
40
|
+
if (options.manifest) {
|
|
41
|
+
validateManifestCoverage(records, options.manifest);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.guidelines = records;
|
|
45
|
+
this.manifest = options.manifest;
|
|
46
|
+
this.byId = byId;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get length(): number {
|
|
50
|
+
return this.guidelines.length;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get size(): number {
|
|
54
|
+
return this.guidelines.length;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get(id: string): Guideline | undefined {
|
|
58
|
+
return this.byId.get(id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
require(id: string): Guideline {
|
|
62
|
+
const guideline = this.get(id);
|
|
63
|
+
if (!guideline) {
|
|
64
|
+
throw new CatalogError(`Unknown guideline id: ${id}.`);
|
|
65
|
+
}
|
|
66
|
+
return guideline;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
labels(): string[] {
|
|
70
|
+
return sortedUnique(this.guidelines.flatMap((guideline) => [...guideline.labels]));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
sectionRoles(): string[] {
|
|
74
|
+
return sortedUnique(
|
|
75
|
+
this.guidelines.flatMap((guideline) =>
|
|
76
|
+
guideline.sections.map((section) => section.role),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
[Symbol.iterator](): Iterator<Guideline> {
|
|
82
|
+
return this.guidelines[Symbol.iterator]();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function copyGuideline(guideline: Guideline): Guideline {
|
|
87
|
+
return {
|
|
88
|
+
id: guideline.id,
|
|
89
|
+
title: guideline.title,
|
|
90
|
+
bibliography: guideline.bibliography,
|
|
91
|
+
description: guideline.description,
|
|
92
|
+
labels: [...guideline.labels],
|
|
93
|
+
body: guideline.body,
|
|
94
|
+
sections: guideline.sections.map((section) => ({
|
|
95
|
+
role: section.role,
|
|
96
|
+
title: section.title,
|
|
97
|
+
content: section.content,
|
|
98
|
+
})),
|
|
99
|
+
references: [...guideline.references],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function sortedUnique(values: Iterable<string>): string[] {
|
|
104
|
+
return Array.from(new Set(values)).sort();
|
|
105
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { CatalogError } from "./errors";
|
|
2
|
+
import { normalizeLabel } from "./labels";
|
|
3
|
+
import type { Guideline, GuidelineSection } from "./model";
|
|
4
|
+
|
|
5
|
+
export type CatalogRowWire = {
|
|
6
|
+
id: string;
|
|
7
|
+
guideline: {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
bibliography?: string | null;
|
|
11
|
+
description: string;
|
|
12
|
+
labels: string[];
|
|
13
|
+
body: string;
|
|
14
|
+
sections: GuidelineSection[];
|
|
15
|
+
};
|
|
16
|
+
references: string[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
20
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isStringArray(value: unknown): value is string[] {
|
|
24
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isGuidelineSection(value: unknown): value is GuidelineSection {
|
|
28
|
+
return (
|
|
29
|
+
isRecord(value) &&
|
|
30
|
+
typeof value.role === "string" &&
|
|
31
|
+
value.role.trim().length > 0 &&
|
|
32
|
+
typeof value.title === "string" &&
|
|
33
|
+
typeof value.content === "string"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isSectionArray(value: unknown): value is GuidelineSection[] {
|
|
38
|
+
return Array.isArray(value) && value.every(isGuidelineSection);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function sectionsFromWire(value: GuidelineSection[]): GuidelineSection[] {
|
|
42
|
+
return value.map((section) => ({
|
|
43
|
+
role: section.role.trim(),
|
|
44
|
+
title: section.title,
|
|
45
|
+
content: section.content,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function isCatalogRowWire(value: unknown): value is CatalogRowWire {
|
|
50
|
+
if (!isRecord(value)) return false;
|
|
51
|
+
if (typeof value.id !== "string" || value.id.length === 0) return false;
|
|
52
|
+
if (!isRecord(value.guideline)) return false;
|
|
53
|
+
if (value.guideline.id !== value.id) return false;
|
|
54
|
+
if (typeof value.guideline.title !== "string") return false;
|
|
55
|
+
if (typeof value.guideline.description !== "string") return false;
|
|
56
|
+
if (typeof value.guideline.body !== "string") return false;
|
|
57
|
+
if (!isStringArray(value.guideline.labels)) return false;
|
|
58
|
+
if (!isSectionArray(value.guideline.sections)) return false;
|
|
59
|
+
if (!isStringArray(value.references)) return false;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function guidelineFromWire(value: unknown): Guideline | null {
|
|
64
|
+
if (!isCatalogRowWire(value)) return null;
|
|
65
|
+
|
|
66
|
+
const { guideline, references } = value;
|
|
67
|
+
const sections = sectionsFromWire(guideline.sections);
|
|
68
|
+
let labels: string[];
|
|
69
|
+
try {
|
|
70
|
+
labels = guideline.labels.map((label) => normalizeLabel(label, "guideline label"));
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
id: guideline.id,
|
|
77
|
+
title: guideline.title,
|
|
78
|
+
bibliography: typeof guideline.bibliography === "string" ? guideline.bibliography : undefined,
|
|
79
|
+
description: guideline.description,
|
|
80
|
+
labels,
|
|
81
|
+
body: guideline.body,
|
|
82
|
+
sections,
|
|
83
|
+
references: [...references],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function requireGuidelineFromWire(value: unknown, context?: string): Guideline {
|
|
88
|
+
const guideline = guidelineFromWire(value);
|
|
89
|
+
if (guideline) return guideline;
|
|
90
|
+
|
|
91
|
+
const suffix = context ? ` (${context})` : "";
|
|
92
|
+
throw new CatalogError(`Invalid catalog row format${suffix}.`);
|
|
93
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { Catalog, type CatalogOptions, type Guideline, type GuidelineSection } from "./catalog/model";
|
|
2
|
+
export { CatalogError } from "./catalog/errors";
|
|
3
|
+
export {
|
|
4
|
+
DEFAULT_CATALOG,
|
|
5
|
+
catalogArtifact,
|
|
6
|
+
catalogArtifactUrl,
|
|
7
|
+
parseCatalogReleaseMetadata,
|
|
8
|
+
type ArtifactDescriptor,
|
|
9
|
+
type ArtifactKind,
|
|
10
|
+
type CatalogReleaseMetadata,
|
|
11
|
+
} from "./catalog/artifacts";
|
|
12
|
+
export {
|
|
13
|
+
loadCatalog,
|
|
14
|
+
type AsyncBuffer,
|
|
15
|
+
type CatalogBytes,
|
|
16
|
+
type LoadCatalogInput,
|
|
17
|
+
type ParquetBytes,
|
|
18
|
+
} from "./catalog/load-parquet-core";
|
|
19
|
+
export {
|
|
20
|
+
type CatalogManifest,
|
|
21
|
+
type ManifestDefinition,
|
|
22
|
+
parseCatalogManifest,
|
|
23
|
+
} from "./catalog/manifest";
|
|
24
|
+
export { parseLabel, type CatalogLabel } from "./catalog/labels";
|
|
25
|
+
export { toMarkdown } from "./catalog/markdown";
|