diff --git a/packages/subscriptions-nextjs/.eslintrc.cjs b/packages/subscriptions-nextjs/.eslintrc.cjs new file mode 100644 index 00000000..bc73447a --- /dev/null +++ b/packages/subscriptions-nextjs/.eslintrc.cjs @@ -0,0 +1,14 @@ +/** + * @type {import("eslint").Linter.Config} + */ +module.exports = { + extends: "@bonfhir/eslint-config", + overrides: [ + { + files: ["**/{.*,*.config}.{cjs,js,mjs,ts}"], + env: { + node: true, + }, + }, + ], +}; diff --git a/packages/subscriptions-nextjs/.prettierignore b/packages/subscriptions-nextjs/.prettierignore new file mode 100644 index 00000000..849ddff3 --- /dev/null +++ b/packages/subscriptions-nextjs/.prettierignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/subscriptions-nextjs/CHANGELOG.md b/packages/subscriptions-nextjs/CHANGELOG.md new file mode 100644 index 00000000..e4d87c4d --- /dev/null +++ b/packages/subscriptions-nextjs/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/subscriptions-nextjs/README.md b/packages/subscriptions-nextjs/README.md new file mode 100644 index 00000000..27687bf0 --- /dev/null +++ b/packages/subscriptions-nextjs/README.md @@ -0,0 +1,3 @@ +# Bonfhir Subscriptions NextJS Adapter + +See https://bonfhir.dev/packages/integrations/subscriptions-nextjs for more information. diff --git a/packages/subscriptions-nextjs/package.js b/packages/subscriptions-nextjs/package.js new file mode 100644 index 00000000..7209a9ea --- /dev/null +++ b/packages/subscriptions-nextjs/package.js @@ -0,0 +1,99 @@ +/** + * This script is a helper to create the final NPM package. + */ + +import { existsSync } from "fs"; +import { + copyFile, + readdir, + readFile, + rename, + unlink, + writeFile, +} from "fs/promises"; +import { exec } from "node:child_process"; +import { promisify } from "node:util"; +import { dirname, join } from "path"; +import { fileURLToPath } from "url"; + +const execAsync = promisify(exec); + +(async () => { + const rootPackageDirectory = dirname(fileURLToPath(import.meta.url)); + const distDirectory = join(rootPackageDirectory, "dist"); + + // Copy package.json while stripping unwanted information + const packageJson = JSON.parse( + await readFile(join(rootPackageDirectory, "package.json"), "utf8") + ); + delete packageJson.scripts; + delete packageJson.packageManager; + delete packageJson.devDependencies; + delete packageJson.prettier; + + packageJson.exports = {}; + for (const packageRootDirName of ( + await readdir(distDirectory, { withFileTypes: true }) + ) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name)) { + packageJson.exports[`./${packageRootDirName}`] = { + types: `./${packageRootDirName}/index.d.ts`, + require: `./${packageRootDirName}/index.cjs`, + import: `./${packageRootDirName}/index.js`, + default: `./${packageRootDirName}/index.js`, + }; + } + + await writeFile( + join(distDirectory, "package.json"), + JSON.stringify(packageJson), + "utf8" + ); + + // Copy README.md + await copyFile( + join(rootPackageDirectory, "README.md"), + join(distDirectory, "README.md") + ); + + // Copy CHANGELOG.md + await copyFile( + join(rootPackageDirectory, "CHANGELOG.md"), + join(distDirectory, "CHANGELOG.md") + ); + + // Delete TypeScript build info + try { + await unlink(join(distDirectory, "tsconfig.build.tsbuildinfo")); + } catch { + // Ignore the error if the file does not exists. + } + + // eslint-disable-next-line no-undef + const npmCommand = process.argv[2] || "pack"; + // eslint-disable-next-line no-undef + const npmCommandOptions = process.argv.slice(3).join(" "); + + // Run npm in the dist directory + const result = await execAsync(`npm ${npmCommand} ${npmCommandOptions}`, { + cwd: distDirectory, + maxBuffer: 1024 * 1000 * 10, + }); + + // eslint-disable-next-line no-undef + console.log(result.stdout); + // eslint-disable-next-line no-undef + console.error(result.stderr); + + // Copy the package back to root if was produced + const packageFilename = `${packageJson.name + .replace("@", "") + .replace("/", "-")}-${packageJson.version}.tgz`; + if (existsSync(join(distDirectory, packageFilename))) { + await rename( + join(distDirectory, packageFilename), + join(rootPackageDirectory, packageFilename) + ); + } +})(); diff --git a/packages/subscriptions-nextjs/package.json b/packages/subscriptions-nextjs/package.json new file mode 100644 index 00000000..6e2a3777 --- /dev/null +++ b/packages/subscriptions-nextjs/package.json @@ -0,0 +1,39 @@ +{ + "name": "@bonfhir/subscriptions-nextjs", + "description": "NextJS adapter for bonfhir subscriptions.", + "version": "0.1.0", + "repository": "https://github.com/bonfhir/bonfhir.git", + "license": "APACHE-2.0", + "type": "module", + "scripts": { + "build": "yarn clean && tsup r4b/index.ts --format esm,cjs --out-dir dist/r4b --shims --dts --tsconfig tsconfig.build.json", + "check": "prettier --check ./**/*.ts && eslint ./**/*.ts && tsc --noEmit", + "clean": "rimraf dist/", + "format": "prettier --loglevel warn --write ./**/*.ts && eslint --fix ./**/*.ts", + "package:create": "yarn build && node package.js pack", + "package:publish": "yarn build && node package.js publish" + }, + "packageManager": "yarn@3.3.1", + "devDependencies": { + "@bonfhir/codegen": "^1.0.0-alpha.5", + "@bonfhir/eslint-config": "^1.1.0-alpha.1", + "@bonfhir/prettier-config": "^1.0.1-alpha.1", + "@bonfhir/typescript-config": "^1.0.1-alpha.2", + "@types/fhir": "^0.0.35", + "@types/node": "^18.15.3", + "@types/react": "^18.0.15", + "@types/react-dom": "^18.0.6", + "eslint": "^8.36.0", + "jest": "^29.5.0", + "prettier": "^2.8.4", + "rimraf": "^4.4.0", + "tsup": "^6.7.0", + "typescript": "^5.0.3" + }, + "dependencies": { + "@bonfhir/core": "^1.0.0-alpha.16", + "@bonfhir/subscriptions": "^0.1.0-alpha.7", + "next": "latest" + }, + "prettier": "@bonfhir/prettier-config" +} diff --git a/packages/subscriptions-nextjs/r4b/index.ts b/packages/subscriptions-nextjs/r4b/index.ts new file mode 100644 index 00000000..758c3c68 --- /dev/null +++ b/packages/subscriptions-nextjs/r4b/index.ts @@ -0,0 +1 @@ +export * from "./middleware"; diff --git a/packages/subscriptions-nextjs/r4b/middleware.ts b/packages/subscriptions-nextjs/r4b/middleware.ts new file mode 100644 index 00000000..06ff8a77 --- /dev/null +++ b/packages/subscriptions-nextjs/r4b/middleware.ts @@ -0,0 +1,187 @@ +import { FhirRestfulClient } from "@bonfhir/core/r4b"; +import { + AuditEventConfiguration, + createErrorAuditEvent, + errorToString, + FhirSubscription, + registerSubscriptions, + SubscriptionLogger, +} from "@bonfhir/subscriptions/r4b"; + +import { Subscription } from "fhir/r4"; +import { NextRequest, NextResponse } from "next/server"; + +export interface FhirSubscriptionsConfig { + /** + * The {@link FhirRestfulClient} to use to register subscriptions. + * If this is a function, it is invoked prior to every handler invocation as well. + */ + fhirClient: + | FhirRestfulClient + | (() => FhirRestfulClient | Promise); + + /** The subscriptions handlers to register. */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + subscriptions: FhirSubscription[]; + + /** The API base URL */ + baseUrl: string | URL | null | undefined; + + /** + * Indicates how to register the subscriptions: + * - during startup + * - when a specific endpoint is invoked + * - or disable registration + */ + register: "startup" | "endpoint" | "off"; + + /** + * The registration endpoint to use. Defaults to /fhir/register-subscriptions. + */ + registerEndpoint?: string | null | undefined; + + /** + * The name of the security header used. Defaults to "X-Subscription-Auth" + */ + securityHeader?: string | null | undefined; + + /** A secret shared between the API and the FHIR subscription use to secure the endpoint. */ + webhookSecret: string; + + /** The subscription payload, a.k.a. MIME type. Defaults to application/fhir+json */ + payload?: Subscription["channel"]["payload"] | null | undefined; + + /** Logger to use. Defaults to console. */ + logger?: SubscriptionLogger | null | undefined; + + /** + * If set, will automatically create AuditEvent using this as a source / reference. + * Alternatively, you can build the AuditEvents yourself. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + auditEvent?: AuditEventConfiguration | null | undefined; +} + +/** + * Register subscriptions and routes to webhooks handlers. + */ +export async function fhirSubscriptions( + config: FhirSubscriptionsConfig +): Promise<(request: NextRequest) => Promise> { + const logger = config.logger ?? console; + + const fhirClient = + typeof config.fhirClient === "function" + ? await config.fhirClient() + : config.fhirClient; + + if (config.register === "startup") { + await registerSubscriptions({ + baseUrl: config.baseUrl, + fhirClient, + logger, + subscriptions: config.subscriptions, + webhookSecret: config.webhookSecret, + securityHeader: config.securityHeader, + auditEvent: config.auditEvent, + payload: config.payload, + }); + } + + const securityHeader = config.securityHeader || "X-Subscription-Auth"; + + function verifySecurityHeader(request: NextRequest): boolean { + return ( + request.headers.has(securityHeader) && + request.headers.get(securityHeader) === config.webhookSecret + ); + } + + async function fhirMiddleware(request: NextRequest): Promise { + if (config.register === "endpoint") { + if (!verifySecurityHeader(request)) { + logger?.warn(`Received unauthorized request for ${request.url}.`); + return new NextResponse(null, { + status: 401, + }); + } + if ( + request.method === "POST" && + request.nextUrl.pathname === + (config.registerEndpoint || "/fhir/register-subscriptions") + ) { + try { + await registerSubscriptions({ + baseUrl: config.baseUrl, + fhirClient, + logger, + subscriptions: config.subscriptions, + webhookSecret: config.webhookSecret, + auditEvent: config.auditEvent, + payload: config.payload, + }); + return new NextResponse(null, { status: 204 }); + } catch (error) { + return new NextResponse(`Error: ${error}`, { status: 500 }); + } + } + } + + const subscription = config.subscriptions.find( + (sub) => `${sub.endpoint}` === request.nextUrl.pathname + ); + + if (subscription) { + if (!verifySecurityHeader(request)) { + logger?.warn(`Received unauthorized request for ${request.url}.`); + return new NextResponse(null, { + status: 401, + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const resource = await request.json(); + try { + const result = await subscription.handler({ + fhirClient: + typeof config.fhirClient === "function" + ? await config.fhirClient() + : config.fhirClient, + resource, + logger, + }); + + if (result == null) { + return new NextResponse(null, { + status: 204, + }); + } else { + return new NextResponse(JSON.stringify(result), { + status: 200, + }); + } + } catch (error) { + logger.error(error); + if (config.auditEvent) { + try { + await createErrorAuditEvent({ + auditEvent: config.auditEvent, + error, + fhirClient, + relatedResource: resource, + }); + } catch (auditEventError) { + logger.error(auditEventError); + } + } + return new NextResponse(errorToString(error), { + status: 500, + }); + } + } + + return NextResponse.next(); + } + + return fhirMiddleware; +} diff --git a/packages/subscriptions-nextjs/tsconfig.build.json b/packages/subscriptions-nextjs/tsconfig.build.json new file mode 100644 index 00000000..480c70ff --- /dev/null +++ b/packages/subscriptions-nextjs/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["**/*.test.ts", "**/__fixtures__"], + "compilerOptions": { + "incremental": false, + "outDir": "dist" + } +} diff --git a/packages/subscriptions-nextjs/tsconfig.json b/packages/subscriptions-nextjs/tsconfig.json new file mode 100644 index 00000000..76f85f11 --- /dev/null +++ b/packages/subscriptions-nextjs/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@bonfhir/typescript-config/tsconfig.json", + "include": ["**/*.ts"], + "compilerOptions": { + "rootDir": "." + } +} diff --git a/samples/sample-api-nextjs/.eslintrc.cjs b/samples/sample-api-nextjs/.eslintrc.cjs new file mode 100644 index 00000000..de604f66 --- /dev/null +++ b/samples/sample-api-nextjs/.eslintrc.cjs @@ -0,0 +1,14 @@ +/** + * @type {import("eslint").Linter.Config} + */ +module.exports = { + extends: "@bonfhir/eslint-config", + overrides: [ + { + files: ["**/{.*,*.config}.{cjs,js,mjs,ts,tsx}"], + env: { + node: true, + }, + }, + ], +}; diff --git a/samples/sample-api-nextjs/.gitignore b/samples/sample-api-nextjs/.gitignore new file mode 100644 index 00000000..8f322f0d --- /dev/null +++ b/samples/sample-api-nextjs/.gitignore @@ -0,0 +1,35 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/samples/sample-api-nextjs/.prettierignore b/samples/sample-api-nextjs/.prettierignore new file mode 100644 index 00000000..849ddff3 --- /dev/null +++ b/samples/sample-api-nextjs/.prettierignore @@ -0,0 +1 @@ +dist/ diff --git a/samples/sample-api-nextjs/middleware.ts b/samples/sample-api-nextjs/middleware.ts new file mode 100644 index 00000000..2a33774f --- /dev/null +++ b/samples/sample-api-nextjs/middleware.ts @@ -0,0 +1,41 @@ +import { NextRequest } from "next/server"; + +import { fhirSubscriptions } from "@bonfhir/subscriptions-nextjs/r4b"; +import { MedplumClient } from "@medplum/core"; + +import { buildFhirRestfulClientAdapter } from "@bonfhir/medplum/r4b"; +import { communicationRequests } from "./requests/communication-requests"; + +// Limit the middleware to paths starting with `/fhir/` +export const config = { + matcher: ["/fhir/:function*"], +}; + +const initializeSubscriptionMiddleware = async () => + fhirSubscriptions({ + baseUrl: process.env.BASE_URL || "http://devcontainer:3001", + fhirClient: async () => { + const medplum = new MedplumClient({ + baseUrl: process.env.MEDPLUM_SERVER_URL!, + fetch: fetch, + }); + + await medplum.startClientLogin( + process.env.MEDPLUM_CLIENT_ID!, + process.env.MEDPLUM_CLIENT_SECRET! + ); + + return buildFhirRestfulClientAdapter(medplum); + }, + register: "startup", + subscriptions: [communicationRequests], + webhookSecret: "thewebhooksecret", + auditEvent: "Sample API", + }); + +const subscriptionMiddleware = initializeSubscriptionMiddleware(); + +export async function middleware(request: NextRequest) { + const subs = await subscriptionMiddleware; + return subs(request); +} diff --git a/samples/sample-api-nextjs/package.json b/samples/sample-api-nextjs/package.json new file mode 100644 index 00000000..c66bf9e7 --- /dev/null +++ b/samples/sample-api-nextjs/package.json @@ -0,0 +1,41 @@ +{ + "name": "@bonfhir/sample-api-nextjs", + "description": "Bonfhir Sample api", + "version": "0.1.0-alpha.1", + "private": true, + "repository": "https://github.com/bonfhir/bonfhir.git", + "license": "APACHE-2.0", + "scripts": { + "dev": "next -p 4000", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "@bonfhir/core": "^1.0.0-alpha.8", + "@bonfhir/medplum": "^1.0.0-alpha.9", + "@bonfhir/subscriptions": "^0.1.0-alpha.2", + "@bonfhir/subscriptions-nextjs": "0.1.0", + "@medplum/core": "2.0.14", + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "swr": "^2.0.0" + }, + "devDependencies": { + "@bonfhir/eslint-config": "^1.1.0-alpha.0", + "@bonfhir/prettier-config": "^1.0.0", + "@bonfhir/typescript-config": "^1.0.1-alpha.0", + "@swc/core": "^1.3.41", + "@swc/jest": "^0.2.24", + "@types/fhir": "^0.0.35", + "@types/node": "^18.0.3", + "@types/react": "^18.0.15", + "@types/react-dom": "^18.0.6", + "prettier": "^2.8.4", + "rimraf": "^4.4.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.3" + }, + "packageManager": "yarn@3.3.1", + "prettier": "@bonfhir/prettier-config" +} diff --git a/samples/sample-api-nextjs/pages/index.tsx b/samples/sample-api-nextjs/pages/index.tsx new file mode 100644 index 00000000..4972e2f9 --- /dev/null +++ b/samples/sample-api-nextjs/pages/index.tsx @@ -0,0 +1,3 @@ +export default function Index() { + return

