@alizarin/napi 2.0.0-alpha.96 → 2.0.0-alpha.98
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/__test__/index.mjs +42 -1
- package/{alizarin-napi-2.0.0-alpha.96.tgz → alizarin-napi-2.0.0-alpha.98.tgz} +0 -0
- package/alizarin-napi.darwin-arm64.node +0 -0
- package/alizarin-napi.darwin-x64.node +0 -0
- package/alizarin-napi.linux-x64-gnu.node +0 -0
- package/alizarin-napi.win32-x64-msvc.node +0 -0
- package/index.d.ts +33 -4
- package/index.js +3 -1
- package/package.json +4 -4
- package/src/instance_wrapper_napi.rs +13 -2
- package/src/lib.rs +100 -3
package/__test__/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
|
|
7
|
-
import { NapiStaticGraph, NapiStaticResourceRegistry, NapiResourceModelWrapper, NapiResourceInstanceWrapper } from '../index.js';
|
|
7
|
+
import { NapiStaticGraph, NapiStaticResourceRegistry, NapiResourceModelWrapper, NapiResourceInstanceWrapper, NapiNodeConfigManager } from '../index.js';
|
|
8
8
|
|
|
9
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const TEST_DATA = path.resolve(__dirname, '..', '..', '..', 'tests');
|
|
@@ -522,3 +522,44 @@ describe('NapiResourceInstanceWrapper methods', () => {
|
|
|
522
522
|
assert.equal(tiles.length, 0, 'should be empty');
|
|
523
523
|
});
|
|
524
524
|
});
|
|
525
|
+
|
|
526
|
+
// =============================================================================
|
|
527
|
+
// NapiNodeConfigManager + NapiStaticGraph integration
|
|
528
|
+
// Regression: loadFromGraph used JSON.stringify on a NapiStaticGraph, which
|
|
529
|
+
// produced {} because NAPI class getters are not enumerable own properties.
|
|
530
|
+
// The fix uses buildFromGraph (direct struct reference) instead.
|
|
531
|
+
// =============================================================================
|
|
532
|
+
|
|
533
|
+
describe('NapiNodeConfigManager', () => {
|
|
534
|
+
const personJson = fs.readFileSync(
|
|
535
|
+
path.join(TEST_DATA, 'data', 'models', 'Person.json'),
|
|
536
|
+
'utf-8'
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
it('buildFromGraph accepts a NapiStaticGraph without error', () => {
|
|
540
|
+
const graph = NapiStaticGraph.fromJsonString(personJson);
|
|
541
|
+
const ncm = new NapiNodeConfigManager();
|
|
542
|
+
// This is the path that failed when using JSON.stringify(napiGraph)
|
|
543
|
+
ncm.buildFromGraph(graph);
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('buildFromGraphJson fails on JSON.stringify of a NapiStaticGraph', () => {
|
|
547
|
+
const graph = NapiStaticGraph.fromJsonString(personJson);
|
|
548
|
+
const ncm = new NapiNodeConfigManager();
|
|
549
|
+
// Demonstrates the bug: NAPI objects don't stringify their getters
|
|
550
|
+
const stringified = JSON.stringify(graph);
|
|
551
|
+
assert.throws(
|
|
552
|
+
() => ncm.buildFromGraphJson(stringified),
|
|
553
|
+
/Failed to parse graph/i,
|
|
554
|
+
'JSON.stringify of NapiStaticGraph should produce invalid graph JSON'
|
|
555
|
+
);
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
it('buildFromGraphJson works with raw graph JSON', () => {
|
|
559
|
+
const parsed = JSON.parse(personJson);
|
|
560
|
+
const graphObj = parsed.graph ? parsed.graph[0] : parsed;
|
|
561
|
+
const ncm = new NapiNodeConfigManager();
|
|
562
|
+
// Raw JS object stringifies correctly
|
|
563
|
+
ncm.buildFromGraphJson(JSON.stringify(graphObj));
|
|
564
|
+
});
|
|
565
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare function buildGraphFromCsvs(graphCsv: string, nodesCsv: string, c
|
|
|
17
17
|
* Returns the resources wrapped in the `{ business_data: { resources: [...] } }`
|
|
18
18
|
* format expected by PrebuildLoader.
|
|
19
19
|
*/
|
|
20
|
-
export declare function buildBusinessDataFromCsv(csvData: string, graphJson: string, collectionsJson: string): any
|
|
20
|
+
export declare function buildBusinessDataFromCsv(csvData: string, graphJson: string, collectionsJson: string, defaultLanguage?: string | undefined | null, strictConcepts?: boolean | undefined | null): any
|
|
21
21
|
/**
|
|
22
22
|
* Coerce a value using the registered extension handler for the given datatype.
|
|
23
23
|
*
|
|
@@ -44,6 +44,10 @@ export declare function getRegisteredExtensionHandlers(): Array<string>
|
|
|
44
44
|
export declare function parseSkosXml(xmlContent: string, baseUri: string): any
|
|
45
45
|
/** Parse SKOS RDF/XML and return a single collection (the first one found). */
|
|
46
46
|
export declare function parseSkosXmlToCollection(xmlContent: string, baseUri: string): any
|
|
47
|
+
/** Serialize a single SkosCollection to SKOS RDF/XML. */
|
|
48
|
+
export declare function collectionToSkosXml(collectionJson: any, baseUri: string): string
|
|
49
|
+
/** Serialize multiple SkosCollections to SKOS RDF/XML. */
|
|
50
|
+
export declare function collectionsToSkosXml(collectionsJson: any, baseUri: string): string
|
|
47
51
|
/**
|
|
48
52
|
* Build a mapping from node alias to collection ID based on graph definition.
|
|
49
53
|
*
|
|
@@ -193,8 +197,13 @@ export declare class NapiValuesFromNodegroupResult {
|
|
|
193
197
|
get impliedNodegroups(): Array<string>
|
|
194
198
|
}
|
|
195
199
|
export declare class NapiResourceInstanceWrapper {
|
|
196
|
-
/**
|
|
197
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Create a wrapper for a given graph (must be registered).
|
|
202
|
+
*
|
|
203
|
+
* If `resource_id` is provided, a minimal resource metadata is created so
|
|
204
|
+
* the tile-source fast path can look up tiles by resource.
|
|
205
|
+
*/
|
|
206
|
+
constructor(graphId: string, resourceId?: string | undefined | null)
|
|
198
207
|
/** Load tiles from a JSON string (single-pass deserialization). */
|
|
199
208
|
loadTiles(tilesJson: string): void
|
|
200
209
|
/** Load tiles directly from a StaticResource JSON string (single-pass deserialization). */
|
|
@@ -368,6 +377,13 @@ export declare class NapiStaticGraph {
|
|
|
368
377
|
get name(): any
|
|
369
378
|
/** Register this graph in the global registry so NapiResourceInstanceWrapper can use it. */
|
|
370
379
|
register(): void
|
|
380
|
+
/**
|
|
381
|
+
* Set a descriptor template for computing resource name/description/slug.
|
|
382
|
+
*
|
|
383
|
+
* Template placeholders use `<Node Name>` syntax, e.g. `"<Headword>"`.
|
|
384
|
+
* Descriptor types: "name", "description", "slug".
|
|
385
|
+
*/
|
|
386
|
+
setDescriptorTemplate(descriptorType: string, stringTemplate: string): void
|
|
371
387
|
}
|
|
372
388
|
export declare class NapiStaticResourceRegistry {
|
|
373
389
|
constructor()
|
|
@@ -384,7 +400,7 @@ export declare class NapiStaticResourceRegistry {
|
|
|
384
400
|
/** Insert full resources from a business_data JSON file string. */
|
|
385
401
|
mergeFromBusinessDataJson(businessDataJson: string, storeFull?: boolean | undefined | null): void
|
|
386
402
|
/** Build an inverted index: visibility value -> [resource IDs]. */
|
|
387
|
-
getValueToResourcesIndex(graph: NapiStaticGraph, nodeIdentifier: string, flattenLocalized?: boolean | undefined | null): Record<string, Array<string>>
|
|
403
|
+
getValueToResourcesIndex(graph: NapiStaticGraph, nodeIdentifier: string, flattenLocalized?: boolean | undefined | null, rdmCache?: NapiRdmCache | undefined | null): Record<string, Array<string>>
|
|
388
404
|
/**
|
|
389
405
|
* Extract values from one node in tiles where another node matches a filter.
|
|
390
406
|
*
|
|
@@ -406,4 +422,17 @@ export declare class NapiStaticResourceRegistry {
|
|
|
406
422
|
memoryStats(): any
|
|
407
423
|
/** Get detailed stats including estimated byte sizes (expensive — re-serializes all data) */
|
|
408
424
|
memoryStatsDetailed(): any
|
|
425
|
+
/**
|
|
426
|
+
* Populate __cache on resources with summaries for referenced resources.
|
|
427
|
+
*
|
|
428
|
+
* Uses the graph to identify resource-instance nodes, then populates
|
|
429
|
+
* cache entries for each referenced resource found in this registry.
|
|
430
|
+
*
|
|
431
|
+
* If `enrich_relationships` is true, also adds ontologyProperty to tile data.
|
|
432
|
+
* If `recompute_descriptors` is true, recomputes resource name/description from
|
|
433
|
+
* descriptor templates set on the graph.
|
|
434
|
+
*
|
|
435
|
+
* Returns `{ resources, unknownReferences, hasUnknown }`.
|
|
436
|
+
*/
|
|
437
|
+
populateCachesFromJson(resourcesJson: string, graph: NapiStaticGraph, enrichRelationships?: boolean | undefined | null, strict?: boolean | undefined | null, recomputeDescriptors?: boolean | undefined | null): any
|
|
409
438
|
}
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { NapiRdmCache, NapiNodeConfigManager, NapiTileData, NapiStaticTile, NapiPseudoValue, NapiPseudoList, NapiPopulateResult, NapiEnsureNodegroupResult, NapiValuesFromNodegroupResult, NapiResourceInstanceWrapper, NapiResourceModelWrapper, NapiPrebuildExporter, NapiStaticGraph, NapiStaticResourceRegistry, buildGraphFromCsvs, buildBusinessDataFromCsv, extensionCoerce, extensionRenderDisplay, extensionResolveMarkers, hasExtensionHandler, getRegisteredExtensionHandlers, parseSkosXml, parseSkosXmlToCollection, buildAliasToCollectionMap, findNeededCollections, isValidUuid, getDefaultResolvableDatatypes, getDefaultConfigKeys, importPrebuild } = nativeBinding
|
|
313
|
+
const { NapiRdmCache, NapiNodeConfigManager, NapiTileData, NapiStaticTile, NapiPseudoValue, NapiPseudoList, NapiPopulateResult, NapiEnsureNodegroupResult, NapiValuesFromNodegroupResult, NapiResourceInstanceWrapper, NapiResourceModelWrapper, NapiPrebuildExporter, NapiStaticGraph, NapiStaticResourceRegistry, buildGraphFromCsvs, buildBusinessDataFromCsv, extensionCoerce, extensionRenderDisplay, extensionResolveMarkers, hasExtensionHandler, getRegisteredExtensionHandlers, parseSkosXml, parseSkosXmlToCollection, collectionToSkosXml, collectionsToSkosXml, buildAliasToCollectionMap, findNeededCollections, isValidUuid, getDefaultResolvableDatatypes, getDefaultConfigKeys, importPrebuild } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.NapiRdmCache = NapiRdmCache
|
|
316
316
|
module.exports.NapiNodeConfigManager = NapiNodeConfigManager
|
|
@@ -335,6 +335,8 @@ module.exports.hasExtensionHandler = hasExtensionHandler
|
|
|
335
335
|
module.exports.getRegisteredExtensionHandlers = getRegisteredExtensionHandlers
|
|
336
336
|
module.exports.parseSkosXml = parseSkosXml
|
|
337
337
|
module.exports.parseSkosXmlToCollection = parseSkosXmlToCollection
|
|
338
|
+
module.exports.collectionToSkosXml = collectionToSkosXml
|
|
339
|
+
module.exports.collectionsToSkosXml = collectionsToSkosXml
|
|
338
340
|
module.exports.buildAliasToCollectionMap = buildAliasToCollectionMap
|
|
339
341
|
module.exports.findNeededCollections = findNeededCollections
|
|
340
342
|
module.exports.isValidUuid = isValidUuid
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alizarin/napi",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.98",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@napi-rs/cli": "^2.18.0"
|
|
31
31
|
},
|
|
32
32
|
"optionalDependencies": {
|
|
33
|
-
"@alizarin/napi-win32-x64-msvc": "2.0.0-alpha.
|
|
34
|
-
"@alizarin/napi-darwin-x64": "2.0.0-alpha.
|
|
35
|
-
"@alizarin/napi-linux-x64-gnu": "2.0.0-alpha.
|
|
33
|
+
"@alizarin/napi-win32-x64-msvc": "2.0.0-alpha.98",
|
|
34
|
+
"@alizarin/napi-darwin-x64": "2.0.0-alpha.98",
|
|
35
|
+
"@alizarin/napi-linux-x64-gnu": "2.0.0-alpha.98"
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -44,7 +44,14 @@ fn sc_err(e: SemanticChildError) -> napi::Error {
|
|
|
44
44
|
|
|
45
45
|
#[napi]
|
|
46
46
|
pub struct NapiRdmCache {
|
|
47
|
-
inner: RdmCache,
|
|
47
|
+
pub(crate) inner: RdmCache,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl NapiRdmCache {
|
|
51
|
+
/// Crate-internal accessor (field not directly accessible through #[napi] macro).
|
|
52
|
+
pub(crate) fn inner(&self) -> &RdmCache {
|
|
53
|
+
&self.inner
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
#[napi]
|
|
@@ -780,8 +787,11 @@ impl NapiResourceInstanceWrapper {
|
|
|
780
787
|
// =========================================================================
|
|
781
788
|
|
|
782
789
|
/// Create a wrapper for a given graph (must be registered).
|
|
790
|
+
///
|
|
791
|
+
/// If `resource_id` is provided, a minimal resource metadata is created so
|
|
792
|
+
/// the tile-source fast path can look up tiles by resource.
|
|
783
793
|
#[napi(constructor)]
|
|
784
|
-
pub fn new(graph_id: String) -> Result<Self> {
|
|
794
|
+
pub fn new(graph_id: String, _resource_id: Option<String>) -> Result<Self> {
|
|
785
795
|
let graph = alizarin_core::get_graph(&graph_id).ok_or_else(|| {
|
|
786
796
|
napi::Error::from_reason(format!(
|
|
787
797
|
"Graph '{}' not registered. Call registerGraph() first.",
|
|
@@ -790,6 +800,7 @@ impl NapiResourceInstanceWrapper {
|
|
|
790
800
|
})?;
|
|
791
801
|
|
|
792
802
|
let model_access = GraphModelAccess::from_graph(&graph);
|
|
803
|
+
// TODO: use resource_id with new_for_resource_id once tile-source plumbing is committed
|
|
793
804
|
let mut core = ResourceInstanceWrapperCore::new(graph_id);
|
|
794
805
|
core.set_cached_indices(&model_access);
|
|
795
806
|
|
package/src/lib.rs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
mod instance_wrapper_napi;
|
|
2
|
+
use instance_wrapper_napi::NapiRdmCache;
|
|
2
3
|
|
|
3
4
|
use std::collections::HashMap;
|
|
4
5
|
use std::sync::OnceLock;
|
|
@@ -166,6 +167,21 @@ impl NapiStaticGraph {
|
|
|
166
167
|
pub fn register(&self) {
|
|
167
168
|
alizarin_core::register_graph_owned(self.inner.clone());
|
|
168
169
|
}
|
|
170
|
+
|
|
171
|
+
/// Set a descriptor template for computing resource name/description/slug.
|
|
172
|
+
///
|
|
173
|
+
/// Template placeholders use `<Node Name>` syntax, e.g. `"<Headword>"`.
|
|
174
|
+
/// Descriptor types: "name", "description", "slug".
|
|
175
|
+
#[napi(js_name = "setDescriptorTemplate")]
|
|
176
|
+
pub fn set_descriptor_template(
|
|
177
|
+
&mut self,
|
|
178
|
+
descriptor_type: String,
|
|
179
|
+
string_template: String,
|
|
180
|
+
) -> Result<()> {
|
|
181
|
+
self.inner
|
|
182
|
+
.set_descriptor_template(&descriptor_type, &string_template)
|
|
183
|
+
.map_err(napi::Error::from_reason)
|
|
184
|
+
}
|
|
169
185
|
}
|
|
170
186
|
|
|
171
187
|
// ============================================================================
|
|
@@ -281,9 +297,12 @@ impl NapiStaticResourceRegistry {
|
|
|
281
297
|
graph: &NapiStaticGraph,
|
|
282
298
|
node_identifier: String,
|
|
283
299
|
flatten_localized: Option<bool>,
|
|
300
|
+
rdm_cache: Option<&NapiRdmCache>,
|
|
284
301
|
) -> Result<HashMap<String, Vec<String>>> {
|
|
285
302
|
let ctx = SerializationContext {
|
|
286
303
|
extension_registry: Some(extension_registry()),
|
|
304
|
+
external_resolver: rdm_cache
|
|
305
|
+
.map(|r| r.inner() as &dyn alizarin_core::type_serialization::ExternalResolver),
|
|
287
306
|
..SerializationContext::empty()
|
|
288
307
|
};
|
|
289
308
|
self.inner
|
|
@@ -397,6 +416,47 @@ impl NapiStaticResourceRegistry {
|
|
|
397
416
|
serde_json::to_value(&stats)
|
|
398
417
|
.map_err(|e| napi::Error::from_reason(format!("Serialization failed: {}", e)))
|
|
399
418
|
}
|
|
419
|
+
|
|
420
|
+
/// Populate __cache on resources with summaries for referenced resources.
|
|
421
|
+
///
|
|
422
|
+
/// Uses the graph to identify resource-instance nodes, then populates
|
|
423
|
+
/// cache entries for each referenced resource found in this registry.
|
|
424
|
+
///
|
|
425
|
+
/// If `enrich_relationships` is true, also adds ontologyProperty to tile data.
|
|
426
|
+
/// If `recompute_descriptors` is true, recomputes resource name/description from
|
|
427
|
+
/// descriptor templates set on the graph.
|
|
428
|
+
///
|
|
429
|
+
/// Returns `{ resources, unknownReferences, hasUnknown }`.
|
|
430
|
+
#[napi(js_name = "populateCachesFromJson")]
|
|
431
|
+
pub fn populate_caches_from_json(
|
|
432
|
+
&self,
|
|
433
|
+
resources_json: String,
|
|
434
|
+
graph: &NapiStaticGraph,
|
|
435
|
+
enrich_relationships: Option<bool>,
|
|
436
|
+
strict: Option<bool>,
|
|
437
|
+
recompute_descriptors: Option<bool>,
|
|
438
|
+
) -> Result<serde_json::Value> {
|
|
439
|
+
let mut resources: Vec<StaticResource> = serde_json::from_str(&resources_json)
|
|
440
|
+
.map_err(|e| napi::Error::from_reason(format!("Failed to parse resources: {}", e)))?;
|
|
441
|
+
|
|
442
|
+
let result = self
|
|
443
|
+
.inner
|
|
444
|
+
.populate_caches(
|
|
445
|
+
&mut resources,
|
|
446
|
+
&graph.inner,
|
|
447
|
+
enrich_relationships.unwrap_or(true),
|
|
448
|
+
strict.unwrap_or(false),
|
|
449
|
+
recompute_descriptors.unwrap_or(false),
|
|
450
|
+
)
|
|
451
|
+
.map_err(napi::Error::from_reason)?;
|
|
452
|
+
|
|
453
|
+
let output = serde_json::json!({
|
|
454
|
+
"resources": resources,
|
|
455
|
+
"unknownReferences": result.unknown_references,
|
|
456
|
+
"hasUnknown": result.has_unknown_references(),
|
|
457
|
+
});
|
|
458
|
+
Ok(output)
|
|
459
|
+
}
|
|
400
460
|
}
|
|
401
461
|
|
|
402
462
|
// ============================================================================
|
|
@@ -459,15 +519,21 @@ pub fn build_business_data_from_csv(
|
|
|
459
519
|
csv_data: String,
|
|
460
520
|
graph_json: String,
|
|
461
521
|
collections_json: String,
|
|
522
|
+
default_language: Option<String>,
|
|
523
|
+
strict_concepts: Option<bool>,
|
|
462
524
|
) -> Result<serde_json::Value> {
|
|
463
525
|
let graph: StaticGraph = serde_json::from_str(&graph_json)
|
|
464
526
|
.map_err(|e| napi::Error::from_reason(format!("Invalid graph JSON: {e}")))?;
|
|
465
527
|
let collections: Vec<SkosCollection> = serde_json::from_str(&collections_json)
|
|
466
528
|
.map_err(|e| napi::Error::from_reason(format!("Invalid collections JSON: {e}")))?;
|
|
467
529
|
|
|
468
|
-
let
|
|
469
|
-
|
|
470
|
-
|
|
530
|
+
let options = alizarin_core::csv_business_data_loader::BusinessDataCsvOptions {
|
|
531
|
+
default_language: default_language.unwrap_or_else(|| "en".to_string()),
|
|
532
|
+
strict_concepts: strict_concepts.unwrap_or(true),
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
let resources = build_resources_from_business_csv(&csv_data, &graph, &collections, options)
|
|
536
|
+
.map_err(csv_err)?;
|
|
471
537
|
|
|
472
538
|
Ok(wrap_business_data(&resources))
|
|
473
539
|
}
|
|
@@ -574,6 +640,37 @@ pub fn parse_skos_xml_to_collection(
|
|
|
574
640
|
serde_json::to_value(&collection).map_err(|e| napi::Error::from_reason(e.to_string()))
|
|
575
641
|
}
|
|
576
642
|
|
|
643
|
+
/// Serialize a single SkosCollection to SKOS RDF/XML.
|
|
644
|
+
#[napi(js_name = "collectionToSkosXml")]
|
|
645
|
+
pub fn collection_to_skos_xml_napi(
|
|
646
|
+
collection_json: serde_json::Value,
|
|
647
|
+
base_uri: String,
|
|
648
|
+
) -> Result<String> {
|
|
649
|
+
let collection: SkosCollection = serde_json::from_value(collection_json).map_err(|e| {
|
|
650
|
+
napi::Error::from_reason(format!("Failed to deserialize collection: {}", e))
|
|
651
|
+
})?;
|
|
652
|
+
Ok(alizarin_core::skos::collection_to_skos_xml(
|
|
653
|
+
&collection,
|
|
654
|
+
&base_uri,
|
|
655
|
+
))
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/// Serialize multiple SkosCollections to SKOS RDF/XML.
|
|
659
|
+
#[napi(js_name = "collectionsToSkosXml")]
|
|
660
|
+
pub fn collections_to_skos_xml_napi(
|
|
661
|
+
collections_json: serde_json::Value,
|
|
662
|
+
base_uri: String,
|
|
663
|
+
) -> Result<String> {
|
|
664
|
+
let collections: Vec<SkosCollection> =
|
|
665
|
+
serde_json::from_value(collections_json).map_err(|e| {
|
|
666
|
+
napi::Error::from_reason(format!("Failed to deserialize collections: {}", e))
|
|
667
|
+
})?;
|
|
668
|
+
Ok(alizarin_core::skos::collections_to_skos_xml(
|
|
669
|
+
&collections,
|
|
670
|
+
&base_uri,
|
|
671
|
+
))
|
|
672
|
+
}
|
|
673
|
+
|
|
577
674
|
// ============================================================================
|
|
578
675
|
// Label resolution utilities
|
|
579
676
|
// ============================================================================
|