diff --git a/README.md b/README.md index b5caae8..5bc0a98 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ There are three endpoints that need to be set up: - `/.well-known/openid-configuration` - `/path/to/jwks` -- `/path/to/webId` +- `/path/to/webId` - unless you want to use custom webId -For more details, you can check the output of `getEndpoints(webId)` from the `@soid/core` package. +For more details, you can check the output of `getEndpoints(webId: string, issuer?: string)` from the `@soid/core` package. ## Limitations diff --git a/packages/core/README.md b/packages/core/README.md index 960aafe..d8da89e 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -2,12 +2,35 @@ Give your Solid service a Solid-OIDC-compatible identity. +## Overview + +This library: + +- tells you which endpoints your service has to serve: `getEndpoints(webId: string, issuer?: string)` +- provides authenticated fetch that your service can use to access protected resources: `await getAuthenticatedFetch(webId: string, issuer?: string)` + +Please consider using higher-level libraries like [`@soid/koa`](https://npmjs.com/package/@soid/koa). + ## Usage +### Installation + +```bash +npm install --save @soid/core +# or +yarn add @soid/core +``` + +### Identity for service's own webId + +By default, your service can serve its own webId. + ```ts import { getEndpoints, getAuthenticatedFetch } from '@soid/core' +// The webId's origin has to match the service's origin const webId = 'https://service.example/profile/card#bot' + // Get authenticated fetch to make requests with this identity const authenticatedFetch = await getAuthenticatedFetch(webId) @@ -24,4 +47,21 @@ const endpoints = getEndpoints(webId) // }] ``` -Please consider using higher-level libraries like [`@soid/koa`](https://npmjs.com/package/@soid/koa). +### Identity for custom webId + +You can also authenticate your service with custom webId, such as your personal webId. + +You MUST add triple ` solid:oidcIssuer .` to your webId, where `issuer` MUST match the origin of the service. (no trailing slashes!) + +```ts +import { getEndpoints, getAuthenticatedFetch } from '@soid/core' + +const webId = 'https://custom.webid/profile/card#me' // you'll have to serve this somewhere, and add the required triple +const issuer = 'https://service.example' // this has to match your service's origin + +// Get authenticated fetch to make requests with this identity +const authenticatedFetch = await getAuthenticatedFetch(webId, issuer) + +// Get configuration of endpoints that your service MUST serve +const endpoints = getEndpoints(webId, issuer) +``` diff --git a/packages/core/src/endpoints.ts b/packages/core/src/endpoints.ts index b94bb80..00e177a 100644 --- a/packages/core/src/endpoints.ts +++ b/packages/core/src/endpoints.ts @@ -7,17 +7,22 @@ export interface Endpoint { defaultContentType: string } -export const getEndpoints = (webId: string): Endpoint[] => { +export const getEndpoints = (webId: string, issuer?: string): Endpoint[] => { const { pathname, hash, origin } = new URL(webId) - return [ + issuer = issuer ? new URL(issuer).origin : origin + + // make sure that issuer doesn't contain any unwanted paths etc. + issuer = new URL(issuer).origin + + const endpoints: Endpoint[] = [ { method: 'get', path: '/.well-known/openid-configuration', body: { 'application/json': { - issuer: origin, - jwks_uri: new URL('/jwks', webId).toString(), + issuer, + jwks_uri: new URL('/jwks', issuer).toString(), response_types_supported: ['id_token', 'token'], scopes_supported: ['openid', 'webid'], }, @@ -30,7 +35,10 @@ export const getEndpoints = (webId: string): Endpoint[] => { body: { 'application/json': { keys: [fullJwkPublicKey] } }, defaultContentType: 'application/json', }, - { + ] + + if (issuer === origin) { + endpoints.push({ method: 'get', path: pathname, body: { @@ -46,6 +54,8 @@ export const getEndpoints = (webId: string): Endpoint[] => { }, // TODO add application/ld+json defaultContentType: 'text/turtle', - }, - ] + }) + } + + return endpoints } diff --git a/packages/core/src/identity.ts b/packages/core/src/identity.ts index c0a0eb7..e2b0497 100644 --- a/packages/core/src/identity.ts +++ b/packages/core/src/identity.ts @@ -22,8 +22,11 @@ export const fullJwkPublicKey = { export const getAuthenticatedFetch = async ( webId: string, + issuer?: string, ): Promise => { - const { origin: baseUrl } = new URL(webId) + const { origin } = new URL(webId) + + issuer = issuer ? new URL(issuer).origin : origin const dpopKey = await generateDpopKeyPair() const jkt = await calculateJwkThumbprint( @@ -42,7 +45,7 @@ export const getAuthenticatedFetch = async ( .setIssuedAt(now) .setExpirationTime(now + 3600) .setAudience('solid') - .setIssuer(baseUrl) + .setIssuer(issuer) .setJti(randomUUID()) .sign(tokenKeyPair.privateKey) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 78362dc..6cd18d3 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,2 @@ -export { - fullJwkPublicKey as jwkPublicKey, - getAuthenticatedFetch, -} from './identity.js' export { getEndpoints, type Endpoint } from './endpoints.js' +export { getAuthenticatedFetch } from './identity.js' diff --git a/packages/koa/README.md b/packages/koa/README.md index 2673e72..f2bd2c8 100644 --- a/packages/koa/README.md +++ b/packages/koa/README.md @@ -2,13 +2,35 @@ Give your koa-based Solid service a Solid-OIDC-compatible identity. +## Overview + +This library provides: + +- identity-serving middleware for your koa-based service: `solidIdentity(webId: string, issuer?: string)` +- authenticated fetch that your service can use to access protected resources: `await getAuthenticatedFetch(webId: string, issuer?: string)` + +If you want to serve identity for a koa-incompatible service, consider using lower level [`@soid/core`](https://npmjs.com/package/@soid/core), or [open an issue](https://github.com/solidcouch/solid-identity/issues). + ## Usage +### Installation + +```bash +npm install --save @soid/koa +# or +yarn add @soid/koa +``` + +### Identity for service's own webId + +By default, your service can serve its own webId. + ```ts import Koa from 'koa' import Router from '@koa/router' import { solidIdentity, getAuthenticatedFetch } from '@soid/koa' +// The webId origin MUST match your service origin const webId = 'https://service.example/profile/card#bot' const app = new Koa() @@ -19,8 +41,38 @@ router.use(solidIdentity(webId).routes()) // register your other routes // ... -app.use(router) +app.use(router.routes()).use(router.allowedMethods()) // use the authenticated fetch to make authenticated requests to Solid Pods or other Solid-compatible services const fetch = getAuthenticatedFetch(webId) ``` + +### Identity for custom webId + +You can also authenticate your service with custom webId (for example your own webId) + +You MUST add triple ` solid:oidcIssuer .` to your webId, where `issuer` MUST match the origin of the service. (no trailing slashes!) + +```ts +import Koa from 'koa' +import Router from '@koa/router' +import { solidIdentity, getAuthenticatedFetch } from '@soid/koa' + +// The webId must serve a profile in text/turtle, and has to contain the required triple pointing to the issuer +const webId = 'https://custom.webid/profile/card#me' +// The issuer has to match your service's origin +const issuer = 'https://service.example' + +const app = new Koa() +const router = new Router() + +// register the identity middleware in your router +router.use(solidIdentity(webId, issuer).routes()) +// register your other routes +// ... + +app.use(router.routes()).use(router.allowedMethods()) + +// use the authenticated fetch to make authenticated requests to Solid Pods or other Solid-compatible services +const fetch = getAuthenticatedFetch(webId, issuer) +``` diff --git a/packages/koa/package.json b/packages/koa/package.json index 84c7b8d..6b811a7 100644 --- a/packages/koa/package.json +++ b/packages/koa/package.json @@ -66,6 +66,7 @@ "@types/koa__router": "^12.0.4", "css-authn": "^0.0.16", "koa": "^2.15.3", + "msw": "^2.6.0", "rdf-namespaces": "^1.12.0", "typescript": "^5.6.2", "vitest": "^2.1.4" diff --git a/packages/koa/src/index.ts b/packages/koa/src/index.ts index 5d8df81..d2fbf7b 100644 --- a/packages/koa/src/index.ts +++ b/packages/koa/src/index.ts @@ -2,10 +2,10 @@ import Router from '@koa/router' import { getEndpoints } from '@soid/core' export { getAuthenticatedFetch } from '@soid/core' -export const solidIdentity = (identity: string) => { +export const solidIdentity = (webId: string, issuer?: string) => { const router = new Router() - const endpoints = getEndpoints(identity) + const endpoints = getEndpoints(webId, issuer) for (const endpoint of endpoints) { router[endpoint.method](endpoint.path, async ctx => { diff --git a/packages/koa/src/tests/identity.test.ts b/packages/koa/src/tests/identity.test.ts index 1a27017..96c6c1c 100644 --- a/packages/koa/src/tests/identity.test.ts +++ b/packages/koa/src/tests/identity.test.ts @@ -1,6 +1,7 @@ -import Router from '@koa/router' import { App, AppRunner, joinFilePath } from '@solid/community-server' import Koa from 'koa' +import * as msw from 'msw' +import * as mswNode from 'msw/node' import { beforeAll, beforeEach, describe, expect, test } from 'vitest' import { getAuthenticatedFetch, solidIdentity } from '../index.js' import { getDefaultPerson } from './helpers/index.js' @@ -19,7 +20,7 @@ beforeAll(async () => { css = await new AppRunner().create({ shorthand: { port: 4000, - loggingLevel: 'off', + // loggingLevel: 'off', seedConfig: joinFilePath(__dirname, './css-pod-seed.json'), // set up some Solid accounts }, }) @@ -39,9 +40,8 @@ beforeEach(async () => { const setupServer = (...props: Parameters) => { const app = new Koa() - const router = new Router() - router.use(solidIdentity(...props).routes()) - app.use(router.allowedMethods()).use(router.routes()) + const identity = solidIdentity(...props) + app.use(identity.allowedMethods()).use(identity.routes()) return app } @@ -57,11 +57,40 @@ const stopServer = (server: Awaited>) => server.close(err => (err ? reject(err) : resolve())), ) +const setupIdentityServer = ({ + webId, + provider, +}: { + webId: string + provider: string +}) => { + const { pathname, origin, hash } = new URL(webId) + + const url = new URL(pathname, origin).toString() + + return mswNode.setupServer( + msw.http.get(url, () => + msw.HttpResponse.text( + `@prefix solid: . + @prefix foaf: . + @base <${new URL(pathname, origin).toString()}>. + + <${hash}> + a foaf:Agent; + solid:oidcIssuer <${provider}>. + `, + { headers: { 'Content-Type': 'text/turtle' } }, + ), + ), + ) +} + describe('Solid identity', () => { test('Default identity endpoints can be set up', async () => { const webId = 'http://localhost:3000/test/asdf#identity' const app = setupServer(webId) const server = await startServer(app, 3000) + // check .well-known endpoint const response = await fetch( 'http://localhost:3000/.well-known/openid-configuration', @@ -81,7 +110,31 @@ describe('Solid identity', () => { test.todo('default webId can be extended with additional triples') - test.todo('Identity with external webId can be set up') + test('Identity with external webId can be set up', async () => { + const webId = 'http://localhost:5000/test/asdf#identity' + const baseUrl = 'http://localhost:3000' + + const identityServer = setupIdentityServer({ webId, provider: baseUrl }) + identityServer.listen() + const app = setupServer(webId, baseUrl) + const server = await startServer(app, 3000) + // check .well-known endpoint + const response = await fetch( + 'http://localhost:3000/.well-known/openid-configuration', + ) + expect(response.ok).toEqual(true) + const body = await response.json() + expect(body).toHaveProperty('jwks_uri') + + const jwksResponse = await fetch(body.jwks_uri) + expect(jwksResponse.ok).toEqual(true) + + const webIdResponse = await fetch(webId) + expect(webIdResponse.ok).toEqual(true) + + await stopServer(server) + identityServer.close() + }) test('Fetch protected data from CSS with default identity', async () => { // set up the koa service and soid middleware @@ -131,5 +184,62 @@ describe('Solid identity', () => { await stopServer(server) }, 10000) - test.todo('Fetch protected data from CSS with custom webId identity') + test('Fetch protected data from CSS with custom webId identity', async () => { + // set up the koa service and soid middleware + const serviceWebId = 'http://localhost:5000/test/asdf#identity' + const provider = 'http://localhost:3000' + const app = setupServer(serviceWebId, provider) + const server = await startServer(app, 3000) + const identityServer = setupIdentityServer({ + webId: serviceWebId, + provider: 'http://localhost:3000', + }) + + identityServer.listen() + + // setup some data on the server + const person = await getDefaultPerson( + { + email: 'person@example', + password: 'password', + pods: [{ name: 'person' }], + }, + 'http://localhost:4000', + ) + + const resourceUrl = new URL('./testfolder/test', person.podUrl).toString() + + await createResource({ + url: resourceUrl, + body: '<#this> a "test"', + acls: [ + { + permissions: ['Read', 'Write', 'Append', 'Control'], + agents: [person.webId], + }, + { + permissions: ['Read'], + agents: [serviceWebId], + }, + ], + authenticatedFetch: person.fetch, + }) + + // try to fetch data from CSS + // unauthenticated fetch should fail + const plainResponse = await fetch(resourceUrl) + expect(plainResponse.ok).toEqual(false) + + // authenticated fetch should succeed + const authenticatedFetch = await getAuthenticatedFetch( + serviceWebId, + provider, + ) + + const authenticatedResponse = await authenticatedFetch(resourceUrl) + expect(authenticatedResponse.ok).toEqual(true) + // stop the server + await stopServer(server) + identityServer.close() + }) }) diff --git a/yarn.lock b/yarn.lock index e9785e9..0c95d36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43,6 +43,34 @@ __metadata: languageName: node linkType: hard +"@bundled-es-modules/cookie@npm:^2.0.0": + version: 2.0.0 + resolution: "@bundled-es-modules/cookie@npm:2.0.0" + dependencies: + cookie: "npm:^0.5.0" + checksum: 10c0/0655dd331b35d7b5b6dd2301c3bcfb7233018c0e3235a40ced1d53f00463ab92dc01f0091f153812867bc0ef0f8e0a157a30acb16e8d7ef149702bf8db9fe7a6 + languageName: node + linkType: hard + +"@bundled-es-modules/statuses@npm:^1.0.1": + version: 1.0.1 + resolution: "@bundled-es-modules/statuses@npm:1.0.1" + dependencies: + statuses: "npm:^2.0.1" + checksum: 10c0/c1a8ede3efa8da61ccda4b98e773582a9733edfbeeee569d4630785f8e018766202edb190a754a3ec7a7f6bd738e857829affc2fdb676b6dab4db1bb44e62785 + languageName: node + linkType: hard + +"@bundled-es-modules/tough-cookie@npm:^0.1.6": + version: 0.1.6 + resolution: "@bundled-es-modules/tough-cookie@npm:0.1.6" + dependencies: + "@types/tough-cookie": "npm:^4.0.5" + tough-cookie: "npm:^4.1.4" + checksum: 10c0/28bcac878bff6b34719ba3aa8341e9924772ee55de5487680ebe784981ec9fccb70ed5d46f563e2404855a04de606f9e56aa4202842d4f5835bc04a4fe820571 + languageName: node + linkType: hard + "@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": version: 1.6.0 resolution: "@colors/colors@npm:1.6.0" @@ -2703,6 +2731,51 @@ __metadata: languageName: node linkType: hard +"@inquirer/confirm@npm:^5.0.0": + version: 5.0.1 + resolution: "@inquirer/confirm@npm:5.0.1" + dependencies: + "@inquirer/core": "npm:^10.0.1" + "@inquirer/type": "npm:^3.0.0" + peerDependencies: + "@types/node": ">=18" + checksum: 10c0/bd8fafd75d4d591b3c153cb2f76d7ac9163701cb0a032e8e589d51c918a41d1da70ae7aaeb4d8d7394979a9af24c23a7d71ea6106d3308004f9829f133765776 + languageName: node + linkType: hard + +"@inquirer/core@npm:^10.0.1": + version: 10.0.1 + resolution: "@inquirer/core@npm:10.0.1" + dependencies: + "@inquirer/figures": "npm:^1.0.7" + "@inquirer/type": "npm:^3.0.0" + ansi-escapes: "npm:^4.3.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^2.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 10c0/d55682e5c26c41037cb80a3bef5a12ae4eedf14621786b44088f48aeb32eb815dfb0f241950b6dba2eb84bf22131c126a2cb59e8e2d4ef63ad3613d59339063a + languageName: node + linkType: hard + +"@inquirer/figures@npm:^1.0.7": + version: 1.0.7 + resolution: "@inquirer/figures@npm:1.0.7" + checksum: 10c0/d7b4cfcd38dd43d1ac79da52c4478aa89145207004a471aa2083856f1d9b99adef45563f09d66c09d6457b09200fcf784527804b70ad3bd517cbc5e11142c2df + languageName: node + linkType: hard + +"@inquirer/type@npm:^3.0.0": + version: 3.0.0 + resolution: "@inquirer/type@npm:3.0.0" + peerDependencies: + "@types/node": ">=18" + checksum: 10c0/4c26595749782e3dfbfea0c7a19b1db603485e0fce4a9d4fe52be1c9c05fcb2cc3afbc849d03bddbde47896786df93d6f02657eeeae5dbc8cdc78cd8a4f80123 + languageName: node + linkType: hard + "@inrupt/solid-client-authn-core@npm:^1.17.5": version: 1.17.5 resolution: "@inrupt/solid-client-authn-core@npm:1.17.5" @@ -2892,6 +2965,20 @@ __metadata: languageName: node linkType: hard +"@mswjs/interceptors@npm:^0.36.5": + version: 0.36.7 + resolution: "@mswjs/interceptors@npm:0.36.7" + dependencies: + "@open-draft/deferred-promise": "npm:^2.2.0" + "@open-draft/logger": "npm:^0.3.0" + "@open-draft/until": "npm:^2.0.0" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.3" + strict-event-emitter: "npm:^0.5.1" + checksum: 10c0/3a5be0b1c5cac0a5338458bba182c650961950fc8e0143e45cdf66201886a1bc95c4dca7aeee254cd331c6dd8acb630eb6db6bbaef08f629f9da95974314622d + languageName: node + linkType: hard + "@napi-rs/wasm-runtime@npm:0.2.4": version: 0.2.4 resolution: "@napi-rs/wasm-runtime@npm:0.2.4" @@ -3396,6 +3483,30 @@ __metadata: languageName: node linkType: hard +"@open-draft/deferred-promise@npm:^2.2.0": + version: 2.2.0 + resolution: "@open-draft/deferred-promise@npm:2.2.0" + checksum: 10c0/eafc1b1d0fc8edb5e1c753c5e0f3293410b40dde2f92688211a54806d4136887051f39b98c1950370be258483deac9dfd17cf8b96557553765198ef2547e4549 + languageName: node + linkType: hard + +"@open-draft/logger@npm:^0.3.0": + version: 0.3.0 + resolution: "@open-draft/logger@npm:0.3.0" + dependencies: + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.0" + checksum: 10c0/90010647b22e9693c16258f4f9adb034824d1771d3baa313057b9a37797f571181005bc50415a934eaf7c891d90ff71dcd7a9d5048b0b6bb438f31bef2c7c5c1 + languageName: node + linkType: hard + +"@open-draft/until@npm:^2.0.0, @open-draft/until@npm:^2.1.0": + version: 2.1.0 + resolution: "@open-draft/until@npm:2.1.0" + checksum: 10c0/61d3f99718dd86bb393fee2d7a785f961dcaf12f2055f0c693b27f4d0cd5f7a03d498a6d9289773b117590d794a43cd129366fd8e99222e4832f67b1653d54cf + languageName: node + linkType: hard + "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -3697,6 +3808,7 @@ __metadata: "@types/koa__router": "npm:^12.0.4" css-authn: "npm:^0.0.16" koa: "npm:^2.15.3" + msw: "npm:^2.6.0" rdf-namespaces: "npm:^1.12.0" typescript: "npm:^5.6.2" vitest: "npm:^2.1.4" @@ -3904,6 +4016,13 @@ __metadata: languageName: node linkType: hard +"@types/cookie@npm:^0.6.0": + version: 0.6.0 + resolution: "@types/cookie@npm:0.6.0" + checksum: 10c0/5b326bd0188120fb32c0be086b141b1481fec9941b76ad537f9110e10d61ee2636beac145463319c71e4be67a17e85b81ca9e13ceb6e3bb63b93d16824d6c149 + languageName: node + linkType: hard + "@types/cookies@npm:*": version: 0.9.0 resolution: "@types/cookies@npm:0.9.0" @@ -4317,6 +4436,20 @@ __metadata: languageName: node linkType: hard +"@types/statuses@npm:^2.0.4": + version: 2.0.5 + resolution: "@types/statuses@npm:2.0.5" + checksum: 10c0/4dacec0b29483a44be902a022a11a22b339de7a6e7b2059daa4f7add10cb6dbcc28d02d2a416fe9687e48d335906bf983065391836d4e7c847e55ddef4de8fad + languageName: node + linkType: hard + +"@types/tough-cookie@npm:^4.0.5": + version: 4.0.5 + resolution: "@types/tough-cookie@npm:4.0.5" + checksum: 10c0/68c6921721a3dcb40451543db2174a145ef915bc8bcbe7ad4e59194a0238e776e782b896c7a59f4b93ac6acefca9161fccb31d1ce3b3445cb6faa467297fb473 + languageName: node + linkType: hard + "@types/triple-beam@npm:^1.3.2": version: 1.3.5 resolution: "@types/triple-beam@npm:1.3.5" @@ -4696,7 +4829,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -5130,7 +5263,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1": +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -5212,6 +5345,13 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: 10c0/1fbd56413578f6117abcaf858903ba1f4ad78370a4032f916745fa2c7e390183a9d9029cf837df320b0fdce8137668e522f60a30a5f3d6529ff3872d265a955f + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -6865,6 +7005,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^16.8.1": + version: 16.9.0 + resolution: "graphql@npm:16.9.0" + checksum: 10c0/a8850f077ff767377237d1f8b1da2ec70aeb7623cdf1dfc9e1c7ae93accc0c8149c85abe68923be9871a2934b1bce5a2496f846d4d56e1cfb03eaaa7ddba9b6a + languageName: node + linkType: hard + "handlebars@npm:^4.7.7, handlebars@npm:^4.7.8": version: 4.7.8 resolution: "handlebars@npm:4.7.8" @@ -6946,6 +7093,13 @@ __metadata: languageName: node linkType: hard +"headers-polyfill@npm:^4.0.2": + version: 4.0.3 + resolution: "headers-polyfill@npm:4.0.3" + checksum: 10c0/53e85b2c6385f8d411945fb890c5369f1469ce8aa32a6e8d28196df38568148de640c81cf88cbc7c67767103dd9acba48f4f891982da63178fc6e34560022afe + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -7341,6 +7495,13 @@ __metadata: languageName: node linkType: hard +"is-node-process@npm:^1.2.0": + version: 1.2.0 + resolution: "is-node-process@npm:1.2.0" + checksum: 10c0/5b24fda6776d00e42431d7bcd86bce81cb0b6cabeb944142fe7b077a54ada2e155066ad06dbe790abdb397884bdc3151e04a9707b8cd185099efbc79780573ed + languageName: node + linkType: hard + "is-number@npm:^7.0.0": version: 7.0.0 resolution: "is-number@npm:7.0.0" @@ -8466,6 +8627,39 @@ __metadata: languageName: node linkType: hard +"msw@npm:^2.6.0": + version: 2.6.0 + resolution: "msw@npm:2.6.0" + dependencies: + "@bundled-es-modules/cookie": "npm:^2.0.0" + "@bundled-es-modules/statuses": "npm:^1.0.1" + "@bundled-es-modules/tough-cookie": "npm:^0.1.6" + "@inquirer/confirm": "npm:^5.0.0" + "@mswjs/interceptors": "npm:^0.36.5" + "@open-draft/deferred-promise": "npm:^2.2.0" + "@open-draft/until": "npm:^2.1.0" + "@types/cookie": "npm:^0.6.0" + "@types/statuses": "npm:^2.0.4" + chalk: "npm:^4.1.2" + graphql: "npm:^16.8.1" + headers-polyfill: "npm:^4.0.2" + is-node-process: "npm:^1.2.0" + outvariant: "npm:^1.4.3" + path-to-regexp: "npm:^6.3.0" + strict-event-emitter: "npm:^0.5.1" + type-fest: "npm:^4.26.1" + yargs: "npm:^17.7.2" + peerDependencies: + typescript: ">= 4.8.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 10c0/157c92d8faf1871acc1f4e965916ffcc8dd51aa7b1317b58749d8ef427e392db7549dc4ae3aac6b0295a6305f53a985c743272ec4445556b07439fefe5d77529 + languageName: node + linkType: hard + "multimatch@npm:5.0.0": version: 5.0.0 resolution: "multimatch@npm:5.0.0" @@ -8493,6 +8687,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "mute-stream@npm:2.0.0" + checksum: 10c0/2cf48a2087175c60c8dcdbc619908b49c07f7adcfc37d29236b0c5c612d6204f789104c98cc44d38acab7b3c96f4a3ec2cfdc4934d0738d876dbefa2a12c69f4 + languageName: node + linkType: hard + "n3@npm:^1.16.3, n3@npm:^1.17.0, n3@npm:^1.17.1, n3@npm:^1.6.3": version: 1.22.3 resolution: "n3@npm:1.22.3" @@ -9001,6 +9202,13 @@ __metadata: languageName: node linkType: hard +"outvariant@npm:^1.4.0, outvariant@npm:^1.4.3": + version: 1.4.3 + resolution: "outvariant@npm:1.4.3" + checksum: 10c0/5976ca7740349cb8c71bd3382e2a762b1aeca6f33dc984d9d896acdf3c61f78c3afcf1bfe9cc633a7b3c4b295ec94d292048f83ea2b2594fae4496656eba992c + languageName: node + linkType: hard + "p-cancelable@npm:^3.0.0": version: 3.0.0 resolution: "p-cancelable@npm:3.0.0" @@ -10380,7 +10588,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -10645,7 +10853,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1": +"statuses@npm:2.0.1, statuses@npm:^2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 @@ -10689,6 +10897,13 @@ __metadata: languageName: node linkType: hard +"strict-event-emitter@npm:^0.5.1": + version: 0.5.1 + resolution: "strict-event-emitter@npm:0.5.1" + checksum: 10c0/f5228a6e6b6393c57f52f62e673cfe3be3294b35d6f7842fc24b172ae0a6e6c209fa83241d0e433fc267c503bc2f4ffdbe41a9990ff8ffd5ac425ec0489417f7 + languageName: node + linkType: hard + "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -10975,7 +11190,7 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^4.1.3": +"tough-cookie@npm:^4.1.3, tough-cookie@npm:^4.1.4": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" dependencies: @@ -11118,6 +11333,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^4.26.1": + version: 4.26.1 + resolution: "type-fest@npm:4.26.1" + checksum: 10c0/d2719ff8d380befe8a3c61068f37f28d6fa2849fd140c5d2f0f143099e371da6856aad7c97e56b83329d45bfe504afe9fd936a7cff600cc0d46aa9ffb008d6c6 + languageName: node + linkType: hard + "type-is@npm:^1.6.16": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -11588,7 +11810,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^6.0.1": +"wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" dependencies: @@ -11764,6 +11986,13 @@ __metadata: languageName: node linkType: hard +"yoctocolors-cjs@npm:^2.1.2": + version: 2.1.2 + resolution: "yoctocolors-cjs@npm:2.1.2" + checksum: 10c0/a0e36eb88fea2c7981eab22d1ba45e15d8d268626e6c4143305e2c1628fa17ebfaa40cd306161a8ce04c0a60ee0262058eab12567493d5eb1409780853454c6f + languageName: node + linkType: hard + "yup@npm:^1.3.2": version: 1.4.0 resolution: "yup@npm:1.4.0"