@idos-network/credentials 1.1.0
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 +153 -0
- package/assets/country-codes.xml +271 -0
- package/assets/ed25519-signature-2020-v1.json +93 -0
- package/assets/idos-credential-subject-face-id-v1.json +8 -0
- package/assets/idos-credential-subject-v1.json +41 -0
- package/assets/idos-credential-subject-v2.json +37 -0
- package/assets/idos-credentials-v1.json +9 -0
- package/assets/v1.json +273 -0
- package/dist/builder/index.d.mts +20 -0
- package/dist/builder/index.d.mts.map +1 -0
- package/dist/builder/index.mjs +619 -0
- package/dist/builder/index.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/types/index.d.mts +135 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +63 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +33 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +124 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +73 -0
- package/src/builder/builder.test.ts +407 -0
- package/src/builder/index.ts +122 -0
- package/src/builder/loader.ts +76 -0
- package/src/builder/verifier.ts +55 -0
- package/src/index.ts +2 -0
- package/src/types/credentials.ts +282 -0
- package/src/types/index.ts +4 -0
- package/src/types/issuers.ts +21 -0
- package/src/utils/index.ts +277 -0
- package/src/utils/utils.test.ts +303 -0
- package/types/index.d.ts +4 -0
- package/types/internals.d.ts +124 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Ed25519VerificationKey2020, Ed25519VerificationKey2020Options } from "@digitalbazaar/ed25519-verification-key-2020";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/types/credentials.d.ts
|
|
5
|
+
type idOSCredential = {
|
|
6
|
+
id: string;
|
|
7
|
+
user_id: string;
|
|
8
|
+
public_notes: string;
|
|
9
|
+
content: string;
|
|
10
|
+
encryptor_public_key: string;
|
|
11
|
+
issuer_auth_public_key: string;
|
|
12
|
+
original_id?: string | null;
|
|
13
|
+
};
|
|
14
|
+
type InsertableIDOSCredential = Omit<idOSCredential, "id" | "original_id"> & {
|
|
15
|
+
id?: idOSCredential["id"];
|
|
16
|
+
content_hash?: string;
|
|
17
|
+
public_notes_signature: string;
|
|
18
|
+
broader_signature: string;
|
|
19
|
+
};
|
|
20
|
+
declare const CredentialFieldsSchema: z.ZodObject<{
|
|
21
|
+
id: z.ZodString;
|
|
22
|
+
level: z.ZodString;
|
|
23
|
+
issued: z.ZodOptional<z.ZodDate>;
|
|
24
|
+
approvedAt: z.ZodOptional<z.ZodDate>;
|
|
25
|
+
expirationDate: z.ZodOptional<z.ZodDate>;
|
|
26
|
+
}>;
|
|
27
|
+
type CredentialFields = z.infer<typeof CredentialFieldsSchema>;
|
|
28
|
+
declare const CredentialResidentialAddressSchema: z.ZodObject<{
|
|
29
|
+
street: z.ZodString;
|
|
30
|
+
houseNumber: z.ZodOptional<z.ZodString>;
|
|
31
|
+
additionalAddressInfo: z.ZodOptional<z.ZodString>;
|
|
32
|
+
region: z.ZodOptional<z.ZodString>;
|
|
33
|
+
city: z.ZodString;
|
|
34
|
+
postalCode: z.ZodOptional<z.ZodString>;
|
|
35
|
+
country: z.ZodString;
|
|
36
|
+
proofCategory: z.ZodString;
|
|
37
|
+
proofDateOfIssue: z.ZodOptional<z.ZodDate>;
|
|
38
|
+
proofFile: z.ZodType<Buffer<ArrayBufferLike>>;
|
|
39
|
+
}>;
|
|
40
|
+
type CredentialResidentialAddress = z.infer<typeof CredentialResidentialAddressSchema>;
|
|
41
|
+
declare const IDDocumentTypeSchema: z.ZodEnum<{
|
|
42
|
+
PASSPORT: "PASSPORT";
|
|
43
|
+
DRIVERS: "DRIVERS";
|
|
44
|
+
ID_CARD: "ID_CARD";
|
|
45
|
+
VOTING_CARD: "VOTING_CARD";
|
|
46
|
+
PAN_CARD: "PAN_CARD";
|
|
47
|
+
INTERNAL_PASSPORT: "INTERNAL_PASSPORT";
|
|
48
|
+
RESIDENCE_PERMIT: "RESIDENCE_PERMIT";
|
|
49
|
+
}>;
|
|
50
|
+
type IDDocumentType = z.infer<typeof IDDocumentTypeSchema>;
|
|
51
|
+
declare const GenderSchema: z.ZodEnum<{
|
|
52
|
+
M: "M";
|
|
53
|
+
F: "F";
|
|
54
|
+
OTHER: "OTHER";
|
|
55
|
+
}>;
|
|
56
|
+
type Gender = z.infer<typeof GenderSchema>;
|
|
57
|
+
declare const CredentialSubjectSchema: z.ZodObject<{
|
|
58
|
+
id: z.ZodString;
|
|
59
|
+
firstName: z.ZodOptional<z.ZodString>;
|
|
60
|
+
middleName: z.ZodOptional<z.ZodString>;
|
|
61
|
+
ssn: z.ZodOptional<z.ZodString>;
|
|
62
|
+
gender: z.ZodOptional<typeof GenderSchema>;
|
|
63
|
+
nationality: z.ZodOptional<z.ZodString>;
|
|
64
|
+
familyName: z.ZodOptional<z.ZodString>;
|
|
65
|
+
maidenName: z.ZodOptional<z.ZodString>;
|
|
66
|
+
email: z.ZodOptional<z.ZodEmail>;
|
|
67
|
+
phoneNumber: z.ZodOptional<z.ZodString>;
|
|
68
|
+
dateOfBirth: z.ZodDate;
|
|
69
|
+
placeOfBirth: z.ZodOptional<z.ZodString>;
|
|
70
|
+
idDocumentCountry: z.ZodString;
|
|
71
|
+
idDocumentNumber: z.ZodString;
|
|
72
|
+
idDocumentType: typeof IDDocumentTypeSchema;
|
|
73
|
+
idDocumentDateOfIssue: z.ZodOptional<z.ZodDate>;
|
|
74
|
+
idDocumentDateOfExpiry: z.ZodOptional<z.ZodDate>;
|
|
75
|
+
idDocumentFrontFile: z.ZodType<Buffer<ArrayBufferLike>>;
|
|
76
|
+
idDocumentBackFile: z.ZodOptional<z.ZodType<Buffer<ArrayBufferLike>>>;
|
|
77
|
+
selfieFile: z.ZodType<Buffer<ArrayBufferLike>>;
|
|
78
|
+
residentialAddress: z.ZodOptional<typeof CredentialResidentialAddressSchema>;
|
|
79
|
+
}>;
|
|
80
|
+
type CredentialSubject = z.infer<typeof CredentialSubjectSchema>;
|
|
81
|
+
interface VerifiableCredentialSubject extends Omit<CredentialSubject, "residentialAddress" | "idDocumentBackFile" | "idDocumentFrontFile" | "selfieFile" | "idDocumentDateOfIssue" | "idDocumentDateOfExpiry" | "dateOfBirth"> {
|
|
82
|
+
"@context": string;
|
|
83
|
+
idDocumentFrontFile?: string;
|
|
84
|
+
idDocumentBackFile?: string;
|
|
85
|
+
selfieFile?: string;
|
|
86
|
+
dateOfBirth?: string;
|
|
87
|
+
idDocumentDateOfIssue?: string;
|
|
88
|
+
idDocumentDateOfExpiry?: string;
|
|
89
|
+
residentialAddressStreet?: string;
|
|
90
|
+
residentialAddressHouseNumber?: string;
|
|
91
|
+
residentialAddressAdditionalAddressInfo?: string;
|
|
92
|
+
residentialAddressRegion?: string;
|
|
93
|
+
residentialAddressCity?: string;
|
|
94
|
+
residentialAddressPostalCode?: string;
|
|
95
|
+
residentialAddressCountry?: string;
|
|
96
|
+
residentialAddressProofCategory?: string;
|
|
97
|
+
residentialAddressProofDateOfIssue?: string;
|
|
98
|
+
residentialAddressProofFile?: string;
|
|
99
|
+
}
|
|
100
|
+
interface VerifiedCredentialsProof {
|
|
101
|
+
type: string;
|
|
102
|
+
created: string;
|
|
103
|
+
verificationMethod: string;
|
|
104
|
+
proofValue: string;
|
|
105
|
+
proofPurpose: string;
|
|
106
|
+
}
|
|
107
|
+
declare const CredentialSubjectFaceIdSchema: z.ZodObject<{
|
|
108
|
+
faceSignUserId: z.ZodString;
|
|
109
|
+
}>;
|
|
110
|
+
type CredentialSubjectFaceId = z.infer<typeof CredentialSubjectFaceIdSchema>;
|
|
111
|
+
interface VerifiedCredentials<K> {
|
|
112
|
+
"@context": string[];
|
|
113
|
+
type: string[];
|
|
114
|
+
issuer: string;
|
|
115
|
+
id: string;
|
|
116
|
+
level: string;
|
|
117
|
+
issued: string;
|
|
118
|
+
approvedAt: string;
|
|
119
|
+
expirationDate: string;
|
|
120
|
+
credentialSubject: K;
|
|
121
|
+
issuanceDate: string;
|
|
122
|
+
proof: VerifiedCredentialsProof;
|
|
123
|
+
}
|
|
124
|
+
type VerifiableCredential<K> = VerifiedCredentials<K>;
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/types/issuers.d.ts
|
|
127
|
+
interface CustomIssuerType {
|
|
128
|
+
issuer: string;
|
|
129
|
+
publicKeyMultibase: string;
|
|
130
|
+
privateKeyMultibase?: string;
|
|
131
|
+
}
|
|
132
|
+
type AvailableIssuerType = Omit<Ed25519VerificationKey2020Options, "type"> | Ed25519VerificationKey2020 | CustomIssuerType;
|
|
133
|
+
//#endregion
|
|
134
|
+
export { AvailableIssuerType, CredentialFields, CredentialFieldsSchema, CredentialResidentialAddress, CredentialResidentialAddressSchema, CredentialSubject, CredentialSubjectFaceId, CredentialSubjectFaceIdSchema, CredentialSubjectSchema, CustomIssuerType, Gender, GenderSchema, IDDocumentType, IDDocumentTypeSchema, InsertableIDOSCredential, VerifiableCredential, VerifiableCredentialSubject, VerifiedCredentials, VerifiedCredentialsProof, idOSCredential };
|
|
135
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/types/credentials.ts","../../src/types/issuers.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
//#region src/types/credentials.ts
|
|
3
|
+
const CredentialFieldsSchema = z.object({
|
|
4
|
+
id: z.string(),
|
|
5
|
+
level: z.string(),
|
|
6
|
+
issued: z.date().optional(),
|
|
7
|
+
approvedAt: z.date().optional(),
|
|
8
|
+
expirationDate: z.date().optional()
|
|
9
|
+
});
|
|
10
|
+
const CredentialResidentialAddressSchema = z.object({
|
|
11
|
+
street: z.string(),
|
|
12
|
+
houseNumber: z.string().optional(),
|
|
13
|
+
additionalAddressInfo: z.string().optional(),
|
|
14
|
+
region: z.string().optional(),
|
|
15
|
+
city: z.string(),
|
|
16
|
+
postalCode: z.string().optional(),
|
|
17
|
+
country: z.string().min(2).max(2),
|
|
18
|
+
proofCategory: z.string(),
|
|
19
|
+
proofDateOfIssue: z.date().optional(),
|
|
20
|
+
proofFile: z.instanceof(Buffer)
|
|
21
|
+
});
|
|
22
|
+
const IDDocumentTypeSchema = z.enum([
|
|
23
|
+
"PASSPORT",
|
|
24
|
+
"DRIVERS",
|
|
25
|
+
"ID_CARD",
|
|
26
|
+
"VOTING_CARD",
|
|
27
|
+
"PAN_CARD",
|
|
28
|
+
"INTERNAL_PASSPORT",
|
|
29
|
+
"RESIDENCE_PERMIT"
|
|
30
|
+
]);
|
|
31
|
+
const GenderSchema = z.enum([
|
|
32
|
+
"M",
|
|
33
|
+
"F",
|
|
34
|
+
"OTHER"
|
|
35
|
+
]);
|
|
36
|
+
const CredentialSubjectSchema = z.object({
|
|
37
|
+
id: z.string(),
|
|
38
|
+
firstName: z.string().optional(),
|
|
39
|
+
middleName: z.string().optional(),
|
|
40
|
+
ssn: z.string().min(9).max(9).optional(),
|
|
41
|
+
nationality: z.string().min(2).max(2).optional(),
|
|
42
|
+
gender: GenderSchema.optional(),
|
|
43
|
+
familyName: z.string().optional(),
|
|
44
|
+
maidenName: z.string().optional(),
|
|
45
|
+
dateOfBirth: z.date(),
|
|
46
|
+
placeOfBirth: z.string().optional(),
|
|
47
|
+
email: z.email().optional(),
|
|
48
|
+
phoneNumber: z.string().optional(),
|
|
49
|
+
idDocumentCountry: z.string().min(2).max(2),
|
|
50
|
+
idDocumentNumber: z.string(),
|
|
51
|
+
idDocumentType: IDDocumentTypeSchema,
|
|
52
|
+
idDocumentDateOfIssue: z.date().optional(),
|
|
53
|
+
idDocumentDateOfExpiry: z.date().optional(),
|
|
54
|
+
idDocumentFrontFile: z.instanceof(Buffer),
|
|
55
|
+
idDocumentBackFile: z.instanceof(Buffer).optional(),
|
|
56
|
+
selfieFile: z.instanceof(Buffer),
|
|
57
|
+
residentialAddress: CredentialResidentialAddressSchema.optional()
|
|
58
|
+
}).refine((data) => data.firstName || data.familyName, { message: "At least one of firstName or familyName must be provided" });
|
|
59
|
+
const CredentialSubjectFaceIdSchema = z.object({ faceSignUserId: z.string() });
|
|
60
|
+
//#endregion
|
|
61
|
+
export { CredentialFieldsSchema, CredentialResidentialAddressSchema, CredentialSubjectFaceIdSchema, CredentialSubjectSchema, GenderSchema, IDDocumentTypeSchema };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/types/credentials.ts"],"sourcesContent":["import * as z from \"zod\";\n\nexport type idOSCredential = {\n id: string;\n user_id: string;\n public_notes: string;\n content: string;\n encryptor_public_key: string;\n issuer_auth_public_key: string;\n original_id?: string | null;\n};\n\nexport type InsertableIDOSCredential = Omit<idOSCredential, \"id\" | \"original_id\"> & {\n id?: idOSCredential[\"id\"];\n content_hash?: string;\n public_notes_signature: string;\n broader_signature: string;\n};\n\n// https://github.com/colinhacks/zod/issues/3751\nexport const CredentialFieldsSchema: z.ZodObject<{\n id: z.ZodString;\n level: z.ZodString;\n issued: z.ZodOptional<z.ZodDate>;\n approvedAt: z.ZodOptional<z.ZodDate>;\n expirationDate: z.ZodOptional<z.ZodDate>;\n}> = z.object({\n id: z.string(),\n\n /* Level of KYC verification performed (e.g., basic, intermediate, advanced). */\n level: z.string(),\n\n /* @default Date.now() */\n issued: z.date().optional(),\n\n /* Date the credential was approved. */\n approvedAt: z.date().optional(),\n\n /* Date the credential was revoked. */\n expirationDate: z.date().optional(),\n});\n\nexport type CredentialFields = z.infer<typeof CredentialFieldsSchema>;\n\n// https://github.com/colinhacks/zod/issues/3751\nexport const CredentialResidentialAddressSchema: z.ZodObject<{\n street: z.ZodString;\n houseNumber: z.ZodOptional<z.ZodString>;\n additionalAddressInfo: z.ZodOptional<z.ZodString>;\n region: z.ZodOptional<z.ZodString>;\n city: z.ZodString;\n postalCode: z.ZodOptional<z.ZodString>;\n country: z.ZodString;\n proofCategory: z.ZodString;\n proofDateOfIssue: z.ZodOptional<z.ZodDate>;\n proofFile: z.ZodType<Buffer<ArrayBufferLike>>;\n}> = z.object({\n /* Street address. */\n street: z.string(),\n\n /* House number. */\n houseNumber: z.string().optional(),\n\n /* Additional address information (e.g., apartment number). */\n additionalAddressInfo: z.string().optional(),\n\n /* Region (e.g., state, province). */\n region: z.string().optional(),\n\n /* Locality (e.g., city, town). */\n city: z.string(),\n\n /* Postal code. */\n postalCode: z.string().optional(),\n\n /* Country (ISO 3166-1 alpha-2). */\n country: z.string().min(2).max(2),\n\n /* Residential Address Proof Category\tType of document provided to verify the address(e.g., utility bill, bank statement). */\n proofCategory: z.string(),\n\n /* Residential Address Proof Date Of Issue\tDate the address proof document was issued. */\n proofDateOfIssue: z.date().optional(),\n\n /* Residential Address Proof File or URL of the document provided as address proof. */\n proofFile: z.instanceof(Buffer),\n});\n\nexport type CredentialResidentialAddress = z.infer<typeof CredentialResidentialAddressSchema>;\n\nexport const IDDocumentTypeSchema: z.ZodEnum<{\n PASSPORT: \"PASSPORT\";\n DRIVERS: \"DRIVERS\";\n ID_CARD: \"ID_CARD\";\n VOTING_CARD: \"VOTING_CARD\";\n PAN_CARD: \"PAN_CARD\";\n INTERNAL_PASSPORT: \"INTERNAL_PASSPORT\";\n RESIDENCE_PERMIT: \"RESIDENCE_PERMIT\";\n}> = z.enum([\n \"PASSPORT\",\n \"DRIVERS\",\n \"ID_CARD\",\n \"VOTING_CARD\",\n \"PAN_CARD\",\n \"INTERNAL_PASSPORT\",\n \"RESIDENCE_PERMIT\",\n]);\nexport type IDDocumentType = z.infer<typeof IDDocumentTypeSchema>;\n\nexport const GenderSchema: z.ZodEnum<{\n M: \"M\";\n F: \"F\";\n OTHER: \"OTHER\";\n}> = z.enum([\"M\", \"F\", \"OTHER\"]);\nexport type Gender = z.infer<typeof GenderSchema>;\n\n// https://github.com/colinhacks/zod/issues/3751\nexport const CredentialSubjectSchema: z.ZodObject<{\n id: z.ZodString;\n // Indonesian and Indian did not always have first name\n firstName: z.ZodOptional<z.ZodString>;\n middleName: z.ZodOptional<z.ZodString>;\n ssn: z.ZodOptional<z.ZodString>;\n gender: z.ZodOptional<typeof GenderSchema>;\n nationality: z.ZodOptional<z.ZodString>;\n // Indonesian and Indian did not always have family name\n familyName: z.ZodOptional<z.ZodString>;\n maidenName: z.ZodOptional<z.ZodString>;\n email: z.ZodOptional<z.ZodEmail>;\n phoneNumber: z.ZodOptional<z.ZodString>;\n dateOfBirth: z.ZodDate;\n placeOfBirth: z.ZodOptional<z.ZodString>;\n idDocumentCountry: z.ZodString;\n idDocumentNumber: z.ZodString;\n idDocumentType: typeof IDDocumentTypeSchema;\n idDocumentDateOfIssue: z.ZodOptional<z.ZodDate>;\n idDocumentDateOfExpiry: z.ZodOptional<z.ZodDate>;\n idDocumentFrontFile: z.ZodType<Buffer<ArrayBufferLike>>;\n idDocumentBackFile: z.ZodOptional<z.ZodType<Buffer<ArrayBufferLike>>>;\n selfieFile: z.ZodType<Buffer<ArrayBufferLike>>;\n residentialAddress: z.ZodOptional<typeof CredentialResidentialAddressSchema>;\n}> = z\n .object({\n /* ID(unique credential)\tUnique identifier for the credential itself. */\n id: z.string(),\n\n /* First name. */\n firstName: z.string().optional(),\n\n /* Middle name. */\n middleName: z.string().optional(),\n\n /* Social-security-number (without dashes) */\n ssn: z.string().min(9).max(9).optional(),\n\n /* Nationality (ISO 3166-1 alpha-2). */\n nationality: z.string().min(2).max(2).optional(),\n\n /* Gender (M or F, empty if not provided). */\n gender: GenderSchema.optional(),\n\n /* Family name. */\n familyName: z.string().optional(),\n\n /* Maiden name. */\n maidenName: z.string().optional(),\n\n /* Date of birth. */\n dateOfBirth: z.date(),\n\n /* Place of birth. */\n placeOfBirth: z.string().optional(),\n\n /* Email. */\n email: z.email().optional(),\n\n /* Phone number. */\n phoneNumber: z.string().optional(),\n\n /* Country that issued the identity document (ISO 3166-1 alpha-2). */\n idDocumentCountry: z.string().min(2).max(2),\n\n /* ID Document Number\tUnique number on the identity document. */\n idDocumentNumber: z.string(),\n\n /* ID Document Type\tType of identity document(e.g., Passport, Driver's License, National ID). */\n idDocumentType: IDDocumentTypeSchema,\n\n /* ID Document Date of Issue\tDate the identity document was issued. */\n idDocumentDateOfIssue: z.date().optional(),\n\n /* ID Document Date of Expiry\tExpiration date of the identity document - if applicable. */\n idDocumentDateOfExpiry: z.date().optional(),\n\n /* ID Document Front File\tBuffer with file representing the front of the identity document. */\n idDocumentFrontFile: z.instanceof(Buffer),\n\n /* ID Document Back File\tBuffer with file representing the back of the identity document - if applicable. */\n idDocumentBackFile: z.instanceof(Buffer).optional(),\n\n /* (ID Document) Selfie File\tBuffer with selfie with the identity document for verification purposes. */\n selfieFile: z.instanceof(Buffer),\n\n /* Residential Address Full residential address of the individual - if applicable. */\n residentialAddress: CredentialResidentialAddressSchema.optional(),\n })\n .refine(\n // At least one of firstName or familyName must be present,\n (data) => data.firstName || data.familyName,\n { message: \"At least one of firstName or familyName must be provided\" },\n );\n\nexport type CredentialSubject = z.infer<typeof CredentialSubjectSchema>;\n\nexport interface VerifiableCredentialSubject extends Omit<\n CredentialSubject,\n | \"residentialAddress\"\n | \"idDocumentBackFile\"\n | \"idDocumentFrontFile\"\n | \"selfieFile\"\n | \"idDocumentDateOfIssue\"\n | \"idDocumentDateOfExpiry\"\n | \"dateOfBirth\"\n> {\n \"@context\": string;\n // Files are strings in verifiable credentials, but as an input they should be buffers\n idDocumentFrontFile?: string;\n idDocumentBackFile?: string;\n selfieFile?: string;\n // Dates are strings in verifiable credentials\n dateOfBirth?: string;\n idDocumentDateOfIssue?: string;\n idDocumentDateOfExpiry?: string;\n // This is the residential address flattened fields\n residentialAddressStreet?: string;\n residentialAddressHouseNumber?: string;\n residentialAddressAdditionalAddressInfo?: string;\n residentialAddressRegion?: string;\n residentialAddressCity?: string;\n residentialAddressPostalCode?: string;\n residentialAddressCountry?: string;\n residentialAddressProofCategory?: string;\n // Dates are strings in verifiable credentials\n residentialAddressProofDateOfIssue?: string;\n // Files are strings in verifiable credentials, but as an input they should be buffers\n residentialAddressProofFile?: string;\n}\n\n// TODO: This is a stub of the types for @digitalbazaar/vc\n// when they introduce TypeScript support we should remove this\n// The copy is here because `types.d.ts` file is not bundled.\nexport interface VerifiedCredentialsProof {\n type: string;\n created: string;\n verificationMethod: string;\n proofValue: string;\n proofPurpose: string;\n}\n\nexport const CredentialSubjectFaceIdSchema: z.ZodObject<{\n faceSignUserId: z.ZodString;\n}> = z.object({\n faceSignUserId: z.string(),\n});\n\nexport type CredentialSubjectFaceId = z.infer<typeof CredentialSubjectFaceIdSchema>;\n\nexport interface VerifiedCredentials<K> {\n \"@context\": string[];\n type: string[];\n issuer: string;\n id: string;\n level: string;\n issued: string;\n approvedAt: string;\n expirationDate: string;\n credentialSubject: K;\n issuanceDate: string;\n proof: VerifiedCredentialsProof;\n}\n\nexport type VerifiableCredential<K> = VerifiedCredentials<K>;\n"],"mappings":";;AAoBA,MAAa,yBAMR,EAAE,OAAO;CACZ,IAAI,EAAE,QAAQ;CAGd,OAAO,EAAE,QAAQ;CAGjB,QAAQ,EAAE,MAAM,CAAC,UAAU;CAG3B,YAAY,EAAE,MAAM,CAAC,UAAU;CAG/B,gBAAgB,EAAE,MAAM,CAAC,UAAU;CACpC,CAAC;AAKF,MAAa,qCAWR,EAAE,OAAO;CAEZ,QAAQ,EAAE,QAAQ;CAGlB,aAAa,EAAE,QAAQ,CAAC,UAAU;CAGlC,uBAAuB,EAAE,QAAQ,CAAC,UAAU;CAG5C,QAAQ,EAAE,QAAQ,CAAC,UAAU;CAG7B,MAAM,EAAE,QAAQ;CAGhB,YAAY,EAAE,QAAQ,CAAC,UAAU;CAGjC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;CAGjC,eAAe,EAAE,QAAQ;CAGzB,kBAAkB,EAAE,MAAM,CAAC,UAAU;CAGrC,WAAW,EAAE,WAAW,OAAO;CAChC,CAAC;AAIF,MAAa,uBAQR,EAAE,KAAK;CACV;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAGF,MAAa,eAIR,EAAE,KAAK;CAAC;CAAK;CAAK;CAAQ,CAAC;AAIhC,MAAa,0BAwBR,EACF,OAAO;CAEN,IAAI,EAAE,QAAQ;CAGd,WAAW,EAAE,QAAQ,CAAC,UAAU;CAGhC,YAAY,EAAE,QAAQ,CAAC,UAAU;CAGjC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU;CAGxC,aAAa,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU;CAGhD,QAAQ,aAAa,UAAU;CAG/B,YAAY,EAAE,QAAQ,CAAC,UAAU;CAGjC,YAAY,EAAE,QAAQ,CAAC,UAAU;CAGjC,aAAa,EAAE,MAAM;CAGrB,cAAc,EAAE,QAAQ,CAAC,UAAU;CAGnC,OAAO,EAAE,OAAO,CAAC,UAAU;CAG3B,aAAa,EAAE,QAAQ,CAAC,UAAU;CAGlC,mBAAmB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE;CAG3C,kBAAkB,EAAE,QAAQ;CAG5B,gBAAgB;CAGhB,uBAAuB,EAAE,MAAM,CAAC,UAAU;CAG1C,wBAAwB,EAAE,MAAM,CAAC,UAAU;CAG3C,qBAAqB,EAAE,WAAW,OAAO;CAGzC,oBAAoB,EAAE,WAAW,OAAO,CAAC,UAAU;CAGnD,YAAY,EAAE,WAAW,OAAO;CAGhC,oBAAoB,mCAAmC,UAAU;CAClE,CAAC,CACD,QAEE,SAAS,KAAK,aAAa,KAAK,YACjC,EAAE,SAAS,4DAA4D,CACxE;AAiDH,MAAa,gCAER,EAAE,OAAO,EACZ,gBAAgB,EAAE,QAAQ,EAC3B,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AvailableIssuerType, CredentialFields, CredentialResidentialAddress, CredentialSubject, CredentialSubjectFaceId, InsertableIDOSCredential } from "../types/index.mjs";
|
|
2
|
+
import { Ed25519VerificationKey2020 } from "@digitalbazaar/ed25519-verification-key-2020";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/index.d.ts
|
|
5
|
+
declare function fileToBase85(file: Buffer): string;
|
|
6
|
+
declare function base85ToFile(data: string): Buffer | false;
|
|
7
|
+
declare function capitalizeFirstLetter(str: string): string;
|
|
8
|
+
declare function issuerToKey(issuer: AvailableIssuerType): Promise<Ed25519VerificationKey2020>;
|
|
9
|
+
declare function convertValues<K extends CredentialFields | CredentialSubject | CredentialResidentialAddress | CredentialSubjectFaceId>(fields: K, prefix?: string): Record<string, unknown>;
|
|
10
|
+
type BaseLevel = "basic" | "plus";
|
|
11
|
+
type Addon = "liveness" | "email" | "phoneNumber";
|
|
12
|
+
declare function deriveLevel(credential: CredentialSubject): string;
|
|
13
|
+
declare function parseLevel(level: string): {
|
|
14
|
+
base: BaseLevel;
|
|
15
|
+
addons: Addon[];
|
|
16
|
+
};
|
|
17
|
+
declare function matchLevelOrHigher(level: BaseLevel, requiredAddons: Addon[], currentLevel: string): boolean;
|
|
18
|
+
declare function levelScore(level: string): number;
|
|
19
|
+
declare function pickHighestMatchingLevel(levels: string[], requiredLevel: BaseLevel, requiredAddons: Addon[]): string | null;
|
|
20
|
+
declare function highestMatchingCredential<K extends {
|
|
21
|
+
public_notes: string;
|
|
22
|
+
}>(credentials: K[], requiredLevel: BaseLevel, {
|
|
23
|
+
addons: requiredAddons,
|
|
24
|
+
publicNotesConstraint
|
|
25
|
+
}: {
|
|
26
|
+
addons?: Addon[];
|
|
27
|
+
publicNotesConstraint?: Record<string, number | string>;
|
|
28
|
+
}): K | undefined;
|
|
29
|
+
declare function recordFilter(rec: Record<string, unknown>, pick: Record<string, unknown[]>, omit: Record<string, unknown[]>): boolean;
|
|
30
|
+
declare function buildInsertableIDOSCredential(userId: string, publicNotes: string, content: string, encryptorPublicKey: string): InsertableIDOSCredential;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { base85ToFile, buildInsertableIDOSCredential, capitalizeFirstLetter, convertValues, deriveLevel, fileToBase85, highestMatchingCredential, issuerToKey, levelScore, matchLevelOrHigher, parseLevel, pickHighestMatchingLevel, recordFilter };
|
|
33
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/utils/index.ts"],"mappings":";;;;iBAiBgB,YAAA,CAAa,IAAA,EAAM,MAAA;AAAA,iBAInB,YAAA,CAAa,IAAA,WAAe,MAAA;AAAA,iBAI5B,qBAAA,CAAsB,GAAA;AAAA,iBAuBhB,WAAA,CACpB,MAAA,EAAQ,mBAAA,GACP,OAAA,CAAQ,0BAAA;AAAA,iBAkBK,aAAA,WAEV,gBAAA,GACA,iBAAA,GACA,4BAAA,GACA,uBAAA,CAAA,CACJ,MAAA,EAAQ,CAAA,EAAG,MAAA,YAAkB,MAAA;AAAA,KAqB1B,SAAA;AAAA,KACA,KAAA;AAAA,iBAEW,WAAA,CAAY,UAAA,EAAY,iBAAA;AAAA,iBAyBxB,UAAA,CAAW,KAAA;EACzB,IAAA,EAAM,SAAA;EACN,MAAA,EAAQ,KAAA;AAAA;AAAA,iBAMM,kBAAA,CACd,KAAA,EAAO,SAAA,EACP,cAAA,EAAgB,KAAA,IAChB,YAAA;AAAA,iBAYc,UAAA,CAAW,KAAA;AAAA,iBAaX,wBAAA,CACd,MAAA,YACA,aAAA,EAAe,SAAA,EACf,cAAA,EAAgB,KAAA;AAAA,iBAaF,yBAAA;EAAsC,YAAA;AAAA,EAAA,CACpD,WAAA,EAAa,CAAA,IACb,aAAA,EAAe,SAAA;EAEb,MAAA,EAAQ,cAAA;EACR;AAAA;EAEA,MAAA,GAAS,KAAA;EACT,qBAAA,GAAwB,MAAA;AAAA,IAEzB,CAAA;AAAA,iBAsCa,YAAA,CACd,GAAA,EAAK,MAAA,mBACL,IAAA,EAAM,MAAA,qBACN,IAAA,EAAM,MAAA;AAAA,iBAkBQ,6BAAA,CACd,MAAA,UACA,WAAA,UACA,OAAA,UACA,kBAAA,WACC,wBAAA"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Ed25519VerificationKey2020 } from "@digitalbazaar/ed25519-verification-key-2020";
|
|
2
|
+
import { base64Decode, base64Encode, hexEncode, utf8Encode } from "@idos-network/utils/codecs";
|
|
3
|
+
import * as base85 from "base85";
|
|
4
|
+
import { every, get } from "es-toolkit/compat";
|
|
5
|
+
import invariant from "tiny-invariant";
|
|
6
|
+
import nacl from "tweetnacl";
|
|
7
|
+
//#region src/utils/index.ts
|
|
8
|
+
function fileToBase85(file) {
|
|
9
|
+
return base85.encode(file, "ascii85");
|
|
10
|
+
}
|
|
11
|
+
function base85ToFile(data) {
|
|
12
|
+
return base85.decode(data);
|
|
13
|
+
}
|
|
14
|
+
function capitalizeFirstLetter(str) {
|
|
15
|
+
return str[0].toUpperCase() + str.slice(1);
|
|
16
|
+
}
|
|
17
|
+
function isIssuerKey(issuer) {
|
|
18
|
+
return typeof issuer === "object" && issuer !== null && "type" in issuer && "id" in issuer && "controller" in issuer;
|
|
19
|
+
}
|
|
20
|
+
function isCustomIssuerType(issuer) {
|
|
21
|
+
return typeof issuer === "object" && issuer !== null && "issuer" in issuer && "publicKeyMultibase" in issuer;
|
|
22
|
+
}
|
|
23
|
+
async function issuerToKey(issuer) {
|
|
24
|
+
if (isIssuerKey(issuer)) return issuer;
|
|
25
|
+
if (isCustomIssuerType(issuer)) return await Ed25519VerificationKey2020.from({
|
|
26
|
+
id: `${issuer.issuer}/keys/1`,
|
|
27
|
+
controller: `${issuer.issuer}/issuers/1`,
|
|
28
|
+
publicKeyMultibase: issuer.publicKeyMultibase,
|
|
29
|
+
privateKeyMultibase: issuer.privateKeyMultibase,
|
|
30
|
+
type: "Ed25519VerificationKey2020"
|
|
31
|
+
});
|
|
32
|
+
return await Ed25519VerificationKey2020.from({
|
|
33
|
+
...issuer,
|
|
34
|
+
type: "Ed25519VerificationKey2020"
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function convertValues(fields, prefix) {
|
|
38
|
+
const acc = {};
|
|
39
|
+
for (const key in fields) if (Object.hasOwn(fields, key)) {
|
|
40
|
+
const value = fields[key];
|
|
41
|
+
const name = prefix ? `${prefix}${capitalizeFirstLetter(key)}` : key;
|
|
42
|
+
if (value instanceof Date) acc[name] = value.toISOString();
|
|
43
|
+
else if (value instanceof Buffer) acc[name] = fileToBase85(value);
|
|
44
|
+
else acc[name] = value;
|
|
45
|
+
}
|
|
46
|
+
return acc;
|
|
47
|
+
}
|
|
48
|
+
function deriveLevel(credential) {
|
|
49
|
+
let level = "basic";
|
|
50
|
+
const address = credential.residentialAddress;
|
|
51
|
+
if (address?.proofFile && address?.city && address?.proofCategory) level = "plus";
|
|
52
|
+
const addons = [];
|
|
53
|
+
if (credential.selfieFile) addons.push("liveness");
|
|
54
|
+
if (credential.email) addons.push("email");
|
|
55
|
+
if (credential.phoneNumber) addons.push("phoneNumber");
|
|
56
|
+
return [level, ...addons].join("+");
|
|
57
|
+
}
|
|
58
|
+
function parseLevel(level) {
|
|
59
|
+
const [base, ...addons] = level.split("+");
|
|
60
|
+
return {
|
|
61
|
+
base,
|
|
62
|
+
addons
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function matchLevelOrHigher(level, requiredAddons, currentLevel) {
|
|
66
|
+
const { base: currentBaseLevel, addons: currentAddons } = parseLevel(currentLevel);
|
|
67
|
+
if (level === "plus" && currentBaseLevel !== "plus") return false;
|
|
68
|
+
return requiredAddons.every((addon) => currentAddons.includes(addon));
|
|
69
|
+
}
|
|
70
|
+
function levelScore(level) {
|
|
71
|
+
const { base, addons } = parseLevel(level);
|
|
72
|
+
let score = 0;
|
|
73
|
+
if (base === "plus") score += 100;
|
|
74
|
+
score += addons.length * 10;
|
|
75
|
+
return score;
|
|
76
|
+
}
|
|
77
|
+
function pickHighestMatchingLevel(levels, requiredLevel, requiredAddons) {
|
|
78
|
+
return levels.filter((currentLevel) => matchLevelOrHigher(requiredLevel, requiredAddons, currentLevel)).sort((a, b) => {
|
|
79
|
+
const aAddons = levelScore(a);
|
|
80
|
+
return levelScore(b) - aAddons;
|
|
81
|
+
})[0] ?? null;
|
|
82
|
+
}
|
|
83
|
+
function highestMatchingCredential(credentials, requiredLevel, { addons: requiredAddons = [], publicNotesConstraint = {} }) {
|
|
84
|
+
return credentials.map((credential) => {
|
|
85
|
+
return {
|
|
86
|
+
credential,
|
|
87
|
+
publicNotes: JSON.parse(credential.public_notes || "{}")
|
|
88
|
+
};
|
|
89
|
+
}).filter(({ publicNotes }) => {
|
|
90
|
+
const level = publicNotes.level;
|
|
91
|
+
if (!level) return false;
|
|
92
|
+
if (!matchLevelOrHigher(requiredLevel, requiredAddons, level)) return false;
|
|
93
|
+
for (const key in publicNotesConstraint) if (publicNotes[key] !== publicNotesConstraint[key]) return false;
|
|
94
|
+
return true;
|
|
95
|
+
}).sort((a, b) => {
|
|
96
|
+
const aLevel = a.publicNotes.level;
|
|
97
|
+
const bLevel = b.publicNotes.level;
|
|
98
|
+
return levelScore(bLevel) - levelScore(aLevel);
|
|
99
|
+
}).map(({ credential }) => credential)[0];
|
|
100
|
+
}
|
|
101
|
+
function recordFilter(rec, pick, omit) {
|
|
102
|
+
const matchCriteria = (content, criteria) => every(Object.entries(criteria), ([path, targetSet]) => targetSet.includes(get(content, path)));
|
|
103
|
+
if (Object.keys(pick).length > 0 && !matchCriteria(rec, pick)) return false;
|
|
104
|
+
if (Object.keys(omit).length > 0 && matchCriteria(rec, omit)) return false;
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
function buildInsertableIDOSCredential(userId, publicNotes, content, encryptorPublicKey) {
|
|
108
|
+
invariant(encryptorPublicKey, "Missing `encryptorPublicKey`");
|
|
109
|
+
const ephemeralAuthenticationKeyPair = nacl.sign.keyPair();
|
|
110
|
+
const publicNotesSignature = nacl.sign.detached(utf8Encode(publicNotes), ephemeralAuthenticationKeyPair.secretKey);
|
|
111
|
+
return {
|
|
112
|
+
user_id: userId,
|
|
113
|
+
content,
|
|
114
|
+
public_notes: publicNotes,
|
|
115
|
+
public_notes_signature: base64Encode(publicNotesSignature),
|
|
116
|
+
broader_signature: base64Encode(nacl.sign.detached(Uint8Array.from([...publicNotesSignature, ...base64Decode(content)]), ephemeralAuthenticationKeyPair.secretKey)),
|
|
117
|
+
issuer_auth_public_key: hexEncode(ephemeralAuthenticationKeyPair.publicKey, true),
|
|
118
|
+
encryptor_public_key: encryptorPublicKey
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { base85ToFile, buildInsertableIDOSCredential, capitalizeFirstLetter, convertValues, deriveLevel, fileToBase85, highestMatchingCredential, issuerToKey, levelScore, matchLevelOrHigher, parseLevel, pickHighestMatchingLevel, recordFilter };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["import { Ed25519VerificationKey2020 } from \"@digitalbazaar/ed25519-verification-key-2020\";\nimport { base64Decode, base64Encode, hexEncode, utf8Encode } from \"@idos-network/utils/codecs\";\nimport * as base85 from \"base85\";\nimport { every, get } from \"es-toolkit/compat\";\nimport invariant from \"tiny-invariant\";\nimport nacl from \"tweetnacl\";\n\nimport type {\n AvailableIssuerType,\n CredentialFields,\n CredentialResidentialAddress,\n CredentialSubject,\n CredentialSubjectFaceId,\n CustomIssuerType,\n InsertableIDOSCredential,\n} from \"../types\";\n\nexport function fileToBase85(file: Buffer): string {\n return base85.encode(file, \"ascii85\");\n}\n\nexport function base85ToFile(data: string): Buffer | false {\n return base85.decode(data);\n}\n\nexport function capitalizeFirstLetter(str: string): string {\n return str[0].toUpperCase() + str.slice(1);\n}\n\nfunction isIssuerKey(issuer: AvailableIssuerType): issuer is Ed25519VerificationKey2020 {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"type\" in issuer &&\n \"id\" in issuer &&\n \"controller\" in issuer\n );\n}\n\nfunction isCustomIssuerType(issuer: AvailableIssuerType): issuer is CustomIssuerType {\n return (\n typeof issuer === \"object\" &&\n issuer !== null &&\n \"issuer\" in issuer &&\n \"publicKeyMultibase\" in issuer\n );\n}\n\nexport async function issuerToKey(\n issuer: AvailableIssuerType,\n): Promise<Ed25519VerificationKey2020> {\n if (isIssuerKey(issuer)) {\n return issuer;\n }\n\n if (isCustomIssuerType(issuer)) {\n return await Ed25519VerificationKey2020.from({\n id: `${issuer.issuer}/keys/1`,\n controller: `${issuer.issuer}/issuers/1`,\n publicKeyMultibase: issuer.publicKeyMultibase,\n privateKeyMultibase: issuer.privateKeyMultibase,\n type: \"Ed25519VerificationKey2020\",\n });\n }\n\n return await Ed25519VerificationKey2020.from({ ...issuer, type: \"Ed25519VerificationKey2020\" });\n}\n\nexport function convertValues<\n K extends\n | CredentialFields\n | CredentialSubject\n | CredentialResidentialAddress\n | CredentialSubjectFaceId,\n>(fields: K, prefix?: string): Record<string, unknown> {\n const acc: Record<string, unknown> = {};\n\n for (const key in fields) {\n if (Object.hasOwn(fields, key)) {\n const value = fields[key];\n const name = prefix ? `${prefix}${capitalizeFirstLetter(key)}` : key;\n if (value instanceof Date) {\n acc[name] = value.toISOString();\n } else if (value instanceof Buffer) {\n // Convert file to base85\n acc[name] = fileToBase85(value);\n } else {\n acc[name] = value;\n }\n }\n }\n\n return acc;\n}\n\ntype BaseLevel = \"basic\" | \"plus\";\ntype Addon = \"liveness\" | \"email\" | \"phoneNumber\";\n\nexport function deriveLevel(credential: CredentialSubject): string {\n let level: BaseLevel = \"basic\";\n\n // Address is a sign for plus+\n const address = credential.residentialAddress;\n if (address?.proofFile && address?.city && address?.proofCategory) {\n level = \"plus\";\n }\n\n const addons: Addon[] = [];\n if (credential.selfieFile) {\n addons.push(\"liveness\");\n }\n\n if (credential.email) {\n addons.push(\"email\");\n }\n\n if (credential.phoneNumber) {\n addons.push(\"phoneNumber\");\n }\n\n return [level, ...addons].join(\"+\");\n}\n\nexport function parseLevel(level: string): {\n base: BaseLevel;\n addons: Addon[];\n} {\n const [base, ...addons] = level.split(\"+\") as [BaseLevel, ...Addon[]];\n return { base, addons };\n}\n\nexport function matchLevelOrHigher(\n level: BaseLevel,\n requiredAddons: Addon[],\n currentLevel: string,\n): boolean {\n const { base: currentBaseLevel, addons: currentAddons } = parseLevel(currentLevel);\n\n // TODO: Consider pop+ or uniqueness+ scenarios\n if (level === \"plus\" && currentBaseLevel !== \"plus\") {\n return false;\n }\n\n return requiredAddons.every((addon) => currentAddons.includes(addon));\n}\n\nexport function levelScore(level: string): number {\n const { base, addons } = parseLevel(level);\n let score = 0;\n\n if (base === \"plus\") {\n score += 100;\n }\n\n score += addons.length * 10;\n\n return score;\n}\n\nexport function pickHighestMatchingLevel(\n levels: string[],\n requiredLevel: BaseLevel,\n requiredAddons: Addon[],\n): string | null {\n return (\n levels\n .filter((currentLevel) => matchLevelOrHigher(requiredLevel, requiredAddons, currentLevel))\n .sort((a, b) => {\n const aAddons = levelScore(a);\n const bAddons = levelScore(b);\n return bAddons - aAddons; // descending\n })[0] ?? null\n );\n}\n\nexport function highestMatchingCredential<K extends { public_notes: string }>(\n credentials: K[],\n requiredLevel: BaseLevel,\n {\n addons: requiredAddons = [],\n publicNotesConstraint = {},\n }: {\n addons?: Addon[];\n publicNotesConstraint?: Record<string, number | string>;\n },\n): K | undefined {\n const matchingCredentials = credentials\n .map((credential) => {\n const publicNotes = JSON.parse(credential.public_notes || \"{}\");\n return {\n credential,\n publicNotes,\n };\n })\n .filter(({ publicNotes }) => {\n const level = publicNotes.level;\n\n if (!level) {\n return false;\n }\n\n if (!matchLevelOrHigher(requiredLevel, requiredAddons, level)) {\n return false;\n }\n\n for (const key in publicNotesConstraint) {\n if (publicNotes[key] !== publicNotesConstraint[key]) {\n return false;\n }\n }\n\n return true;\n })\n .sort((a, b) => {\n const aLevel = a.publicNotes.level;\n const bLevel = b.publicNotes.level;\n return levelScore(bLevel) - levelScore(aLevel); // descending\n })\n .map(({ credential }) => credential);\n\n return matchingCredentials[0];\n}\n\nexport function recordFilter(\n rec: Record<string, unknown>,\n pick: Record<string, unknown[]>,\n omit: Record<string, unknown[]>,\n): boolean {\n const matchCriteria = (content: Record<string, unknown>, criteria: Record<string, unknown[]>) =>\n every(Object.entries(criteria), ([path, targetSet]) => targetSet.includes(get(content, path)));\n\n if (Object.keys(pick).length > 0 && !matchCriteria(rec, pick)) {\n // Fast fail on pick criteria\n return false;\n }\n\n if (Object.keys(omit).length > 0 && matchCriteria(rec, omit)) {\n // Fast fail on omit criteria\n return false;\n }\n\n return true;\n}\n\nexport function buildInsertableIDOSCredential(\n userId: string,\n publicNotes: string,\n content: string,\n encryptorPublicKey: string,\n): InsertableIDOSCredential {\n invariant(encryptorPublicKey, \"Missing `encryptorPublicKey`\");\n\n const ephemeralAuthenticationKeyPair = nacl.sign.keyPair();\n\n const publicNotesSignature = nacl.sign.detached(\n utf8Encode(publicNotes),\n ephemeralAuthenticationKeyPair.secretKey,\n );\n\n return {\n user_id: userId,\n content,\n\n public_notes: publicNotes,\n public_notes_signature: base64Encode(publicNotesSignature),\n\n broader_signature: base64Encode(\n nacl.sign.detached(\n Uint8Array.from([...publicNotesSignature, ...base64Decode(content)]),\n ephemeralAuthenticationKeyPair.secretKey,\n ),\n ),\n\n issuer_auth_public_key: hexEncode(ephemeralAuthenticationKeyPair.publicKey, true),\n encryptor_public_key: encryptorPublicKey,\n };\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@idos-network/credentials",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "idOS Credentials JavaScript SDK",
|
|
5
|
+
"homepage": "https://idos.network",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": "github:idos-network/idos-sdk-js",
|
|
8
|
+
"files": [
|
|
9
|
+
"assets/**/*",
|
|
10
|
+
"dist/**/*",
|
|
11
|
+
"src/**/*",
|
|
12
|
+
"types/**/*"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"main": "./dist/index.mjs",
|
|
17
|
+
"module": "dist/index.mjs",
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"import": "./dist/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"./utils": {
|
|
25
|
+
"types": "./dist/utils/index.d.mts",
|
|
26
|
+
"import": "./dist/utils/index.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./builder": {
|
|
29
|
+
"types": "./dist/builder/index.d.mts",
|
|
30
|
+
"import": "./dist/builder/index.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./types": {
|
|
33
|
+
"types": "./dist/types/index.d.mts",
|
|
34
|
+
"import": "./dist/types/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
|
42
|
+
"@digitalbazaar/ed25519-verification-key-2020": "^4.2.0",
|
|
43
|
+
"@digitalbazaar/vc": "^7.2.0",
|
|
44
|
+
"base85": "^3.1.0",
|
|
45
|
+
"es-toolkit": "^1.39.0",
|
|
46
|
+
"jsonld-document-loader": "^2.3.0",
|
|
47
|
+
"tiny-invariant": "^1.3.3",
|
|
48
|
+
"tweetnacl": "^1.0.3",
|
|
49
|
+
"zod": "4.3.6",
|
|
50
|
+
"@idos-network/utils": "1.1.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "25.5.2",
|
|
54
|
+
"tsdown": "^0.21.0",
|
|
55
|
+
"typescript": "^5.8.3",
|
|
56
|
+
"vitest": "4.1.4",
|
|
57
|
+
"web-streams-polyfill": "3.3.3"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"typescript": "^5.8.3"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22.19.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsdown",
|
|
67
|
+
"dev": "tsdown --watch",
|
|
68
|
+
"publint": "pnpx publint --strict",
|
|
69
|
+
"test": "vitest",
|
|
70
|
+
"test:watch": "vitest --watch",
|
|
71
|
+
"typecheck": "tsc --noEmit"
|
|
72
|
+
}
|
|
73
|
+
}
|