diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a27fd79..9313b78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,10 @@ jobs: matrix: os: [ubuntu-latest] node-version: - - 12.x - 14.x - 16.x - 18.x + - 20.x steps: - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 diff --git a/.gitignore b/.gitignore index 31033ab..d4fcda7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ yarn-error.log **/index.d.ts coverage documentation +perf/output.txt diff --git a/README.md b/README.md index eef1ad0..5e93010 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,18 @@ This library exposes many operations that are useful to parse and handle a JSON- For this, the static functions on [`Util`](https://github.com/rubensworks/jsonld-context-parser.js/blob/master/lib/Util.ts) and [`ContextParser`](https://github.com/rubensworks/jsonld-context-parser.js/blob/master/lib/ContextParser.ts) can be used. +##### Context Caching + +This library supports the ability to cache context entry calculations and share them between multiple context parsers. This can be done as follows: + +```ts +import { ContextCache, ContextParser } from 'jsonld-context-parser'; + +const contextCache = new ContextCache(); +const contextParser1 = new ContextParser({ contextCache }); +const contextParser2 = new ContextParser({ contextCache }); +``` + ## Command-line A command-line tool is provided to quickly normalize any context by URL, file or string. diff --git a/index.ts b/index.ts index 04deb76..d0c00a7 100644 --- a/index.ts +++ b/index.ts @@ -5,3 +5,5 @@ export * from './lib/IDocumentLoader'; export * from './lib/JsonLdContext'; export * from './lib/JsonLdContextNormalized'; export * from './lib/Util'; +export * from './lib/ContextCache'; +export * from './lib/IContextCache'; diff --git a/lib/ContextCache.ts b/lib/ContextCache.ts new file mode 100644 index 0000000..da72be4 --- /dev/null +++ b/lib/ContextCache.ts @@ -0,0 +1,46 @@ +import { JsonLdContextNormalized } from "./JsonLdContextNormalized"; +import { JsonLdContext } from "./JsonLdContext"; +import md5 = require("md5"); +import { IParseOptions } from "./ContextParser"; +import { IContextCache } from "./IContextCache"; +import { LRUCache } from "lru-cache"; + +function hashOptions(options: IParseOptions | undefined) { + const opts = { ...options, parentContext: undefined }; + for (const key of Object.keys(opts)) { + if (typeof opts[key as keyof typeof opts] === "undefined") { + delete opts[key as keyof typeof opts]; + } + } + + return md5(JSON.stringify(opts, Object.keys(opts).sort())); +} + +export class ContextCache implements IContextCache { + private cachedParsing: LRUCache>; + + constructor(options?: LRUCache.Options, unknown>) { + this.cachedParsing = new LRUCache(options ?? { max: 512 }) + } + + public hash( + context: JsonLdContext, + options?: IParseOptions + ): string { + let hash = hashOptions(options); + + if (options?.parentContext && Object.keys(options.parentContext).length !== 0) { + hash = md5(hash + md5(JSON.stringify(context))); + } + + return md5(hash + md5(JSON.stringify(context))); + } + + get(context: string): Promise | undefined { + return this.cachedParsing.get(context); + } + + set(context: string, normalized: Promise): void { + this.cachedParsing.set(context, normalized); + } +} diff --git a/lib/ContextParser.ts b/lib/ContextParser.ts index 64fe917..5c4ebd9 100644 --- a/lib/ContextParser.ts +++ b/lib/ContextParser.ts @@ -6,6 +6,8 @@ import {IDocumentLoader} from "./IDocumentLoader"; import {IJsonLdContext, IJsonLdContextNormalizedRaw, IPrefixValue, JsonLdContext} from "./JsonLdContext"; import {JsonLdContextNormalized, defaultExpandOptions, IExpandOptions} from "./JsonLdContextNormalized"; import {Util} from "./Util"; +import { IContextCache } from './IContextCache'; +import { ContextCache } from './ContextCache'; // tslint:disable-next-line:no-var-requires const canonicalizeJson = require('canonicalize'); @@ -23,10 +25,12 @@ export class ContextParser { private readonly expandContentTypeToBase: boolean; private readonly remoteContextsDepthLimit: number; private readonly redirectSchemaOrgHttps: boolean; + private readonly contextCache?: IContextCache; constructor(options?: IContextParserOptions) { options = options || {}; this.documentLoader = options.documentLoader || new FetchDocumentLoader(); + this.contextCache = options.contextCache; this.documentCache = {}; this.validateContext = !options.skipValidation; this.expandContentTypeToBase = !!options.expandContentTypeToBase; @@ -610,14 +614,14 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP const parentContext = {...context}; parentContext[key] = {...parentContext[key]}; delete parentContext[key]['@context']; - await this.parse(value['@context'], + await this._parse(value['@context'], { ...options, external: false, parentContext, ignoreProtection: true, ignoreRemoteScopedContexts: true, ignoreScopedContexts: true }); } catch (e) { throw new ErrorCoded(e.message, ERROR_CODES.INVALID_SCOPED_CONTEXT); } } - value['@context'] = (await this.parse(value['@context'], + value['@context'] = (await this._parse(value['@context'], { ...options, external: false, minimalProcessing: true, ignoreRemoteScopedContexts: true, parentContext: context })) .getContextRaw(); } @@ -633,7 +637,105 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP * @return {Promise} A promise resolving to the context. */ public async parse(context: JsonLdContext, - options: IParseOptions = {}): Promise { + options: IParseOptions = {}): Promise { + if (!this.contextCache) + return this._parse(context, options); + + const hash = this.contextCache.hash(context, options); + const cached = this.contextCache.get(hash); + if (cached) + return cached; + + const parsed = this._parse(context, options); + this.contextCache.set(hash, parsed); + return parsed; + } + + /** + * Fetch the given URL as a raw JSON-LD context. + * @param url An URL. + * @return A promise resolving to a raw JSON-LD context. + */ + public async load(url: string): Promise { + // First try to retrieve the context from cache + const cached = this.documentCache[url]; + if (cached) { + return typeof cached === 'string' ? cached : Array.isArray(cached) ? cached.slice() : {... cached}; + } + + // If not in cache, load it + let document: IJsonLdContext; + try { + document = await this.documentLoader.load(url); + } catch (e) { + throw new ErrorCoded(`Failed to load remote context ${url}: ${e.message}`, + ERROR_CODES.LOADING_REMOTE_CONTEXT_FAILED); + } + + // Validate the context + if (!('@context' in document)) { + throw new ErrorCoded(`Missing @context in remote context at ${url}`, + ERROR_CODES.INVALID_REMOTE_CONTEXT); + } + + return this.documentCache[url] = document['@context']; + } + + /** + * Override the given context that may be loaded. + * + * This will check whether or not the url is recursively being loaded. + * @param url An URL. + * @param options Parsing options. + * @return An overridden context, or null. + * Optionally an error can be thrown if a cyclic context is detected. + */ + public getOverriddenLoad(url: string, options: IParseOptions): IJsonLdContextNormalizedRaw | null { + if (url in (options.remoteContexts || {})) { + if (options.ignoreRemoteScopedContexts) { + return url; + } else { + throw new ErrorCoded('Detected a cyclic context inclusion of ' + url, + ERROR_CODES.RECURSIVE_CONTEXT_INCLUSION); + } + } + return null; + } + + /** + * Load an @import'ed context. + * @param importContextIri The full URI of an @import value. + */ + public async loadImportContext(importContextIri: string): Promise { + // Load the context + const importContext = await this.load(importContextIri); + + // Require the context to be a non-array object + if (typeof importContext !== 'object' || Array.isArray(importContext)) { + throw new ErrorCoded('An imported context must be a single object: ' + importContextIri, + ERROR_CODES.INVALID_REMOTE_CONTEXT); + } + + // Error if the context contains another @import + if ('@import' in importContext) { + throw new ErrorCoded('An imported context can not import another context: ' + importContextIri, + ERROR_CODES.INVALID_CONTEXT_ENTRY); + } + + // Containers have to be converted into hash values the same way as for the importing context + // Otherwise context validation will fail for container values + this.containersToHash(importContext); + return importContext; + } + + /** + * Parse a JSON-LD context in any form. + * @param {JsonLdContext} context A context, URL to a context, or an array of contexts/URLs. + * @param {IParseOptions} options Optional parsing options. + * @return {Promise} A promise resolving to the context. + */ + private async _parse(context: JsonLdContext, + options: IParseOptions): Promise { const { baseIRI, parentContext: parentContextInitial, @@ -667,7 +769,7 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP if (overriddenLoad) { return new JsonLdContextNormalized(overriddenLoad); } - const parsedStringContext = await this.parse(await this.load(contextIri), + const parsedStringContext = await this._parse(await this.load(contextIri), { ...options, baseIRI: contextIri, @@ -699,7 +801,7 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP } const reducedContexts = await contexts.reduce((accContextPromise, contextEntry, i) => accContextPromise - .then((accContext) => this.parse(contextEntry, { + .then((accContext) => this._parse(contextEntry, { ...options, baseIRI: contextIris[i] || options.baseIRI, external: !!contextIris[i] || options.external, @@ -714,7 +816,7 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP return reducedContexts; } else if (typeof context === 'object') { if ('@context' in context) { - return await this.parse(context['@context'], options); + return await this._parse(context['@context'], options); } // Make a deep clone of the given context, to avoid modifying it. @@ -807,83 +909,6 @@ must be one of ${Util.CONTAINERS.join(', ')}`, ERROR_CODES.INVALID_CONTAINER_MAP } } - /** - * Fetch the given URL as a raw JSON-LD context. - * @param url An URL. - * @return A promise resolving to a raw JSON-LD context. - */ - public async load(url: string): Promise { - // First try to retrieve the context from cache - const cached = this.documentCache[url]; - if (cached) { - return typeof cached === 'string' ? cached : Array.isArray(cached) ? cached.slice() : {... cached}; - } - - // If not in cache, load it - let document: IJsonLdContext; - try { - document = await this.documentLoader.load(url); - } catch (e) { - throw new ErrorCoded(`Failed to load remote context ${url}: ${e.message}`, - ERROR_CODES.LOADING_REMOTE_CONTEXT_FAILED); - } - - // Validate the context - if (!('@context' in document)) { - throw new ErrorCoded(`Missing @context in remote context at ${url}`, - ERROR_CODES.INVALID_REMOTE_CONTEXT); - } - - return this.documentCache[url] = document['@context']; - } - - /** - * Override the given context that may be loaded. - * - * This will check whether or not the url is recursively being loaded. - * @param url An URL. - * @param options Parsing options. - * @return An overridden context, or null. - * Optionally an error can be thrown if a cyclic context is detected. - */ - public getOverriddenLoad(url: string, options: IParseOptions): IJsonLdContextNormalizedRaw | null { - if (url in (options.remoteContexts || {})) { - if (options.ignoreRemoteScopedContexts) { - return url; - } else { - throw new ErrorCoded('Detected a cyclic context inclusion of ' + url, - ERROR_CODES.RECURSIVE_CONTEXT_INCLUSION); - } - } - return null; - } - - /** - * Load an @import'ed context. - * @param importContextIri The full URI of an @import value. - */ - public async loadImportContext(importContextIri: string): Promise { - // Load the context - const importContext = await this.load(importContextIri); - - // Require the context to be a non-array object - if (typeof importContext !== 'object' || Array.isArray(importContext)) { - throw new ErrorCoded('An imported context must be a single object: ' + importContextIri, - ERROR_CODES.INVALID_REMOTE_CONTEXT); - } - - // Error if the context contains another @import - if ('@import' in importContext) { - throw new ErrorCoded('An imported context can not import another context: ' + importContextIri, - ERROR_CODES.INVALID_CONTEXT_ENTRY); - } - - // Containers have to be converted into hash values the same way as for the importing context - // Otherwise context validation will fail for container values - this.containersToHash(importContext); - return importContext; - } - } export interface IContextParserOptions { @@ -891,6 +916,10 @@ export interface IContextParserOptions { * An optional loader that should be used for fetching external JSON-LD contexts. */ documentLoader?: IDocumentLoader; + /** + * An optional cache for parsed contexts. + */ + contextCache?: IContextCache; /** * By default, JSON-LD contexts will be validated. * This can be disabled by setting this option to true. diff --git a/lib/IContextCache.ts b/lib/IContextCache.ts new file mode 100644 index 0000000..6269c23 --- /dev/null +++ b/lib/IContextCache.ts @@ -0,0 +1,25 @@ +import { JsonLdContextNormalized } from "./JsonLdContextNormalized"; +import { JsonLdContext } from "./JsonLdContext"; +import { IParseOptions } from "./ContextParser"; + +/** + * Caches the normalized version of a JSON-LD context. + */ +export interface IContextCache { + /** + * Returns a cached version of the normalized version of a JSON-LD context. + * @param {string} context A hashed JSON-LD Context. + * @return {Promise | undefined} A promise resolving to a normalized JSON-LD context. + */ + get(context: string): Promise | undefined; + /** + * Stores a cached version of the normalized version of a JSON-LD context. + * @param {string} context A hashed JSON-LD Context. + * @return {Promise} A promise resolving to a normalized JSON-LD context. + */ + set(context: string, normalized: Promise): void; + /** + * Hash a context/options pair into the key to be used for caching the context. + */ + hash(context: JsonLdContext, options: IParseOptions | undefined): string; +} diff --git a/package.json b/package.json index bf3120a..2918f40 100644 --- a/package.json +++ b/package.json @@ -40,13 +40,17 @@ }, "sideEffects": false, "devDependencies": { + "@types/benchmark": "^2.1.4", "@types/jest": "^28.0.0", + "@types/md5": "^2.3.4", + "benchmark": "^2.1.4", "coveralls": "^3.0.0", "jest": "^28.0.1", "manual-git-changelog": "^1.0.0", "pre-commit": "^1.2.2", "ts-jest": "^28.0.1", "ts-loader": "^9.3.1", + "ts-node": "^10.9.1", "tslint": "^6.0.0", "tslint-eslint-rules": "^5.3.1", "typescript": "^5.0.0", @@ -81,7 +85,8 @@ "build-watch": "tsc --watch", "validate": "npm ls", "prepare": "npm run build", - "version": "manual-git-changelog onversion" + "version": "manual-git-changelog onversion", + "perf": "ts-node perf/bench.ts 2>&1 | tee perf/output.txt" }, "dependencies": { "@types/http-link-header": "^1.0.1", @@ -89,6 +94,8 @@ "canonicalize": "^1.0.1", "cross-fetch": "^3.0.6", "http-link-header": "^1.0.2", + "lru-cache": "^10.0.1", + "md5": "^2.3.0", "relative-to-absolute-iri": "^1.0.5" } } diff --git a/perf/bench.ts b/perf/bench.ts new file mode 100644 index 0000000..16fe5d5 --- /dev/null +++ b/perf/bench.ts @@ -0,0 +1,63 @@ +import { type Options, type Event, Suite } from 'benchmark'; +import contexts from './contexts'; +import { IDocumentLoader, IJsonLdContext, ContextParser, ContextCache } from '..' + +class CachedDocumentLoader implements IDocumentLoader { + load(url: string): Promise { + if (!contexts[url as keyof typeof contexts]) + return Promise.reject(new Error(`No context for ${url}`)); + + return Promise.resolve(contexts[url as keyof typeof contexts]); + } +} + +function deferred(fn: () => Promise): Options { + return { + defer: true, + fn: (deferred: { resolve: () => void }) => fn().then(() => deferred.resolve()) + } +} + +async function main() { + + const suite = new Suite(); + + const contextCache = new ContextCache(); + const context = Object.keys(contexts); + const contextObject = contexts[context[0] as keyof typeof contexts]; + const contextParser = new ContextParser({ documentLoader: new CachedDocumentLoader(), contextCache: new ContextCache() }); + await contextParser.parse(context); + await contextParser.parse(contextObject); + + // add tests + suite + .add( + 'Parse a context that has not been cached; and without caching in place', + deferred(async () => { + const contextParser = new ContextParser({ documentLoader: new CachedDocumentLoader() }); + await contextParser.parse(context); + }), + ).add( + 'Parse a list of iri contexts that have been cached', + deferred(async () => { + const contextParser = new ContextParser({ documentLoader: new CachedDocumentLoader(), contextCache }); + await contextParser.parse(context); + }), + ).add( + 'Parse a context object that has not been cached', + deferred(async () => { + const contextParser = new ContextParser({ documentLoader: new CachedDocumentLoader() }); + await contextParser.parse(contextObject); + }), + ).add( + 'Parse a context object that has been cached', + deferred(async () => { + const contextParser = new ContextParser({ documentLoader: new CachedDocumentLoader(), contextCache }); + await contextParser.parse(contextObject); + }), + ).on('cycle', (event: Event) => { + console.log(event.target.toString()); + }).run(); +} + +main(); diff --git a/perf/contexts/data-integrity.ts b/perf/contexts/data-integrity.ts new file mode 100644 index 0000000..c989876 --- /dev/null +++ b/perf/contexts/data-integrity.ts @@ -0,0 +1,94 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@context": { + id: "@id", + type: "@type", + "@protected": true, + proof: { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph", + }, + DataIntegrityProof: { + "@id": "https://w3id.org/security#DataIntegrityProof", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + challenge: "https://w3id.org/security#challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + domain: "https://w3id.org/security#domain", + expires: { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + nonce: "https://w3id.org/security#nonce", + proofPurpose: { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + assertionMethod: { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set", + }, + authentication: { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityInvocation: { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityDelegation: { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set", + }, + keyAgreement: { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set", + }, + }, + }, + cryptosuite: "https://w3id.org/security#cryptosuite", + proofValue: { + "@id": "https://w3id.org/security#proofValue", + "@type": "https://w3id.org/security#multibase", + }, + verificationMethod: { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id", + }, + }, + }, + }, +}; diff --git a/perf/contexts/ed25519-2020.ts b/perf/contexts/ed25519-2020.ts new file mode 100644 index 0000000..b65eb59 --- /dev/null +++ b/perf/contexts/ed25519-2020.ts @@ -0,0 +1,113 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@context": { + id: "@id", + type: "@type", + "@protected": true, + proof: { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph", + }, + Ed25519VerificationKey2020: { + "@id": "https://w3id.org/security#Ed25519VerificationKey2020", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + controller: { + "@id": "https://w3id.org/security#controller", + "@type": "@id", + }, + revoked: { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + publicKeyMultibase: { + "@id": "https://w3id.org/security#publicKeyMultibase", + "@type": "https://w3id.org/security#multibase", + }, + }, + }, + Ed25519Signature2020: { + "@id": "https://w3id.org/security#Ed25519Signature2020", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + challenge: "https://w3id.org/security#challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + domain: "https://w3id.org/security#domain", + expires: { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + }, + nonce: "https://w3id.org/security#nonce", + proofPurpose: { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + id: "@id", + type: "@type", + assertionMethod: { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set", + }, + authentication: { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityInvocation: { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set", + }, + capabilityDelegation: { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set", + }, + keyAgreement: { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set", + }, + }, + }, + proofValue: { + "@id": "https://w3id.org/security#proofValue", + "@type": "https://w3id.org/security#multibase", + }, + verificationMethod: { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id", + }, + }, + }, + }, +}; diff --git a/perf/contexts/index.ts b/perf/contexts/index.ts new file mode 100644 index 0000000..c398881 --- /dev/null +++ b/perf/contexts/index.ts @@ -0,0 +1,38 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +import VC from "./vc"; +import Inrupt from "./inrupt"; +import vc from "./inrupt-vc"; +import integrity from "./data-integrity"; +import ed25519 from "./ed25519-2020"; +import revocation from "./revocation-list"; +import statusList from "./status-list"; +import odrl from "./odrl"; + +export default { + "https://www.w3.org/2018/credentials/v1": VC, + "https://schema.inrupt.com/credentials/v1.jsonld": Inrupt, + "https://vc.inrupt.com/credentials/v1": vc, + "https://w3id.org/security/data-integrity/v1": integrity, + "https://w3id.org/vc-revocation-list-2020/v1": revocation, + "https://w3id.org/vc/status-list/2021/v1": statusList, + "https://w3id.org/security/suites/ed25519-2020/v1": ed25519, +} as const; diff --git a/perf/contexts/inrupt-vc.ts b/perf/contexts/inrupt-vc.ts new file mode 100644 index 0000000..56ed156 --- /dev/null +++ b/perf/contexts/inrupt-vc.ts @@ -0,0 +1,84 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@context": { + "@version": 1.1, + "@protected": true, + ldp: "http://www.w3.org/ns/ldp#", + acl: "http://www.w3.org/ns/auth/acl#", + gc: "https://w3id.org/GConsent#", + vc: "http://www.w3.org/ns/solid/vc#", + xsd: "http://www.w3.org/2001/XMLSchema#", + issuerService: { "@id": "vc:issuerService", "@type": "@id" }, + statusService: { "@id": "vc:statusService", "@type": "@id" }, + verifierService: { "@id": "vc:verifierService", "@type": "@id" }, + derivationService: { "@id": "vc:derivationService", "@type": "@id" }, + proofService: { "@id": "vc:proofService", "@type": "@id" }, + availabilityService: { "@id": "vc:availabilityService", "@type": "@id" }, + submissionService: { "@id": "vc:submissionService", "@type": "@id" }, + supportedSignatureTypes: { + "@id": "vc:supportedSignatureTypes", + "@type": "@id", + }, + include: { "@id": "vc:include", "@type": "@id" }, + SolidAccessGrant: "vc:SolidAccessGrant", + SolidAccessRequest: "vc:SolidAccessRequest", + ExpiredVerifiableCredential: "vc:ExpiredVerifiableCredential", + inbox: { "@id": "ldp:inbox", "@type": "@id" }, + Read: "acl:Read", + Write: "acl:Write", + Append: "acl:Append", + mode: { "@id": "acl:mode", "@type": "@vocab" }, + Consent: "gc:Consent", + ConsentStatusExpired: "gc:ConsentStatusExpired", + ConsentStatusExplicitlyGiven: "gc:ConsentStatusExplicitlyGiven", + ConsentStatusGivenByDelegation: "gc:ConsentStatusGivenByDelegation", + ConsentStatusImplicitlyGiven: "gc:ConsentStatusImplicitlyGiven", + ConsentStatusInvalidated: "gc:ConsentStatusInvalidated", + ConsentStatusNotGiven: "gc:ConsentStatusNotGiven", + ConsentStatusRefused: "gc:ConsentStatusRefused", + ConsentStatusRequested: "gc:ConsentStatusRequested", + ConsentStatusUnknown: "gc:ConsentStatusUnknown", + ConsentStatusWithdrawn: "gc:ConsentStatusWithdrawn", + forPersonalData: { "@id": "gc:forPersonalData", "@type": "@id" }, + forProcessing: { "@id": "gc:forProcessing", "@type": "@id" }, + forPurpose: { "@id": "gc:forPurpose", "@type": "@id" }, + hasConsent: { "@id": "gc:hasConsent", "@type": "@id" }, + hasContext: { "@id": "gc:hasContext", "@type": "@id" }, + hasStatus: { "@id": "gc:hasStatus", "@type": "@vocab" }, + inMedium: { "@id": "gc:inMedium", "@type": "@id" }, + isConsentForDataSubject: { + "@id": "gc:isConsentForDataSubject", + "@type": "@id", + }, + isProvidedTo: { "@id": "gc:isProvidedTo", "@type": "@id" }, + isProvidedToPerson: { "@id": "gc:isProvidedToPerson", "@type": "@id" }, + isProvidedToController: { + "@id": "gc:isProvidedToController", + "@type": "@id", + }, + providedConsent: { "@id": "gc:providedConsent", "@type": "@id" }, + inherit: { + "@id": "urn:uuid:71ab2f68-a68b-4452-b968-dd23e0570227", + "@type": "xsd:boolean", + }, + }, +}; diff --git a/perf/contexts/inrupt.ts b/perf/contexts/inrupt.ts new file mode 100644 index 0000000..51e2841 --- /dev/null +++ b/perf/contexts/inrupt.ts @@ -0,0 +1,149 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +/** + * The custom Inrupt context for verifiable credentials + * @see https://schema.inrupt.com/credentials/v1.jsonld + */ +export default { + "@context": { + "@version": 1.1, + "@protected": true, + ldp: "http://www.w3.org/ns/ldp#", + acl: "http://www.w3.org/ns/auth/acl#", + gc: "https://w3id.org/GConsent#", + vc: "http://www.w3.org/ns/solid/vc#", + xsd: "http://www.w3.org/2001/XMLSchema#", + issuerService: { + "@id": "vc:issuerService", + "@type": "@id", + }, + statusService: { + "@id": "vc:statusService", + "@type": "@id", + }, + verifierService: { + "@id": "vc:verifierService", + "@type": "@id", + }, + derivationService: { + "@id": "vc:derivationService", + "@type": "@id", + }, + proofService: { + "@id": "vc:proofService", + "@type": "@id", + }, + availabilityService: { + "@id": "vc:availabilityService", + "@type": "@id", + }, + submissionService: { + "@id": "vc:submissionService", + "@type": "@id", + }, + supportedSignatureTypes: { + "@id": "vc:supportedSignatureTypes", + "@type": "@id", + }, + include: { + "@id": "vc:include", + "@type": "@id", + }, + SolidAccessGrant: "vc:SolidAccessGrant", + SolidAccessRequest: "vc:SolidAccessRequest", + ExpiredVerifiableCredential: "vc:ExpiredVerifiableCredential", + inbox: { + "@id": "ldp:inbox", + "@type": "@id", + }, + Read: "acl:Read", + Write: "acl:Write", + Append: "acl:Append", + mode: { + "@id": "acl:mode", + "@type": "@vocab", + }, + Consent: "gc:Consent", + ConsentStatusExpired: "gc:ConsentStatusExpired", + ConsentStatusExplicitlyGiven: "gc:ConsentStatusExplicitlyGiven", + ConsentStatusGivenByDelegation: "gc:ConsentStatusGivenByDelegation", + ConsentStatusImplicitlyGiven: "gc:ConsentStatusImplicitlyGiven", + ConsentStatusInvalidated: "gc:ConsentStatusInvalidated", + ConsentStatusNotGiven: "gc:ConsentStatusNotGiven", + ConsentStatusRefused: "gc:ConsentStatusRefused", + ConsentStatusRequested: "gc:ConsentStatusRequested", + ConsentStatusUnknown: "gc:ConsentStatusUnknown", + ConsentStatusWithdrawn: "gc:ConsentStatusWithdrawn", + forPersonalData: { + "@id": "gc:forPersonalData", + "@type": "@id", + }, + forProcessing: { + "@id": "gc:forProcessing", + "@type": "@id", + }, + forPurpose: { + "@id": "gc:forPurpose", + "@type": "@id", + }, + hasConsent: { + "@id": "gc:hasConsent", + "@type": "@id", + }, + hasContext: { + "@id": "gc:hasContext", + "@type": "@id", + }, + hasStatus: { + "@id": "gc:hasStatus", + "@type": "@vocab", + }, + inMedium: { + "@id": "gc:inMedium", + "@type": "@id", + }, + isConsentForDataSubject: { + "@id": "gc:isConsentForDataSubject", + "@type": "@id", + }, + isProvidedTo: { + "@id": "gc:isProvidedTo", + "@type": "@id", + }, + isProvidedToPerson: { + "@id": "gc:isProvidedToPerson", + "@type": "@id", + }, + isProvidedToController: { + "@id": "gc:isProvidedToController", + "@type": "@id", + }, + providedConsent: { + "@id": "gc:providedConsent", + "@type": "@id", + }, + inherit: { + "@id": "urn:uuid:71ab2f68-a68b-4452-b968-dd23e0570227", + "@type": "xsd:boolean", + }, + }, +} as const; diff --git a/perf/contexts/odrl.ts b/perf/contexts/odrl.ts new file mode 100644 index 0000000..a236c00 --- /dev/null +++ b/perf/contexts/odrl.ts @@ -0,0 +1,222 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@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/perf/contexts/revocation-list.ts b/perf/contexts/revocation-list.ts new file mode 100644 index 0000000..08a7861 --- /dev/null +++ b/perf/contexts/revocation-list.ts @@ -0,0 +1,68 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@context": { + "@protected": true, + RevocationList2020Credential: { + "@id": + "https://w3id.org/vc-revocation-list-2020#RevocationList2020Credential", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + description: "http://schema.org/description", + name: "http://schema.org/name", + }, + }, + RevocationList2020: { + "@id": "https://w3id.org/vc-revocation-list-2020#RevocationList2020", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + encodedList: "https://w3id.org/vc-revocation-list-2020#encodedList", + }, + }, + + RevocationList2020Status: { + "@id": + "https://w3id.org/vc-revocation-list-2020#RevocationList2020Status", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + revocationListCredential: { + "@id": + "https://w3id.org/vc-revocation-list-2020#revocationListCredential", + "@type": "@id", + }, + revocationListIndex: + "https://w3id.org/vc-revocation-list-2020#revocationListIndex", + }, + }, + }, +}; diff --git a/perf/contexts/status-list.ts b/perf/contexts/status-list.ts new file mode 100644 index 0000000..24653e8 --- /dev/null +++ b/perf/contexts/status-list.ts @@ -0,0 +1,68 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +export default { + "@context": { + "@protected": true, + + StatusList2021Credential: { + "@id": "https://w3id.org/vc/status-list#StatusList2021Credential", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + description: "http://schema.org/description", + name: "http://schema.org/name", + }, + }, + + StatusList2021: { + "@id": "https://w3id.org/vc/status-list#StatusList2021", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + statusPurpose: "https://w3id.org/vc/status-list#statusPurpose", + encodedList: "https://w3id.org/vc/status-list#encodedList", + }, + }, + + StatusList2021Entry: { + "@id": "https://w3id.org/vc/status-list#StatusList2021Entry", + "@context": { + "@protected": true, + + id: "@id", + type: "@type", + + statusPurpose: "https://w3id.org/vc/status-list#statusPurpose", + statusListIndex: "https://w3id.org/vc/status-list#statusListIndex", + statusListCredential: { + "@id": "https://w3id.org/vc/status-list#statusListCredential", + "@type": "@id", + }, + }, + }, + }, +}; diff --git a/perf/contexts/vc.ts b/perf/contexts/vc.ts new file mode 100644 index 0000000..36f6b85 --- /dev/null +++ b/perf/contexts/vc.ts @@ -0,0 +1,275 @@ +// +// Copyright Inrupt Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +/** + * The Verifiable Credentials context. + * @see https://www.w3.org/2018/credentials/v1 + */ +export default { + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + VerifiableCredential: { + "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + cred: "https://www.w3.org/2018/credentials#", + sec: "https://w3id.org/security#", + xsd: "http://www.w3.org/2001/XMLSchema#", + credentialSchema: { + "@id": "cred:credentialSchema", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + cred: "https://www.w3.org/2018/credentials#", + 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" }, + refreshService: { + "@id": "cred:refreshService", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + cred: "https://www.w3.org/2018/credentials#", + ManualRefreshService2018: "cred:ManualRefreshService2018", + }, + }, + termsOfUse: { "@id": "cred:termsOfUse", "@type": "@id" }, + validFrom: { "@id": "cred:validFrom", "@type": "xsd:dateTime" }, + validUntil: { "@id": "cred:validUntil", "@type": "xsd:dateTime" }, + }, + }, + VerifiablePresentation: { + "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + 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", + }, + }, + }, + EcdsaSecp256k1Signature2019: { + "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + xsd: "http://www.w3.org/2001/XMLSchema#", + challenge: "sec:challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime", + }, + domain: "sec:domain", + expires: { "@id": "sec:expiration", "@type": "xsd:dateTime" }, + jws: "sec:jws", + nonce: "sec:nonce", + proofPurpose: { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + 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" }, + }, + }, + EcdsaSecp256r1Signature2019: { + "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + xsd: "http://www.w3.org/2001/XMLSchema#", + challenge: "sec:challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime", + }, + domain: "sec:domain", + expires: { "@id": "sec:expiration", "@type": "xsd:dateTime" }, + jws: "sec:jws", + nonce: "sec:nonce", + proofPurpose: { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + 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" }, + }, + }, + Ed25519Signature2018: { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + xsd: "http://www.w3.org/2001/XMLSchema#", + challenge: "sec:challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime", + }, + domain: "sec:domain", + expires: { "@id": "sec:expiration", "@type": "xsd:dateTime" }, + jws: "sec:jws", + nonce: "sec:nonce", + proofPurpose: { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + 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" }, + }, + }, + RsaSignature2018: { + "@id": "https://w3id.org/security#RsaSignature2018", + "@context": { + "@version": 1.1, + "@protected": true, + challenge: "sec:challenge", + created: { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime", + }, + domain: "sec:domain", + expires: { "@id": "sec:expiration", "@type": "xsd:dateTime" }, + jws: "sec:jws", + nonce: "sec:nonce", + proofPurpose: { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + id: "@id", + type: "@type", + sec: "https://w3id.org/security#", + 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" }, + }, + }, + proof: { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph", + }, + }, +} as const; diff --git a/test/ContextParser-test.ts b/test/ContextParser-test.ts index 148ffe3..9328f55 100644 --- a/test/ContextParser-test.ts +++ b/test/ContextParser-test.ts @@ -1,4 +1,5 @@ import { + ContextCache, ContextParser, ERROR_CODES, ErrorCoded, @@ -1444,19 +1445,64 @@ Tried mapping @id to {}`, ERROR_CODES.KEYWORD_REDEFINITION)); }); describe('parse', () => { - it('should error when parsing a context with an invalid context entry', () => { - return expect(parser.parse({ '@base': true })).rejects + it('should error when parsing a context with an invalid context entry', async () => { + await expect(parser.parse({ '@base': true })).rejects + .toEqual(new ErrorCoded('Found an invalid @base IRI: true', + ERROR_CODES.INVALID_BASE_IRI)); + + // Testing multiple times to make sure that rejection works with caching + await expect(parser.parse({ '@base': true })).rejects .toEqual(new ErrorCoded('Found an invalid @base IRI: true', ERROR_CODES.INVALID_BASE_IRI)); }); - it('should parse an object with direct context values', () => { - return expect(parser.parse({ name: "http://xmlns.com/foaf/0.1/name" })).resolves + it('should parse an object with direct context values', async () => { + await expect(parser.parse({ name: "http://xmlns.com/foaf/0.1/name" })).resolves .toEqual(new JsonLdContextNormalized({ name: "http://xmlns.com/foaf/0.1/name", })); }); + describe('caching parser', () => { + let cachingParser: ContextParser; + + beforeEach(() => { + cachingParser = new ContextParser({ contextCache: new ContextCache() }); + }); + + it('should resolve to the same object when parse is called on the same context', async () => { + await expect(cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" })).resolves + .toBe(await cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" })); + }); + + it('should resolve to the same object when parse is called on the same context and with empty parent', async () => { + await expect(cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" })).resolves + .toEqual(await cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" }, { + parentContext: (await cachingParser.parse({})).getContextRaw(), + })); + }); + + it('should not resolve to the same object when parse is called on the same context and with non-empty parent', async () => { + await expect(cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" })).resolves + .not.toEqual(await cachingParser.parse({ name: "http://xmlns.com/foaf/0.1/name" }, { + parentContext: (await cachingParser.parse({ + name2: "http://xmlns.com/foaf/0.1/name" + })).getContextRaw(), + })); + }); + + it('should respect the LRU cache and discard elements once max is reached', async () => { + const myParser = new ContextParser({ + contextCache: new ContextCache({ max: 1 }), + }); + + const r1 = await myParser.parse({ name: "http://xmlns.com/foaf/0.1/name" }); + await myParser.parse({ name1: "http://xmlns.com/foaf/0.1/name" }); + const r3 = await myParser.parse({ name: "http://xmlns.com/foaf/0.1/name" }); + expect(r1).not.toBe(r3); + }); + }); + it('should parse an object with indirect context values', () => { return expect(parser.parse({ "@context": { name: "http://xmlns.com/foaf/0.1/name" } })).resolves .toEqual(new JsonLdContextNormalized({ diff --git a/tsconfig.json b/tsconfig.json index 05adc83..539d38d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,7 @@ ], "exclude": [ "**/node_modules", - "**/test/*" + "**/test/*", + "**/perf/*" ] } diff --git a/yarn.lock b/yarn.lock index a666a5c..2953c43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -284,6 +284,13 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -543,6 +550,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": version "0.3.14" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" @@ -570,6 +585,26 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + "@types/babel__core@^7.1.14": version "7.1.19" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" @@ -603,6 +638,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/benchmark@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@types/benchmark/-/benchmark-2.1.4.tgz#74f331a07ca5a07a14409ccae7e8072775974361" + integrity sha512-rVCCileCU5NhP9Ix1e03sIn4gd0mpjh7VNULVQAxzF+9vddk6A5QAHzp2h5kXH8pkv1Ow45fUf3QP3wOEiISvA== + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -675,6 +715,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/md5@^2.3.4": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.4.tgz#48f2372f0aed1341c196ebc75eb1e13a00f16fd7" + integrity sha512-e/L4hvpCK8GavKXmP02QlNilZOj8lpmZGGA9QGMMPZjCUoKgi1B4BvhXcbruIi6r+PqzpcjLfda/tocpHFKqDA== + "@types/minimist@^1.2.0": version "1.2.2" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" @@ -865,6 +910,11 @@ acorn-import-assertions@^1.7.6: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== +acorn-walk@^8.1.1: + version "8.3.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" + integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== + acorn@^8.4.1, acorn@^8.5.0: version "8.7.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" @@ -924,6 +974,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1045,6 +1100,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ== + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1169,6 +1232,11 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +charenc@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -1301,6 +1369,11 @@ coveralls@^3.0.0: minimist "^1.2.5" request "^2.88.2" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-fetch@^3.0.6: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" @@ -1326,6 +1399,11 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypt@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -1825,6 +1903,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-buffer@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + is-core-module@^2.5.0, is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" @@ -2426,7 +2509,7 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash@^4.17.15: +lodash@^4.17.15, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -2444,6 +2527,11 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" +lru-cache@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" + integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -2466,7 +2554,7 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@1.x: +make-error@1.x, make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -2504,6 +2592,15 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== +md5@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" + integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== + dependencies: + charenc "0.0.2" + crypt "0.0.2" + is-buffer "~1.1.6" + meow@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.1.tgz#d48598f6f4b1472f35bf6317a95945ace347f975" @@ -2815,6 +2912,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +platform@^1.3.3: + version "1.3.6" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" + integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== + pre-commit@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" @@ -3453,6 +3555,25 @@ ts-loader@^9.3.1: micromatch "^4.0.0" semver "^7.3.4" +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tslib@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" @@ -3577,6 +3698,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-to-istanbul@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" @@ -3795,3 +3921,8 @@ yargs@^17.3.1: string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.0.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==