@learncard/linked-claims-plugin 0.1.1
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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/linked-claims-plugin.cjs.development.js +158 -0
- package/dist/linked-claims-plugin.cjs.development.js.map +7 -0
- package/dist/linked-claims-plugin.cjs.production.min.js +2 -0
- package/dist/linked-claims-plugin.cjs.production.min.js.map +7 -0
- package/dist/linked-claims-plugin.esm.js +139 -0
- package/dist/linked-claims-plugin.esm.js.map +7 -0
- package/dist/test/linked-claims.test.d.ts +2 -0
- package/dist/test/linked-claims.test.d.ts.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Learning Economy Foundation <sdk@learningeconomy.io>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# LinkedClaims Plugin
|
|
2
|
+
|
|
3
|
+
Create, verify, store, and retrieve endorsement credentials using Open Badges v3 EndorsementCredential, linked to original credentials by subject id or original credential id.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
Workspace package. Build with Nx:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
pnpm nx run linked-claims-plugin:build
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { getLinkedClaimsPlugin } from '@learncard/linked-claims-plugin';
|
|
16
|
+
|
|
17
|
+
const lc = await LearnCard.fromSomewhere().then(lc => lc.addPlugin(getLinkedClaimsPlugin(lc)));
|
|
18
|
+
|
|
19
|
+
const endorsement = await lc.invoke.endorseCredential(originalVc, {
|
|
20
|
+
// OBv3 fields
|
|
21
|
+
endorsementComment: 'I endorse this credential.',
|
|
22
|
+
name: `Endorsement of ${originalVc.id ?? originalVc.credentialSubject?.id}`,
|
|
23
|
+
description: 'Peer endorsement',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const verified = await lc.invoke.verifyEndorsement(endorsement);
|
|
27
|
+
|
|
28
|
+
const { uri } = await lc.invoke.storeEndorsement(endorsement);
|
|
29
|
+
|
|
30
|
+
const endorsements = await lc.invoke.getEndorsements(originalVc);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Endorsement Context
|
|
34
|
+
Uses VC v2 and OBv3 contexts:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
"@context": [
|
|
38
|
+
"https://www.w3.org/ns/credentials/v2",
|
|
39
|
+
"https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
|
|
40
|
+
]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Endorsement Fields (details input)
|
|
44
|
+
|
|
45
|
+
- `endorsementComment?: string`
|
|
46
|
+
- `name?: string`
|
|
47
|
+
- `description?: string`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LearnCard } from '@learncard/core';
|
|
2
|
+
import type { LinkedClaimsPlugin, LinkedClaimsPluginDependentMethods } from './types';
|
|
3
|
+
export * from './types';
|
|
4
|
+
export declare const getLinkedClaimsPlugin: (learnCard: LearnCard<any, "id" | "store" | "index" | "read", LinkedClaimsPluginDependentMethods>) => LinkedClaimsPlugin;
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAsB,kCAAkC,EAAE,MAAM,SAAS,CAAC;AAE1G,cAAc,SAAS,CAAC;AA8BxB,eAAO,MAAM,qBAAqB,cACnB,SAAS,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,EAAE,kCAAkC,CAAC,KACjG,kBAmHD,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var src_exports = {};
|
|
23
|
+
__export(src_exports, {
|
|
24
|
+
getLinkedClaimsPlugin: () => getLinkedClaimsPlugin
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(src_exports);
|
|
27
|
+
var ENDORSEMENT_CONTEXT = "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json";
|
|
28
|
+
var getSubjectIds = /* @__PURE__ */ __name((credential) => {
|
|
29
|
+
const subject = credential.credentialSubject;
|
|
30
|
+
if (Array.isArray(subject))
|
|
31
|
+
return subject.map((s) => s?.id).filter(Boolean);
|
|
32
|
+
return subject?.id ? [subject.id] : [];
|
|
33
|
+
}, "getSubjectIds");
|
|
34
|
+
var pickIndexProvider = /* @__PURE__ */ __name((learnCard, preferred) => {
|
|
35
|
+
const providers = Object.keys(learnCard.index || {}).filter(
|
|
36
|
+
(k) => !["providers", "all"].includes(k)
|
|
37
|
+
);
|
|
38
|
+
if (!providers.length)
|
|
39
|
+
return void 0;
|
|
40
|
+
if (preferred && providers.includes(preferred))
|
|
41
|
+
return preferred;
|
|
42
|
+
if (providers.includes("LearnCloud"))
|
|
43
|
+
return "LearnCloud";
|
|
44
|
+
return providers[0];
|
|
45
|
+
}, "pickIndexProvider");
|
|
46
|
+
var pickStoreProvider = /* @__PURE__ */ __name((learnCard, preferred) => {
|
|
47
|
+
const providers = Object.keys(learnCard.store || {}).filter((k) => k !== "providers");
|
|
48
|
+
if (!providers.length)
|
|
49
|
+
return void 0;
|
|
50
|
+
if (preferred && providers.includes(preferred))
|
|
51
|
+
return preferred;
|
|
52
|
+
return providers[0];
|
|
53
|
+
}, "pickStoreProvider");
|
|
54
|
+
var ensureArray = /* @__PURE__ */ __name((arr) => Array.isArray(arr) ? arr : arr ? [arr] : [], "ensureArray");
|
|
55
|
+
var getLinkedClaimsPlugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
56
|
+
name: "LinkedClaims",
|
|
57
|
+
displayName: "LinkedClaims",
|
|
58
|
+
description: "Create, verify, store, and retrieve endorsement credentials linked to originals.",
|
|
59
|
+
methods: {
|
|
60
|
+
endorseCredential: async (_learnCard, original, details, opts) => {
|
|
61
|
+
const issuer = learnCard.id.did();
|
|
62
|
+
const originalId = original.id;
|
|
63
|
+
const subjectIds = getSubjectIds(original);
|
|
64
|
+
const targetId = originalId || subjectIds[0];
|
|
65
|
+
if (!targetId)
|
|
66
|
+
throw new Error("Original credential must have either id or credentialSubject.id");
|
|
67
|
+
const context = [
|
|
68
|
+
"https://www.w3.org/ns/credentials/v2",
|
|
69
|
+
ENDORSEMENT_CONTEXT
|
|
70
|
+
];
|
|
71
|
+
const types = ["VerifiableCredential", "EndorsementCredential"];
|
|
72
|
+
const unsigned = {
|
|
73
|
+
"@context": context,
|
|
74
|
+
type: types,
|
|
75
|
+
issuer,
|
|
76
|
+
validFrom: new Date().toISOString(),
|
|
77
|
+
credentialSubject: {
|
|
78
|
+
type: ["EndorsementSubject"],
|
|
79
|
+
id: targetId,
|
|
80
|
+
...details.endorsementComment ? { endorsementComment: details.endorsementComment } : {}
|
|
81
|
+
},
|
|
82
|
+
...details.name ? { name: details.name } : { name: `Endorsement of ${targetId}` },
|
|
83
|
+
...details.description ? { description: details.description } : {}
|
|
84
|
+
};
|
|
85
|
+
return learnCard.invoke.issueCredential(unsigned);
|
|
86
|
+
},
|
|
87
|
+
verifyEndorsement: async (_learnCard, endorsement, options) => {
|
|
88
|
+
const check = await learnCard.invoke.verifyCredential(endorsement, options);
|
|
89
|
+
const ctx = ensureArray(endorsement["@context"]);
|
|
90
|
+
const types = ensureArray(endorsement["type"]);
|
|
91
|
+
if (!ctx.some((c) => c === "https://www.w3.org/ns/credentials/v2") || !ctx.some((c) => c === ENDORSEMENT_CONTEXT)) {
|
|
92
|
+
check.errors.push("linked-claims error: missing VCv2 or OBv3 context");
|
|
93
|
+
}
|
|
94
|
+
if (!types.includes("EndorsementCredential")) {
|
|
95
|
+
check.errors.push("linked-claims error: missing EndorsementCredential type");
|
|
96
|
+
}
|
|
97
|
+
const cs = endorsement.credentialSubject;
|
|
98
|
+
const csObj = Array.isArray(cs) ? cs[0] : cs;
|
|
99
|
+
const csTypes = ensureArray(csObj?.type);
|
|
100
|
+
if (!csTypes.includes("EndorsementSubject")) {
|
|
101
|
+
check.errors.push("linked-claims error: credentialSubject.type must include EndorsementSubject");
|
|
102
|
+
}
|
|
103
|
+
if (!csObj?.id) {
|
|
104
|
+
check.errors.push("linked-claims error: credentialSubject.id missing");
|
|
105
|
+
}
|
|
106
|
+
if (!check.errors.length)
|
|
107
|
+
check.checks.push("linked-claims");
|
|
108
|
+
return check;
|
|
109
|
+
},
|
|
110
|
+
storeEndorsement: async (_learnCard, endorsement, options) => {
|
|
111
|
+
const storeName = pickStoreProvider(learnCard, options?.storeName);
|
|
112
|
+
if (!storeName)
|
|
113
|
+
throw new Error("No store plane provider available");
|
|
114
|
+
if (!learnCard.store[storeName].uploadEncrypted || typeof learnCard.store[storeName].uploadEncrypted !== "function") {
|
|
115
|
+
throw new Error(`Store provider '${storeName}' does not support uploadEncrypted method`);
|
|
116
|
+
}
|
|
117
|
+
const uri = await learnCard.store[storeName].uploadEncrypted(endorsement);
|
|
118
|
+
const indexName = pickIndexProvider(learnCard, options?.indexName);
|
|
119
|
+
let indexed = false;
|
|
120
|
+
let id = endorsement.id || `urn:uuid:${globalThis.crypto?.randomUUID?.() || (() => {
|
|
121
|
+
throw new Error("Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().");
|
|
122
|
+
})()}`;
|
|
123
|
+
if (indexName) {
|
|
124
|
+
const cs = endorsement.credentialSubject;
|
|
125
|
+
const csObj = Array.isArray(cs) ? cs[0] : cs;
|
|
126
|
+
const record = {
|
|
127
|
+
id,
|
|
128
|
+
uri,
|
|
129
|
+
type: ensureArray(endorsement.type),
|
|
130
|
+
endorsedId: csObj?.id,
|
|
131
|
+
subjectId: csObj?.id,
|
|
132
|
+
originalCredentialId: csObj?.id,
|
|
133
|
+
issuedOn: endorsement.validFrom || endorsement.issuanceDate,
|
|
134
|
+
category: "Endorsement"
|
|
135
|
+
};
|
|
136
|
+
indexed = await learnCard.index[indexName].add(record);
|
|
137
|
+
}
|
|
138
|
+
return { uri, indexed, id, indexName, storeName };
|
|
139
|
+
},
|
|
140
|
+
getEndorsements: async (_learnCard, original, options) => {
|
|
141
|
+
const indexName = pickIndexProvider(learnCard, options?.indexName);
|
|
142
|
+
if (!indexName)
|
|
143
|
+
return [];
|
|
144
|
+
const subjectIds = getSubjectIds(original);
|
|
145
|
+
const originalId = original.id;
|
|
146
|
+
const query = originalId ? { originalCredentialId: originalId } : { endorsedId: subjectIds[0] };
|
|
147
|
+
const records = await learnCard.index[indexName].get(query);
|
|
148
|
+
const results = [];
|
|
149
|
+
for (const rec of records) {
|
|
150
|
+
const vc = await learnCard.read.get(rec.uri);
|
|
151
|
+
if (vc)
|
|
152
|
+
results.push(vc);
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}), "getLinkedClaimsPlugin");
|
|
158
|
+
//# sourceMappingURL=linked-claims-plugin.cjs.development.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LearnCard } from '@learncard/core';\nimport type { VC, UnsignedVC } from '@learncard/types';\nimport type { LinkedClaimsPlugin, EndorsementDetails, LinkedClaimsPluginDependentMethods } from './types';\n\nexport * from './types';\n\nconst ENDORSEMENT_CONTEXT = 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json';\n\nconst getSubjectIds = (credential: VC): string[] => {\n const subject = credential.credentialSubject as any;\n if (Array.isArray(subject)) return subject.map(s => s?.id).filter(Boolean);\n return subject?.id ? [subject.id] : [];\n};\n\nconst pickIndexProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.index || {}).filter(\n k => !['providers', 'all'].includes(k)\n );\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n if (providers.includes('LearnCloud')) return 'LearnCloud';\n return providers[0];\n};\n\nconst pickStoreProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.store || {}).filter(k => k !== 'providers');\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n return providers[0];\n};\n\nconst ensureArray = <T>(arr: T | T[] | undefined): T[] =>\n Array.isArray(arr) ? arr : arr ? [arr] : [];\n\nexport const getLinkedClaimsPlugin = (\n learnCard: LearnCard<any, 'id' | 'store' | 'index' | 'read', LinkedClaimsPluginDependentMethods>\n): LinkedClaimsPlugin => ({\n name: 'LinkedClaims',\n displayName: 'LinkedClaims',\n description: 'Create, verify, store, and retrieve endorsement credentials linked to originals.',\n methods: {\n endorseCredential: async (_learnCard, original, details, opts) => {\n const issuer = learnCard.id.did();\n\n const originalId: string | undefined = (original as any).id;\n const subjectIds = getSubjectIds(original);\n const targetId = originalId || subjectIds[0];\n if (!targetId) throw new Error('Original credential must have either id or credentialSubject.id');\n\n const context: (string | Record<string, any>)[] = [\n 'https://www.w3.org/ns/credentials/v2',\n ENDORSEMENT_CONTEXT,\n ];\n\n const types: [string, ...string[]] = ['VerifiableCredential', 'EndorsementCredential'];\n\n const unsigned: UnsignedVC = {\n '@context': context as any,\n type: types,\n issuer,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n type: ['EndorsementSubject'],\n id: targetId,\n ...(details.endorsementComment\n ? { endorsementComment: details.endorsementComment }\n : {}),\n },\n ...(details.name ? { name: details.name } : { name: `Endorsement of ${targetId}` }),\n ...(details.description ? { description: details.description } : {}),\n };\n\n return learnCard.invoke.issueCredential(unsigned);\n },\n\n verifyEndorsement: async (_learnCard, endorsement, options) => {\n const check = await learnCard.invoke.verifyCredential(endorsement, options);\n\n const ctx = ensureArray((endorsement as any)['@context']);\n const types = ensureArray((endorsement as any)['type']);\n\n if (!ctx.some((c: string) => c === 'https://www.w3.org/ns/credentials/v2') || !ctx.some((c: string) => c === ENDORSEMENT_CONTEXT)) {\n check.errors.push('linked-claims error: missing VCv2 or OBv3 context');\n }\n if (!types.includes('EndorsementCredential')) {\n check.errors.push('linked-claims error: missing EndorsementCredential type');\n }\n\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const csTypes = ensureArray<string>(csObj?.type);\n if (!csTypes.includes('EndorsementSubject')) {\n check.errors.push('linked-claims error: credentialSubject.type must include EndorsementSubject');\n }\n if (!csObj?.id) {\n check.errors.push('linked-claims error: credentialSubject.id missing');\n }\n\n if (!check.errors.length) check.checks.push('linked-claims');\n return check;\n },\n\n storeEndorsement: async (_learnCard, endorsement, options) => {\n const storeName = pickStoreProvider(learnCard, options?.storeName);\n if (!storeName) throw new Error('No store plane provider available');\n if (!learnCard.store[storeName].uploadEncrypted || typeof learnCard.store[storeName].uploadEncrypted !== 'function') {\n throw new Error(`Store provider '${storeName}' does not support uploadEncrypted method`);\n }\n const uri: string = await (learnCard.store as any)[storeName].uploadEncrypted(endorsement);\n\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n let indexed = false;\n let id = (endorsement as any).id || `urn:uuid:${(globalThis as any).crypto?.randomUUID?.() || (() => { throw new Error('Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().'); })()}`;\n\n if (indexName) {\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const record = {\n id,\n uri,\n type: ensureArray((endorsement as any).type),\n endorsedId: csObj?.id,\n subjectId: csObj?.id,\n originalCredentialId: csObj?.id,\n issuedOn: (endorsement as any).validFrom || (endorsement as any).issuanceDate,\n category: 'Endorsement',\n };\n indexed = await (learnCard.index as any)[indexName].add(record);\n }\n\n return { uri, indexed, id, indexName, storeName };\n },\n\n getEndorsements: async (_learnCard, original, options) => {\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n if (!indexName) return [];\n\n const subjectIds = getSubjectIds(original);\n const originalId = (original as any).id;\n\n const query = originalId ? { originalCredentialId: originalId } : { endorsedId: subjectIds[0] };\n const records = await (learnCard.index as any)[indexName].get(query);\n\n const results: VC[] = [];\n for (const rec of records) {\n const vc = await learnCard.read.get(rec.uri);\n if (vc) results.push(vc as VC);\n }\n return results;\n },\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var p=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var y=(e,t)=>p(e,"name",{value:t,configurable:!0});var E=(e,t)=>{for(var n in t)p(e,n,{get:t[n],enumerable:!0})},w=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of b(t))!C.call(e,r)&&r!==n&&p(e,r,{get:()=>t[r],enumerable:!(s=v(t,r))||s.enumerable});return e};var k=e=>w(p({},"__esModule",{value:!0}),e);var S={};E(S,{getLinkedClaimsPlugin:()=>x});module.exports=k(S);var g="https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",f=y(e=>{let t=e.credentialSubject;return Array.isArray(t)?t.map(n=>n?.id).filter(Boolean):t?.id?[t.id]:[]},"getSubjectIds"),h=y((e,t)=>{let n=Object.keys(e.index||{}).filter(s=>!["providers","all"].includes(s));if(!!n.length)return t&&n.includes(t)?t:n.includes("LearnCloud")?"LearnCloud":n[0]},"pickIndexProvider"),j=y((e,t)=>{let n=Object.keys(e.store||{}).filter(s=>s!=="providers");if(!!n.length)return t&&n.includes(t)?t:n[0]},"pickStoreProvider"),m=y(e=>Array.isArray(e)?e:e?[e]:[],"ensureArray"),x=y(e=>({name:"LinkedClaims",displayName:"LinkedClaims",description:"Create, verify, store, and retrieve endorsement credentials linked to originals.",methods:{endorseCredential:async(t,n,s,r)=>{let c=e.id.did(),o=n.id,d=f(n),i=o||d[0];if(!i)throw new Error("Original credential must have either id or credentialSubject.id");let l={"@context":["https://www.w3.org/ns/credentials/v2",g],type:["VerifiableCredential","EndorsementCredential"],issuer:c,validFrom:new Date().toISOString(),credentialSubject:{type:["EndorsementSubject"],id:i,...s.endorsementComment?{endorsementComment:s.endorsementComment}:{}},...s.name?{name:s.name}:{name:`Endorsement of ${i}`},...s.description?{description:s.description}:{}};return e.invoke.issueCredential(l)},verifyEndorsement:async(t,n,s)=>{let r=await e.invoke.verifyCredential(n,s),c=m(n["@context"]),o=m(n.type);(!c.some(a=>a==="https://www.w3.org/ns/credentials/v2")||!c.some(a=>a===g))&&r.errors.push("linked-claims error: missing VCv2 or OBv3 context"),o.includes("EndorsementCredential")||r.errors.push("linked-claims error: missing EndorsementCredential type");let d=n.credentialSubject,i=Array.isArray(d)?d[0]:d;return m(i?.type).includes("EndorsementSubject")||r.errors.push("linked-claims error: credentialSubject.type must include EndorsementSubject"),i?.id||r.errors.push("linked-claims error: credentialSubject.id missing"),r.errors.length||r.checks.push("linked-claims"),r},storeEndorsement:async(t,n,s)=>{let r=j(e,s?.storeName);if(!r)throw new Error("No store plane provider available");if(!e.store[r].uploadEncrypted||typeof e.store[r].uploadEncrypted!="function")throw new Error(`Store provider '${r}' does not support uploadEncrypted method`);let c=await e.store[r].uploadEncrypted(n),o=h(e,s?.indexName),d=!1,i=n.id||`urn:uuid:${globalThis.crypto?.randomUUID?.()||(()=>{throw new Error("Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().")})()}`;if(o){let u=n.credentialSubject,a=Array.isArray(u)?u[0]:u,l={id:i,uri:c,type:m(n.type),endorsedId:a?.id,subjectId:a?.id,originalCredentialId:a?.id,issuedOn:n.validFrom||n.issuanceDate,category:"Endorsement"};d=await e.index[o].add(l)}return{uri:c,indexed:d,id:i,indexName:o,storeName:r}},getEndorsements:async(t,n,s)=>{let r=h(e,s?.indexName);if(!r)return[];let c=f(n),o=n.id,d=o?{originalCredentialId:o}:{endorsedId:c[0]},i=await e.index[r].get(d),u=[];for(let a of i){let l=await e.read.get(a.uri);l&&u.push(l)}return u}}}),"getLinkedClaimsPlugin");
|
|
2
|
+
//# sourceMappingURL=linked-claims-plugin.cjs.production.min.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LearnCard } from '@learncard/core';\nimport type { VC, UnsignedVC } from '@learncard/types';\nimport type { LinkedClaimsPlugin, EndorsementDetails, LinkedClaimsPluginDependentMethods } from './types';\n\nexport * from './types';\n\nconst ENDORSEMENT_CONTEXT = 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json';\n\nconst getSubjectIds = (credential: VC): string[] => {\n const subject = credential.credentialSubject as any;\n if (Array.isArray(subject)) return subject.map(s => s?.id).filter(Boolean);\n return subject?.id ? [subject.id] : [];\n};\n\nconst pickIndexProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.index || {}).filter(\n k => !['providers', 'all'].includes(k)\n );\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n if (providers.includes('LearnCloud')) return 'LearnCloud';\n return providers[0];\n};\n\nconst pickStoreProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.store || {}).filter(k => k !== 'providers');\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n return providers[0];\n};\n\nconst ensureArray = <T>(arr: T | T[] | undefined): T[] =>\n Array.isArray(arr) ? arr : arr ? [arr] : [];\n\nexport const getLinkedClaimsPlugin = (\n learnCard: LearnCard<any, 'id' | 'store' | 'index' | 'read', LinkedClaimsPluginDependentMethods>\n): LinkedClaimsPlugin => ({\n name: 'LinkedClaims',\n displayName: 'LinkedClaims',\n description: 'Create, verify, store, and retrieve endorsement credentials linked to originals.',\n methods: {\n endorseCredential: async (_learnCard, original, details, opts) => {\n const issuer = learnCard.id.did();\n\n const originalId: string | undefined = (original as any).id;\n const subjectIds = getSubjectIds(original);\n const targetId = originalId || subjectIds[0];\n if (!targetId) throw new Error('Original credential must have either id or credentialSubject.id');\n\n const context: (string | Record<string, any>)[] = [\n 'https://www.w3.org/ns/credentials/v2',\n ENDORSEMENT_CONTEXT,\n ];\n\n const types: [string, ...string[]] = ['VerifiableCredential', 'EndorsementCredential'];\n\n const unsigned: UnsignedVC = {\n '@context': context as any,\n type: types,\n issuer,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n type: ['EndorsementSubject'],\n id: targetId,\n ...(details.endorsementComment\n ? { endorsementComment: details.endorsementComment }\n : {}),\n },\n ...(details.name ? { name: details.name } : { name: `Endorsement of ${targetId}` }),\n ...(details.description ? { description: details.description } : {}),\n };\n\n return learnCard.invoke.issueCredential(unsigned);\n },\n\n verifyEndorsement: async (_learnCard, endorsement, options) => {\n const check = await learnCard.invoke.verifyCredential(endorsement, options);\n\n const ctx = ensureArray((endorsement as any)['@context']);\n const types = ensureArray((endorsement as any)['type']);\n\n if (!ctx.some((c: string) => c === 'https://www.w3.org/ns/credentials/v2') || !ctx.some((c: string) => c === ENDORSEMENT_CONTEXT)) {\n check.errors.push('linked-claims error: missing VCv2 or OBv3 context');\n }\n if (!types.includes('EndorsementCredential')) {\n check.errors.push('linked-claims error: missing EndorsementCredential type');\n }\n\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const csTypes = ensureArray<string>(csObj?.type);\n if (!csTypes.includes('EndorsementSubject')) {\n check.errors.push('linked-claims error: credentialSubject.type must include EndorsementSubject');\n }\n if (!csObj?.id) {\n check.errors.push('linked-claims error: credentialSubject.id missing');\n }\n\n if (!check.errors.length) check.checks.push('linked-claims');\n return check;\n },\n\n storeEndorsement: async (_learnCard, endorsement, options) => {\n const storeName = pickStoreProvider(learnCard, options?.storeName);\n if (!storeName) throw new Error('No store plane provider available');\n if (!learnCard.store[storeName].uploadEncrypted || typeof learnCard.store[storeName].uploadEncrypted !== 'function') {\n throw new Error(`Store provider '${storeName}' does not support uploadEncrypted method`);\n }\n const uri: string = await (learnCard.store as any)[storeName].uploadEncrypted(endorsement);\n\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n let indexed = false;\n let id = (endorsement as any).id || `urn:uuid:${(globalThis as any).crypto?.randomUUID?.() || (() => { throw new Error('Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().'); })()}`;\n\n if (indexName) {\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const record = {\n id,\n uri,\n type: ensureArray((endorsement as any).type),\n endorsedId: csObj?.id,\n subjectId: csObj?.id,\n originalCredentialId: csObj?.id,\n issuedOn: (endorsement as any).validFrom || (endorsement as any).issuanceDate,\n category: 'Endorsement',\n };\n indexed = await (learnCard.index as any)[indexName].add(record);\n }\n\n return { uri, indexed, id, indexName, storeName };\n },\n\n getEndorsements: async (_learnCard, original, options) => {\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n if (!indexName) return [];\n\n const subjectIds = getSubjectIds(original);\n const originalId = (original as any).id;\n\n const query = originalId ? { originalCredentialId: originalId } : { endorsedId: subjectIds[0] };\n const records = await (learnCard.index as any)[indexName].get(query);\n\n const results: VC[] = [];\n for (const rec of records) {\n const vc = await learnCard.read.get(rec.uri);\n if (vc) results.push(vc as VC);\n }\n return results;\n },\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["src_exports", "__export", "getLinkedClaimsPlugin", "__toCommonJS", "ENDORSEMENT_CONTEXT", "getSubjectIds", "__name", "credential", "subject", "s", "pickIndexProvider", "learnCard", "preferred", "providers", "k", "pickStoreProvider", "ensureArray", "arr", "getLinkedClaimsPlugin", "_learnCard", "original", "details", "opts", "issuer", "originalId", "subjectIds", "targetId", "unsigned", "endorsement", "options", "check", "ctx", "types", "c", "cs", "csObj", "storeName", "uri", "indexName", "indexed", "id", "record", "query", "records", "results", "rec", "vc"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
var ENDORSEMENT_CONTEXT = "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json";
|
|
6
|
+
var getSubjectIds = /* @__PURE__ */ __name((credential) => {
|
|
7
|
+
const subject = credential.credentialSubject;
|
|
8
|
+
if (Array.isArray(subject))
|
|
9
|
+
return subject.map((s) => s?.id).filter(Boolean);
|
|
10
|
+
return subject?.id ? [subject.id] : [];
|
|
11
|
+
}, "getSubjectIds");
|
|
12
|
+
var pickIndexProvider = /* @__PURE__ */ __name((learnCard, preferred) => {
|
|
13
|
+
const providers = Object.keys(learnCard.index || {}).filter(
|
|
14
|
+
(k) => !["providers", "all"].includes(k)
|
|
15
|
+
);
|
|
16
|
+
if (!providers.length)
|
|
17
|
+
return void 0;
|
|
18
|
+
if (preferred && providers.includes(preferred))
|
|
19
|
+
return preferred;
|
|
20
|
+
if (providers.includes("LearnCloud"))
|
|
21
|
+
return "LearnCloud";
|
|
22
|
+
return providers[0];
|
|
23
|
+
}, "pickIndexProvider");
|
|
24
|
+
var pickStoreProvider = /* @__PURE__ */ __name((learnCard, preferred) => {
|
|
25
|
+
const providers = Object.keys(learnCard.store || {}).filter((k) => k !== "providers");
|
|
26
|
+
if (!providers.length)
|
|
27
|
+
return void 0;
|
|
28
|
+
if (preferred && providers.includes(preferred))
|
|
29
|
+
return preferred;
|
|
30
|
+
return providers[0];
|
|
31
|
+
}, "pickStoreProvider");
|
|
32
|
+
var ensureArray = /* @__PURE__ */ __name((arr) => Array.isArray(arr) ? arr : arr ? [arr] : [], "ensureArray");
|
|
33
|
+
var getLinkedClaimsPlugin = /* @__PURE__ */ __name((learnCard) => ({
|
|
34
|
+
name: "LinkedClaims",
|
|
35
|
+
displayName: "LinkedClaims",
|
|
36
|
+
description: "Create, verify, store, and retrieve endorsement credentials linked to originals.",
|
|
37
|
+
methods: {
|
|
38
|
+
endorseCredential: async (_learnCard, original, details, opts) => {
|
|
39
|
+
const issuer = learnCard.id.did();
|
|
40
|
+
const originalId = original.id;
|
|
41
|
+
const subjectIds = getSubjectIds(original);
|
|
42
|
+
const targetId = originalId || subjectIds[0];
|
|
43
|
+
if (!targetId)
|
|
44
|
+
throw new Error("Original credential must have either id or credentialSubject.id");
|
|
45
|
+
const context = [
|
|
46
|
+
"https://www.w3.org/ns/credentials/v2",
|
|
47
|
+
ENDORSEMENT_CONTEXT
|
|
48
|
+
];
|
|
49
|
+
const types = ["VerifiableCredential", "EndorsementCredential"];
|
|
50
|
+
const unsigned = {
|
|
51
|
+
"@context": context,
|
|
52
|
+
type: types,
|
|
53
|
+
issuer,
|
|
54
|
+
validFrom: new Date().toISOString(),
|
|
55
|
+
credentialSubject: {
|
|
56
|
+
type: ["EndorsementSubject"],
|
|
57
|
+
id: targetId,
|
|
58
|
+
...details.endorsementComment ? { endorsementComment: details.endorsementComment } : {}
|
|
59
|
+
},
|
|
60
|
+
...details.name ? { name: details.name } : { name: `Endorsement of ${targetId}` },
|
|
61
|
+
...details.description ? { description: details.description } : {}
|
|
62
|
+
};
|
|
63
|
+
return learnCard.invoke.issueCredential(unsigned);
|
|
64
|
+
},
|
|
65
|
+
verifyEndorsement: async (_learnCard, endorsement, options) => {
|
|
66
|
+
const check = await learnCard.invoke.verifyCredential(endorsement, options);
|
|
67
|
+
const ctx = ensureArray(endorsement["@context"]);
|
|
68
|
+
const types = ensureArray(endorsement["type"]);
|
|
69
|
+
if (!ctx.some((c) => c === "https://www.w3.org/ns/credentials/v2") || !ctx.some((c) => c === ENDORSEMENT_CONTEXT)) {
|
|
70
|
+
check.errors.push("linked-claims error: missing VCv2 or OBv3 context");
|
|
71
|
+
}
|
|
72
|
+
if (!types.includes("EndorsementCredential")) {
|
|
73
|
+
check.errors.push("linked-claims error: missing EndorsementCredential type");
|
|
74
|
+
}
|
|
75
|
+
const cs = endorsement.credentialSubject;
|
|
76
|
+
const csObj = Array.isArray(cs) ? cs[0] : cs;
|
|
77
|
+
const csTypes = ensureArray(csObj?.type);
|
|
78
|
+
if (!csTypes.includes("EndorsementSubject")) {
|
|
79
|
+
check.errors.push("linked-claims error: credentialSubject.type must include EndorsementSubject");
|
|
80
|
+
}
|
|
81
|
+
if (!csObj?.id) {
|
|
82
|
+
check.errors.push("linked-claims error: credentialSubject.id missing");
|
|
83
|
+
}
|
|
84
|
+
if (!check.errors.length)
|
|
85
|
+
check.checks.push("linked-claims");
|
|
86
|
+
return check;
|
|
87
|
+
},
|
|
88
|
+
storeEndorsement: async (_learnCard, endorsement, options) => {
|
|
89
|
+
const storeName = pickStoreProvider(learnCard, options?.storeName);
|
|
90
|
+
if (!storeName)
|
|
91
|
+
throw new Error("No store plane provider available");
|
|
92
|
+
if (!learnCard.store[storeName].uploadEncrypted || typeof learnCard.store[storeName].uploadEncrypted !== "function") {
|
|
93
|
+
throw new Error(`Store provider '${storeName}' does not support uploadEncrypted method`);
|
|
94
|
+
}
|
|
95
|
+
const uri = await learnCard.store[storeName].uploadEncrypted(endorsement);
|
|
96
|
+
const indexName = pickIndexProvider(learnCard, options?.indexName);
|
|
97
|
+
let indexed = false;
|
|
98
|
+
let id = endorsement.id || `urn:uuid:${globalThis.crypto?.randomUUID?.() || (() => {
|
|
99
|
+
throw new Error("Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().");
|
|
100
|
+
})()}`;
|
|
101
|
+
if (indexName) {
|
|
102
|
+
const cs = endorsement.credentialSubject;
|
|
103
|
+
const csObj = Array.isArray(cs) ? cs[0] : cs;
|
|
104
|
+
const record = {
|
|
105
|
+
id,
|
|
106
|
+
uri,
|
|
107
|
+
type: ensureArray(endorsement.type),
|
|
108
|
+
endorsedId: csObj?.id,
|
|
109
|
+
subjectId: csObj?.id,
|
|
110
|
+
originalCredentialId: csObj?.id,
|
|
111
|
+
issuedOn: endorsement.validFrom || endorsement.issuanceDate,
|
|
112
|
+
category: "Endorsement"
|
|
113
|
+
};
|
|
114
|
+
indexed = await learnCard.index[indexName].add(record);
|
|
115
|
+
}
|
|
116
|
+
return { uri, indexed, id, indexName, storeName };
|
|
117
|
+
},
|
|
118
|
+
getEndorsements: async (_learnCard, original, options) => {
|
|
119
|
+
const indexName = pickIndexProvider(learnCard, options?.indexName);
|
|
120
|
+
if (!indexName)
|
|
121
|
+
return [];
|
|
122
|
+
const subjectIds = getSubjectIds(original);
|
|
123
|
+
const originalId = original.id;
|
|
124
|
+
const query = originalId ? { originalCredentialId: originalId } : { endorsedId: subjectIds[0] };
|
|
125
|
+
const records = await learnCard.index[indexName].get(query);
|
|
126
|
+
const results = [];
|
|
127
|
+
for (const rec of records) {
|
|
128
|
+
const vc = await learnCard.read.get(rec.uri);
|
|
129
|
+
if (vc)
|
|
130
|
+
results.push(vc);
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}), "getLinkedClaimsPlugin");
|
|
136
|
+
export {
|
|
137
|
+
getLinkedClaimsPlugin
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=linked-claims-plugin.esm.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type { LearnCard } from '@learncard/core';\nimport type { VC, UnsignedVC } from '@learncard/types';\nimport type { LinkedClaimsPlugin, EndorsementDetails, LinkedClaimsPluginDependentMethods } from './types';\n\nexport * from './types';\n\nconst ENDORSEMENT_CONTEXT = 'https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json';\n\nconst getSubjectIds = (credential: VC): string[] => {\n const subject = credential.credentialSubject as any;\n if (Array.isArray(subject)) return subject.map(s => s?.id).filter(Boolean);\n return subject?.id ? [subject.id] : [];\n};\n\nconst pickIndexProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.index || {}).filter(\n k => !['providers', 'all'].includes(k)\n );\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n if (providers.includes('LearnCloud')) return 'LearnCloud';\n return providers[0];\n};\n\nconst pickStoreProvider = (learnCard: any, preferred?: string): string | undefined => {\n const providers = Object.keys(learnCard.store || {}).filter(k => k !== 'providers');\n if (!providers.length) return undefined;\n if (preferred && providers.includes(preferred)) return preferred;\n return providers[0];\n};\n\nconst ensureArray = <T>(arr: T | T[] | undefined): T[] =>\n Array.isArray(arr) ? arr : arr ? [arr] : [];\n\nexport const getLinkedClaimsPlugin = (\n learnCard: LearnCard<any, 'id' | 'store' | 'index' | 'read', LinkedClaimsPluginDependentMethods>\n): LinkedClaimsPlugin => ({\n name: 'LinkedClaims',\n displayName: 'LinkedClaims',\n description: 'Create, verify, store, and retrieve endorsement credentials linked to originals.',\n methods: {\n endorseCredential: async (_learnCard, original, details, opts) => {\n const issuer = learnCard.id.did();\n\n const originalId: string | undefined = (original as any).id;\n const subjectIds = getSubjectIds(original);\n const targetId = originalId || subjectIds[0];\n if (!targetId) throw new Error('Original credential must have either id or credentialSubject.id');\n\n const context: (string | Record<string, any>)[] = [\n 'https://www.w3.org/ns/credentials/v2',\n ENDORSEMENT_CONTEXT,\n ];\n\n const types: [string, ...string[]] = ['VerifiableCredential', 'EndorsementCredential'];\n\n const unsigned: UnsignedVC = {\n '@context': context as any,\n type: types,\n issuer,\n validFrom: new Date().toISOString(),\n credentialSubject: {\n type: ['EndorsementSubject'],\n id: targetId,\n ...(details.endorsementComment\n ? { endorsementComment: details.endorsementComment }\n : {}),\n },\n ...(details.name ? { name: details.name } : { name: `Endorsement of ${targetId}` }),\n ...(details.description ? { description: details.description } : {}),\n };\n\n return learnCard.invoke.issueCredential(unsigned);\n },\n\n verifyEndorsement: async (_learnCard, endorsement, options) => {\n const check = await learnCard.invoke.verifyCredential(endorsement, options);\n\n const ctx = ensureArray((endorsement as any)['@context']);\n const types = ensureArray((endorsement as any)['type']);\n\n if (!ctx.some((c: string) => c === 'https://www.w3.org/ns/credentials/v2') || !ctx.some((c: string) => c === ENDORSEMENT_CONTEXT)) {\n check.errors.push('linked-claims error: missing VCv2 or OBv3 context');\n }\n if (!types.includes('EndorsementCredential')) {\n check.errors.push('linked-claims error: missing EndorsementCredential type');\n }\n\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const csTypes = ensureArray<string>(csObj?.type);\n if (!csTypes.includes('EndorsementSubject')) {\n check.errors.push('linked-claims error: credentialSubject.type must include EndorsementSubject');\n }\n if (!csObj?.id) {\n check.errors.push('linked-claims error: credentialSubject.id missing');\n }\n\n if (!check.errors.length) check.checks.push('linked-claims');\n return check;\n },\n\n storeEndorsement: async (_learnCard, endorsement, options) => {\n const storeName = pickStoreProvider(learnCard, options?.storeName);\n if (!storeName) throw new Error('No store plane provider available');\n if (!learnCard.store[storeName].uploadEncrypted || typeof learnCard.store[storeName].uploadEncrypted !== 'function') {\n throw new Error(`Store provider '${storeName}' does not support uploadEncrypted method`);\n }\n const uri: string = await (learnCard.store as any)[storeName].uploadEncrypted(endorsement);\n\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n let indexed = false;\n let id = (endorsement as any).id || `urn:uuid:${(globalThis as any).crypto?.randomUUID?.() || (() => { throw new Error('Secure random UUID generation is not available. Please use an environment that supports crypto.randomUUID().'); })()}`;\n\n if (indexName) {\n const cs: any = (endorsement as any).credentialSubject;\n const csObj = Array.isArray(cs) ? cs[0] : cs;\n const record = {\n id,\n uri,\n type: ensureArray((endorsement as any).type),\n endorsedId: csObj?.id,\n subjectId: csObj?.id,\n originalCredentialId: csObj?.id,\n issuedOn: (endorsement as any).validFrom || (endorsement as any).issuanceDate,\n category: 'Endorsement',\n };\n indexed = await (learnCard.index as any)[indexName].add(record);\n }\n\n return { uri, indexed, id, indexName, storeName };\n },\n\n getEndorsements: async (_learnCard, original, options) => {\n const indexName = pickIndexProvider(learnCard, options?.indexName);\n if (!indexName) return [];\n\n const subjectIds = getSubjectIds(original);\n const originalId = (original as any).id;\n\n const query = originalId ? { originalCredentialId: originalId } : { endorsedId: subjectIds[0] };\n const records = await (learnCard.index as any)[indexName].get(query);\n\n const results: VC[] = [];\n for (const rec of records) {\n const vc = await learnCard.read.get(rec.uri);\n if (vc) results.push(vc as VC);\n }\n return results;\n },\n },\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linked-claims.test.d.ts","sourceRoot":"","sources":["../../src/test/linked-claims.test.ts"],"names":[],"mappings":""}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Plugin } from '@learncard/core';
|
|
2
|
+
import { VC, VerificationCheck } from '@learncard/types';
|
|
3
|
+
import { VCPluginMethods } from '@learncard/vc-plugin';
|
|
4
|
+
import { ProofOptions } from '@learncard/didkit-plugin';
|
|
5
|
+
export type EndorsementDetails = {
|
|
6
|
+
endorsementComment?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
};
|
|
10
|
+
export type StoreIndexOptions = {
|
|
11
|
+
storeName?: string;
|
|
12
|
+
indexName?: string;
|
|
13
|
+
};
|
|
14
|
+
export type LinkedClaimsPluginDependentMethods = VCPluginMethods;
|
|
15
|
+
export type LinkedClaimsPluginMethods = {
|
|
16
|
+
endorseCredential: (original: VC, details: EndorsementDetails, options?: {
|
|
17
|
+
contextUrl?: string;
|
|
18
|
+
}) => Promise<VC>;
|
|
19
|
+
verifyEndorsement: (endorsement: VC, options?: Partial<ProofOptions>) => Promise<VerificationCheck>;
|
|
20
|
+
storeEndorsement: (endorsement: VC, options?: StoreIndexOptions) => Promise<{
|
|
21
|
+
uri: string;
|
|
22
|
+
indexed: boolean;
|
|
23
|
+
id: string;
|
|
24
|
+
indexName?: string;
|
|
25
|
+
storeName?: string;
|
|
26
|
+
}>;
|
|
27
|
+
getEndorsements: (original: VC, options?: {
|
|
28
|
+
indexName?: string;
|
|
29
|
+
}) => Promise<VC[]>;
|
|
30
|
+
};
|
|
31
|
+
export type LinkedClaimsPlugin = Plugin<'LinkedClaims', any, LinkedClaimsPluginMethods, 'id' | 'store' | 'index' | 'read', LinkedClaimsPluginDependentMethods>;
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,EAAE,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAE7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG,eAAe,CAAC;AAEjE,MAAM,MAAM,yBAAyB,GAAG;IACpC,iBAAiB,EAAE,CACf,QAAQ,EAAE,EAAE,EACZ,OAAO,EAAE,kBAAkB,EAC3B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAChC,OAAO,CAAC,EAAE,CAAC,CAAC;IAEjB,iBAAiB,EAAE,CACf,WAAW,EAAE,EAAE,EACf,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,KAC9B,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEhC,gBAAgB,EAAE,CACd,WAAW,EAAE,EAAE,EACf,OAAO,CAAC,EAAE,iBAAiB,KAC1B,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEpG,eAAe,EAAE,CACb,QAAQ,EAAE,EAAE,EACZ,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAC/B,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,MAAM,CACnC,cAAc,EACd,GAAG,EACH,yBAAyB,EACzB,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,EACjC,kCAAkC,CACrC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@learncard/linked-claims-plugin",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "LinkedClaims plugin to create, verify, store, and query endorsement credentials.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/linked-claims-plugin.esm.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"author": "Learning Economy Foundation (www.learningeconomy.io)",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://github.com/WeLibraryOS/LearnCard#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/WeLibraryOS/LearnCard.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/WeLibraryOS/LearnCard/issues"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/jest": "^29.2.2",
|
|
22
|
+
"@types/node": "^17.0.31",
|
|
23
|
+
"aqu": "0.4.3",
|
|
24
|
+
"esbuild": "^0.14.38",
|
|
25
|
+
"esbuild-jest": "^0.5.0",
|
|
26
|
+
"esbuild-plugin-copy": "^1.3.0",
|
|
27
|
+
"jest": "^29.3.0",
|
|
28
|
+
"shx": "^0.3.4",
|
|
29
|
+
"ts-jest": "^29.0.3"
|
|
30
|
+
},
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"why-is-node-running": "^2.2.2",
|
|
34
|
+
"@learncard/core": "9.3.41",
|
|
35
|
+
"@learncard/types": "5.8.11",
|
|
36
|
+
"@learncard/vc-plugin": "^1.2.4"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "node ./scripts/build.mjs && shx cp ./scripts/mixedEntypoint.js ./dist/index.js && tsc --p tsconfig.json",
|
|
40
|
+
"test": "jest --passWithNoTests",
|
|
41
|
+
"test:watch": "jest --watch",
|
|
42
|
+
"test:coverage": "jest --silent --ci --coverage --coverageReporters=\"text\" --coverageReporters=\"text-summary\""
|
|
43
|
+
}
|
|
44
|
+
}
|