diff --git a/.eslintrc b/.eslintrc index c2bd0e8b..b5aa9213 100644 --- a/.eslintrc +++ b/.eslintrc @@ -193,7 +193,7 @@ overrides: [ { // Specific rules for test files - files: [ "packages/**/test/**/*-test.js" ], + files: [ "packages/**/test/**/*-test.ts" ], plugins: [ "eslint-plugin-promise" ], rules: { "import/no-extraneous-dependencies": "off", @@ -203,13 +203,13 @@ }, { // TypeScript source files - files: [ "packages/**/*.ts" ], + files: [ "packages/**/*.ts", "test/**/*.ts" ], parser: "@typescript-eslint/parser", extends: [ "plugin:@typescript-eslint/recommended-requiring-type-checking" ], parserOptions: { ecmaVersion: 9, sourceType: "module", - project: "./tsconfig.json" + project: "./tsconfig.eslint.json" }, plugins: [ "@typescript-eslint" ], rules: { diff --git a/package.json b/package.json index 4d82df05..c8ce9aa8 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,18 @@ "workspaces": [ "packages/*" ], + "resolutions": { + "immutable": "^5.1.3" + }, "devDependencies": { "@types/lodash": "^4.17.24", "@types/lru-cache": "^5.1.1", "@types/mime": "^2.0.3", + "@types/n3": "^1.26.3", "@types/node": "^24.0.0", "@types/parse-cache-control": "^1.0.4", "@types/q": "^1.5.8", + "@types/rdf-js": "^4.0.2", "@types/request": "^2.48.13", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", @@ -20,12 +25,12 @@ "eslint-plugin-import": "^2.22.0", "eslint-plugin-promise": "^7.3.0", "lerna": "^4.0.0", + "light-my-request": "^6.6.0", "manual-git-changelog": "^1.0.1", "node-gyp": "^11.0.0", "pre-commit": "^2.0.0", - "rdf-object": "^1.14.0", - "supertest": "^7.0.0", - "typescript": "^5.9.3", + "rdf-object": "^3.0.0", + "typescript": "^6.0.0", "vitest": "^4.1.10" }, "pre-commit": [ @@ -37,8 +42,7 @@ "lint-changed": "lerna run lint --since HEAD", "test": "vitest run --coverage", "test-ci": "vitest run --coverage", - "typecheck": "tsc -p tsconfig.json --noEmit", - "build": "tsc -p tsconfig.json", + "build": "tsc", "lint": "eslint packages/*/bin/* packages/*/lib packages/*/test test --ext .js,.ts", "clean": "rm -rf ./node_modules && rm -rf ./packages/*/node_modules", "dedupe": "npx yarn-deduplicate yarn.lock --scopes", diff --git a/packages/core/lib/datasources/Datasource.ts b/packages/core/lib/datasources/Datasource.ts index ccb4afc1..d8134eea 100644 --- a/packages/core/lib/datasources/Datasource.ts +++ b/packages/core/lib/datasources/Datasource.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import { EventEmitter } from 'events'; import { AsyncIterator, BufferedIterator, empty } from 'asynciterator'; import { stringToTerm } from 'rdf-string'; -import type { DataFactory, Quad, Quad_Graph } from 'rdf-js'; +import type { DataFactory, Quad, Quad_Graph } from '@rdfjs/types'; import { UrlData } from '../UrlData'; import type { DatasourceOptions, Query } from '../types'; import type { CoreOptions, RequestAPI, RequiredUriUrl, Request as RequestT } from 'request'; diff --git a/packages/core/lib/datasources/EmptyDatasource.ts b/packages/core/lib/datasources/EmptyDatasource.ts index f0ce573d..21b52d0e 100644 --- a/packages/core/lib/datasources/EmptyDatasource.ts +++ b/packages/core/lib/datasources/EmptyDatasource.ts @@ -1,7 +1,7 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ /* An empty data source doesn't contain any quads. */ -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import { MemoryDatasource } from './MemoryDatasource'; import type { DatasourceOptions } from '../types'; diff --git a/packages/core/lib/datasources/IndexDatasource.ts b/packages/core/lib/datasources/IndexDatasource.ts index 4dfaa883..53069243 100644 --- a/packages/core/lib/datasources/IndexDatasource.ts +++ b/packages/core/lib/datasources/IndexDatasource.ts @@ -1,7 +1,7 @@ /*! @license MIT ©2014-2016 Ruben Verborgh, Ghent University - imec */ /* An IndexDatasource is a datasource that lists other data sources. */ -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import { MemoryDatasource } from './MemoryDatasource'; import type { DatasourceOptions, DatasourceRegistry } from '../types'; diff --git a/packages/core/lib/datasources/MemoryDatasource.ts b/packages/core/lib/datasources/MemoryDatasource.ts index b10afd1b..53690703 100644 --- a/packages/core/lib/datasources/MemoryDatasource.ts +++ b/packages/core/lib/datasources/MemoryDatasource.ts @@ -2,7 +2,7 @@ /* A MemoryDatasource queries a set of in-memory quads. */ import { Store as N3Store } from 'n3'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import { Datasource } from './Datasource'; import type { DatasourceOptions, Pushable, Query } from '../types'; diff --git a/packages/core/lib/routers/DatasourceRouter.ts b/packages/core/lib/routers/DatasourceRouter.ts index 18393316..1416e98c 100644 --- a/packages/core/lib/routers/DatasourceRouter.ts +++ b/packages/core/lib/routers/DatasourceRouter.ts @@ -2,10 +2,10 @@ /* A DatasourceRouter routes URLs to data sources. */ import { UrlData } from '../UrlData'; -import type { Query, RouterRequest } from '../types'; +import type { Query, Router, RouterRequest } from '../types'; // Creates a new DatasourceRouter -export class DatasourceRouter { +export class DatasourceRouter implements Router { protected _baseLength: number; constructor(options?: { urlData?: UrlData }) { diff --git a/packages/core/lib/routers/PageRouter.ts b/packages/core/lib/routers/PageRouter.ts index 0886e035..0929cee3 100644 --- a/packages/core/lib/routers/PageRouter.ts +++ b/packages/core/lib/routers/PageRouter.ts @@ -1,10 +1,10 @@ /*! @license MIT ©2014-2016 Ruben Verborgh, Ghent University - imec */ /* A PageRouter routes page numbers to offsets */ -import type { Query, RouterRequest } from '../types'; +import type { Query, Router, RouterRequest } from '../types'; // Creates a new PageRouter with the given page size, which defaults to 100. -export class PageRouter { +export class PageRouter implements Router { pageSize: number; constructor(config?: { pageSize?: number }) { diff --git a/packages/core/lib/types.ts b/packages/core/lib/types.ts index 9347ec75..4432791a 100644 --- a/packages/core/lib/types.ts +++ b/packages/core/lib/types.ts @@ -1,7 +1,7 @@ /*! @license MIT ©2026 Ghent University - imec */ /* Shared type definitions for @ldf/core */ -import type { DataFactory, Term } from 'rdf-js'; +import type { DataFactory, Term } from '@rdfjs/types'; import type { IncomingHttpHeaders, IncomingMessage, ServerResponse } from 'http'; import type { ParsedUrlQuery } from 'querystring'; import type { UrlObject } from 'url'; @@ -93,10 +93,15 @@ export interface LdfResponse extends ServerResponse { // The (already-parsed) request shape routers' extractQueryParams receives — // note this is distinct from LdfRequest: callers pass { url: request.parsedUrl, headers }. export interface RouterRequest { - url?: { pathname?: string; query?: ParsedUrlQuery }; + url?: { pathname?: string | null; query?: ParsedUrlQuery }; headers?: IncomingHttpHeaders; } +// A router extracts query parameters from a (partial) request into a query object +export interface Router { + extractQueryParams(request: RouterRequest, query: Query): void; +} + // Options accepted by the Controller base class (and its subclasses) constructor export interface ControllerOptions { urlData?: UrlData; diff --git a/packages/core/lib/views/RdfView.ts b/packages/core/lib/views/RdfView.ts index e4542524..8612dd4a 100644 --- a/packages/core/lib/views/RdfView.ts +++ b/packages/core/lib/views/RdfView.ts @@ -5,7 +5,7 @@ import { View } from './View'; import * as N3 from 'n3'; import { JsonLdSerializer } from 'jsonld-streaming-serializer'; import * as _ from 'lodash'; -import type { DataFactory, Quad } from 'rdf-js'; +import type { DataFactory, Quad } from '@rdfjs/types'; import type { LdfRequest, LdfResponse, RdfViewSettings, RenderDone, ViewSettings } from '../types'; let dcTerms = 'http://purl.org/dc/terms/', diff --git a/packages/core/lib/views/View.ts b/packages/core/lib/views/View.ts index e7035d5d..5554e7cf 100644 --- a/packages/core/lib/views/View.ts +++ b/packages/core/lib/views/View.ts @@ -2,7 +2,7 @@ /* View is a base class for objects that generate server responses. */ import { join } from 'path'; -import type { DataFactory } from 'rdf-js'; +import type { DataFactory } from '@rdfjs/types'; import { ViewCollection } from './ViewCollection'; import type { LdfRequest, LdfResponse, RenderDone, ViewSettings } from '../types'; diff --git a/packages/core/lib/views/error/ErrorRdfView.ts b/packages/core/lib/views/error/ErrorRdfView.ts index 5137a05b..d789cd96 100644 --- a/packages/core/lib/views/error/ErrorRdfView.ts +++ b/packages/core/lib/views/error/ErrorRdfView.ts @@ -2,7 +2,7 @@ /* An ErrorRdfView represents a 500 response in RDF. */ import { RdfView } from '../RdfView'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { RdfViewSettings, RenderDone, ViewSettings } from '../../types'; // Creates a new ErrorRdfView diff --git a/packages/core/lib/views/notfound/NotFoundRdfView.ts b/packages/core/lib/views/notfound/NotFoundRdfView.ts index dc6c9564..e0987358 100644 --- a/packages/core/lib/views/notfound/NotFoundRdfView.ts +++ b/packages/core/lib/views/notfound/NotFoundRdfView.ts @@ -2,7 +2,7 @@ /* A NotFoundRdfView represents a 404 response in RDF. */ import { RdfView } from '../RdfView'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { RdfViewSettings, RenderDone, ViewSettings } from '../../types'; // Creates a new NotFoundRdfView diff --git a/packages/core/package.json b/packages/core/package.json index 9d635f05..4f155332 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,17 +25,18 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "asynciterator": "^3.0.0", + "@rdfjs/types": "^2.0.1", + "asynciterator": "^3.10.0", "componentsjs": "^4.0.4", "forwarded-parse": "^2.1.0", - "jsonld-streaming-serializer": "^3.0.0", + "jsonld-streaming-serializer": "^4.0.0", "lodash": "^4.0.0", "mime": "^2.4.4", - "n3": "^1.10.0", + "n3": "^1.26.0", "negotiate": "^1.0.1", "q": "^1.4.1", "qejs": "^3.0.5", - "rdf-string": "^1.3.1", + "rdf-string": "^1.6.3", "request": "^2.88.2", "uritemplate": "^0.3.4" }, diff --git a/packages/core/test/LinkedDataFragmentsServer-test.js b/packages/core/test/LinkedDataFragmentsServer-test.js deleted file mode 100644 index e6d26994..00000000 --- a/packages/core/test/LinkedDataFragmentsServer-test.js +++ /dev/null @@ -1,188 +0,0 @@ -/*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ - -import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest'; -let LinkedDataFragmentsServer = require('../lib/LinkedDataFragmentsServer').LinkedDataFragmentsServer; // changed to make tests pass, will be revised in follow up pr - -let request = require('supertest'); - -describe('LinkedDataFragmentsServer', () => { - describe('A LinkedDataFragmentsServer instance with one controller', () => { - let server, controller, client; - beforeAll(() => { - controller = { - handleRequest: vi.fn((request, response, next) => { - switch (request.url) { - case '/handle': - response.end('body contents'); - break; - case '/error': - throw new Error('error message'); - default: - next(); - } - }), - }; - server = new LinkedDataFragmentsServer({ - controllers: [controller], - log: vi.fn(), - protocol: 'http', - response: { - headers: { - 'Access-Control-Allow-Origin': '*', - 'My-Header': 'value', - }, - }, - }); - client = request.agent(server); - }); - beforeEach(() => { - controller.handleRequest.mockClear(); - }); - - it('should send the configured headers', async () => { - let response = await client.head('/'); - expect(response.headers).toHaveProperty('access-control-allow-origin', '*'); - expect(response.headers).toHaveProperty('my-header', 'value'); - }); - - it('should not allow POST requests', async () => { - let response = await client.post('/'); - expect(controller.handleRequest).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 405); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'The HTTP method "POST" is not allowed; try "GET" instead.'); - }); - - it('should send a body with GET requests', async () => { - let response = await client.get('/handle'); - expect(controller.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 200); - expect(response).toHaveProperty('text', 'body contents'); - }); - - it('should not send a body with HEAD requests', async () => { - let response = await client.head('/handle'); - expect(controller.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.body).not.toHaveProperty('length'); - }); - - it('should not send a body with OPTIONS requests', async () => { - let response = await client.options('/handle'); - expect(controller.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 200); - expect(response).toHaveProperty('text', ''); - }); - - it('should error when the controller cannot handle the request', async () => { - let response = await client.get('/unsupported'); - expect(controller.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 500); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'Application error: No controller for /unsupported\n'); - }); - - it('should error when the controller errors', async () => { - let response = await client.get('/error'); - expect(controller.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 500); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'Application error: error message\n'); - }); - }); - - describe('A LinkedDataFragmentsServer instance with two controllers', () => { - let server, controllerA, controllerB, client; - beforeAll(() => { - controllerA = { - handleRequest: vi.fn((request, response, next) => { - switch (request.url) { - case '/handleA': - response.end('body contents A'); - break; - case '/errorA': - throw new Error('error message A'); - default: - next(); - } - }), - }; - controllerB = { - handleRequest: vi.fn((request, response, next) => { - switch (request.url) { - case '/handleB': - response.end('body contents B'); - break; - case '/errorB': - next(new Error('error message B')); - break; - default: - next(); - } - }), - }; - server = new LinkedDataFragmentsServer({ - controllers: [controllerA, controllerB], - protocol: 'http', - log: vi.fn(), - }); - client = request.agent(server); - }); - beforeEach(() => { - controllerA.handleRequest.mockClear(); - controllerB.handleRequest.mockClear(); - }); - - it('should not allow POST requests', async () => { - let response = await client.post('/'); - expect(controllerA.handleRequest).not.toHaveBeenCalled(); - expect(controllerB.handleRequest).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 405); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'The HTTP method "POST" is not allowed; try "GET" instead.'); - }); - - it('should use the first controller when it can handle the request', async () => { - let response = await client.get('/handleA'); - expect(controllerA.handleRequest).toHaveBeenCalledOnce(); - expect(controllerB.handleRequest).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response).toHaveProperty('text', 'body contents A'); - }); - - it('should use the second controller when the first cannot handle the request', async () => { - let response = await client.get('/handleB'); - expect(controllerA.handleRequest).toHaveBeenCalledOnce(); - expect(controllerB.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 200); - expect(response).toHaveProperty('text', 'body contents B'); - }); - - it('should error when neither controller can handle the request', async () => { - let response = await client.get('/unsupported'); - expect(controllerA.handleRequest).toHaveBeenCalledOnce(); - expect(controllerB.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 500); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'Application error: No controller for /unsupported\n'); - }); - - it('should error when the first controller errors', async () => { - let response = await client.get('/errorA'); - expect(controllerA.handleRequest).toHaveBeenCalledOnce(); - expect(controllerB.handleRequest).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 500); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'Application error: error message A\n'); - }); - - it('should error when the second controller errors', async () => { - let response = await client.get('/errorB'); - expect(controllerA.handleRequest).toHaveBeenCalledOnce(); - expect(controllerB.handleRequest).toHaveBeenCalledOnce(); - expect(response).toHaveProperty('statusCode', 500); - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - expect(response).toHaveProperty('text', 'Application error: error message B\n'); - }); - }); -}); diff --git a/packages/core/test/LinkedDataFragmentsServer-test.ts b/packages/core/test/LinkedDataFragmentsServer-test.ts new file mode 100644 index 00000000..30339001 --- /dev/null +++ b/packages/core/test/LinkedDataFragmentsServer-test.ts @@ -0,0 +1,192 @@ +/*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ + +import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest'; +import type { Mock } from 'vitest'; +import { LinkedDataFragmentsServer } from '../lib/LinkedDataFragmentsServer'; +import { Controller } from '../lib/controllers/Controller'; +import type { LdfRequestWithUrl, LdfResponse } from '../index'; +import { listen } from '../../../test/test-helpers'; + +describe('LinkedDataFragmentsServer', () => { + describe('A LinkedDataFragmentsServer instance with one controller', () => { + let baseUrl: string, controller: Controller; + // Kept separate so .mockClear() has a Mock-typed target. controller.handleRequest's + // declared type is just Controller's plain function signature, not Mock. + let handleRequestSpy: Mock; + beforeAll(async () => { + controller = new Controller(); + handleRequestSpy = vi.fn((request: LdfRequestWithUrl, response: LdfResponse, next: (error?: Error) => void) => { + switch (request.url) { + case '/handle': + response.end('body contents'); + break; + case '/error': + throw new Error('error message'); + default: + next(); + } + }); + controller.handleRequest = handleRequestSpy; + baseUrl = await listen(new LinkedDataFragmentsServer({ + controllers: [controller], + log: vi.fn(), + response: { + headers: { + 'Access-Control-Allow-Origin': '*', + 'My-Header': 'value', + }, + }, + })); + }); + beforeEach(() => { + handleRequestSpy.mockClear(); + }); + + it('should send the configured headers', async () => { + let response = await fetch(baseUrl + '/', { method: 'HEAD' }); + expect(response.headers.get('access-control-allow-origin')).toBe('*'); + expect(response.headers.get('my-header')).toBe('value'); + }); + + it('should not allow POST requests', async () => { + let response = await fetch(baseUrl + '/', { method: 'POST' }); + expect(controller.handleRequest).not.toHaveBeenCalled(); + expect(response.status).toBe(405); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('The HTTP method "POST" is not allowed; try "GET" instead.'); + }); + + it('should send a body with GET requests', async () => { + let response = await fetch(baseUrl + '/handle'); + expect(controller.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(200); + expect(await response.text()).toBe('body contents'); + }); + + it('should not send a body with HEAD requests', async () => { + let response = await fetch(baseUrl + '/handle', { method: 'HEAD' }); + expect(controller.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(200); + expect(await response.text()).toBe(''); + }); + + it('should not send a body with OPTIONS requests', async () => { + let response = await fetch(baseUrl + '/handle', { method: 'OPTIONS' }); + expect(controller.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(200); + expect(await response.text()).toBe(''); + }); + + it('should error when the controller cannot handle the request', async () => { + let response = await fetch(baseUrl + '/unsupported'); + expect(controller.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(500); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('Application error: No controller for /unsupported\n'); + }); + + it('should error when the controller errors', async () => { + let response = await fetch(baseUrl + '/error'); + expect(controller.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(500); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('Application error: error message\n'); + }); + }); + + describe('A LinkedDataFragmentsServer instance with two controllers', () => { + let baseUrl: string, controllerA: Controller, controllerB: Controller; + // Kept separate so .mockClear() has a Mock-typed target. controllerX.handleRequest's + // declared type is just Controller's plain function signature, not Mock. + let handleRequestSpyA: Mock, handleRequestSpyB: Mock; + beforeAll(async () => { + controllerA = new Controller(); + handleRequestSpyA = vi.fn((request: LdfRequestWithUrl, response: LdfResponse, next: (error?: Error) => void) => { + switch (request.url) { + case '/handleA': + response.end('body contents A'); + break; + case '/errorA': + throw new Error('error message A'); + default: + next(); + } + }); + controllerA.handleRequest = handleRequestSpyA; + controllerB = new Controller(); + handleRequestSpyB = vi.fn((request: LdfRequestWithUrl, response: LdfResponse, next: (error?: Error) => void) => { + switch (request.url) { + case '/handleB': + response.end('body contents B'); + break; + case '/errorB': + next(new Error('error message B')); + break; + default: + next(); + } + }); + controllerB.handleRequest = handleRequestSpyB; + baseUrl = await listen(new LinkedDataFragmentsServer({ + controllers: [controllerA, controllerB], + log: vi.fn(), + })); + }); + beforeEach(() => { + handleRequestSpyA.mockClear(); + handleRequestSpyB.mockClear(); + }); + + it('should not allow POST requests', async () => { + let response = await fetch(baseUrl + '/', { method: 'POST' }); + expect(controllerA.handleRequest).not.toHaveBeenCalled(); + expect(controllerB.handleRequest).not.toHaveBeenCalled(); + expect(response.status).toBe(405); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('The HTTP method "POST" is not allowed; try "GET" instead.'); + }); + + it('should use the first controller when it can handle the request', async () => { + let response = await fetch(baseUrl + '/handleA'); + expect(controllerA.handleRequest).toHaveBeenCalledOnce(); + expect(controllerB.handleRequest).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(await response.text()).toBe('body contents A'); + }); + + it('should use the second controller when the first cannot handle the request', async () => { + let response = await fetch(baseUrl + '/handleB'); + expect(controllerA.handleRequest).toHaveBeenCalledOnce(); + expect(controllerB.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(200); + expect(await response.text()).toBe('body contents B'); + }); + + it('should error when neither controller can handle the request', async () => { + let response = await fetch(baseUrl + '/unsupported'); + expect(controllerA.handleRequest).toHaveBeenCalledOnce(); + expect(controllerB.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(500); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('Application error: No controller for /unsupported\n'); + }); + + it('should error when the first controller errors', async () => { + let response = await fetch(baseUrl + '/errorA'); + expect(controllerA.handleRequest).toHaveBeenCalledOnce(); + expect(controllerB.handleRequest).not.toHaveBeenCalled(); + expect(response.status).toBe(500); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('Application error: error message A\n'); + }); + + it('should error when the second controller errors', async () => { + let response = await fetch(baseUrl + '/errorB'); + expect(controllerA.handleRequest).toHaveBeenCalledOnce(); + expect(controllerB.handleRequest).toHaveBeenCalledOnce(); + expect(response.status).toBe(500); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + expect(await response.text()).toBe('Application error: error message B\n'); + }); + }); +}); diff --git a/packages/core/test/controllers/AssetsController-test.js b/packages/core/test/controllers/AssetsController-test.js deleted file mode 100644 index 1f4f3e2d..00000000 --- a/packages/core/test/controllers/AssetsController-test.js +++ /dev/null @@ -1,69 +0,0 @@ -/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ - -import { describe, it, expect, beforeAll } from 'vitest'; -import { DummyServer } from '../../../../test/DummyServer'; -let AssetsController = require('../../lib/controllers/AssetsController').AssetsController; // changed to make tests pass, will be revised in follow up pr - -let request = require('supertest'), - fs = require('fs'), - path = require('path'); - -describe('AssetsController', () => { - describe('The AssetsController module', () => { - it('should be a function', () => { - expect(typeof AssetsController).toBe('function'); - }); - - it('should be an AssetsController constructor', () => { - expect(new AssetsController()).toBeInstanceOf(AssetsController); - }); - }); - - describe('An AssetsController instance', () => { - let controller, client; - beforeAll(() => { - controller = new AssetsController(); - client = request.agent(new DummyServer(controller), {}); - }); - - it('should correctly serve SVG assets', async () => { - let response = await client.get('/assets/images/logo'); - let asset = fs.readFileSync(path.join(__dirname, '/../../assets/images/logo.svg'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'image/svg+xml'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=1209600'); - expect(response.body.toString()).toBe(asset); - }); - - it('should correctly serve CSS assets', async () => { - let response = await client.get('/assets/styles/ldf-server'); - let asset = fs.readFileSync(path.join(__dirname, '/../../assets/styles/ldf-server.css'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'text/css;charset=utf-8'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=1209600'); - expect(response).toHaveProperty('text', asset); - }); - - it('should correctly serve ICO assets', async () => { - let response = await client.get('/favicon.ico'); - let asset = fs.readFileSync(path.join(__dirname, '/../../assets/favicon.ico'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'image/vnd.microsoft.icon'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=1209600'); - expect(response.body.toString()).toBe(asset); - }); - - it('should hand over to the next controller if no asset with that name is found', async () => { - await client.get('/assets/unknown'); - expect(controller.next).toHaveBeenCalledOnce(); - }); - - it('should hand over to the next controller for non-asset paths', async () => { - await client.get('/other'); - expect(controller.next).toHaveBeenCalledOnce(); - }); - }); -}); diff --git a/packages/core/test/controllers/AssetsController-test.ts b/packages/core/test/controllers/AssetsController-test.ts new file mode 100644 index 00000000..bdca47ff --- /dev/null +++ b/packages/core/test/controllers/AssetsController-test.ts @@ -0,0 +1,69 @@ +/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ + +import { describe, it, expect, beforeAll } from 'vitest'; +import { DummyServer } from '../../../../test/DummyServer'; +import { request } from '../../../../test/test-helpers'; +import { AssetsController } from '../../lib/controllers/AssetsController'; + +import * as fs from 'fs'; +import * as path from 'path'; + +describe('AssetsController', () => { + describe('The AssetsController module', () => { + it('should be a function', () => { + expect(typeof AssetsController).toBe('function'); + }); + + it('should be an AssetsController constructor', () => { + expect(new AssetsController()).toBeInstanceOf(AssetsController); + }); + }); + + describe('An AssetsController instance', () => { + let controller: AssetsController, server: DummyServer; + beforeAll(() => { + controller = new AssetsController(); + server = new DummyServer(controller); + }); + + it('should correctly serve SVG assets', async () => { + let response = await request(server, '/assets/images/logo'); + let asset = fs.readFileSync(path.join(__dirname, '/../../assets/images/logo.svg'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('image/svg+xml'); + expect(response.headers.get('cache-control')).toBe('public,max-age=1209600'); + expect(await response.text()).toBe(asset); + }); + + it('should correctly serve CSS assets', async () => { + let response = await request(server, '/assets/styles/ldf-server'); + let asset = fs.readFileSync(path.join(__dirname, '/../../assets/styles/ldf-server.css'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('text/css;charset=utf-8'); + expect(response.headers.get('cache-control')).toBe('public,max-age=1209600'); + expect(await response.text()).toBe(asset); + }); + + it('should correctly serve ICO assets', async () => { + let response = await request(server, '/favicon.ico'); + let asset = fs.readFileSync(path.join(__dirname, '/../../assets/favicon.ico'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('image/vnd.microsoft.icon'); + expect(response.headers.get('cache-control')).toBe('public,max-age=1209600'); + expect(await response.text()).toBe(asset); + }); + + it('should hand over to the next controller if no asset with that name is found', async () => { + await request(server, '/assets/unknown'); + expect(server.next).toHaveBeenCalledOnce(); + }); + + it('should hand over to the next controller for non-asset paths', async () => { + await request(server, '/other'); + expect(server.next).toHaveBeenCalledOnce(); + }); + }); +}); diff --git a/packages/core/test/controllers/Controller-test.js b/packages/core/test/controllers/Controller-test.ts similarity index 53% rename from packages/core/test/controllers/Controller-test.js rename to packages/core/test/controllers/Controller-test.ts index 491d70ac..015e9586 100644 --- a/packages/core/test/controllers/Controller-test.js +++ b/packages/core/test/controllers/Controller-test.ts @@ -1,13 +1,23 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, vi } from 'vitest'; +import type { Mock } from 'vitest'; import { DummyServer } from '../../../../test/DummyServer'; -// changed to make tests pass, will be revised in follow up pr -let Controller = require('../../lib/controllers/Controller').Controller, - UrlData = require('../../lib/UrlData').UrlData; - -let http = require('http'), - request = require('supertest'); +import { request } from '../../../../test/test-helpers'; +import { Controller } from '../../lib/controllers/Controller'; +import { UrlData } from '../../index'; +import type { LdfRequestWithUrl, LdfResponse, ViewSettings } from '../../index'; + +import * as http from 'http'; + +// Controller marks _handleRequest protected so subclasses can implement it. This +// subclass widens it to public so this suite can spy on the base class's own +// behavior directly, the way its subclasses already do. +class TestableController extends Controller { + override _handleRequest(request: LdfRequestWithUrl, response: LdfResponse, next: (error?: Error) => void, settings?: ViewSettings): void { + return super._handleRequest(request, response, next, settings); + } +} describe('Controller', () => { describe('The Controller module', () => { @@ -21,27 +31,29 @@ describe('Controller', () => { }); describe('A Controller instance without baseURL', () => { - let controller, client; + let controller: TestableController; + let server: DummyServer; + let handleRequestSpy: Mock; beforeAll(() => { - controller = new Controller(); - vi.spyOn(controller, '_handleRequest'); - client = request.agent(new DummyServer(controller), {}); + controller = new TestableController(); + handleRequestSpy = vi.spyOn(controller, '_handleRequest'); + server = new DummyServer(controller); }); describe('receiving a request', () => { - beforeAll(() => client.get('/path?a=b')); + beforeAll(() => request(server, '/path?a=b')); it('should call _handleRequest with request, response and next', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let args = controller._handleRequest.mock.calls[0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let args = handleRequestSpy.mock.calls[0]; expect(args[0]).toHaveProperty('url'); expect(args[1]).toBeInstanceOf(http.ServerResponse); expect(args[2]).toBeInstanceOf(Function); }); it('should extend _handleRequest with the original URL as parsedUrl property', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let request = controller._handleRequest.mock.calls[0][0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let request = handleRequestSpy.mock.calls[0][0]; expect(request).toHaveProperty('parsedUrl'); expect(request.parsedUrl).toEqual({ protocol: 'http:', host: request.headers.host, hostname: undefined, port: undefined, @@ -51,37 +63,41 @@ describe('Controller', () => { }); it('should hand over to the next controller', () => { - expect(controller.next).toHaveBeenCalledOnce(); + expect(server.next).toHaveBeenCalledOnce(); }); }); }); describe('A Controller instance without baseURL using Forwarded header', () => { - let controller, client; + let controller: TestableController; + let server: DummyServer; + let handleRequestSpy: Mock; beforeAll(() => { - controller = new Controller({ urlData: new UrlData({ baseURL: 'http://example.org:1234/base?c=d#f' }) }); - vi.spyOn(controller, '_handleRequest'); - client = request.agent(new DummyServer(controller), {}); + controller = new TestableController({ urlData: new UrlData({ baseURL: 'http://example.org:1234/base?c=d#f' }) }); + handleRequestSpy = vi.spyOn(controller, '_handleRequest'); + server = new DummyServer(controller); }); describe('receiving a request', () => { - beforeAll(() => client - .get('/path?a=b') - .set('X-Forwarded-Host', 'foo:5000') - // NOTE: the priority will go to the Forwarded header over the X-Forwarded-Host header - .set('Forwarded', 'proto=https;host="bar:8000"')); + beforeAll(() => request(server, '/path?a=b', { + headers: { + 'X-Forwarded-Host': 'foo:5000', + // NOTE: the priority will go to the Forwarded header over the X-Forwarded-Host header + 'Forwarded': 'proto=https;host="bar:8000"', + }, + })); it('should call _handleRequest with request, response and next', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let args = controller._handleRequest.mock.calls[0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let args = handleRequestSpy.mock.calls[0]; expect(args[0]).toHaveProperty('url'); expect(args[1]).toBeInstanceOf(http.ServerResponse); expect(args[2]).toBeInstanceOf(Function); }); it('should extend _handleRequest with the original URL as parsedUrl property', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let request = controller._handleRequest.mock.calls[0][0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let request = handleRequestSpy.mock.calls[0][0]; expect(request).toHaveProperty('parsedUrl'); expect(request.parsedUrl).toEqual({ protocol: 'https:', host: 'bar:8000', hostname: 'example.org', port: '1234', @@ -91,36 +107,40 @@ describe('Controller', () => { }); it('should hand over to the next controller', () => { - expect(controller.next).toHaveBeenCalledOnce(); + expect(server.next).toHaveBeenCalledOnce(); }); }); }); describe('A Controller instance without baseURL using X-Forwarded-* headers', () => { - let controller, client; + let controller: TestableController; + let server: DummyServer; + let handleRequestSpy: Mock; beforeAll(() => { - controller = new Controller(); - vi.spyOn(controller, '_handleRequest'); - client = request.agent(new DummyServer(controller), {}); + controller = new TestableController(); + handleRequestSpy = vi.spyOn(controller, '_handleRequest'); + server = new DummyServer(controller); }); describe('receiving a request', () => { - beforeAll(() => client - .get('/path?a=b') - .set('X-Forwarded-Host', 'foo:5000') - .set('X-Forwarded-Proto', 'https')); + beforeAll(() => request(server, '/path?a=b', { + headers: { + 'X-Forwarded-Host': 'foo:5000', + 'X-Forwarded-Proto': 'https', + }, + })); it('should call _handleRequest with request, response and next', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let args = controller._handleRequest.mock.calls[0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let args = handleRequestSpy.mock.calls[0]; expect(args[0]).toHaveProperty('url'); expect(args[1]).toBeInstanceOf(http.ServerResponse); expect(args[2]).toBeInstanceOf(Function); }); it('should extend _handleRequest with the original URL as parsedUrl property', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let request = controller._handleRequest.mock.calls[0][0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let request = handleRequestSpy.mock.calls[0][0]; expect(request).toHaveProperty('parsedUrl'); expect(request.parsedUrl).toEqual({ protocol: 'https:', host: 'foo:5000', hostname: undefined, port: undefined, @@ -130,33 +150,35 @@ describe('Controller', () => { }); it('should hand over to the next controller', () => { - expect(controller.next).toHaveBeenCalledOnce(); + expect(server.next).toHaveBeenCalledOnce(); }); }); }); describe('A Controller instance with baseURL', () => { - let controller, client; + let controller: TestableController; + let server: DummyServer; + let handleRequestSpy: Mock; beforeAll(() => { - controller = new Controller({ urlData: new UrlData({ baseURL: 'http://example.org:1234/base?c=d#f' }) }); - vi.spyOn(controller, '_handleRequest'); - client = request.agent(new DummyServer(controller), {}); + controller = new TestableController({ urlData: new UrlData({ baseURL: 'http://example.org:1234/base?c=d#f' }) }); + handleRequestSpy = vi.spyOn(controller, '_handleRequest'); + server = new DummyServer(controller); }); describe('receiving a request', () => { - beforeAll(() => client.get('/path?a=b')); + beforeAll(() => request(server, '/path?a=b')); it('should call _handleRequest with request, response and next', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let args = controller._handleRequest.mock.calls[0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let args = handleRequestSpy.mock.calls[0]; expect(args[0]).toHaveProperty('url'); expect(args[1]).toBeInstanceOf(http.ServerResponse); expect(args[2]).toBeInstanceOf(Function); }); it('should extend _handleRequest with the rebased URL as parsedUrl property', () => { - expect(controller._handleRequest).toHaveBeenCalledOnce(); - let request = controller._handleRequest.mock.calls[0][0]; + expect(handleRequestSpy).toHaveBeenCalledOnce(); + let request = handleRequestSpy.mock.calls[0][0]; expect(request).toHaveProperty('parsedUrl'); expect(request.parsedUrl).toEqual({ protocol: 'http:', host: 'example.org:1234', hostname: 'example.org', port: '1234', @@ -166,7 +188,7 @@ describe('Controller', () => { }); it('should hand over to the next controller', () => { - expect(controller.next).toHaveBeenCalledOnce(); + expect(server.next).toHaveBeenCalledOnce(); }); }); }); diff --git a/packages/core/test/controllers/DereferenceController-test.js b/packages/core/test/controllers/DereferenceController-test.ts similarity index 54% rename from packages/core/test/controllers/DereferenceController-test.js rename to packages/core/test/controllers/DereferenceController-test.ts index da3032ad..d939f2f7 100644 --- a/packages/core/test/controllers/DereferenceController-test.js +++ b/packages/core/test/controllers/DereferenceController-test.ts @@ -2,9 +2,12 @@ import { describe, it, expect, beforeAll } from 'vitest'; import { DummyServer } from '../../../../test/DummyServer'; -let DereferenceController = require('../../lib/controllers/DereferenceController').DeferenceController; // changed to make tests pass, will be revised in follow up pr +import { request, type FetchLikeResponse } from '../../../../test/test-helpers'; +import { datasources } from '../../index'; +import { DeferenceController as DereferenceController } from '../../lib/controllers/DereferenceController'; +import { DataFactory as dataFactory } from 'n3'; -let request = require('supertest'); +const { Datasource } = datasources; describe('DereferenceController', () => { describe('The DereferenceController module', () => { @@ -18,50 +21,52 @@ describe('DereferenceController', () => { }); describe('A DereferenceController instance', () => { - let controller, client; + let controller: DereferenceController, server: DummyServer; + const hostname = 'localhost:80'; beforeAll(() => { - controller = new DereferenceController({ dereference: { '/resource/': { path: 'dbpedia/2014' } } }); - client = request.agent(new DummyServer(controller), {}); + controller = new DereferenceController({ dereference: { '/resource/': new Datasource({ dataFactory, path: 'dbpedia/2014' }) } }); + server = new DummyServer(controller); }); describe('receiving a request for a dereferenced URL', () => { - let response; - beforeAll(async () => { response = await client.get('/resource/Mickey_Mouse'); }); + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + response = await request(server, '/resource/Mickey_Mouse'); + responseText = await response.text(); + }); it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); + expect(server.next).not.toHaveBeenCalled(); }); it('should set the status code to 303', () => { - expect(response).toHaveProperty('statusCode', 303); + expect(response.status).toBe(303); }); it('should set the text/plain content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); }); it('should set the Location header correctly', () => { - let hostname = response.req.getHeader('Host'), - entityUrl = encodeURIComponent('http://' + hostname + '/resource/Mickey_Mouse'), + let entityUrl = encodeURIComponent('http://' + hostname + '/resource/Mickey_Mouse'), expectedLocation = 'http://' + hostname + '/dbpedia/2014?subject=' + entityUrl; - expect(response.headers).toHaveProperty('location', expectedLocation); + expect(response.headers.get('location')).toBe(expectedLocation); }); it('should mention the desired location in the body', () => { - let hostname = response.req.getHeader('Host'), - entityUrl = encodeURIComponent('http://' + hostname + '/resource/Mickey_Mouse'), + let entityUrl = encodeURIComponent('http://' + hostname + '/resource/Mickey_Mouse'), expectedLocation = 'http://' + hostname + '/dbpedia/2014?subject=' + entityUrl; - expect(response.text).toContain(expectedLocation); + expect(responseText).toContain(expectedLocation); }); }); describe('receiving a request for a non-defererenced URL', () => { - beforeAll(() => client.get('/otherresource/Mickey_Mouse')); + beforeAll(() => request(server, '/otherresource/Mickey_Mouse')); it('should hand over to the next controller', () => { - expect(controller.next).toHaveBeenCalledOnce(); + expect(server.next).toHaveBeenCalledOnce(); }); }); }); diff --git a/packages/core/test/controllers/NotFoundController-test.js b/packages/core/test/controllers/NotFoundController-test.js deleted file mode 100644 index de32100b..00000000 --- a/packages/core/test/controllers/NotFoundController-test.js +++ /dev/null @@ -1,257 +0,0 @@ -/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ - -import { describe, it, expect, beforeAll, vi } from 'vitest'; -import { DummyServer } from '../../../../test/DummyServer'; -let NotFoundController = require('../../lib/controllers/NotFoundController').NotFoundController; - -let request = require('supertest'), - dataFactory = require('n3').DataFactory; - -let NotFoundHtmlView = require('../../lib/views/notfound/NotFoundHtmlView').NotFoundHtmlView, - NotFoundRdfView = require('../../lib/views/notfound/NotFoundRdfView').NotFoundRdfView; - -describe('NotFoundController', () => { - describe('The NotFoundController module', () => { - it('should be a function', () => { - expect(typeof NotFoundController).toBe('function'); - }); - - it('should be a NotFoundController constructor', () => { - expect(new NotFoundController()).toBeInstanceOf(NotFoundController); - }); - }); - - describe('A NotFoundController instance without views', () => { - let controller, client; - beforeAll(() => { - controller = new NotFoundController(); - client = request.agent(new DummyServer(controller), {}); - }); - - describe('receiving a request', () => { - let response; - beforeAll(async () => { - response = await client.get('/notfound'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/plain content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send a textual error body', () => { - expect(response).toHaveProperty('text', '/notfound not found\n'); - }); - }); - }); - - describe('A NotFoundController instance with HTML and RDF views', () => { - let controller, htmlView, rdfView, datasources, client; - beforeAll(() => { - htmlView = new NotFoundHtmlView({ dataFactory }); - rdfView = new NotFoundRdfView({ dataFactory }); - vi.spyOn(htmlView, 'render'); - vi.spyOn(rdfView, 'render'); - datasources = { a: { title: 'foo', url: 'http://example.org/foo#dataset' } }; - controller = new NotFoundController({ views: [htmlView, rdfView], datasources: datasources }); - client = request.agent(new DummyServer(controller), {}); - }); - function resetAll() { - htmlView.render.mockClear(); - rdfView.render.mockClear(); - } - - describe('receiving a request without Accept header', () => { - let response; - beforeAll(async () => { - resetAll(); - response = await client.get('/notfound'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should call the HTML view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); - }); - - it('should not call the RDF view', () => { - expect(rdfView.render).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send an HTML error body', () => { - expect(response.text).toContain('No resource with URL /notfound was found.'); - }); - }); - - describe('receiving a request with an Accept header of */*', () => { - let response; - beforeAll(async () => { - resetAll(); - response = await client.get('/notfound').set('Accept', '*/*'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should call the HTML view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); - }); - - it('should not call the RDF view', () => { - expect(rdfView.render).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send an HTML error body', () => { - expect(response.text).toContain('No resource with URL /notfound was found.'); - }); - }); - - describe('receiving a request with an Accept header of text/html', () => { - let response; - beforeAll(async () => { - resetAll(); - response = await client.get('/notfound').set('Accept', 'text/html'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should call the HTML view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); - }); - - it('should not call the RDF view', () => { - expect(rdfView.render).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send an HTML error body', () => { - expect(response.text).toContain('No resource with URL /notfound was found.'); - }); - }); - - describe('receiving a request with an Accept header of text/turtle', () => { - let response; - beforeAll(async () => { - resetAll(); - response = await client.get('/notfound').set('Accept', 'text/turtle'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should call the RDF view', () => { - expect(rdfView.render).toHaveBeenCalledOnce(); - }); - - it('should not call the HTML view', () => { - expect(htmlView.render).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/turtle content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/turtle;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send a Turtle error body', () => { - expect(response.text).toContain(' a '); - expect(response.text).not.toContain('<#metadata> <>.'); - }); - }); - - describe('receiving a request with an Accept header of application/trig', () => { - let response; - beforeAll(async () => { - resetAll(); - response = await client.get('/notfound').set('Accept', 'application/trig'); - }); - - it('should not hand over to the next controller', () => { - expect(controller.next).not.toHaveBeenCalled(); - }); - - it('should call the RDF view', () => { - expect(rdfView.render).toHaveBeenCalledOnce(); - }); - - it('should not call the HTML view', () => { - expect(htmlView.render).not.toHaveBeenCalled(); - }); - - it('should have a 404 status', () => { - expect(response).toHaveProperty('statusCode', 404); - }); - - it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'application/trig;charset=utf-8'); - }); - - it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); - }); - - it('should send a TriG error body', () => { - expect(response.text).toContain(' a '); - expect(response.text).toContain('<#metadata> <>.'); - }); - }); - }); -}); diff --git a/packages/core/test/controllers/NotFoundController-test.ts b/packages/core/test/controllers/NotFoundController-test.ts new file mode 100644 index 00000000..2e528e54 --- /dev/null +++ b/packages/core/test/controllers/NotFoundController-test.ts @@ -0,0 +1,271 @@ +/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ + +import { describe, it, expect, beforeAll, vi } from 'vitest'; +import type { Mock } from 'vitest'; +import { DummyServer } from '../../../../test/DummyServer'; +import { request, type FetchLikeResponse } from '../../../../test/test-helpers'; +import { datasources as coreDatasources, UrlData } from '../../index'; +import { NotFoundController } from '../../lib/controllers/NotFoundController'; +import { NotFoundHtmlView } from '../../lib/views/notfound/NotFoundHtmlView'; +import { NotFoundRdfView } from '../../lib/views/notfound/NotFoundRdfView'; +import type { DatasourceRegistry } from '../../index'; + +import { DataFactory as dataFactory } from 'n3'; + +const { Datasource } = coreDatasources; + +describe('NotFoundController', () => { + describe('The NotFoundController module', () => { + it('should be a function', () => { + expect(typeof NotFoundController).toBe('function'); + }); + + it('should be a NotFoundController constructor', () => { + expect(new NotFoundController()).toBeInstanceOf(NotFoundController); + }); + }); + + describe('A NotFoundController instance without views', () => { + let controller: NotFoundController, server: DummyServer; + beforeAll(() => { + controller = new NotFoundController(); + server = new DummyServer(controller); + }); + + describe('receiving a request', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + response = await request(server, '/notfound'); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/plain content type', () => { + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send a textual error body', () => { + expect(responseText).toBe('/notfound not found\n'); + }); + }); + }); + + describe('A NotFoundController instance with HTML and RDF views', () => { + let controller: NotFoundController, + htmlView: NotFoundHtmlView, rdfView: NotFoundRdfView, + htmlRenderSpy: Mock, + rdfRenderSpy: Mock, + datasources: DatasourceRegistry, server: DummyServer; + beforeAll(() => { + htmlView = new NotFoundHtmlView({ dataFactory }); + rdfView = new NotFoundRdfView({ dataFactory }); + htmlRenderSpy = vi.spyOn(htmlView, 'render'); + rdfRenderSpy = vi.spyOn(rdfView, 'render'); + datasources = { a: new Datasource({ dataFactory, title: 'foo', path: 'foo', urlData: new UrlData({ baseURL: 'http://example.org/' }) }) }; + controller = new NotFoundController({ views: [htmlView, rdfView], datasources: datasources }); + server = new DummyServer(controller); + }); + function resetAll() { + htmlRenderSpy.mockClear(); + rdfRenderSpy.mockClear(); + } + + describe('receiving a request without Accept header', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + resetAll(); + response = await request(server, '/notfound'); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should call the HTML view', () => { + expect(htmlRenderSpy).toHaveBeenCalledOnce(); + }); + + it('should not call the RDF view', () => { + expect(rdfRenderSpy).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/html content type', () => { + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send an HTML error body', () => { + expect(responseText).toContain('No resource with URL /notfound was found.'); + }); + }); + + describe('receiving a request with an Accept header of */*', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + resetAll(); + response = await request(server, '/notfound', { headers: { Accept: '*/*' } }); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should call the HTML view', () => { + expect(htmlRenderSpy).toHaveBeenCalledOnce(); + }); + + it('should not call the RDF view', () => { + expect(rdfRenderSpy).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/html content type', () => { + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send an HTML error body', () => { + expect(responseText).toContain('No resource with URL /notfound was found.'); + }); + }); + + describe('receiving a request with an Accept header of text/html', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + resetAll(); + response = await request(server, '/notfound', { headers: { Accept: 'text/html' } }); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should call the HTML view', () => { + expect(htmlRenderSpy).toHaveBeenCalledOnce(); + }); + + it('should not call the RDF view', () => { + expect(rdfRenderSpy).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/html content type', () => { + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send an HTML error body', () => { + expect(responseText).toContain('No resource with URL /notfound was found.'); + }); + }); + + describe('receiving a request with an Accept header of text/turtle', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + resetAll(); + response = await request(server, '/notfound', { headers: { Accept: 'text/turtle' } }); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should call the RDF view', () => { + expect(rdfRenderSpy).toHaveBeenCalledOnce(); + }); + + it('should not call the HTML view', () => { + expect(htmlRenderSpy).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/turtle content type', () => { + expect(response.headers.get('content-type')).toBe('text/turtle;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send a Turtle error body', () => { + expect(responseText).toContain(' a '); + expect(responseText).not.toContain('<#metadata> <>.'); + }); + }); + + describe('receiving a request with an Accept header of application/trig', () => { + let response: FetchLikeResponse, responseText: string; + beforeAll(async () => { + resetAll(); + response = await request(server, '/notfound', { headers: { Accept: 'application/trig' } }); + responseText = await response.text(); + }); + + it('should not hand over to the next controller', () => { + expect(server.next).not.toHaveBeenCalled(); + }); + + it('should call the RDF view', () => { + expect(rdfRenderSpy).toHaveBeenCalledOnce(); + }); + + it('should not call the HTML view', () => { + expect(htmlRenderSpy).not.toHaveBeenCalled(); + }); + + it('should have a 404 status', () => { + expect(response.status).toBe(404); + }); + + it('should set the text/html content type', () => { + expect(response.headers.get('content-type')).toBe('application/trig;charset=utf-8'); + }); + + it('should indicate Accept in the Vary header', () => { + expect(response.headers.get('vary')).toBe('Accept'); + }); + + it('should send a TriG error body', () => { + expect(responseText).toContain(' a '); + expect(responseText).toContain('<#metadata> <>.'); + }); + }); + }); +}); diff --git a/packages/core/test/datasources/Datasource-test.js b/packages/core/test/datasources/Datasource-test.ts similarity index 78% rename from packages/core/test/datasources/Datasource-test.js rename to packages/core/test/datasources/Datasource-test.ts index fd085dc1..a96e4191 100644 --- a/packages/core/test/datasources/Datasource-test.js +++ b/packages/core/test/datasources/Datasource-test.ts @@ -1,17 +1,36 @@ /*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest'; -const Datasource = require('../../lib/datasources/Datasource').Datasource; // changed to make tests pass, will be revised in follow up pr - -const EventEmitter = require('events'), - { once } = EventEmitter, - fs = require('fs'), - path = require('path'), - N3 = require('n3'); +import type { Mock } from 'vitest'; +import { Datasource } from '../../lib/datasources/Datasource'; +import { EventEmitter, once } from 'events'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as N3 from 'n3'; +import type { Quad, Term } from '@rdfjs/types'; +import type { BufferedIterator } from 'asynciterator'; +import type { Pushable, Query } from '../../lib/types'; const exampleFile = path.join(__dirname, '../../../../test/assets/test.ttl'); const dataFactory = N3.DataFactory; +// Datasource marks these members protected so subclasses can use them internally. +// This subclass widens them to public so this suite can exercise the base class's +// own behavior directly, the way its subclasses already do. +class TestableDatasource extends Datasource { + override _fetch(options: { url: string } & Record): EventEmitter { + return super._fetch(options); + } + + override _initialize(): Promise { + return super._initialize(); + } + + override _executeQuery(query: Query, destination: BufferedIterator): void { + return super._executeQuery(query, destination); + } +} + describe('Datasource', () => { describe('The Datasource module', () => { it('should be a function', () => { @@ -28,7 +47,7 @@ describe('Datasource', () => { }); describe('A Datasource instance', () => { - let datasource = new Datasource({ dataFactory }); + let datasource = new TestableDatasource({ dataFactory }); datasource.initialize(); it('should not indicate support for any features', () => { @@ -44,7 +63,7 @@ describe('Datasource', () => { }); it('should throw an error when trying to execute an unsupported query', async () => { - let error = await new Promise((resolve) => datasource.select({ features: { a: true, b: true } }, resolve)); + let error = await new Promise((resolve) => datasource.select({ features: { a: true, b: true } }, resolve)); expect(error).toBeInstanceOf(Error); expect(error).toHaveProperty('message', 'The datasource does not support the given query'); }); @@ -71,20 +90,20 @@ describe('Datasource', () => { it('emits an error when the protocol is unknown', async () => { let result = datasource._fetch({ url: 'myprotocol:abc' }); - let [error] = await once(result, 'error'); + let [error]: Error[] = await once(result, 'error'); expect(error.message).toContain('Unknown protocol: myprotocol'); }); it('emits an error on the datasource when no error listener is attached to the result', async () => { let result = datasource._fetch({ url: exampleFile + 'notfound' }); result.on('data', () => {}); - let [error] = await once(datasource, 'error'); + let [error]: Error[] = await once(datasource, 'error'); expect(error.message).toContain('ENOENT: no such file or directory'); }); it('does not emit an error on the datasource when an error listener is attached to the result', async () => { let result = datasource._fetch({ url: exampleFile + 'notfound' }); - let [error] = await once(result, 'error'); + let [error]: Error[] = await once(result, 'error'); expect(error.message).toContain('ENOENT: no such file or directory'); }); }); @@ -96,14 +115,14 @@ describe('Datasource', () => { }); describe('when closed with a callback', () => { - it('should invoke the callback', () => new Promise((resolve) => datasource.close(resolve))); + it('should invoke the callback', () => new Promise((resolve) => datasource.close(resolve))); }); }); describe('A Datasource instance with an initializer', () => { - let datasource, initializedListener, errorListener, initResolver, initSpy; + let datasource: TestableDatasource, initializedListener: Mock, errorListener: Mock, initResolver: () => void, initSpy: Mock<() => Promise>; beforeAll(() => { - datasource = new Datasource({ dataFactory }); + datasource = new TestableDatasource({ dataFactory }); datasource._initialize = () => new Promise((resolve) => { initResolver = resolve; }); initSpy = vi.spyOn(datasource, '_initialize'); Object.defineProperty(datasource, 'supportedFeatures', { @@ -128,7 +147,7 @@ describe('Datasource', () => { }); it('should error when trying to query', async () => { - let error = await new Promise((resolve) => datasource.select({}, resolve)); + let error = await new Promise((resolve) => datasource.select({}, resolve)); expect(error).toHaveProperty('message', 'The datasource is not initialized yet'); }); }); @@ -155,16 +174,16 @@ describe('Datasource', () => { }); it('should allow querying', async () => { - let error = await new Promise((resolve) => datasource.select({}, resolve)); + let error = await new Promise((resolve) => datasource.select({}, resolve)); expect(error).toHaveProperty('message', '_executeQuery has not been implemented'); }); }); }); describe('A Datasource instance with an initializer that errors synchronously', () => { - let datasource, initializedListener, errorListener, error; + let datasource: TestableDatasource, initializedListener: Mock, errorListener: Mock, error: Error; beforeAll(() => { - datasource = new Datasource({ dataFactory }); + datasource = new TestableDatasource({ dataFactory }); error = new Error('initializer error'); datasource._initialize = () => { throw error; }; vi.spyOn(datasource, '_initialize'); @@ -194,9 +213,9 @@ describe('Datasource', () => { }); describe('A Datasource instance with an initializer that errors asynchronously', () => { - let datasource, initializedListener, errorListener, error; + let datasource: TestableDatasource, initializedListener: Mock, errorListener: Mock, error: Error; beforeAll(() => { - datasource = new Datasource({ dataFactory }); + datasource = new TestableDatasource({ dataFactory }); error = new Error('initializer error'); datasource._initialize = () => Promise.reject(error); vi.spyOn(datasource, '_initialize'); @@ -226,7 +245,7 @@ describe('Datasource', () => { }); describe('A derived Datasource instance', () => { - let datasource = new Datasource({ dataFactory }); + let datasource = new TestableDatasource({ dataFactory }); Object.defineProperty(datasource, 'supportedFeatures', { enumerable: true, value: { a: true, b: true, c: false }, @@ -270,7 +289,7 @@ describe('Datasource', () => { }); describe('A Datasource instance with a graph property', () => { - let datasource = new Datasource({ + let datasource = new TestableDatasource({ dataFactory, graph: 'http://example.org/#mygraph', }); @@ -279,22 +298,23 @@ describe('Datasource', () => { value: { custom: true }, }); datasource.initialize(); - datasource._executeQuery = vi.fn((query, destination) => { + const executeQuerySpy = vi.fn((query: Query, destination: Pushable) => { destination._push(dataFactory.quad(dataFactory.namedNode('s'), dataFactory.namedNode('p'), dataFactory.namedNode('o1'))); destination._push(dataFactory.quad(dataFactory.namedNode('s'), dataFactory.namedNode('p'), dataFactory.namedNode('o2'), dataFactory.defaultGraph())); destination._push(dataFactory.quad(dataFactory.namedNode('s'), dataFactory.namedNode('p'), dataFactory.namedNode('o3'), dataFactory.namedNode('g'))); destination.close(); }); + datasource._executeQuery = executeQuerySpy; beforeEach(() => { - datasource._executeQuery.mockClear(); + executeQuerySpy.mockClear(); }); it('should move triples in the default graph to the given graph', async () => { - let quads = await new Promise((resolve, reject) => { - let collected = []; + let quads = await new Promise((resolve, reject) => { + let collected: Quad[] = []; let result = datasource.select({ features: { custom: true } }, reject); - result.on('data', (q) => { collected.push(q); }); + result.on('data', (q: Quad) => { collected.push(q); }); result.on('end', () => { resolve(collected); }); }); expect(quads).toEqual([ @@ -309,8 +329,8 @@ describe('Datasource', () => { graph: dataFactory.namedNode('http://example.org/#mygraph'), features: { custom: true }, }); - expect(datasource._executeQuery.mock.calls[0][0].features).toEqual({ custom: true }), - datasource._executeQuery.mock.calls[0][0].graph.equals(dataFactory.defaultGraph()); + expect(executeQuerySpy.mock.calls[0][0].features).toEqual({ custom: true }), + (executeQuerySpy.mock.calls[0][0].graph as Term).equals(dataFactory.defaultGraph()); }); it('should query the default graph as the empty graph', () => { @@ -318,8 +338,8 @@ describe('Datasource', () => { graph: dataFactory.defaultGraph(), features: { custom: true }, }); - expect(datasource._executeQuery.mock.calls[0][0].features).toEqual({ custom: true }), - datasource._executeQuery.mock.calls[0][0].graph.equals(dataFactory.namedNode('urn:ldf:emptyGraph')); + expect(executeQuerySpy.mock.calls[0][0].features).toEqual({ custom: true }), + (executeQuerySpy.mock.calls[0][0].graph as Term).equals(dataFactory.namedNode('urn:ldf:emptyGraph')); }); }); }); diff --git a/packages/core/test/routers/DatasourceRouter-test.js b/packages/core/test/routers/DatasourceRouter-test.ts similarity index 82% rename from packages/core/test/routers/DatasourceRouter-test.js rename to packages/core/test/routers/DatasourceRouter-test.ts index 9007716c..3aed4fe2 100644 --- a/packages/core/test/routers/DatasourceRouter-test.js +++ b/packages/core/test/routers/DatasourceRouter-test.ts @@ -2,7 +2,14 @@ import { describe, it, expect } from 'vitest'; import { extractQueryParams } from '../../../../test/test-helpers'; -let DatasourceRouter = require('../../lib/routers/DatasourceRouter').DatasourceRouter; // changed to make tests pass, will be revised in follow up pr +import { DatasourceRouter } from '../../lib/routers/DatasourceRouter'; +import { UrlData } from '../../index'; +import type { Query } from '../../index'; + +// Query has no index signature; this lets the test tables below use an +// arbitrary 'a' field as a stand-in for "pre-existing data that should survive". +type TestQuery = Query & { a?: number }; +type QueryParamsTestCase = [string, string, string, TestQuery, TestQuery]; describe('DatasourceRouter', () => { describe('The DatasourceRouter module', () => { @@ -20,7 +27,7 @@ describe('DatasourceRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a root URL without trailing slash or query parameters', 'http://example.org', @@ -70,20 +77,20 @@ describe('DatasourceRouter', () => { { a: 1 }, { a: 1, features: { datasource: true }, datasource: '/my/data-source' }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); describe('A DatasourceRouter instance with a base URL', () => { let router = new DatasourceRouter({ - urlData: { baseURLPath: '/my/base/' }, + urlData: new UrlData({ baseURL: '/my/base' }), }); describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a root URL', 'http://example.org/my/base/', @@ -98,8 +105,8 @@ describe('DatasourceRouter', () => { { a: 1 }, { a: 1, features: { datasource: true }, datasource: '/other/path' }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); diff --git a/packages/core/test/routers/PageRouter-test.js b/packages/core/test/routers/PageRouter-test.ts similarity index 88% rename from packages/core/test/routers/PageRouter-test.js rename to packages/core/test/routers/PageRouter-test.ts index 66e13811..3c85d976 100644 --- a/packages/core/test/routers/PageRouter-test.js +++ b/packages/core/test/routers/PageRouter-test.ts @@ -2,7 +2,13 @@ import { describe, it, expect } from 'vitest'; import { extractQueryParams } from '../../../../test/test-helpers'; -let PageRouter = require('../../lib/routers/PageRouter').PageRouter; // changed to make tests pass, will be revised in follow up pr +import { PageRouter } from '../../lib/routers/PageRouter'; +import type { Query } from '../../index'; + +// Query has no index signature; this lets the test tables below use an +// arbitrary 'a' field as a stand-in for "pre-existing data that should survive". +type TestQuery = Query & { a?: number }; +type QueryParamsTestCase = [string, string, string, TestQuery, TestQuery]; describe('PageRouter', () => { describe('The PageRouter module', () => { @@ -20,7 +26,7 @@ describe('PageRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a URL without query parameters', 'http://example.org/', @@ -77,8 +83,8 @@ describe('PageRouter', () => { { a: 1, features: { a: true, b: true } }, { a: 1, features: { a: true, b: true, limit: true, offset: true }, limit: 100, offset: 200 }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); @@ -88,7 +94,7 @@ describe('PageRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a URL without query parameters', 'http://example.org/', @@ -145,8 +151,8 @@ describe('PageRouter', () => { { a: 1, features: { a: true, b: true } }, { a: 1, features: { a: true, b: true, limit: true, offset: true }, limit: 250, offset: 500 }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); @@ -156,7 +162,7 @@ describe('PageRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a URL without query parameters', 'http://example.org/', @@ -164,8 +170,8 @@ describe('PageRouter', () => { { a: 1 }, { a: 1, features: { limit: true }, limit: 100 }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); diff --git a/packages/core/test/views/View-test.js b/packages/core/test/views/View-test.ts similarity index 61% rename from packages/core/test/views/View-test.js rename to packages/core/test/views/View-test.ts index 364362cd..4f2dd834 100644 --- a/packages/core/test/views/View-test.js +++ b/packages/core/test/views/View-test.ts @@ -1,9 +1,28 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, vi } from 'vitest'; -// changed to make tests pass, will be revised in follow up pr -let View = require('../../lib/views/View').View, - resolve = require('path').resolve; +import { View } from '../../lib/views/View'; +import { resolve } from 'path'; +import { IncomingMessage, ServerResponse } from 'http'; +import { Socket } from 'net'; +import type { LdfRequest, LdfResponse, RenderDone, ViewSettings } from '../../index'; + +// View marks _render protected so subclasses can implement it. This subclass +// widens it to public so this suite can mock/spy on the base class's own +// behavior directly, the way its subclasses already do. +class TestableView extends View { + override _render(settings: ViewSettings, request: LdfRequest, response: LdfResponse, done: RenderDone): void { + return super._render(settings, request, response, done); + } +} + +function createRequest(): LdfRequest { + return new IncomingMessage(new Socket()); +} + +function createResponse(): LdfResponse { + return new ServerResponse(createRequest()); +} describe('View', () => { describe('The View module', () => { @@ -69,54 +88,59 @@ describe('View', () => { describe('without _render method', () => { it('should throw an error on calling render', () => { - let response = { getHeader: vi.fn() }; - expect(() => { new View().render(null, null, response); }) + let response = createResponse(); + response.getHeader = vi.fn(); + expect(() => { new View().render({}, createRequest(), response); }) .toThrow('The _render method is not yet implemented.'); }); }); describe('created without defaults', () => { it('should call _render with the given options', () => { - let view = new View(), - request = {}, response = { getHeader: vi.fn().mockReturnValue('text/html') }, + let view = new TestableView(), + request = createRequest(), response = createResponse(), options = { a: 'b' }; - view._render = vi.fn(); + response.getHeader = vi.fn().mockReturnValue('text/html'); + let renderSpy = vi.fn(); + view._render = renderSpy; view.render(options, request, response, noop); expect(response.getHeader).toHaveBeenCalledOnce(); expect(response.getHeader).toHaveBeenCalledWith('Content-Type'); - expect(view._render.mock.calls[0]).toHaveLength(4); - expect(view._render).toHaveBeenCalledOnce(); - expect(view._render.mock.calls[0][0]).toEqual({ + expect(renderSpy.mock.calls[0]).toHaveLength(4); + expect(renderSpy).toHaveBeenCalledOnce(); + expect(renderSpy.mock.calls[0][0]).toEqual({ a: 'b', contentType: 'text/html', viewPathBase: resolve(__dirname, '../../lib/views/base.html'), }); - expect(view._render.mock.calls[0][1]).toBe(request); - expect(view._render.mock.calls[0][2]).toBe(response); - expect(view._render.mock.calls[0][3]).toBeInstanceOf(Function); + expect(renderSpy.mock.calls[0][1]).toBe(request); + expect(renderSpy.mock.calls[0][2]).toBe(response); + expect(renderSpy.mock.calls[0][3]).toBeInstanceOf(Function); }); }); describe('created with defaults', () => { it('should call _render with the combined defaults and options', () => { - let view = new View(null, null, { c: 'd' }), - request = {}, response = { getHeader: vi.fn().mockReturnValue('text/html') }, + let view = new TestableView(undefined, undefined, { c: 'd' }), + request = createRequest(), response = createResponse(), options = { a: 'b' }; - view._render = vi.fn(); + response.getHeader = vi.fn().mockReturnValue('text/html'); + let renderSpy = vi.fn(); + view._render = renderSpy; view.render(options, request, response, noop); expect(response.getHeader).toHaveBeenCalledOnce(); expect(response.getHeader).toHaveBeenCalledWith('Content-Type'); - expect(view._render).toHaveBeenCalledOnce(); - expect(view._render.mock.calls[0]).toHaveLength(4); - expect(view._render.mock.calls[0][0]).toEqual({ + expect(renderSpy).toHaveBeenCalledOnce(); + expect(renderSpy.mock.calls[0]).toHaveLength(4); + expect(renderSpy.mock.calls[0][0]).toEqual({ a: 'b', c: 'd', contentType: 'text/html', viewPathBase: resolve(__dirname, '../../lib/views/base.html'), }); - expect(view._render.mock.calls[0][1]).toBe(request); - expect(view._render.mock.calls[0][2]).toBe(response); - expect(view._render.mock.calls[0][3]).toBeInstanceOf(Function); + expect(renderSpy.mock.calls[0][1]).toBe(request); + expect(renderSpy.mock.calls[0][2]).toBe(response); + expect(renderSpy.mock.calls[0][3]).toBeInstanceOf(Function); }); }); }); diff --git a/packages/core/test/views/ViewCollection-test.js b/packages/core/test/views/ViewCollection-test.ts similarity index 71% rename from packages/core/test/views/ViewCollection-test.js rename to packages/core/test/views/ViewCollection-test.ts index 6fd69bdc..a019b638 100644 --- a/packages/core/test/views/ViewCollection-test.js +++ b/packages/core/test/views/ViewCollection-test.ts @@ -1,9 +1,15 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll } from 'vitest'; -let ViewCollection = require('../../lib/views/ViewCollection').ViewCollection; // changed to make tests pass, will be revised in follow up pr +import { ViewCollection } from '../../lib/views/ViewCollection'; +import { View } from '../../lib/views/View'; +import { IncomingMessage } from 'http'; +import { Socket } from 'net'; +import type { LdfRequest } from '../../index'; -let View = require('../../lib/views/View').View; // changed to make tests pass, will be revised in follow up pr +function createRequest(): LdfRequest { + return new IncomingMessage(new Socket()); +} describe('ViewCollection', () => { describe('The ViewCollection module', () => { @@ -17,35 +23,35 @@ describe('ViewCollection', () => { }); describe('A ViewCollection instance without views', () => { - let viewCollection; + let viewCollection: ViewCollection; beforeAll(() => { viewCollection = new ViewCollection(); }); it('should throw an error when matching a view', () => { - expect(() => { viewCollection.matchView('Foo'); }) + expect(() => { viewCollection.matchView('Foo', createRequest()); }) .toThrow('No view named Foo found.'); }); }); describe('A ViewCollection instance with one view', () => { - let viewCollection, viewA; + let viewCollection: ViewCollection, viewA: View; beforeAll(() => { viewA = new View('MyView1', 'text/html,application/trig;q=0.7'); viewCollection = new ViewCollection([viewA]); }); it('should throw an error when matching a view with a non-existing type', () => { - expect(() => { viewCollection.matchView('Bar'); }) + expect(() => { viewCollection.matchView('Bar', createRequest()); }) .toThrow('No view named Bar found.'); }); describe('when a client requests HTML', () => { - let viewDetails, request, response; + let viewDetails: ReturnType, request: LdfRequest; beforeAll(() => { - request = { headers: { accept: 'text/html' } }; - response = {}; - viewDetails = viewCollection.matchView('MyView1', request, response); + request = createRequest(); + request.headers.accept = 'text/html'; + viewDetails = viewCollection.matchView('MyView1', request); }); it('should return a match for the view', () => { @@ -56,11 +62,11 @@ describe('ViewCollection', () => { }); describe('when a client requests TriG', () => { - let viewDetails, request, response; + let viewDetails: ReturnType, request: LdfRequest; beforeAll(() => { - request = { headers: { accept: 'application/trig' } }; - response = {}; - viewDetails = viewCollection.matchView('MyView1', request, response); + request = createRequest(); + request.headers.accept = 'application/trig'; + viewDetails = viewCollection.matchView('MyView1', request); }); it('should return a match for the view', () => { @@ -72,7 +78,7 @@ describe('ViewCollection', () => { }); describe('A ViewCollection instance with three views of two types', () => { - let viewCollection, viewA, viewB, viewC; + let viewCollection: ViewCollection, viewA: View, viewB: View, viewC: View; beforeAll(() => { viewA = new View('MyView1', 'text/html,application/trig;q=0.5'); viewB = new View('MyView1', 'text/html;q=1.0,application/trig'); @@ -81,16 +87,16 @@ describe('ViewCollection', () => { }); it('should throw an error when matching a view with a non-existing type', () => { - expect(() => { viewCollection.matchView('Bar'); }) + expect(() => { viewCollection.matchView('Bar', createRequest()); }) .toThrow('No view named Bar found.'); }); describe('when matching a request of one view type as HTML', () => { - let viewDetails, request, response; + let viewDetails: ReturnType, request: LdfRequest; beforeAll(() => { - request = { headers: { accept: 'text/html' } }; - response = {}; - viewDetails = viewCollection.matchView('MyView1', request, response); + request = createRequest(); + request.headers.accept = 'text/html'; + viewDetails = viewCollection.matchView('MyView1', request); }); it('should return a description of the best fitting view', () => { @@ -101,11 +107,11 @@ describe('ViewCollection', () => { }); describe('when matching a request of one view type as TriG', () => { - let viewDetails, request, response; + let viewDetails: ReturnType, request: LdfRequest; beforeAll(() => { - request = { headers: { accept: 'application/trig' } }; - response = {}; - viewDetails = viewCollection.matchView('MyView1', request, response); + request = createRequest(); + request.headers.accept = 'application/trig'; + viewDetails = viewCollection.matchView('MyView1', request); }); it('should return a description of the best fitting view', () => { @@ -116,11 +122,11 @@ describe('ViewCollection', () => { }); describe('when matching a request of another view type as HTML', () => { - let viewDetails, request, response; + let viewDetails: ReturnType, request: LdfRequest; beforeAll(() => { - request = { headers: { accept: 'text/html' } }; - response = {}; - viewDetails = viewCollection.matchView('MyView2', request, response); + request = createRequest(); + request.headers.accept = 'text/html'; + viewDetails = viewCollection.matchView('MyView2', request); }); it('should return a description of the other view', () => { diff --git a/packages/datasource-composite/lib/datasources/CompositeDatasource.ts b/packages/datasource-composite/lib/datasources/CompositeDatasource.ts index cbd5e613..3863294c 100644 --- a/packages/datasource-composite/lib/datasources/CompositeDatasource.ts +++ b/packages/datasource-composite/lib/datasources/CompositeDatasource.ts @@ -3,7 +3,7 @@ import { Datasource } from '@ldf/core/lib/datasources/Datasource'; import LRU = require('lru-cache'); -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions, DatasourceRegistry, Pushable, Query } from '@ldf/core'; interface CompositeDatasourceOptions extends DatasourceOptions { diff --git a/packages/datasource-composite/package.json b/packages/datasource-composite/package.json index 2d1082ea..69c60d0c 100644 --- a/packages/datasource-composite/package.json +++ b/packages/datasource-composite/package.json @@ -22,12 +22,14 @@ "lint": "eslint bin/* lib test" }, "dependencies": { + "@rdfjs/types": "^2.0.1", "lru-cache": "^5.1.1" }, + "devDependencies": { + "@ldf/core": "^3.2.1", + "n3": "^1.26.0" + }, "peerDependencies": { "@ldf/core": "^3.0.0" - }, - "devDependencies": { - "@ldf/core": "^3.2.1" } } diff --git a/packages/datasource-composite/test/datasources/CompositeDatasource-test.js b/packages/datasource-composite/test/datasources/CompositeDatasource-test.ts similarity index 69% rename from packages/datasource-composite/test/datasources/CompositeDatasource-test.js rename to packages/datasource-composite/test/datasources/CompositeDatasource-test.ts index a328dd0c..2620076c 100644 --- a/packages/datasource-composite/test/datasources/CompositeDatasource-test.js +++ b/packages/datasource-composite/test/datasources/CompositeDatasource-test.ts @@ -1,38 +1,47 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -let CompositeDatasource = require('../../').datasources.CompositeDatasource; - -let Datasource = require('@ldf/core').datasources.Datasource, - HdtDatasource = require('@ldf/datasource-hdt').datasources.HdtDatasource, - N3Datasource = require('@ldf/datasource-n3').datasources.N3Datasource, - path = require('path'), - dataFactory = require('n3').DataFactory; - -let EventEmitter = require('events'), - { once } = EventEmitter; +import { datasources as compositeDatasources } from '../../index'; +import { datasources as coreDatasources } from '@ldf/core'; +import { datasources as hdtDatasources } from '@ldf/datasource-hdt'; +import { datasources as n3Datasources } from '@ldf/datasource-n3'; +import type { DatasourceOptions, DatasourceRegistry, Query } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import * as path from 'path'; +import { DataFactory as dataFactory } from 'n3'; + +import { once } from 'events'; + +const { CompositeDatasource } = compositeDatasources; +const { Datasource } = coreDatasources; +const { HdtDatasource } = hdtDatasources; +const { N3Datasource } = n3Datasources; let exampleHdtFile = path.join(__dirname, '../../../../test/assets/test.hdt'); let exampleHdtFileWithBlanks = path.join(__dirname, '../../../../test/assets/test-blank.hdt'); let exampleTurtleUrl = 'file://' + path.join(__dirname, '../../../../test/assets/test.ttl'); let exampleTrigUrl = 'file://' + path.join(__dirname, '../../../../test/assets/test.trig'); +interface DatasourceReferenceConfig { + settings: DatasourceOptions; + datasourceType: new (options: DatasourceOptions) => InstanceType; + size: number; +} + describe('CompositeDatasource', () => { - let references = { - data0: { dataFactory, settings: { dataFactory, file: exampleHdtFile }, datasourceType: HdtDatasource, size: 132 }, - data1: { dataFactory, settings: { dataFactory, file: exampleHdtFileWithBlanks, graph: 'http://example.org/graph0' }, datasourceType: HdtDatasource, size: 6 }, - data2: { dataFactory, settings: { dataFactory, url: exampleTurtleUrl }, datasourceType: N3Datasource, size: 129 }, - data3: { dataFactory, settings: { dataFactory, url: exampleTrigUrl }, datasourceType: N3Datasource, size: 7 }, + let referenceConfigs: Record = { + data0: { settings: { dataFactory, file: exampleHdtFile }, datasourceType: HdtDatasource, size: 132 }, + data1: { settings: { dataFactory, file: exampleHdtFileWithBlanks, graph: 'http://example.org/graph0' }, datasourceType: HdtDatasource, size: 6 }, + data2: { settings: { dataFactory, url: exampleTurtleUrl }, datasourceType: N3Datasource, size: 129 }, + data3: { settings: { dataFactory, url: exampleTrigUrl }, datasourceType: N3Datasource, size: 7 }, }; - Object.keys(references).forEach((datasourceId) => { - let datasource = references[datasourceId]; - let DatasourceType = datasource.datasourceType; - let size = references[datasourceId].size; - references[datasourceId] = new DatasourceType(datasource.settings); - references[datasourceId].size = size; + let references: DatasourceRegistry = {}; + Object.keys(referenceConfigs).forEach((datasourceId) => { + let config = referenceConfigs[datasourceId]; + references[datasourceId] = new config.datasourceType(config.settings); }); - let totalSize = Object.keys(references).reduce((acc, key) => { - return acc + references[key].size; + let totalSize = Object.keys(referenceConfigs).reduce((acc, key) => { + return acc + referenceConfigs[key].size; }, 0); beforeAll(() => Promise.all(Object.keys(references).map(async (key) => { @@ -48,31 +57,31 @@ describe('CompositeDatasource', () => { it('should be an CompositeDatasource constructor', async () => { let instance = new CompositeDatasource({ references: references, dataFactory }); expect(instance).toBeInstanceOf(CompositeDatasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create CompositeDatasource objects', async () => { let instance = new CompositeDatasource({ references: references, dataFactory }); expect(instance).toBeInstanceOf(CompositeDatasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create Datasource objects', async () => { let instance = new CompositeDatasource({ references: references, dataFactory }); expect(instance).toBeInstanceOf(Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); }); describe('A CompositeDatasource instance for 4 Datasources', () => { - let datasource; + let datasource: InstanceType; function getDatasource() { return datasource; } beforeAll(async () => { datasource = new CompositeDatasource({ references: references, dataFactory }); datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(getDatasource, 'the empty query', @@ -161,14 +170,14 @@ describe('CompositeDatasource', () => { }); }); -function itShouldExecute(getDatasource, name, query, - expectedResultsCount, expectedTotalCount, expectedTriples) { +function itShouldExecute(getDatasource: () => InstanceType, name: string, query: Query, + expectedResultsCount: number, expectedTotalCount: number, expectedTriples?: Quad[]) { describe('executing ' + name, () => { - let resultsCount = 0, totalCount, triples = []; + let resultsCount = 0, totalCount: number | undefined, triples: Quad[] = []; beforeAll(async () => { let result = getDatasource().select(query); - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; }); - result.on('data', (triple) => { resultsCount++; expectedTriples && triples.push(triple); }); + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; }); + result.on('data', (triple: Quad) => { resultsCount++; expectedTriples && triples.push(triple); }); await once(result, 'end'); }); diff --git a/packages/datasource-hdt/lib/datasources/ExternalHdtDatasource.ts b/packages/datasource-hdt/lib/datasources/ExternalHdtDatasource.ts index 68ab1397..869708a1 100644 --- a/packages/datasource-hdt/lib/datasources/ExternalHdtDatasource.ts +++ b/packages/datasource-hdt/lib/datasources/ExternalHdtDatasource.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { N3ParserExtended as N3Parser } from '@ldf/core/lib/N3ParserExtended'; import { spawn } from 'child_process'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions, Pushable, Query } from '@ldf/core'; let hdtUtility = path.join(__dirname, '../../node_modules/.bin/hdt'); diff --git a/packages/datasource-hdt/lib/datasources/HdtDatasource.ts b/packages/datasource-hdt/lib/datasources/HdtDatasource.ts index 277369d4..b2c2f0c6 100644 --- a/packages/datasource-hdt/lib/datasources/HdtDatasource.ts +++ b/packages/datasource-hdt/lib/datasources/HdtDatasource.ts @@ -4,7 +4,7 @@ import { Datasource } from '@ldf/core/lib/datasources/Datasource'; import * as hdt from 'hdt'; import { ExternalHdtDatasource } from './ExternalHdtDatasource'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions, Pushable, Query } from '@ldf/core'; interface HdtDatasourceOptions extends DatasourceOptions { diff --git a/packages/datasource-hdt/package.json b/packages/datasource-hdt/package.json index 13106188..e221960f 100644 --- a/packages/datasource-hdt/package.json +++ b/packages/datasource-hdt/package.json @@ -22,14 +22,16 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "n3": "^1.3.5" + "@rdfjs/types": "^2.0.1", + "n3": "^1.26.0" + }, + "devDependencies": { + "@ldf/core": "^3.2.1", + "rdf-string": "^1.6.3" }, "peerDependencies": { "@ldf/core": "^3.0.0" }, - "devDependencies": { - "@ldf/core": "^3.2.1" - }, "optionalDependencies": { "hdt": "^3.3.2" } diff --git a/packages/datasource-hdt/test/datasources/HdtDatasource-test.js b/packages/datasource-hdt/test/datasources/HdtDatasource-test.ts similarity index 82% rename from packages/datasource-hdt/test/datasources/HdtDatasource-test.js rename to packages/datasource-hdt/test/datasources/HdtDatasource-test.ts index 6f4aecdb..01b4de0c 100644 --- a/packages/datasource-hdt/test/datasources/HdtDatasource-test.js +++ b/packages/datasource-hdt/test/datasources/HdtDatasource-test.ts @@ -1,14 +1,17 @@ /*! @license MIT ©2014-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -let HdtDatasource = require('../../').datasources.HdtDatasource; - -let Datasource = require('@ldf/core').datasources.Datasource, - UrlData = require('@ldf/core').UrlData, - path = require('path'), - dataFactory = require('n3').DataFactory, - RdfString = require('rdf-string'), - { once } = require('events'); +import { datasources as hdtDatasources } from '../../index'; +import { datasources as coreDatasources, UrlData } from '@ldf/core'; +import type { Query } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import { stringQuadToQuad, type IStringQuad } from 'rdf-string'; +import * as path from 'path'; +import { DataFactory as dataFactory } from 'n3'; +import { once } from 'events'; + +const { HdtDatasource } = hdtDatasources; +const { Datasource } = coreDatasources; let exampleHdtFile = path.join(__dirname, '../../../../test/assets/test.hdt'); let exampleHdtFileWithBlanks = path.join(__dirname, '../../../../test/assets/test-blank.hdt'); @@ -23,26 +26,26 @@ describe('HdtDatasource', () => { let instance = new HdtDatasource({ dataFactory, file: exampleHdtFile }); instance.initialize(); expect(instance).toBeInstanceOf(HdtDatasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create Datasource objects', async () => { let instance = new HdtDatasource({ dataFactory, file: exampleHdtFile }); instance.initialize(); expect(instance).toBeInstanceOf(Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); }); describe('A HdtDatasource instance for an example HDT file', () => { - let datasource; + let datasource: InstanceType; function getDatasource() { return datasource; } beforeAll(async () => { datasource = new HdtDatasource({ dataFactory, file: exampleHdtFile }); datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(getDatasource, 'the empty query', @@ -96,14 +99,14 @@ describe('HdtDatasource', () => { }); describe('A HdtDatasource instance with blank nodes', () => { - let datasource; + let datasource: InstanceType; function getDatasource() { return datasource; } beforeAll(async () => { datasource = new HdtDatasource({ dataFactory, file: exampleHdtFileWithBlanks }); datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(getDatasource, 'the empty query', @@ -143,7 +146,7 @@ describe('HdtDatasource', () => { }); describe('A HdtDatasource instance with blank nodes and a blank node prefix', () => { - let datasource; + let datasource: InstanceType; function getDatasource() { return datasource; } beforeAll(async () => { datasource = new HdtDatasource({ @@ -154,7 +157,7 @@ describe('HdtDatasource', () => { datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(getDatasource, 'the empty query', @@ -194,14 +197,14 @@ describe('HdtDatasource', () => { }); }); -function itShouldExecute(getDatasource, name, query, - expectedResultsCount, expectedTotalCount, expectedTriples) { +function itShouldExecute(getDatasource: () => InstanceType, name: string, query: Query, + expectedResultsCount: number, expectedTotalCount: number, expectedTriples?: IStringQuad[]) { describe('executing ' + name, () => { - let resultsCount = 0, totalCount, triples = []; + let resultsCount = 0, totalCount: number | undefined, triples: Quad[] = []; beforeAll(async () => { let result = getDatasource().select(query); - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; }); - result.on('data', (triple) => { resultsCount++; expectedTriples && triples.push(triple); }); + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; }); + result.on('data', (triple: Quad) => { resultsCount++; expectedTriples && triples.push(triple); }); await once(result, 'end'); }); @@ -217,7 +220,7 @@ function itShouldExecute(getDatasource, name, query, it('should emit the expected triples', () => { expect(triples.length).toBe(expectedTriples.length); for (let i = 0; i < expectedTriples.length; i++) - expect(triples[i]).toEqual(RdfString.stringQuadToQuad(expectedTriples[i], dataFactory)); + expect(triples[i]).toEqual(stringQuadToQuad(expectedTriples[i], dataFactory)); }); } }); diff --git a/packages/datasource-jsonld/lib/datasources/JsonLdDatasource.ts b/packages/datasource-jsonld/lib/datasources/JsonLdDatasource.ts index f9952f2b..287c9009 100644 --- a/packages/datasource-jsonld/lib/datasources/JsonLdDatasource.ts +++ b/packages/datasource-jsonld/lib/datasources/JsonLdDatasource.ts @@ -3,7 +3,7 @@ import { MemoryDatasource } from '@ldf/core/lib/datasources/MemoryDatasource'; import { JsonLdParser } from 'jsonld-streaming-parser'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions } from '@ldf/core'; let ACCEPT = 'application/ld+json;q=1.0,application/json;q=0.7'; diff --git a/packages/datasource-jsonld/package.json b/packages/datasource-jsonld/package.json index 6d74d79c..ee583e8b 100644 --- a/packages/datasource-jsonld/package.json +++ b/packages/datasource-jsonld/package.json @@ -22,13 +22,15 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "jsonld-streaming-parser": "^4.0.0", - "rdf-string": "^1.3.1" + "@rdfjs/types": "^2.0.1", + "jsonld-streaming-parser": "^5.0.1", + "rdf-string": "^1.6.3" + }, + "devDependencies": { + "@ldf/core": "^3.2.1", + "n3": "^1.26.0" }, "peerDependencies": { "@ldf/core": "^3.0.0" - }, - "devDependencies": { - "@ldf/core": "^3.2.1" } } diff --git a/packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.js b/packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.ts similarity index 79% rename from packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.js rename to packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.ts index 73191087..c221ab70 100644 --- a/packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.js +++ b/packages/datasource-jsonld/test/datasources/JsonLdDatasource-test.ts @@ -2,11 +2,15 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { once } from 'events'; -let JsonLdDatasource = require('../../').datasources.JsonLdDatasource; +import { datasources as jsonLdDatasources } from '../../index'; +import { datasources as coreDatasources } from '@ldf/core'; +import type { Query } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import * as path from 'path'; +import { DataFactory as dataFactory } from 'n3'; -let Datasource = require('@ldf/core').datasources.Datasource, - path = require('path'), - dataFactory = require('n3').DataFactory; +const { JsonLdDatasource } = jsonLdDatasources; +const { Datasource } = coreDatasources; let exampleJsonLdUrl = 'file://' + path.join(__dirname, '../../../../test/assets/test.jsonld'); @@ -19,13 +23,13 @@ describe('JsonLdDatasource', () => { it('should be a JsonLdDatasource constructor', async () => { let instance = new JsonLdDatasource({ dataFactory, url: exampleJsonLdUrl }); expect(instance).toBeInstanceOf(JsonLdDatasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create Datasource objects', async () => { let instance = new JsonLdDatasource({ dataFactory, url: exampleJsonLdUrl }); expect(instance).toBeInstanceOf(Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); }); @@ -35,7 +39,7 @@ describe('JsonLdDatasource', () => { datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(datasource, 'the empty query', @@ -94,13 +98,13 @@ describe('JsonLdDatasource', () => { }); }); -function itShouldExecute(datasource, name, query, expectedResultsCount, expectedTotalCount) { +function itShouldExecute(datasource: InstanceType, name: string, query: Query, expectedResultsCount: number, expectedTotalCount: number) { describe('executing ' + name, () => { - let resultsCount = 0, totalCount; + let resultsCount = 0, totalCount: number | undefined; beforeAll(async () => { let result = datasource.select(query); - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; }); - result.on('data', (triple) => { resultsCount++; }); + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; }); + result.on('data', (_triple: Quad) => { resultsCount++; }); await once(result, 'end'); }); diff --git a/packages/datasource-n3/lib/datasources/N3Datasource.ts b/packages/datasource-n3/lib/datasources/N3Datasource.ts index f0ddedbf..835bd483 100644 --- a/packages/datasource-n3/lib/datasources/N3Datasource.ts +++ b/packages/datasource-n3/lib/datasources/N3Datasource.ts @@ -3,7 +3,7 @@ import { MemoryDatasource } from '@ldf/core/lib/datasources/MemoryDatasource'; import { N3ParserExtended as N3Parser } from '@ldf/core/lib/N3ParserExtended'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions } from '@ldf/core'; let ACCEPT = 'application/trig;q=1.0,application/n-quads;q=0.9,text/turtle;q=0.8,application/n-triples;q=0.7,text/n3;q=0.4'; diff --git a/packages/datasource-n3/package.json b/packages/datasource-n3/package.json index 284ab126..e13b19e1 100644 --- a/packages/datasource-n3/package.json +++ b/packages/datasource-n3/package.json @@ -22,7 +22,8 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "n3": "^1.3.6" + "@rdfjs/types": "^2.0.1", + "n3": "^1.26.0" }, "peerDependencies": { "@ldf/core": "^3.0.0" diff --git a/packages/datasource-n3/test/datasources/N3Datasource-test.js b/packages/datasource-n3/test/datasources/N3Datasource-test.ts similarity index 77% rename from packages/datasource-n3/test/datasources/N3Datasource-test.js rename to packages/datasource-n3/test/datasources/N3Datasource-test.ts index bfe8fba2..186e209d 100644 --- a/packages/datasource-n3/test/datasources/N3Datasource-test.js +++ b/packages/datasource-n3/test/datasources/N3Datasource-test.ts @@ -2,11 +2,15 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { once } from 'events'; -let N3Datasource = require('../../').datasources.N3Datasource; +import { datasources as n3Datasources } from '../../index'; +import { datasources as coreDatasources } from '@ldf/core'; +import type { Query } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import * as path from 'path'; +import { DataFactory as dataFactory } from 'n3'; -let Datasource = require('@ldf/core').datasources.Datasource, - path = require('path'), - dataFactory = require('n3').DataFactory; +const { N3Datasource } = n3Datasources; +const { Datasource } = coreDatasources; let exampleTurtleUrl = 'file://' + path.join(__dirname, '../../../../test/assets/test.ttl'); @@ -19,13 +23,13 @@ describe('N3Datasource', () => { it('should be a N3Datasource constructor', async () => { let instance = new N3Datasource({ dataFactory, url: exampleTurtleUrl }); expect(instance).toBeInstanceOf(N3Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create Datasource objects', async () => { let instance = new N3Datasource({ dataFactory, url: exampleTurtleUrl }); expect(instance).toBeInstanceOf(Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); }); @@ -35,7 +39,7 @@ describe('N3Datasource', () => { datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(datasource, 'the empty query', @@ -84,13 +88,13 @@ describe('N3Datasource', () => { }); }); -function itShouldExecute(datasource, name, query, expectedResultsCount, expectedTotalCount) { +function itShouldExecute(datasource: InstanceType, name: string, query: Query, expectedResultsCount: number, expectedTotalCount: number) { describe('executing ' + name, () => { - let resultsCount = 0, totalCount; + let resultsCount = 0, totalCount: number | undefined; beforeAll(async () => { let result = datasource.select(query); - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; }); - result.on('data', (triple) => { resultsCount++; }); + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; }); + result.on('data', (_triple: Quad) => { resultsCount++; }); await once(result, 'end'); }); diff --git a/packages/datasource-rdfa/lib/datasources/RdfaDatasource.ts b/packages/datasource-rdfa/lib/datasources/RdfaDatasource.ts index 639cf9a3..285beedb 100644 --- a/packages/datasource-rdfa/lib/datasources/RdfaDatasource.ts +++ b/packages/datasource-rdfa/lib/datasources/RdfaDatasource.ts @@ -3,7 +3,7 @@ import { MemoryDatasource } from '@ldf/core/lib/datasources/MemoryDatasource'; import { RdfaParser } from 'rdfa-streaming-parser'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { DatasourceOptions } from '@ldf/core'; let ACCEPT = 'text/html;q=1.0,application/xhtml+xml;q=0.7'; diff --git a/packages/datasource-rdfa/package.json b/packages/datasource-rdfa/package.json index b1d38124..d81a0f8d 100644 --- a/packages/datasource-rdfa/package.json +++ b/packages/datasource-rdfa/package.json @@ -22,13 +22,15 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "rdf-string": "^1.3.1", - "rdfa-streaming-parser": "^1.5.0" + "@rdfjs/types": "^2.0.1", + "rdf-string": "^1.6.3", + "rdfa-streaming-parser": "^3.0.2" + }, + "devDependencies": { + "@ldf/core": "^3.2.1", + "n3": "^1.26.0" }, "peerDependencies": { "@ldf/core": "^3.0.0" - }, - "devDependencies": { - "@ldf/core": "^3.2.1" } } diff --git a/packages/datasource-rdfa/test/datasources/RdfaDatasource-test.js b/packages/datasource-rdfa/test/datasources/RdfaDatasource-test.ts similarity index 77% rename from packages/datasource-rdfa/test/datasources/RdfaDatasource-test.js rename to packages/datasource-rdfa/test/datasources/RdfaDatasource-test.ts index e54903ff..d2baa302 100644 --- a/packages/datasource-rdfa/test/datasources/RdfaDatasource-test.js +++ b/packages/datasource-rdfa/test/datasources/RdfaDatasource-test.ts @@ -2,11 +2,15 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { once } from 'events'; -let RdfaDatasource = require('../../').datasources.RdfaDatasource; +import { datasources as rdfaDatasources } from '../../index'; +import { datasources as coreDatasources } from '@ldf/core'; +import type { Query } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import * as path from 'path'; +import { DataFactory as dataFactory } from 'n3'; -let Datasource = require('@ldf/core').datasources.Datasource, - path = require('path'), - dataFactory = require('n3').DataFactory; +const { RdfaDatasource } = rdfaDatasources; +const { Datasource } = coreDatasources; let exampleRdfaUrl = 'file://' + path.join(__dirname, '../../../../test/assets/test.html'); @@ -19,13 +23,13 @@ describe('RdfaDatasource', () => { it('should be a RdfaDatasource constructor', async () => { let instance = new RdfaDatasource({ dataFactory, url: exampleRdfaUrl }); expect(instance).toBeInstanceOf(RdfaDatasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); it('should create Datasource objects', async () => { let instance = new RdfaDatasource({ dataFactory, url: exampleRdfaUrl }); expect(instance).toBeInstanceOf(Datasource); - await new Promise((resolve) => instance.close(resolve)); + await new Promise((resolve) => instance.close(resolve)); }); }); @@ -35,7 +39,7 @@ describe('RdfaDatasource', () => { datasource.initialize(); await once(datasource, 'initialized'); }); - afterAll(() => new Promise((resolve) => datasource.close(resolve))); + afterAll(() => new Promise((resolve) => datasource.close(resolve))); itShouldExecute(datasource, 'the empty query', @@ -84,13 +88,13 @@ describe('RdfaDatasource', () => { }); }); -function itShouldExecute(datasource, name, query, expectedResultsCount, expectedTotalCount) { +function itShouldExecute(datasource: InstanceType, name: string, query: Query, expectedResultsCount: number, expectedTotalCount: number) { describe('executing ' + name, () => { - let resultsCount = 0, totalCount; + let resultsCount = 0, totalCount: number | undefined; beforeAll(async () => { let result = datasource.select(query); - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; }); - result.on('data', (triple) => { resultsCount++; }); + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; }); + result.on('data', (_triple: Quad) => { resultsCount++; }); await once(result, 'end'); }); diff --git a/packages/datasource-sparql/lib/datasources/SparqlDatasource.ts b/packages/datasource-sparql/lib/datasources/SparqlDatasource.ts index b074339d..1a9d0973 100644 --- a/packages/datasource-sparql/lib/datasources/SparqlDatasource.ts +++ b/packages/datasource-sparql/lib/datasources/SparqlDatasource.ts @@ -5,7 +5,7 @@ import { Datasource } from '@ldf/core/lib/datasources/Datasource'; import { SparqlJsonParser } from 'sparqljson-parse'; import LRU = require('lru-cache'); import type { IBindings } from 'sparqljson-parse'; -import type { Literal, NamedNode, Quad, Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject, Term } from 'rdf-js'; +import type { Literal, NamedNode, Quad, Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject, Term } from '@rdfjs/types'; import type { DatasourceOptions, Pushable, Query } from '@ldf/core'; interface SparqlDatasourceOptions extends DatasourceOptions { diff --git a/packages/datasource-sparql/package.json b/packages/datasource-sparql/package.json index 3215fe83..d78a8a07 100644 --- a/packages/datasource-sparql/package.json +++ b/packages/datasource-sparql/package.json @@ -22,15 +22,17 @@ "lint": "eslint bin/* lib test" }, "dependencies": { + "@rdfjs/types": "^2.0.1", "lru-cache": "^5.1.1", - "n3": "^1.3.5", - "rdf-string": "^1.3.1", - "sparqljson-parse": "^2.0.0" + "n3": "^1.26.0", + "rdf-string": "^1.6.3", + "sparqljson-parse": "^3.3.0" + }, + "devDependencies": { + "@ldf/core": "^3.2.1", + "asynciterator": "^3.10.0" }, "peerDependencies": { "@ldf/core": "^3.0.0" - }, - "devDependencies": { - "@ldf/core": "^3.2.1" } } diff --git a/packages/datasource-sparql/test/datasources/SparqlDatasource-test.js b/packages/datasource-sparql/test/datasources/SparqlDatasource-test.ts similarity index 84% rename from packages/datasource-sparql/test/datasources/SparqlDatasource-test.js rename to packages/datasource-sparql/test/datasources/SparqlDatasource-test.ts index b39dcdcb..c9a5ee3f 100644 --- a/packages/datasource-sparql/test/datasources/SparqlDatasource-test.js +++ b/packages/datasource-sparql/test/datasources/SparqlDatasource-test.ts @@ -1,28 +1,49 @@ /*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, vi } from 'vitest'; +import type { Mock } from 'vitest'; import { createHttpResponse, streamLength } from '../../../../test/test-helpers'; import { once } from 'events'; -let SparqlDatasource = require('../../').datasources.SparqlDatasource; +import { datasources as sparqlDatasources } from '../../index'; +import { datasources as coreDatasources } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import type { Query } from '@ldf/core'; +import type { AsyncIterator } from 'asynciterator'; +import * as fs from 'fs'; +import * as path from 'path'; +import { parse as parseUrl } from 'url'; +import { DataFactory as dataFactory } from 'n3'; + +const { SparqlDatasource } = sparqlDatasources; +const { Datasource } = coreDatasources; + +type HttpResponse = ReturnType; + +let jsonResult = fs.readFileSync(path.join(__dirname, '../../../../test/assets/sparql-quads-response.json'), 'utf8'); +let countResult = '"c"\n12345678\n'; -let Datasource = require('@ldf/core').datasources.Datasource, - fs = require('fs'), - path = require('path'), - URL = require('url'), - dataFactory = require('n3').DataFactory; +interface RequestOptions { + url: string; + headers?: Record; + timeout?: number; +} +type RequestCallback = (error?: { message: string }) => void; -let jsonResult = fs.readFileSync(path.join(__dirname, '../../../../test/assets/sparql-quads-response.json')); -let countResult = '"c"\n12345678\n'; +interface RequestStub extends Mock<(options: RequestOptions, callback?: RequestCallback) => HttpResponse> { + onFirstCall(value: HttpResponse): void; + onSecondCall(value: HttpResponse): void; +} // Mimics sinon's onFirstCall/onSecondCall: each configured return value stays // tied to that call index until overridden, and survives mockClear (unlike // mockReturnValueOnce, which is consumed after a single use). -function createRequestStub() { - let responses = []; - let request = vi.fn(() => responses[request.mock.calls.length - 1]); - request.onFirstCall = (value) => { responses[0] = value; }; - request.onSecondCall = (value) => { responses[1] = value; }; - return request; +function createRequestStub(): RequestStub { + let responses: HttpResponse[] = []; + let fn = vi.fn(() => responses[fn.mock.calls.length - 1]); + return Object.assign(fn, { + onFirstCall: (value: HttpResponse) => { responses[0] = value; }, + onSecondCall: (value: HttpResponse) => { responses[1] = value; }, + }); } describe('SparqlDatasource', () => { @@ -68,7 +89,7 @@ describe('SparqlDatasource', () => { }); it('should throw an error when trying to execute an unsupported query', async () => { - let error = await new Promise((resolve) => datasource.select({ features: { a: true, b: true } }, resolve)); + let error = await new Promise((resolve) => datasource.select({ features: { a: true, b: true } }, resolve)); expect(error).toBeInstanceOf(Error); expect(error).toHaveProperty('message', 'The datasource does not support the given query'); }); @@ -207,7 +228,7 @@ describe('SparqlDatasource', () => { null /* count should be cached, since this pattern already occurred above */); describe('when invalid JSON is returned in response to the data query', () => { - let result, error; + let result: AsyncIterator, error: Error; beforeAll(async () => { request.mockClear(); request.onFirstCall(createHttpResponse('invalid', 'application/sparql-results+json')); @@ -223,7 +244,7 @@ describe('SparqlDatasource', () => { }); describe('when invalid JSON is returned in response to the count query', () => { - let result, error; + let result: AsyncIterator, error: Error; beforeAll(async () => { request.mockClear(); request.onFirstCall(createHttpResponse(jsonResult, 'application/sparql-results+json')); @@ -239,13 +260,13 @@ describe('SparqlDatasource', () => { }); describe('when the data query request errors', () => { - let result, error; + let result: AsyncIterator, error: Error; beforeAll(async () => { request.mockClear(); let query = { subject: dataFactory.namedNode('abcde'), features: { quadPattern: true } }; result = datasource.select(query); let errorEvent = once(result, 'error'); - request.mock.calls[0][1](new Error('query response error')); + (request.mock.calls[0][1] as RequestCallback)(new Error('query response error')); [error] = await errorEvent; }); @@ -255,14 +276,16 @@ describe('SparqlDatasource', () => { }); describe('when the count query request errors', () => { - let totalCount; + let totalCount: number | undefined; beforeAll(async () => { request.mockClear(); let query = { subject: dataFactory.namedNode('abcdef'), features: { quadPattern: true } }; let result = datasource.select(query); - request.mock.results[1].value.emit('error', new Error()); - await new Promise((resolve) => { - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; resolve(); }); + let secondCallResult = request.mock.results[1]; + if (secondCallResult.type === 'return') + secondCallResult.value.emit('error', new Error()); + await new Promise((resolve) => { + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; resolve(); }); }); }); @@ -309,31 +332,31 @@ describe('SparqlDatasource', () => { }); }); -function itShouldExecute(datasource, request, name, query, constructQuery, countQuery) { +function itShouldExecute(datasource: InstanceType, request: RequestStub, name: string, query: Query, constructQuery: string, countQuery: string | null) { describe('executing ' + name, () => { - let result, totalCount; + let result: AsyncIterator, totalCount: number | undefined; beforeAll(async () => { request.mockClear(); request.onFirstCall(createHttpResponse(jsonResult, 'application/sparql-results+json')); request.onSecondCall(createHttpResponse(countResult, 'text/csv')); result = datasource.select(query); - await new Promise((resolve) => { - result.getProperty('metadata', (metadata) => { totalCount = metadata.totalCount; resolve(); }); + await new Promise((resolve) => { + result.getProperty('metadata', (metadata: { totalCount: number }) => { totalCount = metadata.totalCount; resolve(); }); }); }); it('should request a matching CONSTRUCT query', () => { expect(request).toHaveBeenCalled(); - let url = URL.parse(request.mock.calls[0][0].url, true); - expect(url.protocol + '//' + url.host + url.pathname).toBe('http://ex.org/sparql'); + let url = parseUrl(request.mock.calls[0][0].url, true); + expect(`${String(url.protocol)}//${String(url.host)}${String(url.pathname)}`).toBe('http://ex.org/sparql'); expect(url.query.query).toBe(constructQuery); }); if (countQuery) { it('should request a matching COUNT query', () => { expect(request).toHaveBeenCalledTimes(2); - let url = URL.parse(request.mock.calls[1][0].url, true); - expect(url.protocol + '//' + url.host + url.pathname).toBe('http://ex.org/sparql'); + let url = parseUrl(request.mock.calls[1][0].url, true); + expect(`${String(url.protocol)}//${String(url.host)}${String(url.pathname)}`).toBe('http://ex.org/sparql'); expect(url.query.query).toBe(countQuery); }); } diff --git a/packages/feature-qpf/lib/controllers/QuadPatternFragmentsController.ts b/packages/feature-qpf/lib/controllers/QuadPatternFragmentsController.ts index 5842c671..54b986a2 100644 --- a/packages/feature-qpf/lib/controllers/QuadPatternFragmentsController.ts +++ b/packages/feature-qpf/lib/controllers/QuadPatternFragmentsController.ts @@ -5,14 +5,10 @@ import { Controller } from '@ldf/core/lib/controllers/Controller'; import * as url from 'url'; import * as _ from 'lodash'; import type { ParsedUrlQuery } from 'querystring'; -import type { ControllerOptions, LdfRequestWithUrl, LdfResponse, Query, QueryFeatures, RouterRequest, ViewSettings } from '@ldf/core'; +import type { ControllerOptions, LdfRequestWithUrl, LdfResponse, Query, QueryFeatures, Router, RouterRequest, ViewSettings } from '@ldf/core'; import type { Datasource } from '@ldf/core/lib/datasources/Datasource'; import type { FragmentInfo } from '../views/quadpatternfragments/QuadPatternFragmentsRdfView'; -interface Router { - extractQueryParams(request: RouterRequest, query: Query): void; -} - interface QuadPatternFragmentsControllerOptions extends ControllerOptions { routers?: Router[]; extensions?: Controller[]; diff --git a/packages/feature-qpf/lib/routers/QuadPatternRouter.ts b/packages/feature-qpf/lib/routers/QuadPatternRouter.ts index 0055fd9e..9a1508ae 100644 --- a/packages/feature-qpf/lib/routers/QuadPatternRouter.ts +++ b/packages/feature-qpf/lib/routers/QuadPatternRouter.ts @@ -2,8 +2,8 @@ /** A QuadPatternRouter routes basic quad patterns */ import { stringToTerm } from 'rdf-string'; -import type { DataFactory, Term } from 'rdf-js'; -import type { Query, RouterRequest } from '@ldf/core'; +import type { DataFactory, Term } from '@rdfjs/types'; +import type { Query, Router, RouterRequest } from '@ldf/core'; let iriMatcher = /^(][^"<>]*)>?$/; let literalMatcher = /^("[^]*")(?:|\^\^]+)>?|@[a-z0-9\-]+)$/i; @@ -20,7 +20,7 @@ interface QuadPatternRouterConfig { } // Creates a new QuadPatternRouter -export class QuadPatternRouter { +export class QuadPatternRouter implements Router { protected _prefixes: Record; dataFactory?: DataFactory; diff --git a/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsHtmlView.ts b/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsHtmlView.ts index 1186c5a5..534e6ade 100644 --- a/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsHtmlView.ts +++ b/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsHtmlView.ts @@ -4,7 +4,7 @@ import { HtmlView } from '@ldf/core/lib/views/HtmlView'; import { join } from 'path'; import type { AsyncIterator } from 'asynciterator'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { LdfRequest, LdfResponse, RenderDone, ViewSettings } from '@ldf/core'; import type { IndexDatasource } from '@ldf/core/lib/datasources/IndexDatasource'; diff --git a/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsRdfView.ts b/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsRdfView.ts index 483ca1be..f0205e89 100644 --- a/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsRdfView.ts +++ b/packages/feature-qpf/lib/views/quadpatternfragments/QuadPatternFragmentsRdfView.ts @@ -5,7 +5,7 @@ import { RdfView } from '@ldf/core/lib/views/RdfView'; import { stringQuadToQuad } from 'rdf-string'; import type { IStringQuad } from 'rdf-string'; import type { AsyncIterator } from 'asynciterator'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { Query, RdfViewSettings, RenderDone, ViewSettings } from '@ldf/core'; import type { Datasource } from '@ldf/core/lib/datasources/Datasource'; diff --git a/packages/feature-qpf/package.json b/packages/feature-qpf/package.json index 35a9a2fc..ffc1f84e 100644 --- a/packages/feature-qpf/package.json +++ b/packages/feature-qpf/package.json @@ -23,9 +23,11 @@ "lint": "eslint bin/* lib test" }, "dependencies": { + "@rdfjs/types": "^2.0.1", + "asynciterator": "^3.10.0", "lodash": "^4.0.0", - "n3": "^1.3.5", - "rdf-string": "^1.3.1" + "n3": "^1.26.0", + "rdf-string": "^1.6.3" }, "peerDependencies": { "@ldf/core": "^3.0.0" diff --git a/packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.js b/packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.ts similarity index 53% rename from packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.js rename to packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.ts index 97d463e8..a9811f30 100644 --- a/packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.js +++ b/packages/feature-qpf/test/controllers/QuadPatternFragmentsController-test.ts @@ -1,16 +1,25 @@ /*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ import { describe, it, expect, beforeAll, vi } from 'vitest'; +import type { Mock } from 'vitest'; import { DummyServer } from '../../../../test/DummyServer'; -let QuadPatternFragmentsController = require('../../').controllers.QuadPatternFragmentsController; +import { request, type FetchLikeResponse } from '../../../../test/test-helpers'; +import { QuadPatternFragmentsController } from '../../lib/controllers/QuadPatternFragmentsController'; +import { QuadPatternFragmentsHtmlView } from '../../lib/views/quadpatternfragments/QuadPatternFragmentsHtmlView'; +import { QuadPatternFragmentsRdfView } from '../../lib/views/quadpatternfragments/QuadPatternFragmentsRdfView'; +import { datasources as coreDatasources, UrlData } from '@ldf/core'; +import type { Datasource as DatasourceType } from '@ldf/core/lib/datasources/Datasource'; +import type { DatasourceRegistry, Query, QueryFeatures, RouterRequest } from '@ldf/core'; +import type { Quad } from '@rdfjs/types'; +import { empty } from 'asynciterator'; +import type { AsyncIterator } from 'asynciterator'; -let request = require('supertest'), - http = require('http'); +import * as http from 'http'; +import { DataFactory as dataFactory } from 'n3'; -let QuadPatternFragmentsHtmlView = require('../../').views.quadpatternfragments.QuadPatternFragmentsHtmlView, - QuadPatternFragmentsRdfView = require('../../').views.quadpatternfragments.QuadPatternFragmentsRdfView, - UrlData = require('@ldf/core').UrlData, - dataFactory = require('n3').DataFactory; +const { Datasource } = coreDatasources; + +type MutableQuery = Query & { features: QueryFeatures }; describe('QuadPatternFragmentsController', () => { describe('The QuadPatternFragmentsController module', () => { @@ -24,27 +33,40 @@ describe('QuadPatternFragmentsController', () => { }); describe('A QuadPatternFragmentsController instance with 3 routers', () => { - let controller, client, routerA, routerB, routerC, datasource, datasources, view, prefixes; + let controller: QuadPatternFragmentsController, + server: DummyServer, + routerA: { extractQueryParams: Mock<(request: RouterRequest, query: Query) => void> }, + routerB: { extractQueryParams: Mock<(request: RouterRequest, query: Query) => void> }, + routerC: { extractQueryParams: Mock<(request: RouterRequest, query: MutableQuery) => void> }, + datasource: DatasourceType, + supportsQuerySpy: Mock<(query: Query) => boolean>, + selectSpy: Mock<(query: Query) => AsyncIterator>, + selectResult: AsyncIterator, + datasources: DatasourceRegistry, + view: QuadPatternFragmentsRdfView, + renderSpy: Mock, + prefixes: Record; beforeAll(() => { routerA = { extractQueryParams: vi.fn() }; routerB = { extractQueryParams: vi.fn(() => { throw new Error('second router error'); }) }; routerC = { - extractQueryParams: vi.fn((request, query) => { + extractQueryParams: vi.fn((request: RouterRequest, query: MutableQuery) => { query.features.datasource = true; query.features.other = true; query.datasource = '/my-datasource'; - query.other = 'other'; }), }; - datasource = { - title: 'My data', - supportsQuery: vi.fn().mockReturnValue(true), - select: vi.fn().mockReturnValue({ stream: 'items' }), - supportedFeatures: { quadPattern: true }, - }; + datasource = new Datasource({ dataFactory, title: 'My data' }); + supportsQuerySpy = vi.fn().mockReturnValue(true); + datasource.supportsQuery = supportsQuerySpy; + selectResult = empty(); + selectResult.setProperty('metadata', {}); + selectSpy = vi.fn().mockReturnValue(selectResult); + datasource.select = selectSpy; + datasource.supportedFeatures = { quadPattern: true }; datasources = { 'my-datasource': datasource }; - view = new QuadPatternFragmentsRdfView({ dataFactory }), - vi.spyOn(view, 'render'); + view = new QuadPatternFragmentsRdfView({ dataFactory }); + renderSpy = vi.spyOn(view, 'render'); prefixes = { a: 'a' }; controller = new QuadPatternFragmentsController({ urlData: new UrlData({ baseURL: 'https://example.org/base/?bar=foo' }), @@ -53,20 +75,20 @@ describe('QuadPatternFragmentsController', () => { views: [view], prefixes: prefixes, }); - client = request.agent(new DummyServer(controller), {}); + server = new DummyServer(controller); }); function resetAll() { routerA.extractQueryParams.mockClear(); routerB.extractQueryParams.mockClear(); routerC.extractQueryParams.mockClear(); - datasource.supportsQuery.mockClear(); - datasource.select.mockClear(); + supportsQuerySpy.mockClear(); + selectSpy.mockClear(); } describe('receiving a request for a fragment', () => { beforeAll(async () => { resetAll(); - await client.get('/my-datasource?a=b&c=d'); + await request(server, '/my-datasource?a=b&c=d'); }); it('should call the first router with the request and an empty query', () => { @@ -77,7 +99,7 @@ describe('QuadPatternFragmentsController', () => { expect(args[0].url).toHaveProperty('path', '/my-datasource?a=b&c=d'); expect(args[0].url).toHaveProperty('pathname', '/my-datasource'); expect(args[0].url).toHaveProperty('query'); - expect(args[0].url.query).toEqual({ a: 'b', c: 'd' }); + expect(args[0].url?.query).toEqual({ a: 'b', c: 'd' }); expect(typeof args[1]).toBe('object'); expect(args[1]).toHaveProperty('features'); @@ -104,19 +126,19 @@ describe('QuadPatternFragmentsController', () => { it('should verify whether the data source supports the query', () => { let query = routerC.extractQueryParams.mock.calls[0][1]; - expect(datasource.supportsQuery).toHaveBeenCalledOnce(); - expect(datasource.supportsQuery).toHaveBeenCalledWith(query); + expect(supportsQuerySpy).toHaveBeenCalledOnce(); + expect(supportsQuerySpy).toHaveBeenCalledWith(query); }); it('should send the query to the right data source', () => { let query = routerC.extractQueryParams.mock.calls[0][1]; - expect(datasource.select).toHaveBeenCalledOnce(); - expect(datasource.select.mock.calls[0][0]).toBe(query); + expect(selectSpy).toHaveBeenCalledOnce(); + expect(selectSpy.mock.calls[0][0]).toBe(query); }); it('should pass the query result to the output view', () => { - expect(view.render).toHaveBeenCalledOnce(); - let args = view.render.mock.calls[0]; + expect(renderSpy).toHaveBeenCalledOnce(); + let args = renderSpy.mock.calls[0]; expect(typeof args[0]).toBe('object'); // settings expect(args[1]).toBeInstanceOf(http.IncomingMessage); @@ -124,9 +146,9 @@ describe('QuadPatternFragmentsController', () => { }); it('should pass the correct settings to the view', () => { - expect(view.render).toHaveBeenCalledOnce(); + expect(renderSpy).toHaveBeenCalledOnce(); let query = routerC.extractQueryParams.mock.calls[0][1]; - let settings = view.render.mock.calls[0][0]; + let settings = renderSpy.mock.calls[0][0]; expect(settings.datasource).toHaveProperty('title', 'My data'); expect(settings.datasource).toHaveProperty('index', 'https://example.org/#dataset'); @@ -140,9 +162,7 @@ describe('QuadPatternFragmentsController', () => { nextPageUrl: 'https://example.org/my-datasource?a=b&c=d&page=2', previousPageUrl: null, }); - expect(settings.results).toEqual({ - stream: 'items', - }); + expect(settings.results).toBe(selectResult); expect(settings.prefixes).toEqual(prefixes); expect(settings.query).toEqual(query); expect(settings.datasources).toEqual({ '/my-datasource': datasource }); @@ -153,170 +173,170 @@ describe('QuadPatternFragmentsController', () => { describe('receiving a request for an unsupported fragment', () => { beforeAll(async () => { resetAll(); - datasource.supportsQuery = vi.fn().mockReturnValue(false); - await client.get('/my-datasource?a=b&c=d'); + supportsQuerySpy = vi.fn().mockReturnValue(false); + datasource.supportsQuery = supportsQuerySpy; + await request(server, '/my-datasource?a=b&c=d'); }); it('should verify whether the data source supports the query', () => { let query = routerC.extractQueryParams.mock.calls[0][1]; - expect(datasource.supportsQuery).toHaveBeenCalledOnce(); - expect(datasource.supportsQuery).toHaveBeenCalledWith(query); + expect(supportsQuerySpy).toHaveBeenCalledOnce(); + expect(supportsQuerySpy).toHaveBeenCalledWith(query); }); it('should not send the query to the data source', () => { - expect(datasource.select).not.toHaveBeenCalled(); + expect(selectSpy).not.toHaveBeenCalled(); }); }); }); describe('A QuadPatternFragmentsController instance with 2 views', () => { - let controller, client, htmlView, rdfView; + let controller: QuadPatternFragmentsController, + server: DummyServer, + htmlView: QuadPatternFragmentsHtmlView, rdfView: QuadPatternFragmentsRdfView, + htmlRenderSpy: Mock, + rdfRenderSpy: Mock; beforeAll(() => { - let datasource = { - supportsQuery: vi.fn().mockReturnValue(true), - select: vi.fn().mockReturnValue({ - // Mocks AsyncIterator's own on(event, callback) signature. - // eslint-disable-next-line promise/prefer-await-to-callbacks - on: function (event, callback) { - if (event === 'end' || event === 'metadata') - setImmediate(callback, {}); - }, - }), - supportedFeatures: { triplePattern: true }, - }; + let datasource = new Datasource({ dataFactory }); + datasource.supportsQuery = vi.fn().mockReturnValue(true); + datasource.select = vi.fn(() => { + let it = empty(); + it.setProperty('metadata', {}); + return it; + }); + datasource.supportedFeatures = { triplePattern: true }; let router = { - extractQueryParams: function (request, query) { + extractQueryParams: function (request: RouterRequest, query: MutableQuery) { query.features.datasource = true; query.datasource = '/my-datasource'; }, }; htmlView = new QuadPatternFragmentsHtmlView(); rdfView = new QuadPatternFragmentsRdfView({ dataFactory }); - vi.spyOn(htmlView, 'render'); - vi.spyOn(rdfView, 'render'); + htmlRenderSpy = vi.spyOn(htmlView, 'render'); + rdfRenderSpy = vi.spyOn(rdfView, 'render'); controller = new QuadPatternFragmentsController({ routers: [router], datasources: { 'my-datasource': datasource }, views: [htmlView, rdfView], }); - client = request.agent(new DummyServer(controller), {}); + server = new DummyServer(controller); }); function resetAll() { - htmlView.render.mockClear(); - rdfView.render.mockClear(); + htmlRenderSpy.mockClear(); + rdfRenderSpy.mockClear(); } describe('receiving a request without Accept header', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { resetAll(); - response = await client.get('/my-datasource'); + response = await request(server, '/my-datasource'); }); it('should call the default view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); + expect(htmlRenderSpy).toHaveBeenCalledOnce(); }); it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); describe('receiving a request with an Accept header of */*', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { resetAll(); - response = await client.get('/my-datasource').set('Accept', '*/*'); + response = await request(server, '/my-datasource', { headers: { Accept: '*/*' } }); }); it('should call the HTML view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); + expect(htmlRenderSpy).toHaveBeenCalledOnce(); }); it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); describe('receiving a request with an Accept header of text/html', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { resetAll(); - response = await client.get('/my-datasource').set('Accept', 'text/html'); + response = await request(server, '/my-datasource', { headers: { Accept: 'text/html' } }); }); it('should call the HTML view', () => { - expect(htmlView.render).toHaveBeenCalledOnce(); + expect(htmlRenderSpy).toHaveBeenCalledOnce(); }); it('should set the text/html content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/html;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/html;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); describe('receiving a request with an Accept header of text/turtle', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { resetAll(); - response = await client.get('/my-datasource').set('Accept', 'text/turtle'); + response = await request(server, '/my-datasource', { headers: { Accept: 'text/turtle' } }); }); it('should call the Turtle view', () => { - expect(rdfView.render).toHaveBeenCalledOnce(); + expect(rdfRenderSpy).toHaveBeenCalledOnce(); }); it('should set the text/turtle content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/turtle;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/turtle;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); describe('receiving a request with an Accept header of text/n3', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { resetAll(); - response = await client.get('/my-datasource').set('Accept', 'text/n3'); + response = await request(server, '/my-datasource', { headers: { Accept: 'text/n3' } }); }); it('should call the Turtle view', () => { - expect(rdfView.render).toHaveBeenCalledOnce(); + expect(rdfRenderSpy).toHaveBeenCalledOnce(); }); it('should set the text/n3 content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/n3;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/n3;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); }); describe('A QuadPatternFragmentsController instance without matching view', () => { - let controller, client; + let controller: QuadPatternFragmentsController, server: DummyServer; beforeAll(() => { - let datasource = { - supportsQuery: vi.fn().mockReturnValue(true), - select: vi.fn(), - supportedFeatures: { triplePattern: true }, - }; + let datasource = new Datasource({ dataFactory }); + datasource.supportsQuery = vi.fn().mockReturnValue(true); + datasource.select = vi.fn(() => empty()); + datasource.supportedFeatures = { triplePattern: true }; let router = { - extractQueryParams: function (request, query) { + extractQueryParams: function (request: RouterRequest, query: MutableQuery) { query.features.datasource = true; query.datasource = '/my-datasource'; }, @@ -325,70 +345,72 @@ describe('QuadPatternFragmentsController', () => { routers: [router], datasources: { 'my-datasource': datasource }, }); - client = request.agent(new DummyServer(controller), {}); + server = new DummyServer(controller); }); describe('receiving a request without Accept header', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { - response = await client.get('/my-datasource'); + response = await request(server, '/my-datasource'); }); it('should return status code 406', () => { - expect(response).toHaveProperty('statusCode', 406); + expect(response.status).toBe(406); }); it('should set the text/plain content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); describe('receiving a request with an Accept header of text/html', () => { - let response; + let response: FetchLikeResponse; beforeAll(async () => { - response = await client.get('/my-datasource').set('Accept', 'text/html'); + response = await request(server, '/my-datasource', { headers: { Accept: 'text/html' } }); }); it('should return status code 406', () => { - expect(response).toHaveProperty('statusCode', 406); + expect(response.status).toBe(406); }); it('should set the text/plain content type', () => { - expect(response.headers).toHaveProperty('content-type', 'text/plain;charset=utf-8'); + expect(response.headers.get('content-type')).toBe('text/plain;charset=utf-8'); }); it('should indicate Accept in the Vary header', () => { - expect(response.headers).toHaveProperty('vary', 'Accept'); + expect(response.headers.get('vary')).toBe('Accept'); }); }); }); describe('A QuadPatternFragmentsController instance with a datasource that synchronously errors', () => { - let controller, client, router, datasource, error, view; + let controller: QuadPatternFragmentsController, + server: DummyServer, + router: { extractQueryParams: Mock<(request: RouterRequest, query: MutableQuery) => void> }, + datasource: DatasourceType, error: Error, view: QuadPatternFragmentsRdfView; beforeAll(() => { router = { - extractQueryParams: vi.fn((request, query) => { + extractQueryParams: vi.fn((request: RouterRequest, query: MutableQuery) => { query.features.datasource = true; query.datasource = '/my-datasource'; }), }; - error = new Error('datasource error'), - datasource = { - supportsQuery: vi.fn().mockReturnValue(true), - select: vi.fn(() => { throw error; }), - supportedFeatures: { triplePattern: true }, - }; - view = new QuadPatternFragmentsRdfView({ dataFactory }), + error = new Error('datasource error'); + datasource = new Datasource({ dataFactory }); + datasource.supportsQuery = vi.fn().mockReturnValue(true); + datasource.select = vi.fn(() => { throw error; }); + datasource.supportedFeatures = { triplePattern: true }; + view = new QuadPatternFragmentsRdfView({ dataFactory }); controller = new QuadPatternFragmentsController({ routers: [router], views: [view], datasources: { '/my-datasource': datasource }, }); - client = request.agent(new DummyServer(controller), {}); + server = new DummyServer(controller); }); function resetAll() { router.extractQueryParams.mockClear(); @@ -397,40 +419,43 @@ describe('QuadPatternFragmentsController', () => { describe('receiving a request for a fragment', () => { beforeAll(async () => { resetAll(); - await client.get('/my-datasource?a=b&c=d'); + await request(server, '/my-datasource?a=b&c=d'); }); it('should emit the error', () => { - expect(controller.error).toBe(error); + expect(server.error).toBe(error); }); }); }); describe('A QuadPatternFragmentsController instance with a datasource that asynchronously errors', () => { - let controller, client, router, datasource, error, view; + let controller: QuadPatternFragmentsController, + server: DummyServer, + router: { extractQueryParams: Mock<(request: RouterRequest, query: MutableQuery) => void> }, + datasource: DatasourceType, error: Error, view: QuadPatternFragmentsRdfView; beforeAll(() => { router = { - extractQueryParams: vi.fn((request, query) => { + extractQueryParams: vi.fn((request: RouterRequest, query: MutableQuery) => { query.features.datasource = true; query.datasource = '/my-datasource'; }), }; - error = new Error('datasource error'), - datasource = { - supportsQuery: vi.fn().mockReturnValue(true), - // Mocks Datasource.select's own callback-based signature. - // eslint-disable-next-line promise/prefer-await-to-callbacks - select: function (query, callback) { setImmediate(callback.bind(null, error)); }, - supportedFeatures: { triplePattern: true }, - }; - view = new QuadPatternFragmentsRdfView({ dataFactory }), + error = new Error('datasource error'); + datasource = new Datasource({ dataFactory }); + datasource.supportsQuery = vi.fn().mockReturnValue(true); + datasource.select = vi.fn((query: Query, onError?: (error?: Error) => void) => { + setImmediate(() => onError?.(error)); + return empty(); + }); + datasource.supportedFeatures = { triplePattern: true }; + view = new QuadPatternFragmentsRdfView({ dataFactory }); view.render = vi.fn(); // avoid writing a partial body controller = new QuadPatternFragmentsController({ routers: [router], views: [view], datasources: { 'my-datasource': datasource }, }); - client = request.agent(new DummyServer(controller), {}); + server = new DummyServer(controller); }); function resetAll() { router.extractQueryParams.mockClear(); @@ -439,11 +464,11 @@ describe('QuadPatternFragmentsController', () => { describe('receiving a request for a fragment', () => { beforeAll(async () => { resetAll(); - await client.get('/my-datasource?a=b&c=d'); + await request(server, '/my-datasource?a=b&c=d'); }); it('should emit the error', () => { - expect(controller.error).toBe(error); + expect(server.error).toBe(error); }); }); }); diff --git a/packages/feature-qpf/test/routers/QuadPatternRouter-test.js b/packages/feature-qpf/test/routers/QuadPatternRouter-test.ts similarity index 96% rename from packages/feature-qpf/test/routers/QuadPatternRouter-test.js rename to packages/feature-qpf/test/routers/QuadPatternRouter-test.ts index 5b419f3d..760f77d0 100644 --- a/packages/feature-qpf/test/routers/QuadPatternRouter-test.js +++ b/packages/feature-qpf/test/routers/QuadPatternRouter-test.ts @@ -2,8 +2,16 @@ import { describe, it, expect } from 'vitest'; import { extractQueryParams } from '../../../../test/test-helpers'; -let QuadPatternRouter = require('../../').routers.QuadPatternRouter; -const dataFactory = require('n3').DataFactory; +import { routers } from '../../index'; +import type { Query } from '@ldf/core'; + +// Query has no index signature; this lets the test tables below use an +// arbitrary 'a' field as a stand-in for "pre-existing data that should survive". +type TestQuery = Query & { a?: number }; +type QueryParamsTestCase = [string, string, string, TestQuery, TestQuery]; +import { DataFactory as dataFactory } from 'n3'; + +const { QuadPatternRouter } = routers; describe('QuadPatternRouter', () => { describe('The QuadPatternRouter module', () => { @@ -21,7 +29,7 @@ describe('QuadPatternRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a URL without query parameters', 'http://example.org/', @@ -225,8 +233,8 @@ describe('QuadPatternRouter', () => { { a: 1 }, { a: 1, features: { quadPattern: true }, graph: dataFactory.defaultGraph() }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); @@ -242,7 +250,7 @@ describe('QuadPatternRouter', () => { describe('extractUrlParams', () => { describe('with an existing query', () => { - [ + const rows: QueryParamsTestCase[] = [ [ 'a URL without query parameters', 'http://example.org/', @@ -467,8 +475,8 @@ describe('QuadPatternRouter', () => { { a: 1 }, { a: 1, features: { quadPattern: true }, graph: dataFactory.namedNode('foo:bar') }, ], - ] - .forEach((args) => { extractQueryParams(router, ...args); }); + ]; + rows.forEach((args) => { extractQueryParams(router, ...args); }); }); }); }); diff --git a/packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.js b/packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.ts similarity index 76% rename from packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.js rename to packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.ts index 642d7405..350ce72c 100644 --- a/packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.js +++ b/packages/feature-qpf/test/views/quadpatternfragments/QuadPatternFragmentsRdfView-test.ts @@ -2,15 +2,24 @@ import { describe, it, expect, beforeAll, vi } from 'vitest'; import { createStreamCapture } from '../../../../../test/test-helpers'; -let QuadPatternFragmentsRdfView = require('../../../').views.quadpatternfragments.QuadPatternFragmentsRdfView; +import { views } from '../../../index'; -let _ = require('lodash'), - fs = require('fs'), - path = require('path'), - AsyncIterator = require('asynciterator'), - N3 = require('n3'); +import * as _ from 'lodash'; +import * as fs from 'fs'; +import * as path from 'path'; +import { empty, fromArray, TransformIterator } from 'asynciterator'; +import type { AsyncIterator } from 'asynciterator'; +import type { Quad } from '@rdfjs/types'; +import { DataFactory as dataFactory } from 'n3'; +import { IncomingMessage } from 'http'; +import { Socket } from 'net'; +import type { LdfRequest } from '../../../../core/index'; -const dataFactory = N3.DataFactory; +const { QuadPatternFragmentsRdfView } = views.quadpatternfragments; + +function createRequest(): LdfRequest { + return new IncomingMessage(new Socket()); +} describe('QuadPatternFragmentsRdfView', () => { describe('The QuadPatternFragmentsRdfView module', () => { @@ -25,7 +34,7 @@ describe('QuadPatternFragmentsRdfView', () => { describe('A QuadPatternFragmentsRdfView instance', () => { let view = new QuadPatternFragmentsRdfView({ dataFactory }); - let settings = { + let settings: { results?: AsyncIterator; [key: string]: unknown } = { datasource: { title: 'My data', index: 'http://ex.org/#dataset', @@ -63,18 +72,18 @@ describe('QuadPatternFragmentsRdfView', () => { }, (extension, format) => { describe('when render is called for ' + format, () => { - function readAsset(name) { + function readAsset(name: string) { let file = path.join(__dirname, '../../../../../test/assets/', name + '.' + extension); return fs.readFileSync(file, 'utf8'); } describe('with an empty triple stream', () => { - let results = AsyncIterator.empty(); + let results = empty(); let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { + beforeAll(() => new Promise((resolve) => { settings.results = results; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); + view.render(settings, createRequest(), response, resolve); results.setProperty('metadata', { totalCount: 1234 }); })); @@ -84,18 +93,19 @@ describe('QuadPatternFragmentsRdfView', () => { }); describe('with a non-empty triple stream that writes metadata first', () => { - let results = AsyncIterator.fromArray([ + let results = fromArray([ dataFactory.quad(dataFactory.namedNode('a'), dataFactory.namedNode('b'), dataFactory.namedNode('c'), dataFactory.defaultGraph()), dataFactory.quad(dataFactory.namedNode('a'), dataFactory.namedNode('d'), dataFactory.namedNode('e'), dataFactory.defaultGraph()), dataFactory.quad(dataFactory.namedNode('f'), dataFactory.namedNode('g'), dataFactory.namedNode('h'), dataFactory.defaultGraph()), ]); let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { - settings.results = new AsyncIterator.TransformIterator(); + beforeAll(() => new Promise((resolve) => { + let transform = new TransformIterator(); + settings.results = transform; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); - settings.results.setProperty('metadata', { totalCount: 1234 }); - settings.results.source = results; + view.render(settings, createRequest(), response, resolve); + transform.setProperty('metadata', { totalCount: 1234 }); + transform.source = results; })); it('should write data and metadata', () => { @@ -104,16 +114,16 @@ describe('QuadPatternFragmentsRdfView', () => { }); describe('with a non-empty triple stream that writes metadata afterwards', () => { - let results = AsyncIterator.fromArray([ + let results = fromArray([ dataFactory.quad(dataFactory.namedNode('a'), dataFactory.namedNode('b'), dataFactory.namedNode('c'), dataFactory.defaultGraph()), dataFactory.quad(dataFactory.namedNode('a'), dataFactory.namedNode('d'), dataFactory.namedNode('e'), dataFactory.defaultGraph()), dataFactory.quad(dataFactory.namedNode('f'), dataFactory.namedNode('g'), dataFactory.namedNode('h'), dataFactory.defaultGraph()), ]); let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { + beforeAll(() => new Promise((resolve) => { settings.results = results; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); + view.render(settings, createRequest(), response, resolve); setImmediate(() => { results.setProperty('metadata', { totalCount: 1234 }); }); @@ -125,8 +135,8 @@ describe('QuadPatternFragmentsRdfView', () => { }); describe('with a query with a limit but no offset', () => { - let results = AsyncIterator.empty(); - let settings = { + let results = empty(); + let settings: { results?: AsyncIterator; [key: string]: unknown } = { datasource: { }, fragment: { pageUrl: 'mypage', @@ -137,10 +147,10 @@ describe('QuadPatternFragmentsRdfView', () => { query: { limit: 100 }, }; let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { + beforeAll(() => new Promise((resolve) => { settings.results = results; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); + view.render(settings, createRequest(), response, resolve); results.setProperty('metadata', { totalCount: 1234 }); })); @@ -158,8 +168,8 @@ describe('QuadPatternFragmentsRdfView', () => { }); describe('with a query with a limit and offset before the end', () => { - let results = AsyncIterator.empty(); - let settings = { + let results = empty(); + let settings: { results?: AsyncIterator; [key: string]: unknown } = { datasource: { }, fragment: { pageUrl: 'mypage', @@ -170,10 +180,10 @@ describe('QuadPatternFragmentsRdfView', () => { query: { limit: 100, offset: 1133 }, }; let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { + beforeAll(() => new Promise((resolve) => { settings.results = results; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); + view.render(settings, createRequest(), response, resolve); results.setProperty('metadata', { totalCount: 1234 }); })); @@ -191,8 +201,8 @@ describe('QuadPatternFragmentsRdfView', () => { }); describe('with a query with a limit and offset past the end', () => { - let results = AsyncIterator.empty(); - let settings = { + let results = empty(); + let settings: { results?: AsyncIterator; [key: string]: unknown } = { datasource: { }, fragment: { pageUrl: 'mypage', @@ -203,10 +213,10 @@ describe('QuadPatternFragmentsRdfView', () => { query: { limit: 100, offset: 1135 }, }; let response = createStreamCapture(); - beforeAll(() => new Promise((resolve) => { + beforeAll(() => new Promise((resolve) => { settings.results = results; response.getHeader = vi.fn().mockReturnValue(format); - view.render(settings, {}, response, resolve); + view.render(settings, createRequest(), response, resolve); results.setProperty('metadata', { totalCount: 1234 }); })); diff --git a/packages/feature-summary/lib/views/summary/QuadPatternFragmentsRdfView-Summary.ts b/packages/feature-summary/lib/views/summary/QuadPatternFragmentsRdfView-Summary.ts index e5f6b668..93db85a8 100644 --- a/packages/feature-summary/lib/views/summary/QuadPatternFragmentsRdfView-Summary.ts +++ b/packages/feature-summary/lib/views/summary/QuadPatternFragmentsRdfView-Summary.ts @@ -2,7 +2,7 @@ /* A SummaryRdfViewExtension extends the Quad Pattern Fragments RDF view with a summary link. */ import { RdfView } from '@ldf/core/lib/views/RdfView'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { RdfViewSettings, RenderDone, ViewSettings } from '@ldf/core'; import type { SummaryRenderSettings } from '../../controllers/SummaryController'; diff --git a/packages/feature-summary/lib/views/summary/SummaryRdfView.ts b/packages/feature-summary/lib/views/summary/SummaryRdfView.ts index 2ea55b90..25e5980d 100644 --- a/packages/feature-summary/lib/views/summary/SummaryRdfView.ts +++ b/packages/feature-summary/lib/views/summary/SummaryRdfView.ts @@ -3,7 +3,7 @@ import { RdfView } from '@ldf/core/lib/views/RdfView'; import type { StreamParser } from 'n3'; -import type { Quad } from 'rdf-js'; +import type { Quad } from '@rdfjs/types'; import type { RdfViewSettings, RenderDone, ViewSettings } from '@ldf/core'; interface SummaryRdfViewSettings extends ViewSettings { diff --git a/packages/feature-summary/package.json b/packages/feature-summary/package.json index 5757897f..87531134 100644 --- a/packages/feature-summary/package.json +++ b/packages/feature-summary/package.json @@ -27,7 +27,8 @@ "lint": "eslint bin/* lib test" }, "dependencies": { - "n3": "^1.3.5" + "@rdfjs/types": "^2.0.1", + "n3": "^1.26.0" }, "peerDependencies": { "@ldf/core": "^3.0.0" diff --git a/packages/feature-summary/test/controllers/SummaryController-test.js b/packages/feature-summary/test/controllers/SummaryController-test.js deleted file mode 100644 index 4a1f5e1e..00000000 --- a/packages/feature-summary/test/controllers/SummaryController-test.js +++ /dev/null @@ -1,84 +0,0 @@ -/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ - -import { describe, it, expect, beforeAll } from 'vitest'; -import { DummyServer } from '../../../../test/DummyServer'; -let SummaryController = require('../../lib/controllers/SummaryController').SummaryController; // changed to make tests pass, will be revised in follow up pr - -let request = require('supertest'), - fs = require('fs'), - path = require('path'); - -let SummaryRdfView = require('../../lib/views/summary/SummaryRdfView.js').SummaryRdfView; // changed to make tests pass, will be revised in follow up pr - -let dataFactory = require('n3').DataFactory; - -describe('SummaryController', () => { - describe('The SummaryController module', () => { - it('should be a function', () => { - expect(typeof SummaryController).toBe('function'); - }); - - it('should be an SummaryController constructor', () => { - expect(new SummaryController()).toBeInstanceOf(SummaryController); - }); - - it('should create new SummaryController objects', () => { - expect(new SummaryController()).toBeInstanceOf(SummaryController); - }); - }); - - describe('An SummaryController instance', () => { - let controller, client; - beforeAll(() => { - controller = new SummaryController({ - views: [new SummaryRdfView({ dataFactory })], - summaries: { dir: path.join(__dirname, '/../../../../test/assets') }, - prefixes: { - ds: 'http://semweb.mmlab.be/ns/datasummaries#', - rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - }, - }); - client = request.agent(new DummyServer(controller), {}); - }); - - it('should correctly serve summary in Turtle', async () => { - let response = await client.get('/summaries/summary').set('Accept', 'text/turtle'); - let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.ttl'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'text/turtle;charset=utf-8'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=604800'); - expect(response.text).toBe(summary); - }); - - it('should correctly serve summary in Trig', async () => { - let response = await client.get('/summaries/summary'); - let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.ttl'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'application/trig;charset=utf-8'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=604800'); - expect(response.text).toBe(summary); - }); - - it('should correctly serve summary in ntriples', async () => { - let response = await client.get('/summaries/summary').set('Accept', 'application/n-triples'); - let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.nt'), 'utf8'); - expect(controller.next).not.toHaveBeenCalled(); - expect(response).toHaveProperty('statusCode', 200); - expect(response.headers).toHaveProperty('content-type', 'application/n-triples;charset=utf-8'); - expect(response.headers).toHaveProperty('cache-control', 'public,max-age=604800'); - expect(response.text).toBe(summary); - }); - - it('should hand over to the next controller if no summary with that name is found', async () => { - await client.get('/summaries/unknown'); - expect(controller.next).toHaveBeenCalledOnce(); - }); - - it('should hand over to the next controller for non-summary paths', async () => { - await client.get('/other'); - expect(controller.next).toHaveBeenCalledOnce(); - }); - }); -}); diff --git a/packages/feature-summary/test/controllers/SummaryController-test.ts b/packages/feature-summary/test/controllers/SummaryController-test.ts new file mode 100644 index 00000000..5662c9d5 --- /dev/null +++ b/packages/feature-summary/test/controllers/SummaryController-test.ts @@ -0,0 +1,85 @@ +/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ + +import { describe, it, expect, beforeAll } from 'vitest'; +import { DummyServer } from '../../../../test/DummyServer'; +import { request } from '../../../../test/test-helpers'; +import { views } from '../../index'; +import { SummaryController } from '../../lib/controllers/SummaryController'; + +import * as fs from 'fs'; +import * as path from 'path'; + +import { DataFactory as dataFactory } from 'n3'; + +const { SummaryRdfView } = views.summary; + +describe('SummaryController', () => { + describe('The SummaryController module', () => { + it('should be a function', () => { + expect(typeof SummaryController).toBe('function'); + }); + + it('should be an SummaryController constructor', () => { + expect(new SummaryController()).toBeInstanceOf(SummaryController); + }); + + it('should create new SummaryController objects', () => { + expect(new SummaryController()).toBeInstanceOf(SummaryController); + }); + }); + + describe('An SummaryController instance', () => { + let controller: SummaryController, server: DummyServer; + beforeAll(() => { + controller = new SummaryController({ + views: [new SummaryRdfView({ dataFactory })], + summaries: { dir: path.join(__dirname, '/../../../../test/assets') }, + prefixes: { + ds: 'http://semweb.mmlab.be/ns/datasummaries#', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + }, + }); + server = new DummyServer(controller); + }); + + it('should correctly serve summary in Turtle', async () => { + let response = await request(server, '/summaries/summary', { headers: { Accept: 'text/turtle' } }); + let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.ttl'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('text/turtle;charset=utf-8'); + expect(response.headers.get('cache-control')).toBe('public,max-age=604800'); + expect(await response.text()).toBe(summary); + }); + + it('should correctly serve summary in Trig', async () => { + let response = await request(server, '/summaries/summary'); + let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.ttl'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('application/trig;charset=utf-8'); + expect(response.headers.get('cache-control')).toBe('public,max-age=604800'); + expect(await response.text()).toBe(summary); + }); + + it('should correctly serve summary in ntriples', async () => { + let response = await request(server, '/summaries/summary', { headers: { Accept: 'application/n-triples' } }); + let summary = fs.readFileSync(path.join(__dirname, '/../../../../test/assets/summary.nt'), 'utf8'); + expect(server.next).not.toHaveBeenCalled(); + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toBe('application/n-triples;charset=utf-8'); + expect(response.headers.get('cache-control')).toBe('public,max-age=604800'); + expect(await response.text()).toBe(summary); + }); + + it('should hand over to the next controller if no summary with that name is found', async () => { + await request(server, '/summaries/unknown'); + expect(server.next).toHaveBeenCalledOnce(); + }); + + it('should hand over to the next controller for non-summary paths', async () => { + await request(server, '/other'); + expect(server.next).toHaveBeenCalledOnce(); + }); + }); +}); diff --git a/packages/feature-webid/package.json b/packages/feature-webid/package.json index c442fc2f..72be37b1 100644 --- a/packages/feature-webid/package.json +++ b/packages/feature-webid/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "lru-cache": "^5.1.1", - "n3": "^1.3.5", + "n3": "^1.26.0", "parse-cache-control": "^1.0.1" }, "peerDependencies": { diff --git a/test/DummyServer.js b/test/DummyServer.js deleted file mode 100644 index 23d39f37..00000000 --- a/test/DummyServer.js +++ /dev/null @@ -1,21 +0,0 @@ -/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ - -import * as http from 'http'; -import { vi } from 'vitest'; - -/* Dummy server that emulates LinkedDataFragmentsServer */ -export function DummyServer(controller) { - const server = http.createServer(); - server.on('request', (request, response) => { - // End the response if the controller did not handle the request - controller.next = vi.fn((error) => { - controller.error = error; - if (!response.headersSent) - response.writeHead(error ? 500 : 200); - response.end(error && error.message || ''); - }); - try { controller.result = controller.handleRequest(request, response, controller.next); } - catch (error) { controller.next(error); } - }); - return server; -} diff --git a/test/DummyServer.ts b/test/DummyServer.ts new file mode 100644 index 00000000..d4f44dc2 --- /dev/null +++ b/test/DummyServer.ts @@ -0,0 +1,29 @@ +/*! @license MIT ©2015-2016 Ruben Verborgh, Ghent University - imec */ + +import * as http from 'http'; +import { vi, type Mock } from 'vitest'; +import type { Controller } from '@ldf/core/lib/controllers/Controller'; +import type { LdfRequest, LdfRequestWithUrl, LdfResponse } from '@ldf/core'; + +/* Dummy server that emulates LinkedDataFragmentsServer, tracking how the + controller ultimately handled each request */ +export class DummyServer extends http.Server { + // Only set once a request has been handled + next?: Mock<(error?: Error) => void>; + error?: Error; + + constructor(controller: Controller) { + super(); + this.on('request', (request: LdfRequest, response: LdfResponse) => { + // End the response if the controller did not handle the request + this.next = vi.fn((error?: Error) => { + this.error = error; + if (!response.headersSent) + response.writeHead(error ? 500 : 200); + response.end(error && error.message || ''); + }); + try { controller.handleRequest(request as LdfRequestWithUrl, response, this.next); } + catch (error) { this.next(error as Error); } + }); + } +} diff --git a/test/test-helpers.js b/test/test-helpers.js deleted file mode 100644 index c5318730..00000000 --- a/test/test-helpers.js +++ /dev/null @@ -1,44 +0,0 @@ -/*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ -import { it, expect } from 'vitest'; -import { parse as parseUrl } from 'url'; -import { Readable, Writable } from 'stream'; -import { once } from 'events'; - -// Generates an `it` block that verifies a router's extractQueryParams behavior -export function extractQueryParams(router, description, url, intent, query, expectedQuery) { - it(description + ' ' + intent, () => { - const result = router.extractQueryParams({ url: parseUrl(url, true) }, query); - expect(result, 'should not return anything').toBeUndefined(); - expect(query, 'should match the expected query').toEqual(expectedQuery); - }); -} - -// Creates a dummy HTTP response -export function createHttpResponse(contents, contentType) { - const response = new Readable(); - response._read = () => {}; - response.statusCode = 200; - response.headers = { 'content-type': contentType }; - response.abort = () => { response.aborted = true; }; - setImmediate(() => { response.push(contents); response.push(null); }); - return response; -} - -// Creates an in-memory stream -export function createStreamCapture() { - const stream = new Writable({ objectMode: true }); - stream.buffer = ''; - stream._write = (chunk, encoding, callback) => { - stream.buffer += chunk; - callback && callback(); - }; - return stream; -} - -// Counts the elements in a stream and resolves once it ends -export async function streamLength(stream) { - let length = 0; - stream.on('data', () => { length++; }); - await once(stream, 'end'); - return length; -} diff --git a/test/test-helpers.ts b/test/test-helpers.ts new file mode 100644 index 00000000..dd6fd8c5 --- /dev/null +++ b/test/test-helpers.ts @@ -0,0 +1,108 @@ +/*! @license MIT ©2013-2016 Ruben Verborgh, Ghent University - imec */ +import { it, expect } from 'vitest'; +import { parse as parseUrl } from 'url'; +import { IncomingMessage, ServerResponse, type Server } from 'http'; +import { Socket } from 'net'; +import { EventEmitter, once } from 'events'; +import inject = require('light-my-request'); +import type { Query, Router } from '../packages/core/lib/types'; + +type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS'; + +export interface FetchLikeInit { + method?: HttpMethod; + headers?: Record; +} + +export interface FetchLikeResponse { + status: number; + headers: { get(name: string): string | null }; + text(): Promise; +} + +// Starts the given server on an ephemeral port and resolves with its base URL. +// Only needed for servers whose error handling relies on emitting 'error' on the +// response as a recoverable signal (LinkedDataFragmentsServer): light-my-request's +// `request()` below treats any such 'error' event as fatal to the whole injected exchange. +export async function listen(server: Server): Promise { + await new Promise((resolve) => server.listen(0, resolve)); + const address = server.address(); + if (address === null || typeof address === 'string') + throw new Error('Expected the server to report a network address'); + return `http://localhost:${address.port}`; +} + +export async function request(server: Server, path: string, init: FetchLikeInit = {}): Promise { + const response = await inject((req, res) => server.emit('request', req, res), { + url: path, + method: init.method, + headers: init.headers, + Request: IncomingMessage, + }); + return { + status: response.statusCode, + headers: { + get(name: string) { + const value = response.headers[name.toLowerCase()]; + return Array.isArray(value) ? value.join(', ') : value === undefined ? null : String(value); + }, + }, + text: () => Promise.resolve(response.payload), + }; +} + +// Generates an `it` block that verifies a router's extractQueryParams behavior +export function extractQueryParams(router: Router, description: string, url: string, intent: string, query: Query, expectedQuery: Query) { + it(description + ' ' + intent, () => { + const parsed = parseUrl(url, true); + const result = router.extractQueryParams({ url: parsed }, query); + expect(result, 'should not return anything').toBeUndefined(); + expect(query, 'should match the expected query').toEqual(expectedQuery); + }); +} + +// A dummy HTTP response, as returned by createHttpResponse +class HttpResponse extends IncomingMessage { + override statusCode = 200; + + constructor(contentType: string) { + super(new Socket()); + this.headers = { 'content-type': contentType }; + } + + override _read(): void {} +} + +// Creates a dummy HTTP response +export function createHttpResponse(contents: string, contentType: string): HttpResponse { + const response = new HttpResponse(contentType); + setImmediate(() => { response.push(contents); response.push(null); }); + return response; +} + +// An in-memory HTTP response, as returned by createStreamCapture +class StreamCapture extends ServerResponse { + buffer = ''; + + constructor() { + super(new IncomingMessage(new Socket())); + } + + override write(chunk: unknown): boolean { + this.buffer += chunk; + return true; + } +} + +// Creates an in-memory HTTP response that captures everything written to it +export function createStreamCapture(): StreamCapture { + return new StreamCapture(); +} + +// Counts the elements in a stream and resolves once it ends +export async function streamLength(stream: EventEmitter): Promise { + let length = 0; + stream.on('data', () => { length++; }); + await once(stream, 'end'); + return length; +} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 00000000..63681949 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,19 @@ +{ + // Only ESLint's type-aware rules use this: its parser needs every file it lints + // to belong to a program, which the build config deliberately does not cover. + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": true + }, + "include": [ + "packages/*/index.ts", + "packages/*/lib/**/*", + "packages/*/bin/**/*", + "packages/*/test/**/*.ts", + "test/**/*.ts", + "vitest.config.mts" + ], + "exclude": [ + "**/node_modules" + ] +} diff --git a/tsconfig.json b/tsconfig.json index ad063175..bc6cb073 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,10 @@ "compilerOptions": { "target": "es2023", "lib": ["es2023"], + // This monorepo is CommonJS: no package.json here declares "type": "module". + // Since TypeScript 6 this also implies exports-aware module resolution, which + // is what lets vitest's type definitions resolve their own subpath imports in + // tsconfig.eslint.json without skipLibCheck. "module": "commonjs", "resolveJsonModule": true, "types": ["node"], @@ -20,8 +24,7 @@ "include": [ "packages/*/index.ts", "packages/*/lib/**/*", - "packages/*/bin/**/*", - "types/**/*.d.ts" + "packages/*/bin/**/*" ], "exclude": [ "**/node_modules" diff --git a/vitest.config.mts b/vitest.config.mts index 92ed70a4..2b73eedb 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -1,21 +1,40 @@ +import { existsSync } from 'fs'; +import type { Plugin } from 'vite'; import { defineConfig } from 'vitest/config'; -import { fileURLToPath } from 'url'; -const rootDir = fileURLToPath(new URL('.', import.meta.url)); +/** + * Resolves this monorepo's compiled JavaScript back to the TypeScript it was built from. + * + * Packages refer to each other as `@ldf/x`, which resolves through package.json's "main" to + * `index.js`. Without this, tests would run against the last build instead of the working tree, + * and a class reached through both routes would end up with two identities, breaking `instanceof`. + * @returns A Vite plugin redirecting built files to their sources. + */ +function preferTypeScriptSources(): Plugin { + return { + name: 'ldf:prefer-typescript-sources', + enforce: 'pre', + async resolveId(source, importer, options) { + const resolved = await this.resolve(source, importer, { ...options, skipSelf: true }); + if (resolved && !resolved.external && resolved.id.endsWith('.js') && !resolved.id.includes('node_modules')) { + const typescriptSource = `${resolved.id.slice(0, -'.js'.length)}.ts`; + if (existsSync(typescriptSource)) + return { ...resolved, id: typescriptSource }; + } + return resolved; + }, + }; +} export default defineConfig({ + plugins: [preferTypeScriptSources()], resolve: { // Prefer .ts so a require()/import never mixes compiled .js and its own // .ts source into two module instances of the same class (breaks instanceof). extensions: ['.ts', '.mjs', '.js', '.mts', '.jsx', '.tsx', '.json'], - alias: [ - // Bare `@ldf/x` resolves via package.json's "main" instead, a step the - // extensions list above doesn't cover. Force it onto .ts too, same reason. - { find: /^@ldf\/([^/]+)$/, replacement: `${rootDir}packages/$1/index.ts` }, - ], }, test: { - include: ['packages/*/test/**/*-test.js'], + include: ['packages/*/test/**/*-test.ts'], environment: 'node', testTimeout: 5000, setupFiles: ['./test/vitest-setup.js'], diff --git a/yarn.lock b/yarn.lock index 8ab33914..f5587ea2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -65,134 +65,163 @@ dependencies: buffer "^6.0.3" -"@comunica/actor-abstract-mediatyped@^1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-abstract-mediatyped/-/actor-abstract-mediatyped-1.19.0.tgz#db3eeef7ca4393ce825461421890ace81a605803" - integrity sha512-f8KPM8xsjB70REsS2ALrLNlMRHwPmb21OJy6u5YPARKCr95Usff38L/Fntm3ey6oQTwfwK/XEYuQVtwx5EqJYw== +"@bergos/jsonparse@^1.4.1": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@bergos/jsonparse/-/jsonparse-1.4.2.tgz#75ba0065ec448bf24ea8b3494944a1e3219c5e46" + integrity sha512-qUt0QNJjvg4s1zk+AuLM6s/zcsQ8MvGn7+1f0vPuxvpCYa08YtTryuDInngbEyW5fNGGYe2znKt61RMGd5HnXg== + dependencies: + buffer "^6.0.3" -"@comunica/actor-http-native@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-http-native/-/actor-http-native-1.19.0.tgz#025690da4836bc7309168de6bf8f26d678f89d93" - integrity sha512-j7HsxDRcVjywPvh3ur1qx5WRS+UbT503jR+8lszR045h3ODTLX5PeCMh96Eq4dPkbVp22vkGqTFXux38dYRBSw== +"@comunica/actor-abstract-mediatyped@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-abstract-mediatyped/-/actor-abstract-mediatyped-1.22.0.tgz#e61a4dad537096a84a9684bf7cd6ec1b8fda2368" + integrity sha512-+KQLPpx8GFqrhWFfuvrsA4Rjlfbo/QOIo2IvzSgmDwy6YVQZXaSQiNQv/BnrnedaFCf2ONV+w+PMLqXgzn8N9A== + +"@comunica/actor-http-native@~1.22.0": + version "1.22.1" + resolved "https://registry.yarnpkg.com/@comunica/actor-http-native/-/actor-http-native-1.22.1.tgz#60671bc654d6e63c4775f9dd678fd3c67fbfc214" + integrity sha512-BdB+hvQ9CJF9tI42hNhcvTMagOty+jw21LIQDJWI628xMcXZ88BJaUX0Ulc7g2nrWH97ZRm5+KjLC4Zf+OGwZg== dependencies: + "@comunica/context-entries" "^1.22.0" "@types/parse-link-header" "^1.0.0" cross-fetch "^3.0.5" follow-redirects "^1.5.1" parse-link-header "^1.0.1" -"@comunica/actor-rdf-parse-html-microdata@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-microdata/-/actor-rdf-parse-html-microdata-1.19.0.tgz#08945ca3577fc2b3114e0e556b327d4201e03e18" - integrity sha512-IG0ZWdyQ8RP+cPjcoEnQePDgWmJa3isX3h1pO2WVOgAF9ih7wOimXl20++SGq/KlrtcMFFwM1qNlUE67RuaI6w== +"@comunica/actor-rdf-parse-html-microdata@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-microdata/-/actor-rdf-parse-html-microdata-1.22.0.tgz#a4476bc1430e7e367f5b733eeb71eea3ca26e041" + integrity sha512-OdB3Z7ZCtVAcsVU2Vs0ytGbiz0eYkeBwVA3k0vGVhSN3ygng5Thj+t8jxG6QWHlLvaIXfJFh0x57qY5tXkr8uQ== dependencies: - microdata-rdf-streaming-parser "^1.1.0" + microdata-rdf-streaming-parser "^1.2.0" -"@comunica/actor-rdf-parse-html-rdfa@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-rdfa/-/actor-rdf-parse-html-rdfa-1.19.0.tgz#43120b60161a6bbb502c6ecea166a7204a5785ef" - integrity sha512-Wzq7VZ1sqR+Dx4omKpuGyf+Ep01uh1cOPQfNNH98Nhnu4hfvN2O3+LMT9oN9xE/IlYA0CsgOJbvIthJCN7kiBw== +"@comunica/actor-rdf-parse-html-rdfa@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-rdfa/-/actor-rdf-parse-html-rdfa-1.22.0.tgz#d7ea6b5e0cb27321990b387348ecb2131ffcfefa" + integrity sha512-yVjYLpm9rbpPiqU1OE4Yioyk/YHtO6ywVMbdOPUNLeOwrtWou8vKX0Xh4UUR24Qrt8nuhE+p0kCJiZZtM1PmSQ== dependencies: - rdfa-streaming-parser "^1.4.0" + rdfa-streaming-parser "^1.5.0" -"@comunica/actor-rdf-parse-html-script@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-script/-/actor-rdf-parse-html-script-1.19.0.tgz#81c08f056619c7ec6cd0e2b0957392d47d02a7b9" - integrity sha512-rvcYPzng1gFij6GtI7O7QNuL/9EyJ9wXeq4D+lt3tNbptw1KeK+vQZrEa8odCdfZsiGMzj0JpgsmySvux6VtNg== +"@comunica/actor-rdf-parse-html-script@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html-script/-/actor-rdf-parse-html-script-1.22.0.tgz#8bbaabd28c05b6fb6d400c8858ef8e1647fcade1" + integrity sha512-gQSY56wkS/uftRyjQf+/dQFRpA/jZ6z2o2RgGbQc2avgKTkhaiTtPxpfO1oarLskm1sPlQOFo24ZwqUSqjOwcA== dependencies: - "@comunica/bus-rdf-parse-html" "^1.19.0" - "@types/rdf-js" "*" + "@comunica/bus-rdf-parse-html" "^1.22.0" + "@rdfjs/types" "*" relative-to-absolute-iri "^1.0.5" -"@comunica/actor-rdf-parse-html@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html/-/actor-rdf-parse-html-1.19.0.tgz#9bef88c9efea2566abae0a5e2a12bf6dae87720f" - integrity sha512-oxaS7cNkk3LZY7mKHnsLlUYZtCrBVt1w+dkgR9Hw/X2o5vet29W2dovYfOMeseSZJW9SOTQWDnQesvdMUgl2Tg== +"@comunica/actor-rdf-parse-html@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-html/-/actor-rdf-parse-html-1.22.0.tgz#b217e491f77ee4853190c2ab0613c3cf46dbd71e" + integrity sha512-U9pznSpQ1POSH+ekOke3lYKO0fsUbNdv1g1nfuWz/MV3xMCF/d2f3CVBjXRSx9qwyb/2zUrOjBCJRrYkfZ6geQ== dependencies: - "@comunica/bus-rdf-parse-html" "^1.19.0" - "@types/rdf-js" "*" - htmlparser2 "^6.0.0" + "@comunica/bus-rdf-parse-html" "^1.22.0" + "@rdfjs/types" "*" + htmlparser2 "^7.0.0" -"@comunica/actor-rdf-parse-jsonld@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-jsonld/-/actor-rdf-parse-jsonld-1.19.0.tgz#bf789dab64625587b8a1c12e1ebb6d3733333fd0" - integrity sha512-0I2wmsJlmurort2sSJh3kPm4NXYQqpmWBIkWzUGRuFJv5pAWd0AqnZpleuoM3GdaFdVtMbO3Pa4oBLBIVHfg2Q== +"@comunica/actor-rdf-parse-jsonld@^1.22.0": + version "1.22.1" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-jsonld/-/actor-rdf-parse-jsonld-1.22.1.tgz#7f071607fe42dd2c5668ce6d744c5fd976ef889d" + integrity sha512-MFFhJ6eGyO40Be80zsFKAbRjkPXr80PvCqvVKsEstdv3u9C6GFV3nqZpCwvsVCz22IPQhW+rzb8ZyasmgHnurA== dependencies: - "@types/rdf-js" "*" - jsonld-context-parser "^2.1.1" - jsonld-streaming-parser "^2.1.1" + "@comunica/context-entries" "^1.22.0" + "@rdfjs/types" "*" + jsonld-context-parser "^2.1.2" + jsonld-streaming-parser "^2.4.0" stream-to-string "^1.2.0" -"@comunica/actor-rdf-parse-n3@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-n3/-/actor-rdf-parse-n3-1.19.0.tgz#afe8aa1792cd64760d2af4ce2d6b85b205933da1" - integrity sha512-JkbLLym/dTT0qqBWtIw8wXAJQI6Gxa9aETCgu4slsZRTizUNHpCh/nkQBGMsL+gufTDUPazKEKdwdKDQuwY1VA== +"@comunica/actor-rdf-parse-n3@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-n3/-/actor-rdf-parse-n3-1.22.0.tgz#c027a84980e2891e5696cd16ac6c01b0c9d62496" + integrity sha512-qHrGfh5k/pZa4imy7m9gJ1kt9aW1uxXqLDKnLKvR2l0m09YiEx/YOYWr1Wtu1YtH/Yyc13OX4mo/OwaE5PfrHQ== dependencies: "@types/n3" "^1.4.4" n3 "^1.6.3" -"@comunica/actor-rdf-parse-rdfxml@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-rdfxml/-/actor-rdf-parse-rdfxml-1.19.0.tgz#00d3bf74d97e7b989de788427566559195840ac9" - integrity sha512-3ZqtJu7TKD43eaKZltbWwz/g4Zf5yy/qV+bzmUwa1+3TTAnunZqltf0HbcrVvwPI7l/HXSrpE/UbR6sW8Zy8Qg== +"@comunica/actor-rdf-parse-rdfxml@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-rdfxml/-/actor-rdf-parse-rdfxml-1.22.0.tgz#ea7260db32d409d6568a2e3422fbe7eb0ff10512" + integrity sha512-k47WEAZ6qKEhf1eBZZeI5aVywlrUUKP3BKHw2zKJUjuWq5k+w/rp2WALCyt0Owtb37UlJbET3fTlUhXKvT+2aw== dependencies: - rdfxml-streaming-parser "^1.4.0" + rdfxml-streaming-parser "^1.5.0" -"@comunica/actor-rdf-parse-xml-rdfa@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-xml-rdfa/-/actor-rdf-parse-xml-rdfa-1.19.0.tgz#a4f93b1ffec2e945b78cd0ce54b0a6732c8ee0d0" - integrity sha512-pYj71b7Vcoistyalwh5qzvS+jeHkOqaDbRyOmfJMtRjfaWTWg5gHfG0Y4gYjK+14kuo5QGzn5vT7O3jWqef6jA== +"@comunica/actor-rdf-parse-xml-rdfa@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/actor-rdf-parse-xml-rdfa/-/actor-rdf-parse-xml-rdfa-1.22.0.tgz#6bf1248e057b7137060db16438da8ab13282ffbc" + integrity sha512-y315YcZTz7AizKf8Jl022IocAJIh3OHSlzNrRNH3zB7i/ch+WHj1VL9pjIf6y77PD4BR75EdeoQCPafpm5Gsbg== dependencies: - rdfa-streaming-parser "^1.3.0" + rdfa-streaming-parser "^1.5.0" -"@comunica/bus-http@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/bus-http/-/bus-http-1.19.0.tgz#853de224b08258169da8bacc6dfc87b3da59256f" - integrity sha512-tbhB68Fj4b8WO+/38vVBvxV/E0W3d2yca5cANbCmdfrlO/TZXQ2JVD+1ylxR0uZiHXSHvClwGE7nSExBrE7gtw== +"@comunica/bus-http@~1.22.0": + version "1.22.1" + resolved "https://registry.yarnpkg.com/@comunica/bus-http/-/bus-http-1.22.1.tgz#0ac597826a9aa4e5b230edc62250e6bc445fc46a" + integrity sha512-CZ0NDWZH0k0FOshuRQJzYr3Z+2ZM1vqr9ZepONuaoYDwyKaxl29xPs3hNfjSy6YawjEQP+elr/WDc3TxKIpu8g== dependencies: + "@comunica/context-entries" "^1.22.0" + "@types/readable-stream" "^2.3.11" is-stream "^2.0.0" + readable-web-to-node-stream "^3.0.2" web-streams-node "^0.4.0" -"@comunica/bus-init@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/bus-init/-/bus-init-1.19.0.tgz#f93e4513e3cd843269486ccd30d60b9d81a74235" - integrity sha512-QRsKQBa1dNJ55s5BiKcWzjXJPfKHx0HaS/ZQClusxQJkKqYJHcMw2KdQjlxYV5vxEowKAXKISMxdhtqPvpUBvA== +"@comunica/bus-init@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/bus-init/-/bus-init-1.22.0.tgz#b75976f6e59ad66eb6e7f45dd5257f9486b852dc" + integrity sha512-NIfEJLI8EYFdTWJB0PV/lxPagStPl+gUj3LtOnovcF1ZhC5rgcJSC/tq1r04n0TziY2KVangnLDsF4752LjD6g== -"@comunica/bus-rdf-parse-html@^1.19.0", "@comunica/bus-rdf-parse-html@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/bus-rdf-parse-html/-/bus-rdf-parse-html-1.19.0.tgz#a308bd7554cf87d71e450e829c5bc404537170fc" - integrity sha512-UZ5brAVi7LlEw2kdjoVjc4191BoRVby9DIV/fiIADL2L0ALmt2YpoMEaIefHzKAxdgT3rUDkQMvJWFoQBKjw3Q== +"@comunica/bus-rdf-parse-html@^1.22.0", "@comunica/bus-rdf-parse-html@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/bus-rdf-parse-html/-/bus-rdf-parse-html-1.22.0.tgz#6854946ca7bfecd1f962bf8535b8b112ced93420" + integrity sha512-zqdLdF5qvru1vnzN4t9eXpJhi6khKm1ZWhUovBB9pfYnnyGRCQCPlFpcgJPrD8JfKd6nTvhgdLB5QcAbBb1I0A== dependencies: - "@types/rdf-js" "*" + "@rdfjs/types" "*" -"@comunica/bus-rdf-parse@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/bus-rdf-parse/-/bus-rdf-parse-1.19.0.tgz#97999d045401c315b06633a18cbff09ee2204bfd" - integrity sha512-j/1QCVxSmpJPk56rbK2Q3W/jCd2jPIxnBSPFz+2OQ6vXvKojHIx7j5jI18aFhSfQcxEX1LRoSbXAjS7PzKWByQ== +"@comunica/bus-rdf-parse@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/bus-rdf-parse/-/bus-rdf-parse-1.22.0.tgz#e94d93b3d941017f20bb3786fb0f7edbe047934e" + integrity sha512-ohZGlabX5K+dEmn+v4BzP+IZVyRc1ovWItHDLznnRqsHQr8W19WPG21lEFh5kk2MK4YnyQWmlUax1Yxrg7cbXg== dependencies: - "@comunica/actor-abstract-mediatyped" "^1.19.0" - "@types/rdf-js" "*" + "@comunica/actor-abstract-mediatyped" "^1.22.0" + "@rdfjs/types" "*" -"@comunica/core@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/core/-/core-1.19.0.tgz#9a1d38e95eff3e3ce656e24e1de1d8e64c68dec1" - integrity sha512-/G9rNRL8KdvSuxnWObUhA0+sbcHCFejTrj0vnHaf/QZtqEO0b238QzoS9swwP1PdzPdR1R6TJ2ikr8AcVZyd9g== +"@comunica/context-entries@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/context-entries/-/context-entries-1.22.0.tgz#31e61298cf93289449ee67197789baa27b89c591" + integrity sha512-HOYr1HdhgavxABpw8saZa9pueLAeGVVd/6cZ3FWcYnH3CvfQu6Ima06Gd00QdIAiGjQm01qQcWCxp0xURiqLKg== + +"@comunica/core@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/core/-/core-1.22.0.tgz#b60bde5e202a48bfb577a237a4ae6d756166477a" + integrity sha512-tgozygRFTd6t6l0YyvfVUWNC+KXWiTlBclkxtzFioQsplKvUSvg1TPjopRk8hhAvMaNRGMNBK2ZafNaqNTkI4w== dependencies: + "@comunica/context-entries" "^1.22.0" + "@comunica/types" "^1.22.0" immutable "^3.8.2" -"@comunica/mediator-combine-union@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/mediator-combine-union/-/mediator-combine-union-1.19.0.tgz#5468ebab7e9c5589888bf85b52b2a6a0d8ed6e81" - integrity sha512-oNI9LjgJWu9O/sy3IEI78l16Y+I0yJx/mLhKyArDdh4ZgLeS4/7xtfWNFn9xs+1jMbKFBRtZwZe8M2qV0AAiWg== +"@comunica/mediator-combine-union@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/mediator-combine-union/-/mediator-combine-union-1.22.0.tgz#665ca2f5d31bf4805259b93fede7ccbf8810e3a3" + integrity sha512-iUHmEGgWVmk02e80uB7w8xZ5vgTLpiqzrImvbokolJzWcVbobVCUkq8DUxzz3FJbNVRGipZUFrOqkRPAuAX6FA== + +"@comunica/mediator-number@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/mediator-number/-/mediator-number-1.22.0.tgz#81c4b5526663f0ab266662370d6232517d6cb253" + integrity sha512-KDPlJEvj0Lu+JygGXjnH8pf33k01lJ+wgzUlWK216jZJ1Px2lTlfc/COhSqi/e0y+k4ZSBcxx0gnjt2awMpbrQ== -"@comunica/mediator-number@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/mediator-number/-/mediator-number-1.19.0.tgz#69bfde044d772f59fc58d4f707e8eb98b78ba9c9" - integrity sha512-WN8MN64j0hOf1hEuiKGnagW6+m3nIcJWESZN3Y99jepIHxcBwy1XKmLoMK3qIIMPgBOOXFiTrZJQi9QZKJYtig== +"@comunica/mediator-race@~1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/mediator-race/-/mediator-race-1.22.0.tgz#ffe852edb3e163817f4d12ebadd4464561cbdb40" + integrity sha512-hIMaHyf9M4jOS0199OURSVgWFmzkyF2K2keuAb+iHoCH3UUcUnWjPOL1TrdkxvaUnrxmsBWR9SXbnqgMnhIsiQ== -"@comunica/mediator-race@~1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@comunica/mediator-race/-/mediator-race-1.19.0.tgz#0588840eca53bc415fa505288c8a4bafc0248793" - integrity sha512-cW/wqjuC5qxeTL2EUcPDmvHr4ve1AE3faNFyQRrB3+BMB0K3ZnPfBvaajRBmgSIIb8jyxBQXRGcIxAv/RtURlg== +"@comunica/types@^1.22.0": + version "1.22.0" + resolved "https://registry.yarnpkg.com/@comunica/types/-/types-1.22.0.tgz#ba984437d80d37c52636049325a1a16f6368a142" + integrity sha512-ZQ8p+ZvMAKmdq6Hz2QwqIQ2JScwRMotiWz0iSw2zYHsYQOhVmLg7HSMzMHpWNEA5UWzO/A5A+Co/ONXMhlnx3g== + dependencies: + "@rdfjs/types" "*" + asynciterator "^3.2.0" + immutable "^3.8.2" + sparqlalgebrajs "^3.0.1" "@dabh/diagnostics@^2.0.2": version "2.0.2" @@ -962,11 +991,6 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" -"@noble/hashes@^1.1.5": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" - integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1182,39 +1206,25 @@ resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.144.0.tgz#7dfbfbfbbb9c24d4abeb6f9856a1eca02aff6468" integrity sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg== -"@paralleldrive/cuid2@^2.2.2": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz#3d62ea9e7be867d3fa94b9897fab5b0ae187d784" - integrity sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw== - dependencies: - "@noble/hashes" "^1.1.5" - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@rdfjs/data-model@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rdfjs/data-model/-/data-model-1.1.2.tgz#e2f48a422c7e837b8a7d96d240732be3287df713" - integrity sha512-pk/G/JLYGaXesoBLvEmoC/ic0H3B79fTyS0Ujjh5YQB2DZW+mn05ZowFFv88rjB9jf7c1XE5XSmf8jzn6U0HHA== - dependencies: - "@types/rdf-js" "^2.0.1" - -"@rdfjs/types@*": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@rdfjs/types/-/types-1.0.1.tgz#92908a13bc88c71b349b332f3db19178282d6f4e" - integrity sha512-YxVkH0XrCNG3MWeZxfg596GFe+oorTVusmNxRP6ZHTsGczZ8AGvG3UchRNkg3Fy4MyysI7vBAA5YZbESL+VmHQ== - dependencies: - "@types/node" "*" - -"@rdfjs/types@^2.0.0": +"@rdfjs/types@*", "@rdfjs/types@>=1.0.0", "@rdfjs/types@^2.0.0", "@rdfjs/types@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@rdfjs/types/-/types-2.0.1.tgz#f10b50ceffff00b961c4265e60ac9f74550251da" integrity sha512-uyAzpugX7KekAXAHq26m3JlUIZJOC0uSBhpnefGV5i15bevDyyejoB7I+9MKeUrzXD8OOUI3+4FeV1wwQr5ihA== dependencies: "@types/node" "*" +"@rdfjs/types@^1.0.0": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rdfjs/types/-/types-1.1.2.tgz#9c2f9848c44c26d383bed2a808571de3b93b808d" + integrity sha512-wqpOJK1QCbmsGNtyzYnojPU8gRDPid2JO0Q0kMtb4j65xhCK880cnKAfEOwC+dX85VJcCByQx5zOwyyfCjDJsg== + dependencies: + "@types/node" "*" + "@rolldown/binding-android-arm64@1.2.4": version "1.2.4" resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz#9ac390255ded738672ad1425bed6423453ff71fd" @@ -1368,43 +1378,33 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.1.tgz#283f669ff76d7b8260df8ab7a4262cc83d988256" integrity sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg== -"@types/n3@^1.4.4": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@types/n3/-/n3-1.4.4.tgz#ed4f58200766ffc850d281e28c566eacb2b0b592" - integrity sha512-xsWfwyDh0uAH0CXvwqe9vb2UEDafMjRez/pB7yZwbWpd9Olw2wdxaL32FtdHjmmFE6b9i+j249JfRyZnvWkoqg== +"@types/n3@^1.26.3", "@types/n3@^1.4.4": + version "1.26.3" + resolved "https://registry.yarnpkg.com/@types/n3/-/n3-1.26.3.tgz#baee5c23ddd9bc9cdf30a3dac9d94ad5a51c99c7" + integrity sha512-uKEIHcOArBNmzCKDSrSgkEAC/v1h0yZWwldHI5kRnn0mMhvVHAgEpQMovowy03hV6iPpWPnq7BU6S+zg/ssXSw== dependencies: + "@rdfjs/types" "*" "@types/node" "*" - "@types/rdf-js" "*" - -"@types/node@*": - version "13.7.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113" - integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA== -"@types/node@^13.1.0": - version "13.11.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" - integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ== +"@types/node@*", "@types/node@^24.0.0": + version "24.13.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.3.tgz#49f18bd3c647866dcda51a0756c145e14590ce16" + integrity sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q== + dependencies: + undici-types "~7.18.0" "@types/node@^14.14.7": - version "14.14.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" - integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A== + version "14.18.63" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" + integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== "@types/node@^18.0.0": - version "18.19.54" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.54.tgz#f1048dc083f81b242640f04f18fb3e4ccf13fcdb" - integrity sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw== + version "18.19.130" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.130.tgz#da4c6324793a79defb7a62cba3947ec5add00d59" + integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== dependencies: undici-types "~5.26.4" -"@types/node@^24.0.0": - version "24.13.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.3.tgz#49f18bd3c647866dcda51a0756c145e14590ce16" - integrity sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q== - dependencies: - undici-types "~7.18.0" - "@types/normalize-package-data@^2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" @@ -1421,30 +1421,23 @@ integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/parse-link-header@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-link-header/-/parse-link-header-1.0.0.tgz#69f059e40a0fa93dc2e095d4142395ae6adc5d7a" - integrity sha512-fCA3btjE7QFeRLfcD0Sjg+6/CnmC66HpMBoRfRzd2raTaWMJV21CCZ0LO8MOqf8onl5n0EPfjq4zDhbyX8SVwA== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/parse-link-header/-/parse-link-header-1.0.1.tgz#6eade790736a050b9242c7c6b2fceda9b3dc394a" + integrity sha512-E2+Go9rQgPbmpkeA2iFXTWSTxX38KXlXwcdiIbt71Oorqr+G5QtH4AhpuDdxwRVyiTzdUrHnaaIumW/LhiZwVg== "@types/q@^1.5.8": version "1.5.8" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.8.tgz#95f6c6a08f2ad868ba230ead1d2d7f7be3db3837" integrity sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw== -"@types/rdf-js@*", "@types/rdf-js@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/rdf-js/-/rdf-js-4.0.0.tgz#96f7314b09b77ecd16fca7f358db90db8ac86d1b" - integrity sha512-2uaR7ks0380MqzUWGOPOOk9yZIr/6MOaCcaj3ntKgd2PqNocgi8j5kSHIJTDe+5ABtTHqKMSE0v0UqrsT8ibgQ== - dependencies: - "@types/node" "*" - -"@types/rdf-js@^2.0.1": - version "2.0.11" - resolved "https://registry.yarnpkg.com/@types/rdf-js/-/rdf-js-2.0.11.tgz#b9e398504ceb9f00eaa3b3036b643dc3490cf362" - integrity sha512-GC5MZU2HbL5JnlrLAzoxSqLprqtKwocz0TNVugqM04t1ZeeNFpZRqqBQc9Jhev35hEwdH84siRLaCesxHHYlmA== +"@types/rdf-js@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/rdf-js/-/rdf-js-4.0.2.tgz#a7eb0ff009915db2c980df97533d3ba7acad0c4b" + integrity sha512-soR/+RMogGiDU1lrpuQl5ZL55/L1eq/JlR2dWx052Uh/RYs9okh3XZHFlIJXHZqjqyjEn4WdbOMfBj7vvc2WVQ== dependencies: - "@types/node" "*" + rdf-js "*" -"@types/readable-stream@^2.3.13": +"@types/readable-stream@^2.3.11": version "2.3.15" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae" integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ== @@ -1480,6 +1473,13 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb" integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ== +"@types/sparqljs@^3.1.2": + version "3.1.12" + resolved "https://registry.yarnpkg.com/@types/sparqljs/-/sparqljs-3.1.12.tgz#29a030615a3aed6eb05fbd618ecf58ab345506f8" + integrity sha512-zg/sdKKtYI0845wKPSuSgunyU1o/+7tRzMw85lHsf4p/0UbA6+65MXAyEtv1nkaqSqrq/bXm7+bqXas+Xo5dpQ== + dependencies: + "@rdfjs/types" ">=1.0.0" + "@types/tough-cookie@*": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" @@ -1961,10 +1961,12 @@ async@^3.1.0: resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== -asynciterator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/asynciterator/-/asynciterator-3.0.0.tgz#0dd253bb8cc314469a5644eea1f88596fc16ffda" - integrity sha512-8aq8lNFDG47H/LlEFHLze17T+zjsHP7yscvEdyWh8MYBEI9CkpBbKuFx6FT4dmMVsBZ0D1LUq8aIh8+vHd/74Q== +asynciterator@^3.10.0, asynciterator@^3.2.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/asynciterator/-/asynciterator-3.10.0.tgz#b9baade3792078235dbb6b35f6d28e88db76c5ce" + integrity sha512-eDOBoUf2m+4ht0ETVn2SCfuBIZZ6UWyyQbP++LRPKoK7PmrCQq37pJ6vRvyef4o1Pn+CwWnzMlkXxGdh/krVIw== + dependencies: + tiny-set-immediate "^1.0.2" asynckit@^0.4.0: version "0.4.0" @@ -2364,27 +2366,22 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" -component-emitter@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" - integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== - componentsjs@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/componentsjs/-/componentsjs-4.0.4.tgz#6a9a7106eaac08726c69b4193cbb329104ef1ab3" - integrity sha512-ShaiHDcUPmiBbfRQwiV6b5oiooAkzzW7l//gZaE+rTwhRBWGbMU7Ll3NKsf+T77pn297VDzgvj7xw631Op95HA== + version "4.5.0" + resolved "https://registry.yarnpkg.com/componentsjs/-/componentsjs-4.5.0.tgz#099f6b823b7c4fa9d16e26ce8f3b30412a99c0fa" + integrity sha512-0F473HDUFfizVXZH1KBP4jmZRBAqYdVdpGhaNmHFmla/AB76B8NN7hQk7YDGaKkESl9zYqQ6kF3i8UgJBQ+rtg== dependencies: + "@rdfjs/types" "*" "@types/minimist" "^1.2.0" "@types/node" "^14.14.7" - "@types/rdf-js" "*" "@types/semver" "^7.3.4" jsonld-context-parser "^2.1.1" minimist "^1.2.0" - rdf-data-factory "^1.0.4" - rdf-object "^1.8.0" - rdf-parse "^1.7.0" + rdf-data-factory "^1.1.0" + rdf-object "^1.11.1" + rdf-parse "^1.9.1" rdf-quad "^1.5.0" - rdf-terms "^1.6.2" + rdf-terms "^1.7.0" semver "^7.3.2" winston "^3.3.3" @@ -2508,15 +2505,10 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie-signature@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" - integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== - -cookiejar@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" - integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== +cookie@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" + integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2545,7 +2537,14 @@ coveralls@^3.0.9: minimist "^1.2.0" request "^2.88.0" -cross-fetch@^3.0.5, cross-fetch@^3.0.6: +cross-fetch@^3.0.5: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" + integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== + dependencies: + node-fetch "^2.7.0" + +cross-fetch@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== @@ -2642,7 +2641,7 @@ debug@^2.6.9: dependencies: ms "2.0.0" -debug@^4.0.1, debug@^4.3.3, debug@^4.3.4, debug@^4.3.7: +debug@^4.0.1, debug@^4.3.3, debug@^4.3.4: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -2751,7 +2750,7 @@ detect-libc@^2.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== -dezalgo@^1.0.0, dezalgo@^1.0.4: +dezalgo@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== @@ -2782,41 +2781,59 @@ doctrine@^3.0.0: esutils "^2.0.2" dom-serializer@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" - integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== dependencies: domelementtype "^2.0.1" - domhandler "^4.0.0" + domhandler "^4.2.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" -domhandler@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" - integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: - domelementtype "^2.0.1" + domelementtype "^2.2.0" -domhandler@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" - integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== dependencies: - domelementtype "^2.1.0" + domelementtype "^2.3.0" -domutils@^2.4.2, domutils@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" - integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== dependencies: dom-serializer "^1.0.1" - domelementtype "^2.0.1" - domhandler "^4.0.0" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" dot-prop@^5.1.0: version "5.3.0" @@ -2890,9 +2907,19 @@ enquirer@^2.3.5: strip-ansi "^6.0.1" entities@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +entities@^4.2.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== env-paths@^2.2.0: version "2.2.0" @@ -3356,11 +3383,6 @@ fast-safe-stringify@^2.0.4: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-safe-stringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" - integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== - fast-uri@^3.0.1: version "3.1.4" resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.4.tgz#3b3daf9ce68f41f956df0b505132c0cfce9ec7af" @@ -3444,9 +3466,9 @@ fn.name@1.x.x: integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== follow-redirects@^1.5.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" @@ -3480,17 +3502,6 @@ form-data@^2.5.5: mime-types "^2.1.35" safe-buffer "^5.2.1" -form-data@^4.0.5: - version "4.0.6" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.6.tgz#28e864e1b786dbebb68db1f452f9635278665827" - integrity sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.4" - mime-types "^2.1.35" - form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3500,15 +3511,6 @@ form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -formidable@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.4.tgz#ac9a593b951e829b3298f21aa9a2243932f32ed9" - integrity sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug== - dependencies: - "@paralleldrive/cuid2" "^2.2.2" - dezalgo "^1.0.4" - once "^1.4.0" - forwarded-parse@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.0.tgz#1ae9d7a4be3af884f74d936d856f7d8c6abd0439" @@ -3920,6 +3922,14 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" @@ -3961,25 +3971,35 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== -htmlparser2@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" - integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== +htmlparser2@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== dependencies: domelementtype "^2.0.1" - domhandler "^3.3.0" - domutils "^2.4.2" + domhandler "^4.0.0" + domutils "^2.5.2" entities "^2.0.0" -htmlparser2@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01" - integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw== +htmlparser2@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" + integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== dependencies: domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.4.4" - entities "^2.0.0" + domhandler "^4.2.2" + domutils "^2.8.0" + entities "^3.0.1" + +htmlparser2@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-9.1.0.tgz#cdb498d8a75a51f739b61d3f718136c369bc8c23" + integrity sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.1.0" + entities "^4.5.0" http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.2.0" @@ -4081,10 +4101,10 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -immutable@^3.8.2: - version "3.8.2" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" - integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= +immutable@^3.8.2, immutable@^5.1.3: + version "5.1.9" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.9.tgz#ac23c3a01992ab665e14ac9ffff298f28cd74a0c" + integrity sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg== import-fresh@^3.0.0: version "3.3.1" @@ -4445,7 +4465,7 @@ is-ssh@^1.3.0: is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== is-stream@^2.0.0: version "2.0.0" @@ -4674,14 +4694,13 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonld-context-parser@^2.0.1, jsonld-context-parser@^2.0.2, jsonld-context-parser@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/jsonld-context-parser/-/jsonld-context-parser-2.1.1.tgz#0de2b459465b199ef6014a94d7af9b2485360374" - integrity sha512-7yKhnwFaiCnDPUZYQuAWyT0zZBfOKZDyjtqFVNbXrYRkboU+m55UsastsfXbo7qNroTGdFiEyxHEHDEfBC0P4Q== +jsonld-context-parser@^2.0.2, jsonld-context-parser@^2.1.1, jsonld-context-parser@^2.1.2, jsonld-context-parser@^2.1.3: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonld-context-parser/-/jsonld-context-parser-2.4.0.tgz#fae15a56c5ceabd1c4520ab1a9cc12c9a0a8b67d" + integrity sha512-ZYOfvh525SdPd9ReYY58dxB3E2RUEU4DJ6ZibO8AitcowPeBH4L5rCAitE2om5G1P+HMEgYEYEr4EZKbVN4tpA== dependencies: "@types/http-link-header" "^1.0.1" - "@types/node" "^13.1.0" - canonicalize "^1.0.1" + "@types/node" "^18.0.0" cross-fetch "^3.0.6" http-link-header "^1.0.2" relative-to-absolute-iri "^1.0.5" @@ -4696,41 +4715,50 @@ jsonld-context-parser@^3.0.0: http-link-header "^1.0.2" relative-to-absolute-iri "^1.0.5" -jsonld-streaming-parser@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-2.2.0.tgz#08bb8c53842e584fc82a8e1404be2dbaeb5bc96e" - integrity sha512-uaBP4sSFqYuupkfsQhRj7U/0KEFTqxElaUPn3CtYGEOsjvqCEexTkrVI7fPLoDERMIC2UfbzdHuN6rmESFGzgw== +jsonld-context-parser@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/jsonld-context-parser/-/jsonld-context-parser-3.1.1.tgz#aecba5cc18438f01ee93b56915e167f8ff8a69e7" + integrity sha512-Tv3fZBo3t83Em3TB9s8+B2GoXiXTl8R0wzBekIZDSzmu6O/BaXZp2enr/YZ+5AIl5NBEYX/FTbwgFoXehQDqyA== dependencies: "@types/http-link-header" "^1.0.1" - "@types/rdf-js" "^4.0.0" + "@types/node" "^24.0.0" + http-link-header "^1.0.2" + relative-to-absolute-iri "^1.0.5" + +jsonld-streaming-parser@^2.4.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-2.4.3.tgz#d8faa1df6dc3b760fb9455602edc5f001d756ef0" + integrity sha512-ysuevJ+l8+Y4W3J/yQW3pa9VCBNDHo2tZkKmPAnfhfsmFMyxuueAeXMmTbpJZdrpagzeeDVr3A8EZVuHliQJ9A== + dependencies: + "@rdfjs/types" "*" + "@types/http-link-header" "^1.0.1" canonicalize "^1.0.1" http-link-header "^1.0.2" - jsonld-context-parser "^2.0.1" + jsonld-context-parser "^2.1.3" jsonparse "^1.3.1" - rdf-data-factory "^1.0.2" + rdf-data-factory "^1.1.0" -jsonld-streaming-parser@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-4.0.1.tgz#41212df7ef21df1f970d09ef989261fbb566194b" - integrity sha512-6M4y9YGgADk3nXJebbRrxEdMVBJ9bnz+peAvjTXUievopqaE8sg/qml/I6Sp1ln7rpOKffsNZWSre6B7N76szw== +jsonld-streaming-parser@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonld-streaming-parser/-/jsonld-streaming-parser-5.0.1.tgz#b61f0eea4d1311ec903ee0c48a5d73cf45cf673d" + integrity sha512-Rf230DRAWe5p1H4e7phk1vo4FHEMOmC5xVcIywKJBBcwy6zaJWFcAvPcwngufNTdJs7dpTMbKQDjp4TYDpMKUQ== dependencies: "@bergos/jsonparse" "^1.4.0" - "@rdfjs/types" "*" "@types/http-link-header" "^1.0.1" "@types/readable-stream" "^4.0.0" buffer "^6.0.3" canonicalize "^1.0.1" http-link-header "^1.0.2" - jsonld-context-parser "^3.0.0" - rdf-data-factory "^1.1.0" + jsonld-context-parser "^3.1.0" + rdf-data-factory "^2.0.0" readable-stream "^4.0.0" -jsonld-streaming-serializer@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/jsonld-streaming-serializer/-/jsonld-streaming-serializer-3.0.1.tgz#42c4e748060924586d71b0f80800c0f05e613a63" - integrity sha512-lw5Z785Km53DRZ0ngyEamC3ojGdjFRDKvUt3b7lW5e8sqmTc7GHZxFBBw7IIqbb0Wc2WNksoXewmF13FC9bPNg== +jsonld-streaming-serializer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonld-streaming-serializer/-/jsonld-streaming-serializer-4.0.0.tgz#43144323ab099ccbeb0b840bd4c81a2591c01f23" + integrity sha512-YUg5Er7CSWB6+ZiHreBUoBILEttowto/fT654W5a+6iIiAI65Jz023Wtsi/XUCLUkItDWYe6ckYUbCV1aSQbtA== dependencies: - "@rdfjs/types" "*" + "@rdfjs/types" "^2.0.0" "@types/readable-stream" "^4.0.0" buffer "^6.0.3" jsonld-context-parser "^3.0.0" @@ -4826,6 +4854,15 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +light-my-request@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-6.6.0.tgz#c9448772323f65f33720fb5979c7841f14060add" + integrity sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A== + dependencies: + cookie "^1.0.1" + process-warning "^4.0.0" + set-cookie-parser "^2.6.0" + lightningcss-android-arm64@1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907" @@ -4993,11 +5030,6 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash.uniqwith@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3" - integrity sha1-egy/ZfQ7WShiWp1NDcVLGMrcfvM= - lodash@^4.0.0: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -5237,19 +5269,14 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -methods@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -microdata-rdf-streaming-parser@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/microdata-rdf-streaming-parser/-/microdata-rdf-streaming-parser-1.1.0.tgz#1807cfc9b44e739defac7be879f89545cb4dfe59" - integrity sha512-nvPEFzG4vZWzWJP2x8Ax7mJmdrFkSYrfhdTUDHLtXYZJVl8Ip7ScHUPLkUfX+Ci4g7sOdgHsotkxuccnlxtCAg== +microdata-rdf-streaming-parser@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/microdata-rdf-streaming-parser/-/microdata-rdf-streaming-parser-1.2.0.tgz#130967af0be59ce8ddf5fef4588a0ffa44a17d69" + integrity sha512-cMLNLEcS0mPaiA9iwq6BnsQK9sx2uBwjpRZIEvMRBNJpbvV58f8AFtPeYzNFh3OPyX9B49NYJ77bB0jNAUCurw== dependencies: - "@types/rdf-js" "^4.0.0" - htmlparser2 "^5.0.0" - rdf-data-factory "^1.0.2" + "@rdfjs/types" "*" + htmlparser2 "^6.0.0" + rdf-data-factory "^1.1.0" relative-to-absolute-iri "^1.0.2" micromatch@^4.0.8: @@ -5284,11 +5311,6 @@ mime-types@^2.1.35: dependencies: mime-db "1.52.0" -mime@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mime@^2.4.4: version "2.4.4" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" @@ -5304,6 +5326,11 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== +minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" @@ -5526,31 +5553,13 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -n3@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/n3/-/n3-1.10.0.tgz#4ebc725ad4c7a1ee4fac184b7b42114d620ba8c7" - integrity sha512-y+qpS0GktEBttOaDR+BF1t1G2fw4Xn4nCZWNn+7MvEmD2I4YpMH6OJF/xHKSwInCxOC9vu9eI6pluB9/RDUyZQ== +n3@^1.26.0, n3@^1.6.3: + version "1.26.0" + resolved "https://registry.yarnpkg.com/n3/-/n3-1.26.0.tgz#3d69de04bee680b9ebec9dbc1033dc1e6934d351" + integrity sha512-SQknS0ua90rN+3RHuk8BeIqeYyqIH/+ecViZxX08jR4j6MugqWRjtONl3uANG/crWXnOM2WIqBJtjIhVYFha+w== dependencies: - queue-microtask "^1.1.2" - readable-stream "^3.6.0" - -n3@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/n3/-/n3-1.3.5.tgz#ea00062b64fef71dc3a92befc00413a733ef1029" - integrity sha512-McWb1tCWGGAmHeGEakqZj/UqxQR9cpEYZ/JivBj59YfiOAuaIWZxu0B+jnhbCwCZ2AsxdgQ5Dq8fehIJpYQaMQ== - -n3@^1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/n3/-/n3-1.3.6.tgz#02766d11dd4afe4b3546555a00a095027d6e0256" - integrity sha512-EPaniuSPI+f66F8MP3GPXI3CA4cgSaA688PGUEs1Q5qyeAf/yIhe/8WbDV5+Hnmn8/ewcP7qYY6vfVnjbL5jcg== - -n3@^1.6.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/n3/-/n3-1.7.0.tgz#618391e1685d24ff642213777156380653807405" - integrity sha512-8R0Qj545WnVLQxOfxxyFKzOpO13hF3jhSMJfO0FNqvbsPZDiR9ZDmGGjXAlcoZDf/88OsCYd7rHML284vm1h6A== - dependencies: - queue-microtask "^1.1.2" - readable-stream "^3.6.0" + buffer "^6.0.3" + readable-stream "^4.0.0" n3@^2.0.1: version "2.1.1" @@ -5605,7 +5614,7 @@ node-fetch@2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -6126,7 +6135,7 @@ parse-json@^5.0.0: parse-link-header@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-link-header/-/parse-link-header-1.0.1.tgz#bedfe0d2118aeb84be75e7b025419ec8a61140a7" - integrity sha1-vt/g0hGK64S+deewJUGeyKYRQKc= + integrity sha512-Z0gpfHmwCIKDr5rRzjypL+p93aHVWO7e+0rFcUl9E3sC67njjs+xHFenuboSXZGlvYtmQqRzRaE3iFpTUnLmFQ== dependencies: xtend "~4.0.1" @@ -6298,6 +6307,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-warning@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.1.tgz#5c1db66007c67c756e4e09eb170cdece15da32fb" + integrity sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q== + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -6380,14 +6394,6 @@ qejs@^3.0.5: dependencies: q "0.8.x" -qs@^6.14.1: - version "6.15.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b" - integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A== - dependencies: - es-define-property "^1.0.1" - side-channel "^1.1.1" - qs@^6.9.4: version "6.9.4" resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" @@ -6408,11 +6414,6 @@ query-string@^6.13.8: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -queue-microtask@^1.1.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -6428,36 +6429,46 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -rdf-data-factory@^1.0.0, rdf-data-factory@^1.0.1, rdf-data-factory@^1.0.2, rdf-data-factory@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/rdf-data-factory/-/rdf-data-factory-1.0.4.tgz#4e22fc462620fbca650eb2d26c4a13a103edd777" - integrity sha512-ZIIwEkLcV7cTc+atvQFzAETFVRHz1BRe/MhdkZqYse8vxskErj8/bF/Ittc3B5c0GTyw6O3jVF2V7xBRGyRoSQ== - dependencies: - "@types/rdf-js" "^4.0.0" - -rdf-data-factory@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/rdf-data-factory/-/rdf-data-factory-1.1.0.tgz#d0510b9f100dd79e94f29559a12d4a5a585054d6" - integrity sha512-g8feOVZ/KL1OK2Pco/jDBDFh4m29QDsOOD+rWloG9qFvIzRFchGy2CviLUX491E0ByewXxMpaq/A3zsWHQA16A== +rdf-data-factory@^1.0.1, rdf-data-factory@^1.1.0, rdf-data-factory@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/rdf-data-factory/-/rdf-data-factory-1.1.3.tgz#11b39e0c80e51bca83828b2ddaa686f733be966f" + integrity sha512-ny6CI7m2bq4lfQQmDYvcb2l1F9KtGwz9chipX4oWu2aAtVoXjb7k3d8J1EsgAsEbMXnBipB/iuRen5H2fwRWWQ== dependencies: - "@rdfjs/types" "*" + "@rdfjs/types" "^1.0.0" -rdf-data-factory@^2.0.0: +rdf-data-factory@^2.0.0, rdf-data-factory@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/rdf-data-factory/-/rdf-data-factory-2.0.2.tgz#dfac1fdf99502f3b6d61f8e99e97af2490346e32" integrity sha512-WzPoYHwQYWvIP9k+7IBLY1b4nIDitzAK4mA37WumAF/Cjvu/KOtYJH9IPZnUTWNSd5K2+pq4vrcE9WZC4sRHhg== dependencies: "@rdfjs/types" "^2.0.0" +rdf-isomorphic@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/rdf-isomorphic/-/rdf-isomorphic-1.3.1.tgz#cd6d433cd85bf79d903d5f0fdeea42a40eb27265" + integrity sha512-6uIhsXTVp2AtO6f41PdnRV5xZsa0zVZQDTBdn0br+DZuFf5M/YD+T6m8hKDUnALI6nFL/IujTMLgEs20MlNidQ== + dependencies: + "@rdfjs/types" "*" + hash.js "^1.1.7" + rdf-string "^1.6.0" + rdf-terms "^1.7.0" + +rdf-js@*: + version "4.0.2" + resolved "https://registry.yarnpkg.com/rdf-js/-/rdf-js-4.0.2.tgz#f01510528bbfc6e004012b71a8a533896c4c4c10" + integrity sha512-ApvlFa/WsQh8LpPK/6hctQwG06Z9ztQQGWVtrcrf9L6+sejHNXLPOqL+w7q3hF+iL0C4sv3AX1PUtGkLNzyZ0Q== + dependencies: + "@rdfjs/types" "*" + rdf-literal@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rdf-literal/-/rdf-literal-1.2.0.tgz#3159cce5587007144ea4a3a713cea31af4fc0c68" - integrity sha512-N7nyfp/xzoiUuJt0xZ80BvBGkCPwWejgVDkCxWDSuooXKSows4ToW+KouYkMHLcoFzGg1Rlw2lk6btjMJg5aSA== + version "1.3.2" + resolved "https://registry.yarnpkg.com/rdf-literal/-/rdf-literal-1.3.2.tgz#6f1bd103bcd0be72a3d969115a6343a53c526eb2" + integrity sha512-79Stlu3sXy0kq9/decHFLf3xNPuY6sfhFPhd/diWErgaFr0Ekyg38Vh9bnVcqDYu48CFRi0t+hrFii49n92Hbw== dependencies: - "@types/rdf-js" "^4.0.0" - rdf-data-factory "^1.0.1" + "@rdfjs/types" "*" + rdf-data-factory "^1.1.0" -rdf-object@^1.14.0, rdf-object@^1.8.0: +rdf-object@^1.11.1: version "1.14.0" resolved "https://registry.yarnpkg.com/rdf-object/-/rdf-object-1.14.0.tgz#a51a2e575d4f838f88eced1e5096616769d17281" integrity sha512-/KSUWr7onDtL7d81kOpcUzJ2vHYOYJc2KU9WzBZRYydBhK0Sksh5Hg4VCQNaxUEvYEgdrrTuq9SLpOOCmag0rQ== @@ -6468,29 +6479,39 @@ rdf-object@^1.14.0, rdf-object@^1.8.0: rdf-string "^1.6.0" streamify-array "^1.0.1" -rdf-parse@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/rdf-parse/-/rdf-parse-1.7.0.tgz#3a57d02d7f3d99a7ecd715691e4f53cbe3e49634" - integrity sha512-P3meLRU9OkZZz9OYq26VeRrxIDrzEBNAScAWcTX1tsf4Z85WTLhiwP5jC+OZBSzRSlybkkb6EYSVA1M4eykiBg== - dependencies: - "@comunica/actor-http-native" "~1.19.0" - "@comunica/actor-rdf-parse-html" "~1.19.0" - "@comunica/actor-rdf-parse-html-microdata" "~1.19.0" - "@comunica/actor-rdf-parse-html-rdfa" "~1.19.0" - "@comunica/actor-rdf-parse-html-script" "~1.19.0" - "@comunica/actor-rdf-parse-jsonld" "~1.19.0" - "@comunica/actor-rdf-parse-n3" "~1.19.0" - "@comunica/actor-rdf-parse-rdfxml" "~1.19.0" - "@comunica/actor-rdf-parse-xml-rdfa" "~1.19.0" - "@comunica/bus-http" "~1.19.0" - "@comunica/bus-init" "~1.19.0" - "@comunica/bus-rdf-parse" "~1.19.0" - "@comunica/bus-rdf-parse-html" "~1.19.0" - "@comunica/core" "~1.19.0" - "@comunica/mediator-combine-union" "~1.19.0" - "@comunica/mediator-number" "~1.19.0" - "@comunica/mediator-race" "~1.19.0" - "@types/rdf-js" "*" +rdf-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rdf-object/-/rdf-object-3.0.0.tgz#016cec06c3954514bd6f66567ddea35c121a784b" + integrity sha512-qefryIRh1d9gqZUKp2qQwzIWLbmQspsc2bvVoOCCp126MZmKubcUTigHiMlCrUI9g7MDCrdTFAwcAal1lVR09A== + dependencies: + jsonld-context-parser "^3.0.0" + rdf-data-factory "^2.0.1" + rdf-string "^2.0.0" + streamify-array "^1.0.1" + +rdf-parse@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/rdf-parse/-/rdf-parse-1.9.1.tgz#6ec1d70a20d7e65b2c5a5acaee811f50b03940ac" + integrity sha512-W6ouYE+ufmCNFmXD1iGs5gUZH75jZekh/I5qF8a4Sl37BUc9mY0Jz5A0CV1tiKKhx+I+HYfxyX9VjOljD8rzgQ== + dependencies: + "@comunica/actor-http-native" "~1.22.0" + "@comunica/actor-rdf-parse-html" "~1.22.0" + "@comunica/actor-rdf-parse-html-microdata" "~1.22.0" + "@comunica/actor-rdf-parse-html-rdfa" "~1.22.0" + "@comunica/actor-rdf-parse-html-script" "~1.22.0" + "@comunica/actor-rdf-parse-jsonld" "^1.22.0" + "@comunica/actor-rdf-parse-n3" "~1.22.0" + "@comunica/actor-rdf-parse-rdfxml" "~1.22.0" + "@comunica/actor-rdf-parse-xml-rdfa" "~1.22.0" + "@comunica/bus-http" "~1.22.0" + "@comunica/bus-init" "~1.22.0" + "@comunica/bus-rdf-parse" "~1.22.0" + "@comunica/bus-rdf-parse-html" "~1.22.0" + "@comunica/core" "~1.22.0" + "@comunica/mediator-combine-union" "~1.22.0" + "@comunica/mediator-number" "~1.22.0" + "@comunica/mediator-race" "~1.22.0" + "@rdfjs/types" "*" stream-to-string "^1.2.0" rdf-quad@^1.5.0: @@ -6502,21 +6523,7 @@ rdf-quad@^1.5.0: rdf-literal "^1.2.0" rdf-string "^1.5.0" -rdf-string@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rdf-string/-/rdf-string-1.3.1.tgz#a9f49db4dbe0ff06bb17bf46e0389862f6a77e49" - integrity sha512-Pcw6aZRfto2cZodK5kSqFZW8mz6nfKLxSfjOSrTi5ajb2CSIwzqGx7UniysgKoV2i7tQL5dpPgCgY80upCiRUw== - dependencies: - "@rdfjs/data-model" "^1.1.1" - -rdf-string@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/rdf-string/-/rdf-string-1.5.0.tgz#5d0118f8788fe509f06d8cefc181fd979d712412" - integrity sha512-3TEJuDIKUADgZrfcZG+zAN4GfVA1Ei2sKA7Z7QVHkAE36wWoRGPJbGihPQMldgzvy9lG2nzZU+CXz+6oGSQNsQ== - dependencies: - rdf-data-factory "^1.0.0" - -rdf-string@^1.6.0: +rdf-string@^1.5.0, rdf-string@^1.6.0, rdf-string@^1.6.3: version "1.6.3" resolved "https://registry.yarnpkg.com/rdf-string/-/rdf-string-1.6.3.tgz#5c3173fad13e6328698277fb8ff151e3423282ab" integrity sha512-HIVwQ2gOqf+ObsCLSUAGFZMIl3rh9uGcRf1KbM85UDhKqP+hy6qj7Vz8FKt3GA54RiThqK3mNcr66dm1LP0+6g== @@ -6524,30 +6531,21 @@ rdf-string@^1.6.0: "@rdfjs/types" "*" rdf-data-factory "^1.1.0" -rdf-string@^2.0.1: +rdf-string@^2.0.0, rdf-string@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/rdf-string/-/rdf-string-2.0.1.tgz#9beff12486a653d6fb0f229a5e0e5f3cc2e962c6" integrity sha512-SMW4ponnKNrsP9kYpOLyICeM4UJmEXIeS3zri7kPK9gzLFsHD88oiza8LnokNYxd76zW4JoYWD+v4x0g8rJBjw== dependencies: rdf-data-factory "^2.0.0" -rdf-terms@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/rdf-terms/-/rdf-terms-1.6.2.tgz#e31ad9023e0ee163dafa3dd847410c61556d0d57" - integrity sha512-dASpdYHYLEwzN9iSymJie1WUj6VHXy1By8Am4g2rJlhTfVvNitsJpDY+A3X2QehlGhCaWjHMzXS4q/JKNPI80A== +rdf-terms@^1.7.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/rdf-terms/-/rdf-terms-1.11.0.tgz#0c2e3a2b43f1042959c9263af27dab08dc4b084d" + integrity sha512-iKlVgnMopRKl9pHVNrQrax7PtZKRCT/uJIgYqvuw1VVQb88zDvurtDr1xp0rt7N9JtKtFwUXoIQoEsjyRo20qQ== dependencies: - lodash.uniqwith "^4.5.0" - rdf-data-factory "^1.0.1" - -rdfa-streaming-parser@^1.3.0, rdfa-streaming-parser@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rdfa-streaming-parser/-/rdfa-streaming-parser-1.4.0.tgz#051bcfba4d8cfb37198011260befdb3e900f7149" - integrity sha512-tx2rsBpK7MhpuvuMFdpoIfH8t8ij/traX6+hiFe4WV648eWlcYKURLVdWwqqPkF4qwnT1PH8mqBpBY7CNpbjvg== - dependencies: - "@types/rdf-js" "^4.0.0" - htmlparser2 "^5.0.0" - rdf-data-factory "^1.0.2" - relative-to-absolute-iri "^1.0.2" + "@rdfjs/types" "*" + rdf-data-factory "^1.1.0" + rdf-string "^1.6.0" rdfa-streaming-parser@^1.5.0: version "1.5.0" @@ -6559,13 +6557,23 @@ rdfa-streaming-parser@^1.5.0: rdf-data-factory "^1.1.0" relative-to-absolute-iri "^1.0.2" -rdfxml-streaming-parser@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rdfxml-streaming-parser/-/rdfxml-streaming-parser-1.4.0.tgz#32cae3cfbc77aef110f6be8e067935fbf156f3b6" - integrity sha512-/FKDCq4tuSWz8PZaaPxqIQpenEvRR3Gsqllqg4VmdPFN6WiWfbaD244cKASfXfQHt9Bw7tLsLHsmtA1isIPBCg== +rdfa-streaming-parser@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rdfa-streaming-parser/-/rdfa-streaming-parser-3.0.2.tgz#069a3cfb11618a8728e3142b1dbda25fbe3db42d" + integrity sha512-1MEkALT5PYjucVo6lNHXd9DbkgW0hSfPOJOs/TpUFe9zoZvkoreKcGIFnMFkbMmYTNLCiGFo/74LviIUnWLkIw== + dependencies: + htmlparser2 "^9.0.0" + rdf-data-factory "^2.0.0" + readable-stream "^4.0.0" + relative-to-absolute-iri "^1.0.2" + +rdfxml-streaming-parser@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/rdfxml-streaming-parser/-/rdfxml-streaming-parser-1.5.0.tgz#485af3a01abc2dc49149b8758da9a12351d34348" + integrity sha512-pnt+7NgeqCMd2/rub+dqxzYJhZwJjBNU2BRwyYdCTmRZu2fr795jCPJB6Io5pjPzAt29ASqy+ODBSRMDKoKGbQ== dependencies: - "@types/rdf-js" "^4.0.0" - rdf-data-factory "^1.0.2" + "@rdfjs/types" "*" + rdf-data-factory "^1.1.0" relative-to-absolute-iri "^1.0.0" sax "^1.2.4" @@ -6686,7 +6694,7 @@ readable-stream-node-to-web@^1.0.1: resolved "https://registry.yarnpkg.com/readable-stream-node-to-web/-/readable-stream-node-to-web-1.0.1.tgz#8b7614faa1465ebfa0da9b9ca6303fa27073b7cf" integrity sha1-i3YU+qFGXr+g2pucpjA/onBzt88= -"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6740,6 +6748,24 @@ readable-stream@^4.0.0: events "^3.3.0" process "^0.11.10" +readable-stream@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readable-web-to-node-stream@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz#392ba37707af5bf62d725c36c1b5d6ef4119eefc" + integrity sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw== + dependencies: + readable-stream "^4.7.0" + readdir-scoped-modules@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -6997,9 +7023,9 @@ safe-regex-test@^1.1.0: integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + version "1.6.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.1.tgz#4c23cf608c0b693ab54b4b5888e92cfe977b9843" + integrity sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q== "semver@2 || 3 || 4 || 5": version "5.7.1" @@ -7038,6 +7064,11 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.6.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" + integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -7117,7 +7148,7 @@ side-channel-weakmap@^1.0.2: object-inspect "^1.13.3" side-channel-map "^1.0.1" -side-channel@^1.1.0, side-channel@^1.1.1: +side-channel@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.1.tgz#ea02c62e05dc4bea67d4442f0fb71ee192f8e0ab" integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ== @@ -7243,16 +7274,35 @@ source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sparqljson-parse@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/sparqljson-parse/-/sparqljson-parse-2.1.2.tgz#e727724ca8c9e6bc979f0d15c550d862f6ae3003" - integrity sha512-RqPeyy+RYQMeqgEsKPTY+ME5ZNXcgXJzg1v0o+tROiTntS9CwUW8mAY3wsx6seSvW3LVyNDEtsqOxnAokoGXOA== +sparqlalgebrajs@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparqlalgebrajs/-/sparqlalgebrajs-3.0.3.tgz#a0b2b2ea2bacf97a74b4e3f3fc90be72b96d94a1" + integrity sha512-XFNhsO55bprayrM35h/jY0kzzuGc3oZ1On3kc+s7Un0BFQBXa046aLcMZFp4MYSvn7GtMe9eZ08ONFnBH5kEsQ== dependencies: "@rdfjs/types" "*" - "@types/readable-stream" "^2.3.13" - buffer "^6.0.3" - jsonparse "^1.3.1" + "@types/sparqljs" "^3.1.2" + fast-deep-equal "^3.1.3" + minimist "^1.2.5" rdf-data-factory "^1.1.0" + rdf-isomorphic "^1.3.0" + rdf-string "^1.6.0" + sparqljs "^3.4.2" + +sparqljs@^3.4.2: + version "3.7.4" + resolved "https://registry.yarnpkg.com/sparqljs/-/sparqljs-3.7.4.tgz#d1f0c525aab6030c521b12ccef7efa2f57c82e90" + integrity sha512-hb4C84gf7KM7vz+iG595mPwvqxOvBJCm9L3dCxtV2zZDht6ZMmMG0tHeeqFeqwb9875yU9U4lhYe4LYRvWj0BQ== + dependencies: + rdf-data-factory "^1.1.2" + +sparqljson-parse@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sparqljson-parse/-/sparqljson-parse-3.3.0.tgz#d6770a1f3fd4fd2e168c2cc7027eaaa72051a31c" + integrity sha512-XrmkCsrx4n69Ak63Ju7t91hVlWw7YhEPPdA+giW2GRTosQxPur0JWh7rQin/8aT0WjKZgBgGpsNnVBYyyWUq1w== + dependencies: + "@bergos/jsonparse" "^1.4.1" + "@types/readable-stream" "^4.0.0" + rdf-data-factory "^2.0.0" readable-stream "^4.0.0" spdx-correct@^3.0.0: @@ -7365,9 +7415,9 @@ stop-iteration-iterator@^1.1.0: internal-slot "^1.1.0" stream-to-string@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/stream-to-string/-/stream-to-string-1.2.0.tgz#3ca506a097ecbf78b0e0aee0b6fa5c4565412a15" - integrity sha512-8drZlFIKBHSMdX9GCWv8V9AAWnQcTqw0iAI6/GC7UJ0H0SwKeFKjOoZfGY1tOU00GGU7FYZQoJ/ZCUEoXhD7yQ== + version "1.2.1" + resolved "https://registry.yarnpkg.com/stream-to-string/-/stream-to-string-1.2.1.tgz#15cb325d88b33cc62accb032c7093f85eb785db2" + integrity sha512-WsvTDNF8UYs369Yko3pcdTducQtYpzEZeOV7cTuReyFvOoA9S/DLJ6sYK+xPafSPHhUMpaxiljKYnT6JSFztIA== dependencies: promise-polyfill "^1.1.6" @@ -7496,7 +7546,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -7579,30 +7629,6 @@ strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -superagent@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-10.3.0.tgz#ff1e39e7976b63f8084291d65f5bfbbbbd156989" - integrity sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ== - dependencies: - component-emitter "^1.3.1" - cookiejar "^2.1.4" - debug "^4.3.7" - fast-safe-stringify "^2.1.1" - form-data "^4.0.5" - formidable "^3.5.4" - methods "^1.1.2" - mime "2.6.0" - qs "^6.14.1" - -supertest@^7.0.0: - version "7.2.2" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.2.2.tgz#dac3ee25a2aa59942a7f641e50c838a7c8819204" - integrity sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA== - dependencies: - cookie-signature "^1.2.2" - methods "^1.1.2" - superagent "^10.3.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -7722,6 +7748,11 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-set-immediate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tiny-set-immediate/-/tiny-set-immediate-1.0.2.tgz#4abcc1cd597e9cdcd6515c3c0a18c8ef4d98f292" + integrity sha512-EVbaM4zXFWS4CIqVoPzY7XIioQ5LU1p49AHizwPO1KyFyp/gxy5SA8mDmfDVl/2WLQiHgUL+esO6Ig+KhpUxUw== + tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" @@ -7922,10 +7953,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^5.9.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== +typescript@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" + integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== uglify-js@^3.1.4: version "3.8.0"