@verii/verii-issuing 1.0.0-pre.1754543687

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.
@@ -0,0 +1,219 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const { VeriiProtocolVersions } = require('@verii/vc-checks');
19
+ const { toRelativeServiceId } = require('@verii/did-doc');
20
+ const { castArray, isEmpty, isObject, omit, uniq } = require('lodash/fp');
21
+ const { VelocityRevocationListType } = require('@verii/vc-checks');
22
+
23
+ /** @import { Issuer, CredentialTypeMetadata, CredentialOffer, Context, JsonLdCredential, LinkedData, CredentialSubject } from "../types/types" */
24
+
25
+ /**
26
+ * Prepares a json-ld credential from an offer
27
+ * @param {Issuer} issuer the issuer
28
+ * @param {string | undefined} credentialSubjectId id of the credential subject to bind into the credential
29
+ * @param {CredentialOffer} offer offer to generate from
30
+ * @param {string} credentialId id of the credential
31
+ * @param {string} contentHash hash of the raw credentialSubject and validity values
32
+ * @param {CredentialTypeMetadata} credentialTypeMetadata credentialType metadata
33
+ * @param {string} revocationUrl revocationUrl for this offer
34
+ * @param {Context} context context
35
+ * @returns {JsonLdCredential} a json-ld formatted unsigned credential
36
+ */
37
+ const prepareJsonLdCredential = (
38
+ issuer,
39
+ credentialSubjectId,
40
+ offer,
41
+ credentialId,
42
+ contentHash,
43
+ credentialTypeMetadata,
44
+ revocationUrl,
45
+ context
46
+ ) => {
47
+ const {
48
+ config: { credentialExtensionsContextUrl },
49
+ } = context;
50
+
51
+ const credentialContexts = uniq([
52
+ 'https://www.w3.org/2018/credentials/v1',
53
+ ...buildCredentialTypeJsonLdContext(credentialTypeMetadata),
54
+ ...extractJsonLdContext(offer),
55
+ credentialExtensionsContextUrl,
56
+ ]);
57
+ const jsonldCredential = {
58
+ ...cleanOffer(offer),
59
+ '@context': credentialContexts,
60
+ id: credentialId,
61
+ type: uniq([...castArray(offer.type), 'VerifiableCredential']),
62
+ issuer: buildIssuer(offer, issuer),
63
+ issuanceDate: new Date().toISOString(),
64
+ credentialSubject: buildCredentialSubject(
65
+ offer,
66
+ credentialSubjectId,
67
+ credentialContexts,
68
+ context
69
+ ),
70
+ credentialSchema: offer.credentialSchema ?? {
71
+ type: 'JsonSchemaValidator2018',
72
+ id: credentialTypeMetadata.schemaUrl,
73
+ },
74
+ credentialStatus: buildCredentialStatus(offer, revocationUrl),
75
+ contentHash: {
76
+ type: 'VelocityContentHash2020',
77
+ value: contentHash,
78
+ },
79
+ vnfProtocolVersion: isEmpty(credentialSubjectId)
80
+ ? VeriiProtocolVersions.PROTOCOL_VERSION_1
81
+ : VeriiProtocolVersions.PROTOCOL_VERSION_2,
82
+ };
83
+
84
+ const refreshService = buildRefreshService(issuer, offer);
85
+ if (refreshService != null) {
86
+ jsonldCredential.refreshService = refreshService;
87
+ }
88
+
89
+ return jsonldCredential;
90
+ };
91
+
92
+ const cleanOffer = omit([
93
+ // these values shouldn't have been selected from the db
94
+ '_id',
95
+ 'exchangeId',
96
+ 'offerId',
97
+ 'offerCreationDate',
98
+ 'offerExpirationDate',
99
+ 'issuer.vendorOrganizationId',
100
+ 'createdAt',
101
+ 'updatedAt',
102
+ 'linkedCredentials',
103
+ 'consentedAt',
104
+ 'rejectedAt',
105
+ ]);
106
+
107
+ /**
108
+ * Prepares an issuer for jsonld credential
109
+ * @param {CredentialOffer} offer the offer
110
+ * @param {Issuer} issuer the issuer
111
+ * @returns { string | { id: string, type?: string | string[], name?: string, image?: string }} the Issuer
112
+ */
113
+ const buildIssuer = (offer, issuer) =>
114
+ offer.issuer != null && isObject(offer.issuer)
115
+ ? {
116
+ id: issuer.did,
117
+ ...omit(['vendorOrganizationId'], offer.issuer),
118
+ }
119
+ : { id: issuer.did };
120
+
121
+ /**
122
+ * Builds credential status for the credential
123
+ * @param {CredentialOffer} offer the offer
124
+ * @param {string} credentialSubjectId the credential subject id
125
+ * @param {Array} credentialContexts the credential contexts
126
+ * @param {Context} context the context
127
+ * @returns {CredentialSubject} the credential subject
128
+ */
129
+ const buildCredentialSubject = (
130
+ { credentialSubject },
131
+ credentialSubjectId,
132
+ credentialContexts,
133
+ context
134
+ ) => {
135
+ const result = omit(['vendorUserId'], credentialSubject);
136
+ if (!isEmpty(credentialSubjectId)) {
137
+ // eslint-disable-next-line better-mutation/no-mutation
138
+ result.id = credentialSubjectId;
139
+ }
140
+ if (context.config.credentialSubjectContext === true) {
141
+ // eslint-disable-next-line better-mutation/no-mutation
142
+ result['@context'] = credentialContexts;
143
+ }
144
+ return result;
145
+ };
146
+
147
+ /**
148
+ * Builds credential status for the credential
149
+ * @param {CredentialOffer} offer the offer
150
+ * @param {string} velocityRevocationUrl the revocation url
151
+ * @returns {LinkedData | LinkedData[]} the credential status
152
+ */
153
+ const buildCredentialStatus = (offer, velocityRevocationUrl) => {
154
+ const velocityCredentialStatus = {
155
+ type: VelocityRevocationListType,
156
+ id: velocityRevocationUrl,
157
+ };
158
+
159
+ return addToPolymorphicArray(
160
+ velocityCredentialStatus,
161
+ offer.credentialStatus
162
+ );
163
+ };
164
+
165
+ /**
166
+ * Builds refresh service(s)
167
+ * @param {Issuer} issuer the issuer
168
+ * @param {CredentialOffer} offer the offer
169
+ * @returns {LinkedData | LinkedData[] | undefined} the refreshservices for this offer
170
+ */
171
+ const buildRefreshService = (issuer, offer) => {
172
+ if (issuer.issuingRefreshServiceId == null) {
173
+ return undefined;
174
+ }
175
+
176
+ const defaultRefreshService = {
177
+ type: 'VelocityNetworkRefreshService2024',
178
+ id: `${issuer.did}${toRelativeServiceId(issuer.issuingRefreshServiceId)}`,
179
+ };
180
+ return addToPolymorphicArray(defaultRefreshService, offer.refreshService);
181
+ };
182
+
183
+ /**
184
+ * Adds to a polymorphic string array
185
+ * @param {string} newVal the new value to add
186
+ * @param {undefined | null | string | string[]} existingVal the existing value if any
187
+ * @returns {string | string[]} the new value
188
+ */
189
+ const addToPolymorphicArray = (newVal, existingVal) => {
190
+ if (existingVal == null) {
191
+ return newVal;
192
+ }
193
+ const array = castArray(existingVal);
194
+ // eslint-disable-next-line better-mutation/no-mutating-methods
195
+ array.push(newVal);
196
+ return array;
197
+ };
198
+
199
+ /**
200
+ * Extracts json ld context values as an string array
201
+ * @param {{"@context"?: string | string[]}} jsonLd any jsonLd
202
+ * @returns {string[]} the array of jsonLD contexts
203
+ */
204
+ const extractJsonLdContext = ({ '@context': jsonLdContext } = {}) =>
205
+ jsonLdContext == null ? [] : castArray(jsonLdContext);
206
+
207
+ /**
208
+ * Builds json ld context for the credential type
209
+ * @param {CredentialTypeMetadata} credentialTypeMetadata credential type metadata
210
+ * @returns {string[]} an array of strings
211
+ */
212
+ const buildCredentialTypeJsonLdContext = (credentialTypeMetadata) =>
213
+ credentialTypeMetadata.jsonldContext != null
214
+ ? castArray(credentialTypeMetadata.jsonldContext)
215
+ : [];
216
+
217
+ module.exports = {
218
+ prepareJsonLdCredential,
219
+ };
package/src/index.js ADDED
@@ -0,0 +1,22 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+ module.exports = {
18
+ ...require('./issue-verii-credentials'),
19
+ ...require('./domain/hash-offer'),
20
+ ...require('./adapters/mongo-allocation-list-queries'),
21
+ ...require('./adapters/get-revocation-registry'),
22
+ };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Copyright 2023 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ const { filter, flow, map } = require('lodash/fp');
18
+ const { allocateListEntries } = require('./allocate-list-entries');
19
+ const {
20
+ initCredentialMetadataContract,
21
+ } = require('./adapters/init-credential-metadata-contract');
22
+ const { createRevocationList } = require('./adapters/create-revocation-list');
23
+ const {
24
+ buildVerifiableCredentials,
25
+ } = require('./domain/build-verifiable-credentials');
26
+
27
+ const REVOCATION_LIST_SIZE = 10240;
28
+ const METADATA_LIST_SIZE = 10000;
29
+
30
+ /** @import { Issuer, AllocationListEntry, CredentialOffer, CredentialMetadata, CredentialTypeMetadata, Context } from "../types/types" */
31
+
32
+ /**
33
+ * Creates verifiable credential from a local offer. Current assumption is that offers contain all required fields
34
+ * including @context, type, contentHash
35
+ * @param {CredentialOffer[]} offers array of offers
36
+ * @param {string} credentialSubjectId optional field if credential subject needs to be bound into the offer
37
+ * @param {{[Name: string]: CredentialTypeMetadata}} credentialTypesMap the credential types metadata
38
+ * @param {Issuer} issuer the issuer
39
+ * @param {Context} context the context
40
+ * @returns {Promise<string[]>} Returns signed credentials for each offer in vc-jwt format
41
+ */
42
+ const issueVeriiCredentials = async (
43
+ offers,
44
+ credentialSubjectId,
45
+ credentialTypesMap,
46
+ issuer,
47
+ context
48
+ ) => {
49
+ // pre-allocate list entries using internal tables/collections
50
+ const revocationListEntries = await allocateListEntries(
51
+ offers.length,
52
+ issuer,
53
+ 'revocationListAllocations',
54
+ REVOCATION_LIST_SIZE,
55
+ context
56
+ );
57
+
58
+ const metadataEntries = await allocateListEntries(
59
+ offers.length,
60
+ issuer,
61
+ 'metadataListAllocations',
62
+ METADATA_LIST_SIZE,
63
+ context
64
+ );
65
+
66
+ // build credential and metadata
67
+ const vcs = await buildVerifiableCredentials(
68
+ offers,
69
+ credentialSubjectId,
70
+ issuer,
71
+ metadataEntries,
72
+ revocationListEntries,
73
+ credentialTypesMap,
74
+ context
75
+ );
76
+
77
+ // create any necessary revocation lists on dlt
78
+ await Promise.all(
79
+ flow(
80
+ filter({ isNewList: true }),
81
+ map((entry) => createRevocationList(entry.listId, issuer, context))
82
+ )(revocationListEntries)
83
+ );
84
+
85
+ const { addEntry, createList } = await initCredentialMetadataContract(
86
+ issuer,
87
+ context
88
+ );
89
+
90
+ // create any necessary metadata lists on dlt
91
+ await Promise.all(
92
+ flow(
93
+ filter({ metadata: { isNewList: true } }),
94
+ map(({ metadata: { listId } }) => createList(listId, issuer, context))
95
+ )(vcs)
96
+ );
97
+
98
+ // create credential metadata entries on dlt
99
+ await Promise.all(map(({ metadata }) => addEntry(metadata), vcs));
100
+
101
+ return map('vcJwt', vcs);
102
+ };
103
+
104
+ module.exports = { issueVeriiCredentials };
@@ -0,0 +1,28 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const { shuffle } = require('fast-shuffle');
19
+
20
+ /**
21
+ * Generates a random set of numbers in an array up to `size`
22
+ * @param {number} size the size of the array to allocate
23
+ * @returns {number[]} the array of random numbers
24
+ */
25
+ const allocateArray = (size) =>
26
+ shuffle(Array.from({ length: size }, (v, i) => i));
27
+
28
+ module.exports = { allocateArray };
@@ -0,0 +1,29 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const OfferType = {
19
+ PREPREPARED_ONLY: 'PREPREPARED_ONLY',
20
+ ALL: 'ALL',
21
+ LEGACY: 'LEGACY',
22
+ };
23
+
24
+ const ISSUING_CHALLENGE_SIZE = 16;
25
+
26
+ module.exports = {
27
+ OfferType,
28
+ ISSUING_CHALLENGE_SIZE,
29
+ };
@@ -0,0 +1,26 @@
1
+ /*
2
+ * Copyright 2024 Velocity Team
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ const crypto = require('crypto');
19
+
20
+ /**
21
+ * Generates a random 6 byte value
22
+ * @returns {number} a random number 6 bytes in length
23
+ */
24
+ const generateListIndex = () => crypto.randomBytes(16).readUIntBE(0, 6);
25
+
26
+ module.exports = { generateListIndex };
@@ -0,0 +1,251 @@
1
+ export interface CredentialMetadata extends AllocationListEntry {
2
+ contentHash: string;
3
+ credentialType: string;
4
+ credentialTypeByteEncoding: string;
5
+ publicKey: string;
6
+ }
7
+
8
+ export interface DbKey {
9
+ privateKey: string;
10
+ kidFragment: string;
11
+ }
12
+
13
+ export interface AllocationListQueries {
14
+ allocateNextEntry: (
15
+ entityName: string,
16
+ issuer: Issuer
17
+ ) => AllocationListEntry;
18
+ createNewAllocationList: (
19
+ entityName: string,
20
+ issuer: Issuer,
21
+ newListId: number,
22
+ allocations: number[]
23
+ ) => AllocationListEntry;
24
+ }
25
+
26
+ // TODO add support for signing transactions and jwts without keys leaving the KMS sandbox
27
+ /**
28
+ * Loads keys and in the future signs transactions or JWTs
29
+ */
30
+ export interface KMS {
31
+ loadKey: (keyId: string) => DbKey;
32
+ // signJwt: (jwtJson: object, keyId: string) => string
33
+ // signTransaction: (transaction: string, keyId: string) => string
34
+ }
35
+
36
+ export interface Context {
37
+ // contracts
38
+ /**
39
+ * Config for the nominated CAO whos creds need to be used for DLT node calls.
40
+ * TODO make this id & credentials instead OR make the contract services injection much smarter
41
+ */
42
+ caoDid: string;
43
+ /**
44
+ * config object containing the contract addresses for the chain.
45
+ * TODO tighten up type safety
46
+ */
47
+ config: object;
48
+ /**
49
+ * A RPC provider.
50
+ * TODO explore removing
51
+ */
52
+ rpcProvider?: unknown;
53
+ // db
54
+ /**
55
+ * The db connection object.
56
+ */
57
+ allocationListQueries: AllocationListQueries;
58
+ // credentialTypes
59
+ /**
60
+ * a registrar fetcher for loading credential types.
61
+ * TODO explore having credential types as a dependency and remvoing fetcher and cache
62
+ */
63
+ registrarFetch: unknown;
64
+ /**
65
+ * a cache for fetch results
66
+ */
67
+ cache?: unknown;
68
+ // key signing
69
+ /**
70
+ * A KMS interface for loading keys.
71
+ *
72
+ */
73
+ kms: KMS;
74
+ }
75
+
76
+ export interface Entity {
77
+ id: string;
78
+ did: string;
79
+ dltOperatorAddress: string;
80
+ dltOperatorKMSKeyId: string;
81
+ issuingServiceDIDKeyId: string;
82
+ dltPrimaryAddress: string;
83
+ }
84
+
85
+ export interface Issuer extends Entity {
86
+ issuingRefreshServiceId: string;
87
+ issuingServiceKMSKeyId: string;
88
+ issuingServiceDIDKeyId: string;
89
+ }
90
+
91
+ /**
92
+ * This file was automatically generated by json-schema-to-typescript.
93
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
94
+ * and run json-schema-to-typescript to regenerate this file.
95
+ */
96
+
97
+ export interface CredentialTypeMetadata {
98
+ id: string;
99
+ credentialType: string;
100
+ jsonldContext: string[];
101
+ schemaUrl: string;
102
+ defaultSignatureAlgorithm: string;
103
+ }
104
+
105
+ export interface BaseCredential {
106
+ /**
107
+ * json-ld context
108
+ */
109
+ '@context'?: string | string[];
110
+ /**
111
+ * type is identical to the standard based type found on verifiable credentials but does not accept a plane string,
112
+ * it only accepts an array to indicate an unordered set of type URIs
113
+ */
114
+ type: string[];
115
+ /**
116
+ * when the credential will start being valid
117
+ */
118
+ validFrom?: string;
119
+ /**
120
+ * when the credential will expire
121
+ */
122
+ validUntil?: string;
123
+ replaces?: NewResourceReference[];
124
+ relatedResource?: NewResourceReference[];
125
+ }
126
+
127
+ /**
128
+ * A DLT list entry is used for managing entries on dlt lists
129
+ */
130
+ export interface AllocationListEntry {
131
+ isNewList: boolean;
132
+ listId: number;
133
+ index: number;
134
+ }
135
+
136
+ export interface CredentialOffer extends BaseCredential {
137
+ /**
138
+ * Contains all the claims of the credential
139
+ */
140
+ credentialSubject: {
141
+ [k: string]: unknown;
142
+ };
143
+ /**
144
+ * issuer to include if using branding for the issuer
145
+ */
146
+ issuer: {
147
+ id: string;
148
+ name?: string;
149
+ image?: string;
150
+ };
151
+ [k: string]: unknown;
152
+ }
153
+
154
+ export interface JsonLdCredential extends BaseCredential {
155
+ /**
156
+ * id of the credential
157
+ */
158
+ id: string;
159
+ /**
160
+ * Contains all the claims of the credential
161
+ */
162
+ credentialSubject: CredentialSubject;
163
+ /**
164
+ * issuer is either a DID or an object containig DID and optional branding information
165
+ */
166
+ issuer:
167
+ | string
168
+ | {
169
+ id: string;
170
+ type?: string | string[];
171
+ name?: string;
172
+ image?: string;
173
+ };
174
+ issued: string;
175
+ /**
176
+ * content hash of the offer
177
+ */
178
+ contentHash: {
179
+ type: string;
180
+ value: string;
181
+ };
182
+ refreshService?: LinkedData | LinkedData[];
183
+ credentialSchema?: LinkedData | LinkedData[];
184
+ vnfProtocolVersion: string;
185
+ [k: string]: unknown;
186
+ }
187
+
188
+ export interface CredentialSubject {
189
+ /**
190
+ * json-ld context
191
+ */
192
+ '@context': string | string[];
193
+ /**
194
+ * type is identical to the standard based type found on verifiable credentials but does not accept a plane string,
195
+ * it only accepts an array to indicate an unordered set of type URIs
196
+ */
197
+ type: string | string[];
198
+ /**
199
+ * Optional id property identifies the credential subject
200
+ */
201
+ id?: string;
202
+ [k: string]: unknown;
203
+ }
204
+
205
+ export type ResourceReference = {
206
+ /**
207
+ * the id of the resource that is an alternative
208
+ */
209
+ id: string;
210
+ /**
211
+ * the type of alternative. PDF or VC
212
+ */
213
+ type?: string | string[];
214
+ /**
215
+ * the media type of the URI. Can be used to validate what is download ed
216
+ */
217
+ mediaType?: string;
218
+ /**
219
+ * the digest of the object
220
+ */
221
+ digestSRI?: string;
222
+ /**
223
+ * the name of the referenced resource
224
+ */
225
+ name?: string;
226
+ /**
227
+ * rendering hints for wallets. Can be used to validate the downloaded credential
228
+ */
229
+ hint?: string | string[];
230
+ };
231
+
232
+ export type NewResourceReference =
233
+ | ResourceReference
234
+ | {
235
+ /**
236
+ * a reference to an offerId in the same exchange
237
+ */
238
+ offerId: string;
239
+ /**
240
+ * the type of alternative. PDF or VC
241
+ */
242
+ type?: string | string[];
243
+ };
244
+
245
+ /**
246
+ * A linked data reference in JSON-LD.
247
+ */
248
+ export interface LinkedData {
249
+ id: string;
250
+ type?: string | string[];
251
+ }