Sample NextJS API for Bonfhir

; +} diff --git a/samples/sample-api-nextjs/requests/communication-requests.ts b/samples/sample-api-nextjs/requests/communication-requests.ts new file mode 100644 index 00000000..c45672f2 --- /dev/null +++ b/samples/sample-api-nextjs/requests/communication-requests.ts @@ -0,0 +1,11 @@ +import { FhirSubscription } from "@bonfhir/subscriptions/r4b"; +import { CommunicationRequest } from "fhir/r4"; + +export const communicationRequests: FhirSubscription = { + criteria: "CommunicationRequest", + reason: "Send communication requests", + endpoint: "/fhir/communication-requests", + async handler({ resource, logger }) { + logger?.info(resource); + }, +}; diff --git a/samples/sample-api-nextjs/tsconfig.json b/samples/sample-api-nextjs/tsconfig.json new file mode 100644 index 00000000..85e3f0f4 --- /dev/null +++ b/samples/sample-api-nextjs/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "@bonfhir/typescript-config/tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "noEmit": true, + "module": "esnext", + "isolatedModules": true, + "jsx": "preserve" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/yarn.lock b/yarn.lock index b7021bbf..24624e3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2298,6 +2298,35 @@ __metadata: languageName: unknown linkType: soft +"@bonfhir/sample-api-nextjs@workspace:samples/sample-api-nextjs": + version: 0.0.0-use.local + resolution: "@bonfhir/sample-api-nextjs@workspace:samples/sample-api-nextjs" + dependencies: + "@bonfhir/core": ^1.0.0-alpha.8 + "@bonfhir/eslint-config": ^1.1.0-alpha.0 + "@bonfhir/medplum": ^1.0.0-alpha.9 + "@bonfhir/prettier-config": ^1.0.0 + "@bonfhir/subscriptions": ^0.1.0-alpha.2 + "@bonfhir/subscriptions-nextjs": 0.1.0 + "@bonfhir/typescript-config": ^1.0.1-alpha.0 + "@medplum/core": 2.0.14 + "@swc/core": ^1.3.41 + "@swc/jest": ^0.2.24 + "@types/fhir": ^0.0.35 + "@types/node": ^18.0.3 + "@types/react": ^18.0.15 + "@types/react-dom": ^18.0.6 + next: latest + prettier: ^2.8.4 + react: ^18.2.0 + react-dom: ^18.2.0 + rimraf: ^4.4.0 + swr: ^2.0.0 + ts-node: ^10.9.1 + typescript: ^5.0.3 + languageName: unknown + linkType: soft + "@bonfhir/subscriptions-koa@^0.1.0-alpha.1, @bonfhir/subscriptions-koa@workspace:packages/subscriptions-koa": version: 0.0.0-use.local resolution: "@bonfhir/subscriptions-koa@workspace:packages/subscriptions-koa" @@ -2326,6 +2355,30 @@ __metadata: languageName: unknown linkType: soft +"@bonfhir/subscriptions-nextjs@0.1.0, @bonfhir/subscriptions-nextjs@workspace:packages/subscriptions-nextjs": + version: 0.0.0-use.local + resolution: "@bonfhir/subscriptions-nextjs@workspace:packages/subscriptions-nextjs" + dependencies: + "@bonfhir/codegen": ^1.0.0-alpha.5 + "@bonfhir/core": ^1.0.0-alpha.16 + "@bonfhir/eslint-config": ^1.1.0-alpha.1 + "@bonfhir/prettier-config": ^1.0.1-alpha.1 + "@bonfhir/subscriptions": ^0.1.0-alpha.7 + "@bonfhir/typescript-config": ^1.0.1-alpha.2 + "@types/fhir": ^0.0.35 + "@types/node": ^18.15.3 + "@types/react": ^18.0.15 + "@types/react-dom": ^18.0.6 + eslint: ^8.36.0 + jest: ^29.5.0 + next: latest + prettier: ^2.8.4 + rimraf: ^4.4.0 + tsup: ^6.7.0 + typescript: ^5.0.3 + languageName: unknown + linkType: soft + "@bonfhir/subscriptions@^0.1.0-alpha.2, @bonfhir/subscriptions@^0.1.0-alpha.7, @bonfhir/subscriptions@workspace:packages/subscriptions": version: 0.0.0-use.local resolution: "@bonfhir/subscriptions@workspace:packages/subscriptions" @@ -4636,6 +4689,76 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:13.4.4": + version: 13.4.4 + resolution: "@next/env@npm:13.4.4" + checksum: 9ab9b9ac4e5920cb9911273a776a5e2564844ac735a9c483bf584e090a7f534ce4a8fcda313132d338837e634513f680885c6b4ee811d5175db1b98b0bba4b35 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-darwin-arm64@npm:13.4.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-darwin-x64@npm:13.4.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-linux-arm64-gnu@npm:13.4.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-linux-arm64-musl@npm:13.4.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-linux-x64-gnu@npm:13.4.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-linux-x64-musl@npm:13.4.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-win32-arm64-msvc@npm:13.4.4" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-ia32-msvc@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-win32-ia32-msvc@npm:13.4.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:13.4.4": + version: 13.4.4 + resolution: "@next/swc-win32-x64-msvc@npm:13.4.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@ngneat/falso@npm:^6.4.0": version: 6.4.0 resolution: "@ngneat/falso@npm:6.4.0" @@ -6283,6 +6406,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:0.5.1": + version: 0.5.1 + resolution: "@swc/helpers@npm:0.5.1" + dependencies: + tslib: ^2.4.0 + checksum: 71e0e27234590435e4c62b97ef5e796f88e786841a38c7116a5e27a3eafa7b9ead7cdec5249b32165902076de78446945311c973e59bddf77c1e24f33a8f272a + languageName: node + linkType: hard + "@swc/helpers@npm:^0.4.12": version: 0.4.14 resolution: "@swc/helpers@npm:0.4.14" @@ -6952,6 +7084,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.0.3": + version: 18.16.16 + resolution: "@types/node@npm:18.16.16" + checksum: 0efad726dd1e0bef71c392c708fc5d78c5b39c46b0ac5186fee74de4ccb1b2e847b3fa468da67d62812f56569da721b15bf31bdc795e6c69b56c73a45079ed2d + languageName: node + linkType: hard + "@types/node@npm:^18.15.3": version: 18.15.3 resolution: "@types/node@npm:18.15.3" @@ -7033,6 +7172,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:^18.0.6": + version: 18.2.4 + resolution: "@types/react-dom@npm:18.2.4" + dependencies: + "@types/react": "*" + checksum: 8301f35cf1cbfec8c723e9477aecf87774e3c168bd457d353b23c45064737213d3e8008b067c6767b7b08e4f2b3823ee239242a6c225fc91e7f8725ef8734124 + languageName: node + linkType: hard + "@types/react-router-config@npm:*, @types/react-router-config@npm:^5.0.6": version: 5.0.6 resolution: "@types/react-router-config@npm:5.0.6" @@ -7087,6 +7235,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:^18.0.15": + version: 18.2.7 + resolution: "@types/react@npm:18.2.7" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: caa5da4cf929766738ec789301dc6fb6624bd48dd317d851c4c9b84b1f47cd8ebe17fe01398cadaa0bc938cd4d502d67f4b9de9ff771dc132096bdc86228efba + languageName: node + linkType: hard + "@types/retry@npm:0.12.0": version: 0.12.0 resolution: "@types/retry@npm:0.12.0" @@ -9174,6 +9333,15 @@ __metadata: languageName: node linkType: hard +"busboy@npm:1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + "byte-size@npm:7.0.0": version: 7.0.0 resolution: "byte-size@npm:7.0.0" @@ -9369,6 +9537,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001406": + version: 1.0.30001489 + resolution: "caniuse-lite@npm:1.0.30001489" + checksum: 94585a351fd7661b855c83eace474db0ee5a617159b46f2eff1f6fe4b85d7a205418471fdec8cf5cd647a7f79958706d5e664c0bbf3c7c09118b35db9bb95a1b + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001449": version: 1.0.30001450 resolution: "caniuse-lite@npm:1.0.30001450" @@ -9714,6 +9889,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -18755,6 +18937,64 @@ __metadata: languageName: node linkType: hard +"next@npm:latest": + version: 13.4.4 + resolution: "next@npm:13.4.4" + dependencies: + "@next/env": 13.4.4 + "@next/swc-darwin-arm64": 13.4.4 + "@next/swc-darwin-x64": 13.4.4 + "@next/swc-linux-arm64-gnu": 13.4.4 + "@next/swc-linux-arm64-musl": 13.4.4 + "@next/swc-linux-x64-gnu": 13.4.4 + "@next/swc-linux-x64-musl": 13.4.4 + "@next/swc-win32-arm64-msvc": 13.4.4 + "@next/swc-win32-ia32-msvc": 13.4.4 + "@next/swc-win32-x64-msvc": 13.4.4 + "@swc/helpers": 0.5.1 + busboy: 1.6.0 + caniuse-lite: ^1.0.30001406 + postcss: 8.4.14 + styled-jsx: 5.1.1 + zod: 3.21.4 + peerDependencies: + "@opentelemetry/api": ^1.1.0 + fibers: ">= 3.1.0" + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + fibers: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: d84686f3ca313c57d283872a2946a5c18ecd656c4235ad879cbaa0f2f2886c9bcff94656ff07c4998fa672f40023978235150ce760e4c5b1edab6feaa7ed9fa9 + languageName: node + linkType: hard + "nice-try@npm:^1.0.4": version: 1.0.5 resolution: "nice-try@npm:1.0.5" @@ -20606,6 +20846,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:8.4.14": + version: 8.4.14 + resolution: "postcss@npm:8.4.14" + dependencies: + nanoid: ^3.3.4 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: fe58766ff32e4becf65a7d57678995cfd239df6deed2fe0557f038b47c94e4132e7e5f68b5aa820c13adfec32e523b693efaeb65798efb995ce49ccd83953816 + languageName: node + linkType: hard + "postcss@npm:^8.3.11, postcss@npm:^8.4.14, postcss@npm:^8.4.17, postcss@npm:^8.4.19": version: 8.4.20 resolution: "postcss@npm:8.4.20" @@ -23736,6 +23987,13 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + "string-convert@npm:^0.2.0": version: 0.2.1 resolution: "string-convert@npm:0.2.1" @@ -24032,6 +24290,22 @@ __metadata: languageName: node linkType: hard +"styled-jsx@npm:5.1.1": + version: 5.1.1 + resolution: "styled-jsx@npm:5.1.1" + dependencies: + client-only: 0.0.1 + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 523a33b38603492547e861b98e29c873939b04e15fbe5ef16132c6f1e15958126647983c7d4675325038b428a5e91183d996e90141b18bdd1bbadf6e2c45b2fa + languageName: node + linkType: hard + "stylehacks@npm:^5.1.1": version: 5.1.1 resolution: "stylehacks@npm:5.1.1" @@ -24149,6 +24423,17 @@ __metadata: languageName: node linkType: hard +"swr@npm:^2.0.0": + version: 2.1.5 + resolution: "swr@npm:2.1.5" + dependencies: + use-sync-external-store: ^1.2.0 + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + checksum: f2ef9b83ec4b2ebf4d520578ab847307700db01ffcab64b3aec6ea94feaf96ca10e38e8b08bc8adfab8eaa6ec1bdfd64f82b6f5c14cc4cb5cf11e6fd22b405a4 + languageName: node + linkType: hard + "symbol-observable@npm:^1.1.0": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" @@ -26427,6 +26712,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:3.21.4": + version: 3.21.4 + resolution: "zod@npm:3.21.4" + checksum: f185ba87342ff16f7a06686767c2b2a7af41110c7edf7c1974095d8db7a73792696bcb4a00853de0d2edeb34a5b2ea6a55871bc864227dace682a0a28de33e1f + languageName: node + linkType: hard + "zwitch@npm:^1.0.0": version: 1.0.5 resolution: "zwitch@npm:1.0.5"