From 563df6992738433e43d7c24d39b5b40dcfd7b65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthieu=20COLL=C3=89?= Date: Wed, 3 Feb 2021 15:28:40 -0500 Subject: [PATCH] test(Context): added ODRL --- src/constants/contexts.ts | 574 ++++++++++++++++++++++++++--- src/inspectors/computeLocalHash.ts | 7 +- 2 files changed, 526 insertions(+), 55 deletions(-) diff --git a/src/constants/contexts.ts b/src/constants/contexts.ts index 7f1abbf..a519680 100644 --- a/src/constants/contexts.ts +++ b/src/constants/contexts.ts @@ -12,17 +12,33 @@ export const CONTEXTS = { MerkleProof2019: 'sec:MerkleProof2019', BlockcertsCredential: 'bc:BlockcertsCredential', - introductionUrl: { '@id': 'bc:introductionUrl', '@type': '@id' }, + introductionUrl: { + '@id': 'bc:introductionUrl', + '@type': '@id' + }, CryptographicKey: 'sec:Key', domain: 'sec:domain', nonce: 'sec:nonce', proofValue: 'sec:proofValue', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - proofPurpose: { '@id': 'sec:proofPurpose', '@type': '@vocab' }, - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' }, - created: { '@id': 'bc:created', '@type': 'xsd:dateTime' }, + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + proofPurpose: { + '@id': 'sec:proofPurpose', + '@type': '@vocab' + }, + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + }, + created: { + '@id': 'bc:created', + '@type': 'xsd:dateTime' + }, name: { '@id': 'schema:name' @@ -62,15 +78,43 @@ export const CONTEXTS = { JsonSchemaValidator2018: 'cred:JsonSchemaValidator2018' } }, - credentialStatus: { '@id': 'cred:credentialStatus', '@type': '@id' }, - credentialSubject: { '@id': 'cred:credentialSubject', '@type': '@id' }, - evidence: { '@id': 'cred:evidence', '@type': '@id' }, - expirationDate: { '@id': 'cred:expirationDate', '@type': 'xsd:dateTime' }, - holder: { '@id': 'cred:holder', '@type': '@id' }, - issued: { '@id': 'cred:issued', '@type': 'xsd:dateTime' }, - issuer: { '@id': 'cred:issuer', '@type': '@id' }, - issuanceDate: { '@id': 'cred:issuanceDate', '@type': 'xsd:dateTime' }, - proof: { '@id': 'sec:proof', '@type': '@id', '@container': '@graph' }, + credentialStatus: { + '@id': 'cred:credentialStatus', + '@type': '@id' + }, + credentialSubject: { + '@id': 'cred:credentialSubject', + '@type': '@id' + }, + evidence: { + '@id': 'cred:evidence', + '@type': '@id' + }, + expirationDate: { + '@id': 'cred:expirationDate', + '@type': 'xsd:dateTime' + }, + holder: { + '@id': 'cred:holder', + '@type': '@id' + }, + issued: { + '@id': 'cred:issued', + '@type': 'xsd:dateTime' + }, + issuer: { + '@id': 'cred:issuer', + '@type': '@id' + }, + issuanceDate: { + '@id': 'cred:issuanceDate', + '@type': 'xsd:dateTime' + }, + proof: { + '@id': 'sec:proof', + '@type': '@id', + '@container': '@graph' + }, refreshService: { '@id': 'cred:refreshService', '@type': '@id', @@ -85,9 +129,18 @@ export const CONTEXTS = { ManualRefreshService2018: 'cred:ManualRefreshService2018' } }, - termsOfUse: { '@id': 'cred:termsOfUse', '@type': '@id' }, - validFrom: { '@id': 'cred:validFrom', '@type': 'xsd:dateTime' }, - validUntil: { '@id': 'cred:validUntil', '@type': 'xsd:dateTime' } + termsOfUse: { + '@id': 'cred:termsOfUse', + '@type': '@id' + }, + validFrom: { + '@id': 'cred:validFrom', + '@type': 'xsd:dateTime' + }, + validUntil: { + '@id': 'cred:validUntil', + '@type': 'xsd:dateTime' + } } }, @@ -102,9 +155,20 @@ export const CONTEXTS = { cred: 'https://www.w3.org/2018/credentials#', sec: 'https://w3id.org/security#', - holder: { '@id': 'cred:holder', '@type': '@id' }, - proof: { '@id': 'sec:proof', '@type': '@id', '@container': '@graph' }, - verifiableCredential: { '@id': 'cred:verifiableCredential', '@type': '@id', '@container': '@graph' } + holder: { + '@id': 'cred:holder', + '@type': '@id' + }, + proof: { + '@id': 'sec:proof', + '@type': '@id', + '@container': '@graph' + }, + verifiableCredential: { + '@id': 'cred:verifiableCredential', + '@type': '@id', + '@container': '@graph' + } } }, @@ -120,9 +184,15 @@ export const CONTEXTS = { xsd: 'http://www.w3.org/2001/XMLSchema#', challenge: 'sec:challenge', - created: { '@id': 'http://purl.org/dc/terms/created', '@type': 'xsd:dateTime' }, + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime' + }, domain: 'sec:domain', - expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { + '@id': 'sec:expiration', + '@type': 'xsd:dateTime' + }, jws: 'sec:jws', nonce: 'sec:nonce', proofPurpose: { @@ -136,12 +206,23 @@ export const CONTEXTS = { sec: 'https://w3id.org/security#', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - authentication: { '@id': 'sec:authenticationMethod', '@type': '@id', '@container': '@set' } + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set' + } } }, proofValue: 'sec:proofValue', - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' } + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + } } }, @@ -157,9 +238,15 @@ export const CONTEXTS = { xsd: 'http://www.w3.org/2001/XMLSchema#', challenge: 'sec:challenge', - created: { '@id': 'http://purl.org/dc/terms/created', '@type': 'xsd:dateTime' }, + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime' + }, domain: 'sec:domain', - expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { + '@id': 'sec:expiration', + '@type': 'xsd:dateTime' + }, jws: 'sec:jws', nonce: 'sec:nonce', proofPurpose: { @@ -173,12 +260,23 @@ export const CONTEXTS = { sec: 'https://w3id.org/security#', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - authentication: { '@id': 'sec:authenticationMethod', '@type': '@id', '@container': '@set' } + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set' + } } }, proofValue: 'sec:proofValue', - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' } + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + } } }, @@ -194,9 +292,15 @@ export const CONTEXTS = { xsd: 'http://www.w3.org/2001/XMLSchema#', challenge: 'sec:challenge', - created: { '@id': 'http://purl.org/dc/terms/created', '@type': 'xsd:dateTime' }, + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime' + }, domain: 'sec:domain', - expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { + '@id': 'sec:expiration', + '@type': 'xsd:dateTime' + }, jws: 'sec:jws', nonce: 'sec:nonce', proofPurpose: { @@ -210,12 +314,23 @@ export const CONTEXTS = { sec: 'https://w3id.org/security#', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - authentication: { '@id': 'sec:authenticationMethod', '@type': '@id', '@container': '@set' } + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set' + } } }, proofValue: 'sec:proofValue', - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' } + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + } } }, @@ -225,9 +340,15 @@ export const CONTEXTS = { '@version': 1.1, challenge: 'sec:challenge', - created: { '@id': 'http://purl.org/dc/terms/created', '@type': 'xsd:dateTime' }, + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime' + }, domain: 'sec:domain', - expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { + '@id': 'sec:expiration', + '@type': 'xsd:dateTime' + }, jws: 'sec:jws', nonce: 'sec:nonce', proofPurpose: { @@ -241,16 +362,31 @@ export const CONTEXTS = { sec: 'https://w3id.org/security#', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - authentication: { '@id': 'sec:authenticationMethod', '@type': '@id', '@container': '@set' } + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set' + } } }, proofValue: 'sec:proofValue', - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' } + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + } } }, - proof: { '@id': 'https://w3id.org/security#proof', '@type': '@id', '@container': '@graph' } + proof: { + '@id': 'https://w3id.org/security#proof', + '@type': '@id', + '@container': '@graph' + } } }, verifiableCredentialExample: { @@ -282,22 +418,37 @@ export const CONTEXTS = { primaryProof: 'ex:primaryProof', nonRevocationProof: 'ex:nonRevocationProof', - alumniOf: { '@id': 'schema:alumniOf', '@type': 'rdf:HTML' }, - child: { '@id': 'ex:child', '@type': '@id' }, + alumniOf: { + '@id': 'schema:alumniOf', + '@type': 'rdf:HTML' + }, + child: { + '@id': 'ex:child', + '@type': '@id' + }, degree: 'ex:degree', degreeType: 'ex:degreeType', degreeSchool: 'ex:degreeSchool', college: 'ex:college', - name: { '@id': 'schema:name', '@type': 'rdf:HTML' }, + name: { + '@id': 'schema:name', + '@type': 'rdf:HTML' + }, givenName: 'schema:givenName', familyName: 'schema:familyName', - parent: { '@id': 'ex:parent', '@type': '@id' }, + parent: { + '@id': 'ex:parent', + '@type': '@id' + }, referenceId: 'ex:referenceId', documentPresence: 'ex:documentPresence', evidenceDocument: 'ex:evidenceDocument', spouse: 'schema:spouse', subjectPresence: 'ex:subjectPresence', - verifier: { '@id': 'ex:verifier', '@type': '@id' } + verifier: { + '@id': 'ex:verifier', + '@type': '@id' + } }] }, merkleProof2019: { @@ -305,9 +456,15 @@ export const CONTEXTS = { '@context': { '@version': 1.1, challenge: 'sec:challenge', - created: { '@id': 'http://purl.org/dc/terms/created', '@type': 'xsd:dateTime' }, + created: { + '@id': 'http://purl.org/dc/terms/created', + '@type': 'xsd:dateTime' + }, domain: 'sec:domain', - expires: { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + expires: { + '@id': 'sec:expiration', + '@type': 'xsd:dateTime' + }, jws: 'sec:jws', nonce: 'sec:nonce', proofPurpose: { @@ -321,12 +478,327 @@ export const CONTEXTS = { sec: 'https://w3id.org/security#', - assertionMethod: { '@id': 'sec:assertionMethod', '@type': '@id', '@container': '@set' }, - authentication: { '@id': 'sec:authenticationMethod', '@type': '@id', '@container': '@set' } + assertionMethod: { + '@id': 'sec:assertionMethod', + '@type': '@id', + '@container': '@set' + }, + authentication: { + '@id': 'sec:authenticationMethod', + '@type': '@id', + '@container': '@set' + } } }, proofValue: 'sec:proofValue', - verificationMethod: { '@id': 'sec:verificationMethod', '@type': '@id' } + verificationMethod: { + '@id': 'sec:verificationMethod', + '@type': '@id' + } + } + }, + odrl: { + '@context': { + odrl: 'http://www.w3.org/ns/odrl/2/', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + rdfs: 'http://www.w3.org/2000/01/rdf-schema#', + owl: 'http://www.w3.org/2002/07/owl#', + skos: 'http://www.w3.org/2004/02/skos/core#', + dct: 'http://purl.org/dc/terms/', + xsd: 'http://www.w3.org/2001/XMLSchema#', + vcard: 'http://www.w3.org/2006/vcard/ns#', + foaf: 'http://xmlns.com/foaf/0.1/', + schema: 'http://schema.org/', + cc: 'http://creativecommons.org/ns#', + uid: '@id', + type: '@type', + Policy: 'odrl:Policy', + Rule: 'odrl:Rule', + profile: { + '@type': '@id', + '@id': 'odrl:profile' + }, + inheritFrom: { + '@type': '@id', + '@id': 'odrl:inheritFrom' + }, + ConflictTerm: 'odrl:ConflictTerm', + conflict: { + '@type': '@vocab', + '@id': 'odrl:conflict' + }, + perm: 'odrl:perm', + prohibit: 'odrl:prohibit', + invalid: 'odrl:invalid', + Agreement: 'odrl:Agreement', + Assertion: 'odrl:Assertion', + Offer: 'odrl:Offer', + Privacy: 'odrl:Privacy', + Request: 'odrl:Request', + Set: 'odrl:Set', + Ticket: 'odrl:Ticket', + + Asset: 'odrl:Asset', + AssetCollection: 'odrl:AssetCollection', + relation: { + '@type': '@id', + '@id': 'odrl:relation' + }, + hasPolicy: { + '@type': '@id', + '@id': 'odrl:hasPolicy' + }, + target: { + '@type': '@id', + '@id': 'odrl:target' + }, + output: { + '@type': '@id', + '@id': 'odrl:output' + }, + partOf: { + '@type': '@id', + '@id': 'odrl:partOf' + }, + source: { + '@type': '@id', + '@id': 'odrl:source' + }, + Party: 'odrl:Party', + PartyCollection: 'odrl:PartyCollection', + function: { + '@type': '@vocab', + '@id': 'odrl:function' + }, + PartyScope: 'odrl:PartyScope', + assignee: { + '@type': '@id', + '@id': 'odrl:assignee' + }, + assigner: { + '@type': '@id', + '@id': 'odrl:assigner' + }, + assigneeOf: { + '@type': '@id', + '@id': 'odrl:assigneeOf' + }, + assignerOf: { + '@type': '@id', + '@id': 'odrl:assignerOf' + }, + attributedParty: { + '@type': '@id', + '@id': 'odrl:attributedParty' + }, + attributingParty: { + '@type': '@id', + '@id': 'odrl:attributingParty' + }, + compensatedParty: { + '@type': '@id', + '@id': 'odrl:compensatedParty' + }, + compensatingParty: { + '@type': '@id', + '@id': 'odrl:compensatingParty' + }, + consentingParty: { + '@type': '@id', + '@id': 'odrl:consentingParty' + }, + consentedParty: { + '@type': '@id', + '@id': 'odrl:consentedParty' + }, + informedParty: { + '@type': '@id', + '@id': 'odrl:informedParty' + }, + informingParty: { + '@type': '@id', + '@id': 'odrl:informingParty' + }, + trackingParty: { + '@type': '@id', + '@id': 'odrl:trackingParty' + }, + trackedParty: { + '@type': '@id', + '@id': 'odrl:trackedParty' + }, + contractingParty: { + '@type': '@id', + '@id': 'odrl:contractingParty' + }, + contractedParty: { + '@type': '@id', + '@id': 'odrl:contractedParty' + }, + Action: 'odrl:Action', + action: { + '@type': '@vocab', + '@id': 'odrl:action' + }, + includedIn: { + '@type': '@id', + '@id': 'odrl:includedIn' + }, + implies: { + '@type': '@id', + '@id': 'odrl:implies' + }, + Permission: 'odrl:Permission', + permission: { + '@type': '@id', + '@id': 'odrl:permission' + }, + Prohibition: 'odrl:Prohibition', + prohibition: { + '@type': '@id', + '@id': 'odrl:prohibition' + }, + obligation: { + '@type': '@id', + '@id': 'odrl:obligation' + }, + use: 'odrl:use', + grantUse: 'odrl:grantUse', + aggregate: 'odrl:aggregate', + annotate: 'odrl:annotate', + anonymize: 'odrl:anonymize', + archive: 'odrl:archive', + concurrentUse: 'odrl:concurrentUse', + derive: 'odrl:derive', + digitize: 'odrl:digitize', + display: 'odrl:display', + distribute: 'odrl:distribute', + execute: 'odrl:execute', + extract: 'odrl:extract', + give: 'odrl:give', + index: 'odrl:index', + install: 'odrl:install', + modify: 'odrl:modify', + move: 'odrl:move', + play: 'odrl:play', + present: 'odrl:present', + print: 'odrl:print', + read: 'odrl:read', + reproduce: 'odrl:reproduce', + sell: 'odrl:sell', + stream: 'odrl:stream', + textToSpeech: 'odrl:textToSpeech', + transfer: 'odrl:transfer', + transform: 'odrl:transform', + translate: 'odrl:translate', + + Duty: 'odrl:Duty', + duty: { + '@type': '@id', + '@id': 'odrl:duty' + }, + consequence: { + '@type': '@id', + '@id': 'odrl:consequence' + }, + remedy: { + '@type': '@id', + '@id': 'odrl:remedy' + }, + acceptTracking: 'odrl:acceptTracking', + attribute: 'odrl:attribute', + compensate: 'odrl:compensate', + delete: 'odrl:delete', + ensureExclusivity: 'odrl:ensureExclusivity', + include: 'odrl:include', + inform: 'odrl:inform', + nextPolicy: 'odrl:nextPolicy', + obtainConsent: 'odrl:obtainConsent', + reviewPolicy: 'odrl:reviewPolicy', + uninstall: 'odrl:uninstall', + watermark: 'odrl:watermark', + + Constraint: 'odrl:Constraint', + LogicalConstraint: 'odrl:LogicalConstraint', + constraint: { + '@type': '@id', + '@id': 'odrl:constraint' + }, + refinement: { + '@type': '@id', + '@id': 'odrl:refinement' + }, + Operator: 'odrl:Operator', + operator: { + '@type': '@vocab', + '@id': 'odrl:operator' + }, + RightOperand: 'odrl:RightOperand', + rightOperand: 'odrl:rightOperand', + rightOperandReference: { + '@type': 'xsd:anyURI', + '@id': 'odrl:rightOperandReference' + }, + LeftOperand: 'odrl:LeftOperand', + leftOperand: { + '@type': '@vocab', + '@id': 'odrl:leftOperand' + }, + unit: 'odrl:unit', + dataType: { + '@type': 'xsd:anyType', + '@id': 'odrl:datatype' + }, + status: 'odrl:status', + absolutePosition: 'odrl:absolutePosition', + absoluteSpatialPosition: 'odrl:absoluteSpatialPosition', + absoluteTemporalPosition: 'odrl:absoluteTemporalPosition', + absoluteSize: 'odrl:absoluteSize', + count: 'odrl:count', + dateTime: 'odrl:dateTime', + delayPeriod: 'odrl:delayPeriod', + deliveryChannel: 'odrl:deliveryChannel', + elapsedTime: 'odrl:elapsedTime', + event: 'odrl:event', + fileFormat: 'odrl:fileFormat', + industry: 'odrl:industry:', + language: 'odrl:language', + media: 'odrl:media', + meteredTime: 'odrl:meteredTime', + payAmount: 'odrl:payAmount', + percentage: 'odrl:percentage', + product: 'odrl:product', + purpose: 'odrl:purpose', + recipient: 'odrl:recipient', + relativePosition: 'odrl:relativePosition', + relativeSpatialPosition: 'odrl:relativeSpatialPosition', + relativeTemporalPosition: 'odrl:relativeTemporalPosition', + relativeSize: 'odrl:relativeSize', + resolution: 'odrl:resolution', + spatial: 'odrl:spatial', + spatialCoordinates: 'odrl:spatialCoordinates', + systemDevice: 'odrl:systemDevice', + timeInterval: 'odrl:timeInterval', + unitOfCount: 'odrl:unitOfCount', + version: 'odrl:version', + virtualLocation: 'odrl:virtualLocation', + eq: 'odrl:eq', + gt: 'odrl:gt', + gteq: 'odrl:gteq', + lt: 'odrl:lt', + lteq: 'odrl:lteq', + neq: 'odrl:neg', + isA: 'odrl:isA', + hasPart: 'odrl:hasPart', + isPartOf: 'odrl:isPartOf', + isAllOf: 'odrl:isAllOf', + isAnyOf: 'odrl:isAnyOf', + isNoneOf: 'odrl:isNoneOf', + or: 'odrl:or', + xone: 'odrl:xone', + and: 'odrl:and', + andSequence: 'odrl:andSequence', + policyUsage: 'odrl:policyUsage' } } }; diff --git a/src/inspectors/computeLocalHash.ts b/src/inspectors/computeLocalHash.ts index f2a7fa8..b1cda85 100644 --- a/src/inspectors/computeLocalHash.ts +++ b/src/inspectors/computeLocalHash.ts @@ -7,7 +7,8 @@ const { blockcertsV3: BLOCKCERTSV3_CONTEXT, verifiableCredential: VERIFIABLE_CREDENTIAL_CONTEXT, verifiableCredentialExample: VERIFIABLE_CREDENTIAL_EXAMPLE, - merkleProof2019: MERKLE_PROOF_2019 + merkleProof2019: MERKLE_PROOF_2019, + odrl: OPEN_DIGITALS_RIGHTS_LANGUAGE } = ContextsMap; const CONTEXTS = {}; @@ -17,6 +18,7 @@ CONTEXTS['https://www.w3.org/2018/credentials/v1'] = VERIFIABLE_CREDENTIAL_CONTE CONTEXTS['https://www.w3.org/2018/credentials/examples/v1'] = VERIFIABLE_CREDENTIAL_EXAMPLE; CONTEXTS['https://www.w3id.org/blockcerts/schema/3.0-alpha/merkleProof2019Context.json'] = MERKLE_PROOF_2019; CONTEXTS['https://www.blockcerts.org/schema/3.0-alpha/merkleProof2019Context.json'] = MERKLE_PROOF_2019; +CONTEXTS['https://www.w3.org/ns/odrl.jsonld'] = OPEN_DIGITALS_RIGHTS_LANGUAGE; function setJsonLdDocumentLoader (): any { // not typed by jsonld if (typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined') { @@ -37,8 +39,6 @@ export default async function computeLocalHash (document: any): Promise const jsonldDocumentLoader = setJsonLdDocumentLoader(); const customLoader = function (url, callback): any { // Not typed by JSONLD - console.log(url); - if (url in CONTEXTS) { return callback(null, { contextUrl: null, @@ -59,7 +59,6 @@ export default async function computeLocalHash (document: any): Promise return new Promise((resolve, reject) => { jsonld.normalize(theDocument, normalizeArgs, (err, normalized) => { - console.log('normalize error:', err); const isErr = !!err; if (isErr) { reject(