glossarist 0.4.43 → 0.4.44
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/package.json +1 -1
- package/src/errors.ts +6 -25
- package/src/migration/hyperedge-migrator.ts +50 -66
- package/src/models/concept.ts +1 -1
- package/src/sort.ts +1 -5
- package/src/transforms/concept-to-gloss.transform.ts +39 -36
- package/src/transforms/index.ts +0 -1
- package/src/uuid.ts +11 -8
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Base error for all glossarist errors.
|
|
4
|
-
*/
|
|
1
|
+
/** Base error for all glossarist errors. */
|
|
5
2
|
export class GlossaristError extends Error {
|
|
6
|
-
|
|
7
|
-
* @param {string} message
|
|
8
|
-
* @param {{ cause?: Error }} [options]
|
|
9
|
-
*/
|
|
10
|
-
constructor(message, options) {
|
|
3
|
+
constructor(message: string, options?: { cause?: Error }) {
|
|
11
4
|
super(message, options);
|
|
12
5
|
this.name = 'GlossaristError';
|
|
13
6
|
}
|
|
14
7
|
}
|
|
15
8
|
|
|
16
|
-
/**
|
|
17
|
-
* Thrown when a function receives invalid input (null, undefined, wrong type).
|
|
18
|
-
*/
|
|
9
|
+
/** Thrown when a function receives invalid input (null, undefined, wrong type). */
|
|
19
10
|
export class InvalidInputError extends GlossaristError {
|
|
20
|
-
|
|
21
|
-
* @param {string} what - description of the invalid input
|
|
22
|
-
* @param {string} [expected] - description of what was expected
|
|
23
|
-
*/
|
|
24
|
-
constructor(what, expected) {
|
|
11
|
+
constructor(what: string, expected?: string) {
|
|
25
12
|
const msg = expected ? `${what} (expected ${expected})` : what;
|
|
26
13
|
super(msg);
|
|
27
14
|
this.name = 'InvalidInputError';
|
|
28
15
|
}
|
|
29
16
|
}
|
|
30
17
|
|
|
31
|
-
/**
|
|
32
|
-
* Thrown when YAML content cannot be parsed.
|
|
33
|
-
*/
|
|
18
|
+
/** Thrown when YAML content cannot be parsed. */
|
|
34
19
|
export class YamlParseError extends GlossaristError {
|
|
35
|
-
|
|
36
|
-
* @param {string} context - what was being parsed (e.g. concept ID)
|
|
37
|
-
* @param {Error} cause - the original YAML parse error
|
|
38
|
-
*/
|
|
39
|
-
constructor(context, cause) {
|
|
20
|
+
constructor(context: string, cause: Error) {
|
|
40
21
|
super(`Failed to parse YAML for ${context}: ${cause.message}`, { cause });
|
|
41
22
|
this.name = 'YamlParseError';
|
|
42
23
|
}
|
|
@@ -1,120 +1,104 @@
|
|
|
1
|
-
// @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
|
|
2
1
|
// v1 → v2 PartitiveHyperedge migration.
|
|
3
2
|
//
|
|
4
3
|
// Pure function: takes a v1 PartitiveHyperedge hash, returns a v2
|
|
5
|
-
// PartitiveHyperedge hash. Idempotent.
|
|
6
|
-
// legacy YAML; available as a public API for one-shot dataset migrations.
|
|
7
|
-
//
|
|
8
|
-
// Field mapping (per TODO.partitive-relation-v2 items 01–05 and the
|
|
9
|
-
// ISO 704:2022 correction):
|
|
10
|
-
//
|
|
11
|
-
// comprehensive → comprehensive (unchanged shape)
|
|
12
|
-
// parts: [ref, ...] → partitives: [{ ref, presence: 'required', count: 'exactly_one' }, ...]
|
|
13
|
-
// enumeration: open → completeness: partial
|
|
14
|
-
// enumeration: closed→ completeness: complete
|
|
15
|
-
// markers: [...] → (dropped — v2 uses per-member multiplicity
|
|
16
|
-
// + is_delimiting; v1 markers encoded the
|
|
17
|
-
// same information imprecisely and cannot
|
|
18
|
-
// round-trip losslessly)
|
|
19
|
-
// content: "..." → (dropped — structural edges carry no prose)
|
|
20
|
-
//
|
|
21
|
-
// Cardinality check: v1 allowed [1..*] parts; v2 requires [2..*].
|
|
22
|
-
// A v1 hyperedge with exactly 1 part cannot migrate to v2 and is
|
|
23
|
-
// returned with a `migrationWarning` field set so callers can decide
|
|
24
|
-
// whether to surface it (the parser drops these with a warning).
|
|
25
|
-
//
|
|
26
|
-
// ─── Migration decision table (v1 markers → v2 multiplicity) ──────
|
|
27
|
-
//
|
|
28
|
-
// v1 markers encoded per-relation plurality claims. v2 multiplicity
|
|
29
|
-
// is per-member. The mapping is lossy; reviewers must set per-member
|
|
30
|
-
// multiplicity individually after migration.
|
|
31
|
-
//
|
|
32
|
-
// | v1 markers | Likely multiplicity for each partitive | Notes |
|
|
33
|
-
// |-------------------------|------------------------------------------------|--------------------------------------------------------|
|
|
34
|
-
// | (none) | `compulsory` | Default; reviewer confirms |
|
|
35
|
-
// | `[double]` | `compulsory_multiple` or `compulsory_at_least_one` | Reviewer distinguishes "all required" from "≥1 required" |
|
|
36
|
-
// | `[dashed]` | `optional` or `optional_multiple` | Reviewer distinguishes single vs multiple |
|
|
37
|
-
// | `[double, dashed]` | `optional_multiple` or `compulsory_at_least_one` | Plurality claim; reviewer distinguishes uncertain vs required |
|
|
38
|
-
// | (single-part v1 edge) | (cannot migrate — needs ≥2 partitives) | Convert to a binary `has_part` edge |
|
|
39
|
-
//
|
|
40
|
-
// `is_delimiting` is NEVER set by migration. ISO 704:2022 says
|
|
41
|
-
// delimiting depends on the concept system and coordinate concepts,
|
|
42
|
-
// which migration cannot infer. Reviewer sets it based on domain
|
|
43
|
-
// knowledge (see DelimitingCoherenceRule for cross-relation checks).
|
|
4
|
+
// PartitiveHyperedge hash. Idempotent.
|
|
44
5
|
|
|
45
|
-
export
|
|
6
|
+
export interface V1HyperedgeHash {
|
|
7
|
+
comprehensive?: { source?: string; id?: string; text?: string };
|
|
8
|
+
parts?: ReadonlyArray<{ source?: string; id?: string }>;
|
|
9
|
+
enumeration?: string;
|
|
10
|
+
content?: string | null;
|
|
11
|
+
markers?: ReadonlyArray<string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface V2HyperedgeHash {
|
|
15
|
+
comprehensive: { source?: string; id?: string };
|
|
16
|
+
partitives: ReadonlyArray<{
|
|
17
|
+
ref: { source?: string; id?: string };
|
|
18
|
+
presence: string;
|
|
19
|
+
count: string;
|
|
20
|
+
}>;
|
|
21
|
+
completeness: string;
|
|
22
|
+
migrationWarning?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function migrateHyperedgeToRelation(
|
|
26
|
+
v1Hash: V1HyperedgeHash | ReadonlyArray<V1HyperedgeHash> | null,
|
|
27
|
+
): V2HyperedgeHash | ReadonlyArray<V2HyperedgeHash | null> | null {
|
|
46
28
|
if (v1Hash == null) return null;
|
|
47
29
|
if (Array.isArray(v1Hash)) {
|
|
48
|
-
return v1Hash.map(migrateHyperedgeToRelation)
|
|
30
|
+
return v1Hash.map(migrateHyperedgeToRelation) as ReadonlyArray<V2HyperedgeHash | null>;
|
|
49
31
|
}
|
|
50
32
|
|
|
51
|
-
const
|
|
52
|
-
|
|
33
|
+
const h = v1Hash as V1HyperedgeHash;
|
|
34
|
+
const out: Partial<V2HyperedgeHash> = {
|
|
35
|
+
comprehensive: { ...(h.comprehensive ?? {}) },
|
|
53
36
|
};
|
|
54
37
|
|
|
55
|
-
const parts = Array.isArray(
|
|
56
|
-
out.partitives = parts.map(ref => ({
|
|
38
|
+
const parts = Array.isArray(h.parts) ? h.parts : [];
|
|
39
|
+
out.partitives = parts.map((ref) => ({
|
|
57
40
|
ref: { ...ref },
|
|
58
41
|
presence: 'required',
|
|
59
42
|
count: 'exactly_one',
|
|
60
43
|
}));
|
|
61
44
|
|
|
62
|
-
out.completeness = _migrateEnumeration(
|
|
45
|
+
out.completeness = _migrateEnumeration(h.enumeration);
|
|
63
46
|
|
|
64
|
-
// v1 content + markers fields are dropped per TODO.partitive-relation-v2.
|
|
65
47
|
if (parts.length < 2) {
|
|
66
48
|
out.migrationWarning =
|
|
67
49
|
`v1 PartitiveHyperedge had ${parts.length} part(s); v2 requires ≥2. ` +
|
|
68
50
|
`Either add more partitives or convert to a binary has_part edge.`;
|
|
69
51
|
}
|
|
70
52
|
|
|
71
|
-
if (
|
|
53
|
+
if (h.content != null) {
|
|
72
54
|
out.migrationWarning = (out.migrationWarning ?? '') +
|
|
73
55
|
` v1 'content' field was dropped (v2 PartitiveHyperedge carries no prose; ` +
|
|
74
56
|
`move valuable text to the comprehensive concept's notes).`;
|
|
75
57
|
}
|
|
76
58
|
|
|
77
|
-
if (Array.isArray(
|
|
59
|
+
if (Array.isArray(h.markers) && h.markers.length > 0) {
|
|
78
60
|
out.migrationWarning = (out.migrationWarning ?? '') +
|
|
79
|
-
` v1 'markers' field (${
|
|
61
|
+
` v1 'markers' field (${h.markers.join(', ')}) was dropped; ` +
|
|
80
62
|
`v2 uses per-member 'multiplicity' + 'is_delimiting'. Reviewer ` +
|
|
81
63
|
`should set these per partitive based on ISO 704:2022 notation.`;
|
|
82
64
|
}
|
|
83
65
|
|
|
84
|
-
return out;
|
|
66
|
+
return out as V2HyperedgeHash;
|
|
85
67
|
}
|
|
86
68
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
export function downgradeRelationToHyperedge(v2Hash) {
|
|
69
|
+
export function downgradeRelationToHyperedge(
|
|
70
|
+
v2Hash: Partial<V2HyperedgeHash> | ReadonlyArray<Partial<V2HyperedgeHash>> | null,
|
|
71
|
+
): unknown {
|
|
91
72
|
if (v2Hash == null) return null;
|
|
92
73
|
if (Array.isArray(v2Hash)) {
|
|
93
74
|
return v2Hash.map(downgradeRelationToHyperedge);
|
|
94
75
|
}
|
|
95
76
|
|
|
96
|
-
const
|
|
97
|
-
|
|
77
|
+
const v2 = v2Hash as V2HyperedgeHash;
|
|
78
|
+
const out: {
|
|
79
|
+
comprehensive: { source?: string; id?: string };
|
|
80
|
+
parts: Array<{ source?: string; id?: string }>;
|
|
81
|
+
enumeration: string;
|
|
82
|
+
} = {
|
|
83
|
+
comprehensive: { ...(v2.comprehensive ?? {}) },
|
|
84
|
+
parts: [],
|
|
85
|
+
enumeration: '',
|
|
98
86
|
};
|
|
99
87
|
|
|
100
|
-
out.parts = (
|
|
101
|
-
out.enumeration = _downgradeCompleteness(
|
|
102
|
-
|
|
103
|
-
// criterion, multiplicity, is_delimiting have no v1 equivalent —
|
|
104
|
-
// dropped silently.
|
|
88
|
+
out.parts = (v2.partitives ?? []).map((m) => ({ ...(m.ref ?? {}) }));
|
|
89
|
+
out.enumeration = _downgradeCompleteness(v2.completeness);
|
|
105
90
|
|
|
106
91
|
return out;
|
|
107
92
|
}
|
|
108
93
|
|
|
109
|
-
function _migrateEnumeration(value) {
|
|
94
|
+
function _migrateEnumeration(value: string | undefined): string {
|
|
110
95
|
if (value === 'open') return 'partial';
|
|
111
96
|
if (value === 'closed') return 'complete';
|
|
112
97
|
if (value == null) return 'complete';
|
|
113
|
-
// Unknown values default to complete (safe default — v2's safe default).
|
|
114
98
|
return 'complete';
|
|
115
99
|
}
|
|
116
100
|
|
|
117
|
-
function _downgradeCompleteness(value) {
|
|
101
|
+
function _downgradeCompleteness(value: string | undefined): string {
|
|
118
102
|
if (value === 'partial') return 'open';
|
|
119
103
|
return 'closed';
|
|
120
104
|
}
|
package/src/models/concept.ts
CHANGED
|
@@ -467,7 +467,7 @@ function _migrateV1Hash(
|
|
|
467
467
|
hash: Record<string, unknown>,
|
|
468
468
|
): Record<string, unknown> | null {
|
|
469
469
|
if (cls === PartitiveHyperedge && v1Key === 'partitive_hyperedges') {
|
|
470
|
-
return migrateHyperedgeToRelation(hash) as Record<string, unknown>;
|
|
470
|
+
return migrateHyperedgeToRelation(hash) as unknown as Record<string, unknown>;
|
|
471
471
|
}
|
|
472
472
|
return null;
|
|
473
473
|
}
|
package/src/sort.ts
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
// @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
|
|
2
1
|
const NATURAL_SORT_RE = /(\d+|\D+)/g;
|
|
3
2
|
const DIGIT_RE = /^\d+$/;
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Natural sort comparator for concept IDs like "3.1.1.1", "551-12-39".
|
|
7
|
-
* @param {string} a
|
|
8
|
-
* @param {string} b
|
|
9
|
-
* @returns {number}
|
|
10
6
|
*/
|
|
11
|
-
export function naturalSort(a, b) {
|
|
7
|
+
export function naturalSort(a: string, b: string): number {
|
|
12
8
|
const pa = a.match(NATURAL_SORT_RE) || [];
|
|
13
9
|
const pb = b.match(NATURAL_SORT_RE) || [];
|
|
14
10
|
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
//
|
|
2
|
-
// ConceptToGlossTransform — JS port of glossarist-ruby's
|
|
3
|
-
// `Transforms::ConceptToGlossTransform`. Converts a Concept model into
|
|
4
|
-
// ontology-faithful RDF (Turtle, JSON-LD, N-Triples).
|
|
1
|
+
// ConceptToGlossTransform — converts a Concept model into RDF.
|
|
5
2
|
//
|
|
6
3
|
// Usage:
|
|
7
|
-
//
|
|
8
|
-
// const xform = new ConceptToGlossTransform({ registerId: 'iso-6709', uriBase: 'https://glossarist.org' });
|
|
4
|
+
// const xform = new ConceptToGlossTransform({ registerId: 'iso-6709', uriBase: 'https://example.org' });
|
|
9
5
|
// const ttl = await xform.toTurtle(concept);
|
|
10
|
-
|
|
11
|
-
// const ttlAll = await xform.toTurtleAll(concepts);
|
|
6
|
+
|
|
12
7
|
import { conceptToQuads } from '../rdf/gloss-concept.js';
|
|
13
8
|
import {
|
|
14
9
|
collectQuads,
|
|
@@ -17,62 +12,70 @@ import {
|
|
|
17
12
|
writeJsonld,
|
|
18
13
|
} from '../rdf/document-writer.js';
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
type QuadLike = unknown;
|
|
16
|
+
type ConceptLike = { registerId?: string; id?: string | number } & Record<string, unknown>;
|
|
17
|
+
|
|
18
|
+
interface WriterEntry {
|
|
19
|
+
write: (quads: QuadLike[] | QuadLike, transform: ConceptToGlossTransform) => Promise<unknown>;
|
|
20
|
+
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
ntriples: { write: (quads) => writeNTriples(quads) },
|
|
28
|
-
jsonld: { write: (quads, t) => writeJsonld(quads, t._jsonldOpts()) },
|
|
22
|
+
const WRITERS: Readonly<Record<string, WriterEntry>> = Object.freeze({
|
|
23
|
+
turtle: { write: (quads) => writeTurtle(quads as QuadLike[]) },
|
|
24
|
+
ntriples: { write: (quads) => writeNTriples(quads as QuadLike[]) },
|
|
25
|
+
jsonld: { write: (quads, t) => writeJsonld(quads as QuadLike[], t._jsonldOpts() as never) },
|
|
29
26
|
});
|
|
30
27
|
|
|
31
28
|
export class ConceptToGlossTransform {
|
|
32
|
-
|
|
29
|
+
readonly registerId: string | undefined;
|
|
30
|
+
readonly uriBase: string;
|
|
31
|
+
readonly jsonldContext: unknown;
|
|
32
|
+
|
|
33
|
+
constructor(options: { registerId?: string; uriBase?: string; jsonldContext?: unknown } = {}) {
|
|
33
34
|
this.registerId = options.registerId;
|
|
34
|
-
this.uriBase = options.uriBase
|
|
35
|
-
if (!this.uriBase)
|
|
35
|
+
this.uriBase = (options.uriBase ?? '').replace(/\/+$/, '');
|
|
36
|
+
if (!this.uriBase) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'ConceptToGlossTransform requires options.uriBase — the deployment canonical URI root.',
|
|
39
|
+
);
|
|
40
|
+
}
|
|
36
41
|
this.jsonldContext = options.jsonldContext;
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
toTurtle(target)
|
|
40
|
-
toTurtleAll(targets) { return this._serialize(targets, 'turtle'); }
|
|
41
|
-
toJsonld(target)
|
|
42
|
-
toJsonldAll(targets) { return this._serialize(targets, 'jsonld'); }
|
|
43
|
-
toNTriples(target)
|
|
44
|
-
toNTriplesAll(targets) { return this._serialize(targets, 'ntriples'); }
|
|
44
|
+
toTurtle(target: ConceptLike): Promise<unknown> { return this._serialize(target, 'turtle'); }
|
|
45
|
+
toTurtleAll(targets: Iterable<ConceptLike>): Promise<unknown> { return this._serialize(targets, 'turtle'); }
|
|
46
|
+
toJsonld(target: ConceptLike): Promise<unknown> { return this._serialize(target, 'jsonld'); }
|
|
47
|
+
toJsonldAll(targets: Iterable<ConceptLike>): Promise<unknown> { return this._serialize(targets, 'jsonld'); }
|
|
48
|
+
toNTriples(target: ConceptLike): Promise<unknown> { return this._serialize(target, 'ntriples'); }
|
|
49
|
+
toNTriplesAll(targets: Iterable<ConceptLike>): Promise<unknown> { return this._serialize(targets, 'ntriples'); }
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
// off to the registered writer. Replaces six near-identical methods.
|
|
48
|
-
async _serialize(target, format) {
|
|
51
|
+
async _serialize(target: ConceptLike | Iterable<ConceptLike>, format: string): Promise<unknown> {
|
|
49
52
|
const writer = WRITERS[format];
|
|
50
53
|
if (!writer) throw new Error(`Unknown serialization format: ${format}`);
|
|
51
54
|
const quads = this._collect(target);
|
|
52
55
|
return writer.write(quads, this);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
_collect(target) {
|
|
56
|
-
if (target && typeof target[Symbol.iterator] === 'function') {
|
|
57
|
-
const quads = [];
|
|
58
|
-
for (const concept of target) {
|
|
59
|
-
for (const q of conceptToQuads(concept, this._optsFor(concept))) {
|
|
58
|
+
_collect(target: ConceptLike | Iterable<ConceptLike>): QuadLike[] {
|
|
59
|
+
if (target && typeof (target as Iterable<ConceptLike>)[Symbol.iterator] === 'function') {
|
|
60
|
+
const quads: QuadLike[] = [];
|
|
61
|
+
for (const concept of target as Iterable<ConceptLike>) {
|
|
62
|
+
for (const q of conceptToQuads(concept as never, this._optsFor(concept))) {
|
|
60
63
|
quads.push(q);
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
return quads;
|
|
64
67
|
}
|
|
65
|
-
return collectQuads(conceptToQuads(target, this._optsFor(target)));
|
|
68
|
+
return collectQuads(conceptToQuads(target as never, this._optsFor(target as ConceptLike)));
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
_optsFor(concept) {
|
|
71
|
+
_optsFor(concept: ConceptLike): { registerId: string; uriBase: string } {
|
|
69
72
|
return {
|
|
70
73
|
registerId: this.registerId ?? concept.registerId ?? 'glossary',
|
|
71
74
|
uriBase: this.uriBase,
|
|
72
75
|
};
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
_jsonldOpts() {
|
|
78
|
+
_jsonldOpts(): { context?: unknown } {
|
|
76
79
|
return this.jsonldContext ? { context: this.jsonldContext } : {};
|
|
77
80
|
}
|
|
78
81
|
}
|
package/src/transforms/index.ts
CHANGED
package/src/uuid.ts
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
// @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
|
|
2
1
|
import { createHash } from 'crypto';
|
|
3
2
|
|
|
4
3
|
const NAMESPACE_UUID = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
|
5
4
|
|
|
6
|
-
export function uuidV5(namespace, name) {
|
|
5
|
+
export function uuidV5(namespace: string, name: string): string {
|
|
7
6
|
const ns = _parseUuid(namespace);
|
|
8
7
|
const hash = createHash('sha1').update(ns).update(name, 'utf8').digest();
|
|
9
|
-
hash[6] = (hash[6] & 0x0f) | 0x50;
|
|
10
|
-
hash[8] = (hash[8] & 0x3f) | 0x80;
|
|
8
|
+
hash[6] = ((hash[6] ?? 0) & 0x0f) | 0x50;
|
|
9
|
+
hash[8] = ((hash[8] ?? 0) & 0x3f) | 0x80;
|
|
11
10
|
return _formatUuid(hash);
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
export function conceptUuid(conceptId, namespace = NAMESPACE_UUID) {
|
|
13
|
+
export function conceptUuid(conceptId: string, namespace: string = NAMESPACE_UUID): string {
|
|
15
14
|
return uuidV5(namespace, conceptId);
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
export function localizedConceptUuid(
|
|
17
|
+
export function localizedConceptUuid(
|
|
18
|
+
conceptId: string,
|
|
19
|
+
languageCode: string,
|
|
20
|
+
namespace: string = NAMESPACE_UUID,
|
|
21
|
+
): string {
|
|
19
22
|
return uuidV5(namespace, `${conceptId}:${languageCode}`);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
function _parseUuid(str) {
|
|
25
|
+
function _parseUuid(str: string): Buffer {
|
|
23
26
|
return Buffer.from(str.replace(/-/g, ''), 'hex');
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
function _formatUuid(hash) {
|
|
29
|
+
function _formatUuid(hash: Buffer): string {
|
|
27
30
|
const h = hash.toString('hex');
|
|
28
31
|
return [h.slice(0, 8), h.slice(8, 12), h.slice(12, 16), h.slice(16, 20), h.slice(20, 32)].join('-');
|
|
29
32
|
}
|