-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathselect.js
More file actions
111 lines (100 loc) · 2.99 KB
/
select.js
File metadata and controls
111 lines (100 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
* Copyright 2026 Digital Bazaar, Inc.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
import {parsePointer} from './pointer.js';
// this is the standard `selectJsonLd` algorithm from:
// https://www.w3.org/TR/vc-di-ecdsa/#selectjsonld
export function selectJsonLd({document, pointers} = {}) {
if(!(document && typeof document === 'object')) {
throw new TypeError('"document" must be an object.');
}
if(!Array.isArray(pointers)) {
throw new TypeError('"pointers" must be an array.');
}
if(pointers.length === 0) {
// no pointers, so nothing selected
return null;
}
// track arrays to make them dense after selection
const arrays = [];
// perform selection
const selectionDocument = {'@context': structuredClone(document['@context'])};
_initSelection({selection: selectionDocument, source: document});
for(const pointer of pointers) {
// parse pointer into individual paths
const paths = parsePointer(pointer);
if(paths.length === 0) {
// whole document selected
return structuredClone(document);
}
_selectPaths({document, pointer, paths, selectionDocument, arrays});
}
// make any sparse arrays dense
for(const array of arrays) {
let i = 0;
while(i < array.length) {
if(array[i] === undefined) {
array.splice(i, 1);
continue;
}
i++;
}
}
return selectionDocument;
}
function _selectPaths({
document, pointer, paths, selectionDocument, arrays
} = {}) {
// make pointer path in selection document
let parentValue = document;
let value = parentValue;
let selectedParent = selectionDocument;
let selectedValue = selectedParent;
for(const path of paths) {
selectedParent = selectedValue;
parentValue = value;
// get next document value
value = parentValue[path];
if(value === undefined) {
throw new TypeError(
`JSON pointer "${pointer}" does not match document.`);
}
// get next value selection
selectedValue = selectedParent[path];
if(selectedValue === undefined) {
if(Array.isArray(value)) {
selectedValue = [];
arrays.push(selectedValue);
} else {
selectedValue = _initSelection({source: value});
}
selectedParent[path] = selectedValue;
}
}
// path traversal complete, compute selected value
if(typeof value !== 'object') {
// literal selected
selectedValue = value;
} else if(Array.isArray(value)) {
// full array selected
selectedValue = structuredClone(value);
} else {
// object selected, blend with `id` / `type` / `@context`
selectedValue = {...selectedValue, ...structuredClone(value)};
}
// add selected value to selected parent
selectedParent[paths.at(-1)] = selectedValue;
}
function _initSelection({selection = {}, source}) {
// must include non-blank node IDs
if(source.id && !source.id.startsWith('_:')) {
selection.id = source.id;
}
// always include types
if(source.type) {
selection.type = source.type;
}
return selection;
}