diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3cab0da3..5ff0c73c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -37,7 +37,7 @@ jobs: env: CI: true - - name: Test installed Playwright consumer + - name: Test installed package consumers run: pnpm run test:package-contract env: CI: true @@ -47,7 +47,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: playwright-report - path: apps/e2e/playwright-report/ + path: | + apps/e2e/playwright-report/ + packages/graph/playwright-report/ retention-days: 7 - name: Upload test screenshots @@ -55,5 +57,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-results - path: apps/e2e/test-results/ + path: | + apps/e2e/test-results/ + packages/graph/test-results/ retention-days: 7 diff --git a/CLAUDE.md b/CLAUDE.md index 7bbadef9..f5e53a1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ pnpm run dev pnpm run storybook # Build for production -pnpm run build:publish +pnpm run build # Type checking pnpm run typecheck diff --git a/apps/e2e/entry.ts b/apps/e2e/entry.ts index 324add32..78c6db87 100644 --- a/apps/e2e/entry.ts +++ b/apps/e2e/entry.ts @@ -1,3 +1,3 @@ -import "@gravity-ui/graph/react"; +import "@gravity-ui/graph/styles.css"; export * from "@gravity-ui/graph"; diff --git a/apps/e2e/react-entry.ts b/apps/e2e/react-entry.ts index 3a4f80df..b035d9d4 100644 --- a/apps/e2e/react-entry.ts +++ b/apps/e2e/react-entry.ts @@ -2,6 +2,8 @@ import React from "react"; import ReactDOM from "react-dom/client"; +import "@gravity-ui/graph/styles.css"; + export * from "@gravity-ui/graph"; export { GraphBlock, GraphBlockAnchor, GraphCanvas } from "@gravity-ui/graph/react"; export { React, ReactDOM }; diff --git a/apps/storybook/.storybook/preview.tsx b/apps/storybook/.storybook/preview.tsx index a7c654f9..e66169fe 100644 --- a/apps/storybook/.storybook/preview.tsx +++ b/apps/storybook/.storybook/preview.tsx @@ -4,6 +4,7 @@ import { ThemeProvider } from "@gravity-ui/uikit"; import type { Preview } from "@storybook/react-webpack5"; import "./styles/global.css"; +import "@gravity-ui/graph/styles.css"; import "@gravity-ui/uikit/styles/styles.css"; const preview: Preview = { diff --git a/apps/storybook/src/stories/plugins/layered/layered.stories.tsx b/apps/storybook/src/stories/plugins/layered/layered.stories.tsx index f1a96baa..34bf020c 100644 --- a/apps/storybook/src/stories/plugins/layered/layered.stories.tsx +++ b/apps/storybook/src/stories/plugins/layered/layered.stories.tsx @@ -1,15 +1,8 @@ import React, { useEffect, useMemo } from "react"; import type { LayeredLayoutOptions, TMultipointConnection } from "@gravity-ui/graph"; -import { - BezierMultipointConnection, - Graph, - GraphState, - TBlock, - TConnection, - useLayeredLayout, -} from "@gravity-ui/graph"; -import { GraphCanvas, useGraph, useGraphEvent } from "@gravity-ui/graph/react"; +import { BezierMultipointConnection, Graph, GraphState, TBlock, TConnection } from "@gravity-ui/graph"; +import { GraphCanvas, useGraph, useGraphEvent, useLayeredLayout } from "@gravity-ui/graph/react"; import { ThemeProvider } from "@gravity-ui/uikit"; import { Description, Meta as StorybookMeta, Title } from "@storybook/addon-docs/blocks"; import type { Meta, StoryFn } from "@storybook/react-webpack5"; @@ -144,8 +137,7 @@ const meta: Meta = { "- `layerSpacingFactor`: Multiplier for spacing between layers (default: 1.7)\n\n" + "## Example\n\n" + "```tsx\n" + - "import { useLayeredLayout } from '@gravity-ui/graph';\n" + - "import { useGraph } from '@gravity-ui/graph/react';\n\n" + + "import { useGraph, useLayeredLayout } from '@gravity-ui/graph/react';\n\n" + "const { graph, setEntities, start } = useGraph();\n" + "const { isLoading, result } = useLayeredLayout({\n" + " blocks: [{ id: '1', width: 100, height: 50 }, { id: '2', width: 100, height: 50 }],\n" + diff --git a/package.json b/package.json index f2b76e74..f1d592d8 100644 --- a/package.json +++ b/package.json @@ -14,14 +14,9 @@ "lint": "pnpm --filter @gravity-ui/graph run lint && pnpm --filter @gravity-ui/graph-storybook run lint", "test:unit": "pnpm --filter @gravity-ui/graph run test:unit", "test": "pnpm --filter @gravity-ui/graph run test", - "storybook": "pnpm --filter @gravity-ui/graph run build:publish && pnpm --filter @gravity-ui/graph-storybook run storybook", - "storybook:build": "pnpm --filter @gravity-ui/graph run build:publish && pnpm --filter @gravity-ui/graph-storybook run build-storybook", - "build-storybook": "pnpm --filter @gravity-ui/graph run build:publish && pnpm --filter @gravity-ui/graph-storybook run build-storybook", - "ci:storybook:build": "pnpm run build-storybook", + "storybook": "pnpm --filter @gravity-ui/graph run build && pnpm --filter @gravity-ui/graph-storybook run storybook", + "build-storybook": "pnpm --filter @gravity-ui/graph run build && pnpm --filter @gravity-ui/graph-storybook run build-storybook", "build:docs": "pnpm --filter @gravity-ui/graph run build:docs", - "copy-styles": "pnpm --filter @gravity-ui/graph run copy-styles", - "build:playwright-runtime": "pnpm --filter @gravity-ui/graph run build:playwright-runtime", - "build:publish": "pnpm --filter @gravity-ui/graph run build:publish", "build": "pnpm --filter @gravity-ui/graph run build", "dev": "pnpm --filter @gravity-ui/graph run dev", "e2e:bundle": "pnpm --filter @gravity-ui/graph-e2e run e2e:bundle", diff --git a/packages/graph/README-ru.md b/packages/graph/README-ru.md index 6c4f5ba6..3e3056e5 100644 --- a/packages/graph/README-ru.md +++ b/packages/graph/README-ru.md @@ -70,6 +70,7 @@ import React, { useEffect } from "react"; import type { Graph, TBlock } from "@gravity-ui/graph"; import { EAnchorType, GraphState } from "@gravity-ui/graph"; import { GraphCanvas, GraphBlock, useGraph } from "@gravity-ui/graph/react"; +import "@gravity-ui/graph/styles.css"; const config = {}; @@ -154,6 +155,7 @@ export function GraphEditor() { ```javascript import { Graph } from "@gravity-ui/graph"; +import "@gravity-ui/graph/styles.css"; // Создаем контейнер const container = document.createElement('div'); diff --git a/packages/graph/README.md b/packages/graph/README.md index 7d5b84ff..eb3ef35d 100644 --- a/packages/graph/README.md +++ b/packages/graph/README.md @@ -70,6 +70,7 @@ import React, { useEffect } from "react"; import type { Graph, TBlock } from "@gravity-ui/graph"; import { EAnchorType, GraphState } from "@gravity-ui/graph"; import { GraphCanvas, GraphBlock, useGraph } from "@gravity-ui/graph/react"; +import "@gravity-ui/graph/styles.css"; const config = {}; @@ -155,6 +156,7 @@ export function GraphEditor() { ```javascript import { Graph } from "@gravity-ui/graph"; +import "@gravity-ui/graph/styles.css"; // Create container element const container = document.createElement('div'); diff --git a/packages/graph/__mocks__/styleMock.js b/packages/graph/__mocks__/styleMock.cjs similarity index 100% rename from packages/graph/__mocks__/styleMock.js rename to packages/graph/__mocks__/styleMock.cjs diff --git a/packages/graph/docs/react/usage.md b/packages/graph/docs/react/usage.md index 86cd4f93..6634c207 100644 --- a/packages/graph/docs/react/usage.md +++ b/packages/graph/docs/react/usage.md @@ -10,6 +10,9 @@ import { Graph } from "@gravity-ui/graph"; // React components (requires React) import { GraphCanvas, GraphBlock, GraphBlockAnchor, useGraph, useGraphEvent } from "@gravity-ui/graph/react"; + +// Public component styles +import "@gravity-ui/graph/styles.css"; ``` ## Architecture diff --git a/packages/graph/jest.config.ts b/packages/graph/jest.config.ts index 2d63ca11..f8eeaaae 100644 --- a/packages/graph/jest.config.ts +++ b/packages/graph/jest.config.ts @@ -3,10 +3,10 @@ import type { Config } from "jest"; const jestConfig: Config = { testPathIgnorePatterns: ["/node_modules/", "/build/"], testEnvironment: "jsdom", - setupFiles: ["/setupJest.js", "jest-canvas-mock"], + setupFiles: ["/setupJest.cjs", "jest-canvas-mock"], transformIgnorePatterns: [], moduleNameMapper: { - "\\.(css|less)$": "/__mocks__/styleMock.js", + "\\.(css|less)$": "/__mocks__/styleMock.cjs", }, transform: { "^.+\\.(t|j)sx?$": "@swc/jest", diff --git a/packages/graph/package.json b/packages/graph/package.json index 352673af..c8aecc02 100644 --- a/packages/graph/package.json +++ b/packages/graph/package.json @@ -1,6 +1,7 @@ { "name": "@gravity-ui/graph", "version": "1.11.3", + "type": "module", "packageManager": "pnpm@10.34.5", "description": "Modern graph editor component", "main": "build/index.js", @@ -32,16 +33,26 @@ "exports": { ".": { "types": "./build/index.d.ts", + "import": "./build/index.js", "default": "./build/index.js" }, "./react": { "types": "./build/react-components/index.d.ts", + "import": "./build/react-components/index.js", "default": "./build/react-components/index.js" }, "./playwright": { - "types": "./build/playwright/index.d.ts", + "import": { + "types": "./build/playwright/index.d.ts", + "default": "./build/playwright/index.js" + }, + "require": { + "types": "./build/playwright/index.d.cts", + "default": "./build/playwright/index.cjs" + }, "default": "./build/playwright/index.js" - } + }, + "./styles.css": "./build/styles.css" }, "license": "MIT", "repository": { @@ -50,42 +61,46 @@ "directory": "packages/graph" }, "files": [ - "build", - "tsconfig.json" + "build" ], "scripts": { - "typecheck": "pnpm run build:publish && pnpm run typecheck:published-playwright-types", + "typecheck": "tsc -p tsconfig.json --noEmit && pnpm run build && pnpm run typecheck:published-playwright-types", "typecheck:published-playwright-types": "node scripts/check-playwright-consumer-types.mjs", "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"", "test:unit": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests --updateSnapshot", "test": "pnpm run test:unit", "build:docs": "node scripts/build-docs.mjs", - "copy-styles": "copyfiles -u 1 \"src/**/*.css\" build/", - "build:playwright-runtime": "esbuild src/playwright/index.ts --bundle --format=cjs --platform=node --target=node20 --packages=external --outfile=build/playwright/index.js", - "build:publish": "tsc -p tsconfig.publish.json && pnpm run build:playwright-runtime && pnpm run copy-styles && pnpm run build:docs", - "build": "tsc --declaration && pnpm run build:playwright-runtime && pnpm run copy-styles && pnpm run build:docs", - "prepublishOnly": "pnpm run typecheck && pnpm run test && pnpm run build:publish", - "dev": "concurrently \"tsc --watch --declaration\" \"chokidar 'src/**/*.css' -c 'pnpm run copy-styles'\"", - "test:package-contract": "node tests/package-contract/playwright-installed-consumer/run.mjs" + "build": "node scripts/build.mjs", + "prepublishOnly": "pnpm run typecheck && pnpm run test", + "dev": "chokidar \"src/**/*\" -c \"pnpm run build\" --initial", + "test:package-contract": "node tests/package-contract/run.mjs" }, "peerDependencies": { "@playwright/test": ">=1.58.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "peerDependenciesMeta": { "@playwright/test": { "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true } }, "dependencies": { "@preact/signals-core": "^1.12.2", + "elkjs": "^0.9.3", "intersects": "^2.7.2", "lodash": "^4.17.21", "rbush": "^3.0.1", "style-observer": "^0.1.1" }, "devDependencies": { + "@arethetypeswrong/cli": "^0.18.5", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@gravity-ui/eslint-config": "^3.2.0", @@ -101,15 +116,12 @@ "@types/lodash": "^4.17.13", "@types/node": "^20.17.0", "@types/rbush": "^3.0.0", - "@types/react": "^18.2.14", - "@types/react-dom": "^18.2.6", + "@types/react": "^18.3.31", + "@types/react-dom": "^18.3.7", "@typescript-eslint/eslint-plugin": "5.39.0", "@typescript-eslint/parser": "5.39.0", - "concurrently": "^9.2.1", "chokidar-cli": "^3.0.0", - "copyfiles": "^2.4.1", "cross-env": "^7.0.3", - "elkjs": "^0.9.3", "esbuild": "^0.27.2", "eslint": "^8.0.0", "eslint-config-prettier": "^8.10.0", @@ -122,6 +134,7 @@ "jest-environment-jsdom": "^30.0.5", "mitata": "^1.0.34", "prettier": "^3.0.0", + "publint": "^0.3.23", "react": "^18.2.0", "react-dom": "^18.2.0", "ts-node": "^10.9.2", diff --git a/packages/graph/scripts/build-docs.mjs b/packages/graph/scripts/build-docs.mjs index 36778e79..8dd7b080 100644 --- a/packages/graph/scripts/build-docs.mjs +++ b/packages/graph/scripts/build-docs.mjs @@ -1,7 +1,7 @@ // Builds the AI-facing docs tree (cleaned README + docs/ guides) into build/docs so an // agent in a consumer project reads docs matching the installed version from -// node_modules/@gravity-ui/graph/build/docs. Appended to the build/build:publish package -// scripts. Uses @gravity-ui/readme-validator's buildDocs(). +// node_modules/@gravity-ui/graph/build/docs. Called by the canonical build script. Uses +// @gravity-ui/readme-validator's buildDocs(). import path from 'node:path'; import {fileURLToPath} from 'node:url'; diff --git a/packages/graph/scripts/build.mjs b/packages/graph/scripts/build.mjs new file mode 100644 index 00000000..27d32a24 --- /dev/null +++ b/packages/graph/scripts/build.mjs @@ -0,0 +1,253 @@ +import { spawn } from "node:child_process"; +import { access, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { build } from "esbuild"; + +const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const buildDirectory = path.join(packageRoot, "build"); +const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8")); +const externalPackages = [ + ...new Set([...Object.keys(manifest.dependencies ?? {}), ...Object.keys(manifest.peerDependencies ?? {})]), +]; + +// lodash does not expose extensionless subpaths through an exports map, so +// native Node ESM needs the real .js filename. Keep source imports idiomatic +// and normalize only the external specifiers emitted by the production build. +const resolveLodashSubpathsForNodeEsm = { + name: "resolve-lodash-subpaths-for-node-esm", + setup(buildContext) { + buildContext.onResolve({ filter: /^lodash\/[^.]+$/ }, ({ path: importPath }) => ({ + path: `${importPath}.js`, + external: true, + })); + }, +}; + +function run(command, args) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: packageRoot, + stdio: "inherit", + }); + + child.on("error", reject); + child.on("close", (code, signal) => { + if (code === 0) { + resolve(); + return; + } + + reject( + new Error( + signal + ? `${command} ${args.join(" ")} was terminated by ${signal}` + : `${command} ${args.join(" ")} exited with code ${code}` + ) + ); + }); + }); +} + +function assertNoBundledPackages(results) { + const bundledPackages = new Set(); + + for (const result of results) { + for (const input of Object.keys(result.metafile.inputs)) { + const normalizedInput = input.split(path.sep).join("/"); + const nodeModulesMarker = "node_modules/"; + const markerIndex = normalizedInput.lastIndexOf(nodeModulesMarker); + + if (markerIndex !== -1) { + const packagePath = normalizedInput.slice(markerIndex + nodeModulesMarker.length); + const [firstSegment, secondSegment] = packagePath.split("/"); + const packageName = firstSegment.startsWith("@") ? `${firstSegment}/${secondSegment}` : firstSegment; + + bundledPackages.add(packageName); + } + } + } + + if (bundledPackages.size > 0) { + throw new Error( + `Production bundles unexpectedly contain npm packages:\n${[...bundledPackages] + .sort() + .map((dependency) => `- ${dependency}`) + .join("\n")}` + ); + } +} + +async function pathExists(filePath) { + try { + await access(filePath); + return true; + } catch (error) { + if (error?.code === "ENOENT") { + return false; + } + + throw error; + } +} + +async function collectDeclarationFiles(directory) { + const declarationFiles = []; + + for (const entry of await readdir(directory, { withFileTypes: true })) { + const entryPath = path.join(directory, entry.name); + + if (entry.isDirectory()) { + declarationFiles.push(...(await collectDeclarationFiles(entryPath))); + } else if (entry.name.endsWith(".d.ts")) { + declarationFiles.push(entryPath); + } + } + + return declarationFiles; +} + +async function rewriteDeclarationSpecifiersForNodeEsm() { + const relativeSpecifierPattern = /(["'])(\.{1,2}(?:\/[^"'?#]+)?)\1/g; + + for (const declarationFile of await collectDeclarationFiles(buildDirectory)) { + let contents = await readFile(declarationFile, "utf8"); + + // CSS is published through the explicit styles.css entrypoint. Keeping source-side + // CSS imports in declarations would make type-only Node resolution look for files + // that are intentionally not part of the declaration graph. + contents = contents.replace(/^\s*import\s+["']\.{1,2}\/[^"']+\.css["'];\s*\n?/gm, ""); + + const replacements = new Map(); + + for (const [, , specifier] of contents.matchAll(relativeSpecifierPattern)) { + if (replacements.has(specifier)) { + continue; + } + + const declarationDirectory = path.dirname(declarationFile); + const directDeclaration = path.resolve(declarationDirectory, `${specifier}.d.ts`); + const indexDeclaration = path.resolve(declarationDirectory, specifier, "index.d.ts"); + + if (await pathExists(directDeclaration)) { + replacements.set(specifier, `${specifier}.js`); + } else if (await pathExists(indexDeclaration)) { + replacements.set(specifier, `${specifier.replace(/\/$/, "")}/index.js`); + } + } + + contents = contents.replace(relativeSpecifierPattern, (match, quote, specifier) => { + const replacement = replacements.get(specifier); + + return replacement ? `${quote}${replacement}${quote}` : match; + }); + + await writeFile(declarationFile, contents); + } +} + +async function createPlaywrightCjsDeclarations() { + const entryDeclaration = path.join(buildDirectory, "playwright/index.d.ts"); + const pendingDeclarations = [entryDeclaration]; + const processedDeclarations = new Set(); + const relativeSpecifierPattern = /(["'])(\.{1,2}(?:\/[^"'?#]+)?)\1/g; + + while (pendingDeclarations.length > 0) { + const declarationFile = pendingDeclarations.pop(); + + if (!declarationFile || processedDeclarations.has(declarationFile)) { + continue; + } + + processedDeclarations.add(declarationFile); + let contents = await readFile(declarationFile, "utf8"); + const replacements = new Map(); + + for (const [, , specifier] of contents.matchAll(relativeSpecifierPattern)) { + if (!specifier.endsWith(".js") || replacements.has(specifier)) { + continue; + } + + const referencedDeclaration = path.resolve( + path.dirname(declarationFile), + `${specifier.slice(0, -".js".length)}.d.ts` + ); + + if (await pathExists(referencedDeclaration)) { + replacements.set(specifier, `${specifier.slice(0, -".js".length)}.cjs`); + pendingDeclarations.push(referencedDeclaration); + } + } + + contents = contents.replace(relativeSpecifierPattern, (match, quote, specifier) => { + const replacement = replacements.get(specifier); + + return replacement ? `${quote}${replacement}${quote}` : match; + }); + + await writeFile(declarationFile.replace(/\.d\.ts$/, ".d.cts"), contents); + } +} + +await rm(buildDirectory, { recursive: true, force: true }); +await mkdir(buildDirectory, { recursive: true }); + +const sharedOptions = { + absWorkingDir: packageRoot, + bundle: true, + charset: "utf8", + external: externalPackages, + legalComments: "none", + logLevel: "info", + metafile: true, + plugins: [resolveLodashSubpathsForNodeEsm], + sourcemap: false, + target: "es2020", +}; + +const [browserResult, playwrightEsmResult, playwrightCjsResult, stylesResult] = await Promise.all([ + build({ + ...sharedOptions, + chunkNames: "chunks/[name]-[hash]", + entryNames: "[dir]/[name]", + entryPoints: { + index: "src/index.ts", + "react-components/index": "src/react-components/index.ts", + }, + format: "esm", + loader: { ".css": "empty" }, + outdir: buildDirectory, + platform: "neutral", + splitting: true, + }), + build({ + ...sharedOptions, + entryPoints: ["src/playwright/index.ts"], + format: "esm", + outfile: path.join(buildDirectory, "playwright/index.js"), + platform: "node", + }), + build({ + ...sharedOptions, + entryPoints: ["src/playwright/index.ts"], + format: "cjs", + outfile: path.join(buildDirectory, "playwright/index.cjs"), + platform: "node", + }), + build({ + ...sharedOptions, + entryPoints: ["src/styles.css"], + logLevel: "info", + outfile: path.join(buildDirectory, "styles.css"), + platform: "browser", + }), +]); + +assertNoBundledPackages([browserResult, playwrightEsmResult, playwrightCjsResult, stylesResult]); + +const pnpmCommand = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; +await run(pnpmCommand, ["exec", "tsc", "-p", "tsconfig.publish.json"]); +await rewriteDeclarationSpecifiersForNodeEsm(); +await createPlaywrightCjsDeclarations(); +await import(`./build-docs.mjs?build=${Date.now()}`); diff --git a/packages/graph/scripts/check-playwright-consumer-types.mjs b/packages/graph/scripts/check-playwright-consumer-types.mjs index d2a69f74..34f06d19 100644 --- a/packages/graph/scripts/check-playwright-consumer-types.mjs +++ b/packages/graph/scripts/check-playwright-consumer-types.mjs @@ -8,17 +8,16 @@ const fixturePath = path.join( rootDir, "tests", "package-contract", - "playwright-installed-consumer", - "fixture", - "public-types.ts" + "fixtures", + "types", + "playwright-bundler", + "index.ts" ); const buildDir = `${path.join(rootDir, "build")}${path.sep}`; -// The v1 build uses skipLibCheck, so it can emit declarations that work inside -// this repository but fail for consumers (for example, baseUrl-only imports or -// ambient globals that are not emitted). Check the public Playwright entry -// against build/ with skipLibCheck disabled. Keep this focused guard until v2 -// replaces the declaration pipeline. +// Keep this fast local declaration guard aligned with the fixture used by the +// slower packed-and-installed consumer suite. It catches public Playwright +// declaration failures during the regular typecheck without skipLibCheck. const program = ts.createProgram([fixturePath], { module: ts.ModuleKind.ESNext, moduleResolution: ts.ModuleResolutionKind.Bundler, diff --git a/packages/graph/setupJest.js b/packages/graph/setupJest.cjs similarity index 100% rename from packages/graph/setupJest.js rename to packages/graph/setupJest.cjs diff --git a/packages/graph/src/plugins/cssVariables/CSSVariablesLayer.ts b/packages/graph/src/plugins/cssVariables/CSSVariablesLayer.ts index e7057f1b..fd487c5d 100644 --- a/packages/graph/src/plugins/cssVariables/CSSVariablesLayer.ts +++ b/packages/graph/src/plugins/cssVariables/CSSVariablesLayer.ts @@ -8,6 +8,10 @@ import { DEFAULT_CSS_VARIABLES_LAYER_PROPS, SUPPORTED_CSS_VARIABLES } from "./co import { filterSupportedCSSChanges, mapCSSChangesToGraphColors, mapCSSChangesToGraphConstants } from "./mapping"; import type { CSSVariableChange, CSSVariablesLayerProps, CSSVariablesLayerState } from "./types"; +interface CSSVariablesObserver { + unobserve(target: Element): void; +} + /** * CSSVariablesLayer: Synchronizes CSS variables with graph colors and constants * @@ -23,7 +27,7 @@ export class CSSVariablesLayer extends Layer { // Convert StyleObserver records to our CSSVariableChange format @@ -212,11 +216,12 @@ export class CSSVariablesLayer extends Layer, - args?: ElkLayoutArguments & { onError?: (e: Error) => void } -) => { +export const useElk = (config: ElkNode, elk: ELK, args?: ElkLayoutArguments & { onError?: (e: Error) => void }) => { const [result, setResult] = useState(null); const [isLoading, setIsLoading] = useState(true); diff --git a/packages/graph/src/react-components/index.ts b/packages/graph/src/react-components/index.ts index 6883eaf7..f487e981 100644 --- a/packages/graph/src/react-components/index.ts +++ b/packages/graph/src/react-components/index.ts @@ -7,5 +7,7 @@ export * from "./GraphContext"; export * from "./hooks"; export * from "./events"; export * from "./elk"; +export { useLayeredLayout } from "../plugins/layered/hooks/useLayeredLayout"; +export type { UseLayeredLayoutParams } from "../plugins/layered/hooks/useLayeredLayout"; export { TRenderBlockFn } from "./BlocksList"; export * from "./events"; diff --git a/packages/graph/src/store/block/selectors.ts b/packages/graph/src/store/block/selectors.ts index c5189fc6..a4c2e4f9 100644 --- a/packages/graph/src/store/block/selectors.ts +++ b/packages/graph/src/store/block/selectors.ts @@ -1,5 +1,4 @@ -import { TBlock } from "components/canvas/blocks/Block"; - +import type { TBlock } from "../../components/canvas/blocks/Block"; import { Graph } from "../../graph"; import { BlockState, TBlockId } from "./Block"; diff --git a/packages/graph/src/store/group/GroupsList.ts b/packages/graph/src/store/group/GroupsList.ts index ab25d9c2..0e5ab5ba 100644 --- a/packages/graph/src/store/group/GroupsList.ts +++ b/packages/graph/src/store/group/GroupsList.ts @@ -5,6 +5,7 @@ import { Group } from "../../components/canvas/groups"; import { Graph } from "../../graph"; import { MultipleSelectionBucket } from "../../services/selection/MultipleSelectionBucket"; import { ESelectionStrategy } from "../../services/selection/types"; +import type { BlockState } from "../block/Block"; import { RootStore } from "../index"; import { GroupState, TGroup, TGroupId } from "./Group"; @@ -22,7 +23,7 @@ export class GroupsListStore { return Array.from(this.$groupsMap.value.values()); }); - public $blockGroups = computed(() => { + public $blockGroups = computed>(() => { return groupBy(this.rootStore.blocksList.$blocks.value, (item) => item.$state.value.group); }); diff --git a/packages/graph/src/styles.css b/packages/graph/src/styles.css new file mode 100644 index 00000000..ce6b75a8 --- /dev/null +++ b/packages/graph/src/styles.css @@ -0,0 +1,5 @@ +@import "./services/Layer.css"; +@import "./react-components/graph-canvas.css"; +@import "./react-components/Block.css"; +@import "./react-components/Anchor.css"; +@import "./plugins/devtools/devtools-layer.css"; diff --git a/packages/graph/tests/package-contract/README.md b/packages/graph/tests/package-contract/README.md new file mode 100644 index 00000000..238572d5 --- /dev/null +++ b/packages/graph/tests/package-contract/README.md @@ -0,0 +1,189 @@ +# Published package contract + +This suite validates `@gravity-ui/graph` as consumers receive it from npm. Repository source tests cannot detect +missing packed files, incorrect `exports`, broken generated declarations, ESM/CommonJS resolution differences, +incorrect optional-peer metadata, eager React coupling, or runtime duplication of shared dependencies. + +> When a public entrypoint, output file, declaration, peer dependency, stylesheet contract, or consumer scenario +> changes, update the corresponding check or fixture and this README in the same change. + +## Run + +From the repository root: + +```sh +pnpm run typecheck +pnpm run test:package-contract +``` + +Install Chromium once if it is not already available: + +```sh +pnpm --filter @gravity-ui/graph exec playwright install chromium +``` + +Do not run the package-contract suite concurrently with another Graph build or typecheck: the suite deliberately +rebuilds `packages/graph/build`. + +To preserve the temporary consumers for inspection, whether the run passes or fails: + +```sh +KEEP_PACKAGE_CONTRACT_TMP=1 pnpm run test:package-contract +``` + +The runner prints the preserved directory. On a CI failure, Playwright reports and test results are copied to +`packages/graph/playwright-report/package-contract/` and `packages/graph/test-results/package-contract/`. + +## Structure + +```text +tests/package-contract/ +├── README.md +├── run.mjs +├── utils.mjs +├── checks/ +│ ├── artifact.mjs +│ ├── types.mjs +│ ├── runtime.mjs +│ └── browser.mjs +└── fixtures/ + ├── shared/ + ├── apps/ + │ ├── vanilla/ + │ └── react/ + └── types/ + ├── playwright-bundler/ + ├── node-esm/ + └── node-cjs-playwright/ +``` + +`run.mjs` owns orchestration. It must: + +1. build the package exactly once; +2. create exactly one tarball, using the same `pnpm pack --out ... --json` call for both its file list and archive; +3. create isolated vanilla and React consumers from that tarball; +4. install each consumer exactly once; +5. pass the same tarball and consumer directories to every check; +6. derive generated consumers' `packageManager` from the workspace manifest; +7. clean temporary files, or preserve them when requested. + +Checks must not build, pack, or install independently. Splitting checks by goal must not turn the suite into several +package pipelines. + +`checks/` contains package policy and executable assertions. `fixtures/` contains only source files that represent +external consumers: TypeScript inputs, tsconfigs, application code, HTML, CSS, Playwright scenarios, and their server +configuration. Fixtures must import only documented package specifiers such as `@gravity-ui/graph`, +`@gravity-ui/graph/react`, `@gravity-ui/graph/playwright`, and `@gravity-ui/graph/styles.css`; they must never reach into +repository source or `build/` directly. + +The entire fixture tree is copied into each temporary consumer to keep shared relative paths stable. The runner executes +these purpose-specific fixtures: + +- vanilla: `apps/vanilla`, `types/playwright-bundler`, and `types/node-cjs-playwright`; +- React: `apps/react` and `types/node-esm`; +- both applications reuse `shared` browser and TypeScript configuration. + +The vanilla consumer intentionally contains neither React nor React types. + +## Responsibility boundary + +This suite answers one question: **can an external project install the packed package and use every documented public +entrypoint in its supported module and browser environments?** + +It is not a second functional E2E suite: + +- package contract owns tarball contents, package metadata, dependency isolation, declaration resolution, native module + loading, browser bundling, and one installed-package smoke scenario per browser-facing integration; +- `apps/e2e` owns graph and Playwright page-object behavior such as selection, clicks, drag and drop, connections, camera + controls, error states, and interaction edge cases; +- Storybook owns representative component examples and compatibility with the repository's React/Webpack development + environment. + +Add a browser assertion here only when the failure could be caused specifically by packing, installing, resolving, or +bundling the public package. If the same assertion would be equally useful against repository code, it normally belongs +in `apps/e2e` instead. + +## What is checked + +### Artifact + +`checks/artifact.mjs` protects the published archive: + +- a stale sentinel placed in `build/` is removed by the production build; +- the production build rejects accidentally bundled npm dependencies; +- the packed file list stays within a bounded public allowlist and excludes sources, tests, stories, configs, and + lockfiles; +- required root, ESM, CommonJS, declaration, stylesheet, and documentation files are present; +- the installed manifest has the expected package entrypoints, `files`, `exports`, `typesVersions`, and optional peer + metadata; +- published CSS contains the vanilla canvas, React canvas, block, anchor, and devtools selectors; +- `publint --strict` accepts the exact tarball installed by the consumers. + +This catches stale output, missing release artifacts, repository-only files leaking into npm, broken export targets, and +unreviewed package metadata changes. + +### Types + +`checks/types.mjs` checks declarations from the packed or installed package with `strict: true`, `skipLibCheck: false`, +and `noEmit: true`: + +- ATTW checks `.` and `./react` with the `esm-only` profile; +- ATTW checks `./playwright` with the `node16` profile for both ESM and CommonJS; +- the vanilla and React applications type-check with Bundler resolution; +- `types/playwright-bundler` verifies that `GraphPO.evaluate` receives the public `Graph`, block and connection state + methods return public types rather than `any`, and `GraphPoint` and `clickAt` remain usable; +- `types/node-esm` imports `Graph`, `GraphCanvas`, and `useElk` through Node16 ESM resolution; +- `types/node-cjs-playwright` imports `GraphPO` and `GraphCameraState` through Node16 CommonJS resolution. + +“Node16” names TypeScript's module-resolution semantics here; it does not mean the suite executes on Node.js 16. + +The Playwright Bundler fixture is deliberately reused by `scripts/check-playwright-consumer-types.mjs` during +`pnpm run typecheck`, where it checks local generated declarations before the slower installed-consumer suite. Keep both +callers pointed at the same fixture. + +### Runtime + +`checks/runtime.mjs` runs Node against the installed package: + +- ESM imports expose `Graph`, `GraphCanvas`, and `GraphPO` from their documented entrypoints; +- CommonJS `require("@gravity-ui/graph/playwright")` exposes `GraphPO`; +- the vanilla consumer cannot resolve React, proving the core and Playwright entrypoints do not require it eagerly; +- a consumer-side `@preact/signals-core` effect observes a Graph signal update exactly once after its initial run. + +These probes catch invalid conditional exports, ESM/CJS loader failures, accidental React coupling, and a bundled or +otherwise disconnected signals runtime. + +### Browser + +`checks/browser.mjs` bundles the installed vanilla and React applications with esbuild, including the public stylesheet, +then runs a deliberately small Chromium smoke test against each one. Detailed interaction behavior remains in +`apps/e2e`. + +The vanilla scenario covers: + +- bundling and loading the installed `.` and `./playwright` entrypoints; +- `GraphPO.waitForReady` against an application-owned wrapper; +- rendering the canvas with the public stylesheet applied. + +The React scenario covers: + +- bundling and loading the installed `.` and `./react` entrypoints; +- ready-state propagation through `GraphCanvas`, `useGraph`, and `useGraphEvent`; +- rendered `GraphBlock` content and the React canvas stylesheet; +- use of the installed `./playwright` entrypoint with the React application. + +## Maintenance map + +| Change | Required updates | +| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Add, remove, or rename a public subpath | Update the build, manifest, artifact export/file contract, ATTW entrypoints, native import probe, and at least one relevant type fixture. Add a browser fixture when the entrypoint is browser-facing. | +| Change emitted files, chunks, declarations, docs, or CSS | Update the artifact allowlist and required-file set deliberately. Do not broaden patterns merely to make an unexpected file pass. | +| Change public TypeScript APIs | Update the narrow fixture that represents that consumer. Preserve `skipLibCheck: false` and exact anti-`any` assertions. | +| Change ESM/CJS conditions or module support | Update ATTW profiles, the `.mts` or `.cts` fixture, and the matching native `import` or `require` probe. | +| Change dependencies or optional peers | Update manifest assertions and generated consumer manifests. Add or update an absence/interoperability probe when a dependency must remain optional or singleton-like. | +| Change public styles or required selectors | Update the CSS artifact assertions, the consuming application, and its browser assertion together. | +| Change Playwright page-object types or behavior | Update `types/playwright-bundler` for the public type contract and `apps/e2e` for behavior. Change the package browser smoke only when the installed-package integration itself changes. | +| Move or rename fixtures | Update their check registration, consumer paths, server/config paths, and `scripts/check-playwright-consumer-types.mjs` when the shared Playwright type fixture moves. | +| Add a new goal | Add a focused check and the smallest representative fixture, but continue using the existing build, tarball, and installed consumers whenever possible. | + +Do not commit generated consumer manifests, lockfiles, `node_modules`, bundles, reports, or temporary projects. diff --git a/packages/graph/tests/package-contract/checks/artifact.mjs b/packages/graph/tests/package-contract/checks/artifact.mjs new file mode 100644 index 00000000..c2abb7c6 --- /dev/null +++ b/packages/graph/tests/package-contract/checks/artifact.mjs @@ -0,0 +1,177 @@ +import assert from "node:assert/strict"; +import { access, mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; + +import { run } from "../utils.mjs"; + +const expectedExports = { + ".": { + types: "./build/index.d.ts", + import: "./build/index.js", + default: "./build/index.js", + }, + "./react": { + types: "./build/react-components/index.d.ts", + import: "./build/react-components/index.js", + default: "./build/react-components/index.js", + }, + "./playwright": { + import: { + types: "./build/playwright/index.d.ts", + default: "./build/playwright/index.js", + }, + require: { + types: "./build/playwright/index.d.cts", + default: "./build/playwright/index.cjs", + }, + default: "./build/playwright/index.js", + }, + "./styles.css": "./build/styles.css", +}; + +const expectedTypesVersions = { + "*": { + react: ["build/react-components/index.d.ts"], + playwright: ["build/playwright/index.d.ts"], + }, +}; + +const allowedPackageRootFiles = new Set(["LICENSE", "README.md", "package.json"]); +const allowedRuntimeFiles = new Set([ + "build/index.js", + "build/react-components/index.js", + "build/playwright/index.js", + "build/playwright/index.cjs", + "build/playwright/index.d.cts", + "build/styles.css", +]); +const forbiddenPackedPathPatterns = [ + /(?:^|\/)(?:src|story|stories|__tests__)(?:\/|$)/i, + /(?:^|\/)[^/]*\.(?:test|spec|story|stories)\.[^/]+$/i, + /(?:^|\/)(?:tsconfig(?:\.[^/]+)?\.json|jest\.[^/]+|eslint\.[^/]+|prettier\.[^/]+)$/i, + /(?:^|\/)(?:pnpm-lock\.yaml|package-lock\.json|yarn\.lock)$/i, +]; + +function getPackMetadata(output) { + const metadata = JSON.parse(output); + + return Array.isArray(metadata) ? metadata[0] : metadata; +} + +function assertPackedFiles(metadata) { + assert.equal(metadata.name, "@gravity-ui/graph"); + assert.ok(Array.isArray(metadata.files), "pnpm pack did not report the packed file list."); + + const packedFiles = metadata.files.map(({ path: packedPath }) => packedPath).sort(); + const unexpectedFiles = packedFiles.filter((packedPath) => { + if (allowedPackageRootFiles.has(packedPath) || allowedRuntimeFiles.has(packedPath)) { + return false; + } + + return ( + !/^build\/.+\.d\.(?:ts|cts)$/.test(packedPath) && + !/^build\/chunks\/[^/]+\.js$/.test(packedPath) && + !/^build\/docs\/.+\.md$/.test(packedPath) + ); + }); + const forbiddenFiles = packedFiles.filter((packedPath) => + forbiddenPackedPathPatterns.some((pattern) => pattern.test(packedPath)) + ); + + assert.deepEqual(unexpectedFiles, [], `Tarball contains files outside the public allowlist: ${unexpectedFiles}`); + assert.deepEqual(forbiddenFiles, [], `Tarball contains repository-only artifacts: ${forbiddenFiles}`); + + for (const requiredFile of [ + ...allowedPackageRootFiles, + "build/index.js", + "build/index.d.ts", + "build/react-components/index.js", + "build/react-components/index.d.ts", + "build/playwright/index.js", + "build/playwright/index.d.ts", + "build/playwright/index.cjs", + "build/playwright/index.d.cts", + "build/styles.css", + "build/docs/INDEX.md", + ]) { + assert.ok(packedFiles.includes(requiredFile), `Tarball is missing required file ${requiredFile}.`); + } +} + +async function assertPathDoesNotExist(targetPath, message) { + try { + await access(targetPath); + } catch (error) { + if (error?.code === "ENOENT") { + return; + } + throw error; + } + + assert.fail(message); +} + +export async function buildAndPackArtifact({ packageRoot, staleBuildSentinelPath, tarballPath }) { + console.log("\n[package-contract] Building published files..."); + await mkdir(path.dirname(staleBuildSentinelPath), { recursive: true }); + await writeFile(staleBuildSentinelPath, "The production build must remove this stale artifact.\n"); + await run("pnpm", ["run", "build"], { cwd: packageRoot }); + await assertPathDoesNotExist(staleBuildSentinelPath, "The production build did not clean its output directory."); + + console.log("\n[package-contract] Packing @gravity-ui/graph and checking the tarball allowlist..."); + const packMetadata = getPackMetadata( + await run("pnpm", ["pack", "--out", tarballPath, "--json"], { + cwd: packageRoot, + printStdout: false, + }) + ); + assertPackedFiles(packMetadata); + + console.log("\n[package-contract] Linting the packed package metadata..."); + await run("pnpm", ["exec", "publint", tarballPath, "--strict"], { cwd: packageRoot }); +} + +export async function checkInstalledArtifact(consumerDirectory) { + const packageRoot = path.join(consumerDirectory, "node_modules", "@gravity-ui", "graph"); + const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8")); + + assert.equal(manifest.name, "@gravity-ui/graph"); + assert.notEqual(manifest.private, true); + assert.equal(manifest.type, "module"); + assert.equal(manifest.main.replace(/^\.\//, ""), "build/index.js"); + assert.equal(manifest.module.replace(/^\.\//, ""), "build/index.js"); + assert.equal(manifest.types.replace(/^\.\//, ""), "build/index.d.ts"); + assert.deepEqual(manifest.files, ["build"]); + assert.deepEqual(manifest.exports, expectedExports); + assert.deepEqual(manifest.typesVersions, expectedTypesVersions); + assert.equal(manifest.peerDependencies?.["@playwright/test"], ">=1.58.0"); + assert.equal(manifest.peerDependencies?.react, "^18.0.0"); + assert.equal(manifest.peerDependencies?.["react-dom"], "^18.0.0"); + assert.equal(manifest.peerDependenciesMeta?.["@playwright/test"]?.optional, true); + assert.equal(manifest.peerDependenciesMeta?.react?.optional, true); + assert.equal(manifest.peerDependenciesMeta?.["react-dom"]?.optional, true); + + await Promise.all( + [ + "build/index.js", + "build/index.d.ts", + "build/react-components/index.js", + "build/react-components/index.d.ts", + "build/playwright/index.js", + "build/playwright/index.d.ts", + "build/playwright/index.cjs", + "build/playwright/index.d.cts", + "build/styles.css", + "build/docs/INDEX.md", + "README.md", + "LICENSE", + ].map((relativePath) => access(path.join(packageRoot, relativePath))) + ); + + const publicStyles = await readFile(path.join(packageRoot, "build", "styles.css"), "utf8"); + assert.match(publicStyles, /\.layer\b/, "Public styles do not include the vanilla canvas contract."); + assert.match(publicStyles, /\.graph-wrapper\b/, "Public styles do not include the React canvas contract."); + assert.match(publicStyles, /\.graph-block-container\b/, "Public styles do not include the React block contract."); + assert.match(publicStyles, /\.graph-block-anchor\b/, "Public styles do not include the React anchor contract."); + assert.match(publicStyles, /\.devtools-ruler-bg\b/, "Public styles do not include the devtools contract."); +} diff --git a/packages/graph/tests/package-contract/checks/browser.mjs b/packages/graph/tests/package-contract/checks/browser.mjs new file mode 100644 index 00000000..1bac4965 --- /dev/null +++ b/packages/graph/tests/package-contract/checks/browser.mjs @@ -0,0 +1,69 @@ +import { cp, mkdir } from "node:fs/promises"; +import { createServer } from "node:net"; +import path from "node:path"; + +import { run } from "../utils.mjs"; + +async function getFreePort() { + const server = createServer(); + + return new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(0, "127.0.0.1", () => { + const address = server.address(); + if (!address || typeof address === "string") { + server.close(); + reject(new Error("Could not reserve a local port for the consumer test.")); + return; + } + + server.close((error) => { + if (error) { + reject(error); + } else { + resolve(address.port); + } + }); + }); + }); +} + +export async function checkBrowserConsumer({ consumerDirectory, consumerName, appName, entryPoint }) { + console.log(`\n[package-contract:${consumerName}] Bundling ${entryPoint}...`); + await run("pnpm", ["exec", "esbuild", entryPoint, "--bundle", "--outfile=dist/app.js"], { + cwd: consumerDirectory, + }); + + console.log(`\n[package-contract:${consumerName}] Running the installed-package browser scenario...`); + const port = await getFreePort(); + await run("pnpm", ["exec", "playwright", "test", "--config", "fixtures/shared/playwright.config.ts"], { + cwd: consumerDirectory, + env: { + ...process.env, + PACKAGE_CONTRACT_APP: appName, + PACKAGE_CONTRACT_PORT: String(port), + }, + }); +} + +export async function preserveBrowserArtifacts({ consumerNames, packageRoot, temporaryDirectory }) { + if (!process.env.CI) { + return; + } + + for (const consumerName of consumerNames) { + for (const directory of ["playwright-report", "test-results"]) { + const source = path.join(temporaryDirectory, consumerName, directory); + const target = path.join(packageRoot, directory, "package-contract", consumerName); + + try { + await mkdir(path.dirname(target), { recursive: true }); + await cp(source, target, { recursive: true }); + } catch (error) { + if (error?.code !== "ENOENT") { + console.error(`[package-contract] Could not preserve ${consumerName}/${directory}:`, error); + } + } + } + } +} diff --git a/packages/graph/tests/package-contract/checks/runtime.mjs b/packages/graph/tests/package-contract/checks/runtime.mjs new file mode 100644 index 00000000..6e6cdd17 --- /dev/null +++ b/packages/graph/tests/package-contract/checks/runtime.mjs @@ -0,0 +1,98 @@ +import { run } from "../utils.mjs"; + +const nativeImportProbes = { + root: ` + const graph = await import("@gravity-ui/graph"); + if (typeof graph.Graph !== "function") throw new Error("Root entrypoint does not export Graph."); + `, + react: ` + const react = await import("@gravity-ui/graph/react"); + if (typeof react.GraphCanvas !== "function") throw new Error("React entrypoint does not export GraphCanvas."); + `, + playwright: ` + const playwright = await import("@gravity-ui/graph/playwright"); + if (typeof playwright.GraphPO !== "function") throw new Error("Playwright entrypoint does not export GraphPO."); + `, +}; + +async function checkNativeImports(consumerDirectory, entrypoints) { + for (const entrypoint of entrypoints) { + console.log(`\n[package-contract] Importing the ${entrypoint} entrypoint directly with Node ESM...`); + await run(process.execPath, ["--input-type=module", "--eval", nativeImportProbes[entrypoint]], { + cwd: consumerDirectory, + }); + } + + console.log("\n[package-contract] Requiring the Playwright entrypoint directly with Node CommonJS..."); + await run( + process.execPath, + [ + "--input-type=commonjs", + "--eval", + ` + const playwright = require("@gravity-ui/graph/playwright"); + if (typeof playwright.GraphPO !== "function") { + throw new Error("Playwright CommonJS entrypoint does not export GraphPO."); + } + `, + ], + { cwd: consumerDirectory } + ); +} + +async function checkSignalInterop(consumerDirectory) { + const probe = ` + const [{ SingleSelectionBucket }, { effect }] = await Promise.all([ + import("@gravity-ui/graph"), + import("@preact/signals-core"), + ]); + const bucket = new SingleSelectionBucket("blocks"); + let runs = 0; + const dispose = effect(() => { + bucket.$selected.value; + runs += 1; + }); + bucket.select(["block-1"]); + dispose(); + if (runs !== 2) { + throw new Error( + \`Graph signals and the consumer's @preact/signals-core runtime are disconnected (effect ran \${runs} times).\`, + ); + } + `; + + console.log("\n[package-contract] Verifying consumer-side @preact/signals-core interoperability..."); + await run(process.execPath, ["--input-type=module", "--eval", probe], { cwd: consumerDirectory }); +} + +async function checkReactIsAbsent(consumerDirectory) { + const probe = ` + try { + await import("react"); + } catch (error) { + if (error?.code === "ERR_MODULE_NOT_FOUND") process.exit(0); + throw error; + } + throw new Error("The vanilla consumer unexpectedly resolves React."); + `; + + console.log("\n[package-contract] Confirming React is absent from the vanilla project..."); + await run(process.execPath, ["--input-type=module", "--eval", probe], { cwd: consumerDirectory }); +} + +export async function checkRuntimeConsumer({ + consumerDirectory, + entrypoints, + expectNoReact = false, + verifySignalInterop = false, +}) { + if (expectNoReact) { + await checkReactIsAbsent(consumerDirectory); + } + + await checkNativeImports(consumerDirectory, entrypoints); + + if (verifySignalInterop) { + await checkSignalInterop(consumerDirectory); + } +} diff --git a/packages/graph/tests/package-contract/checks/types.mjs b/packages/graph/tests/package-contract/checks/types.mjs new file mode 100644 index 00000000..022f5749 --- /dev/null +++ b/packages/graph/tests/package-contract/checks/types.mjs @@ -0,0 +1,24 @@ +import { run } from "../utils.mjs"; + +export async function checkTarballTypes({ packageRoot, tarballPath }) { + console.log("\n[package-contract] Checking the root and React ESM type contracts with ATTW..."); + await run( + "pnpm", + ["exec", "attw", tarballPath, "--profile", "esm-only", "--entrypoints", ".", "./react", "--no-emoji", "--no-color"], + { cwd: packageRoot } + ); + + console.log("\n[package-contract] Checking the Playwright ESM and CommonJS type contracts with ATTW..."); + await run( + "pnpm", + ["exec", "attw", tarballPath, "--profile", "node16", "--entrypoints", "./playwright", "--no-emoji", "--no-color"], + { cwd: packageRoot } + ); +} + +export async function checkConsumerTypes({ consumerDirectory, consumerName, configs }) { + for (const config of configs) { + console.log(`\n[package-contract:${consumerName}] Type-checking the public consumer API with ${config}...`); + await run("pnpm", ["exec", "tsc", "-p", config, "--noEmit"], { cwd: consumerDirectory }); + } +} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react-app.css b/packages/graph/tests/package-contract/fixtures/apps/react/app.css similarity index 100% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react-app.css rename to packages/graph/tests/package-contract/fixtures/apps/react/app.css diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react-app.tsx b/packages/graph/tests/package-contract/fixtures/apps/react/app.tsx similarity index 95% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react-app.tsx rename to packages/graph/tests/package-contract/fixtures/apps/react/app.tsx index 35617b8f..74503725 100644 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react-app.tsx +++ b/packages/graph/tests/package-contract/fixtures/apps/react/app.tsx @@ -3,9 +3,10 @@ import { createRoot } from "react-dom/client"; import { Graph, GraphState, type TBlock } from "@gravity-ui/graph"; import { GraphBlock, GraphCanvas, useGraph, useGraphEvent } from "@gravity-ui/graph/react"; +import "@gravity-ui/graph/styles.css"; +import "../../shared/base.css"; import "./app.css"; -import "./react-app.css"; const blocks = [ { diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.pw.ts b/packages/graph/tests/package-contract/fixtures/apps/react/graph.pw.ts similarity index 54% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.pw.ts rename to packages/graph/tests/package-contract/fixtures/apps/react/graph.pw.ts index 060b647c..f97814b0 100644 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.pw.ts +++ b/packages/graph/tests/package-contract/fixtures/apps/react/graph.pw.ts @@ -1,17 +1,15 @@ import { expect, test } from "@playwright/test"; -import type { TBlock } from "@gravity-ui/graph"; import { GraphPO } from "@gravity-ui/graph/playwright"; -test("installed package exposes a working React entrypoint", async ({ page }) => { - await page.goto("/react.html"); +test("installed React entrypoint renders with public styles", async ({ page }) => { + await page.goto("/"); const root = page.locator("#react-root"); await expect(root).toHaveAttribute("data-state", "ready"); + await expect(root.locator(".graph-wrapper")).toHaveCSS("position", "relative"); const graph = new GraphPO(page.locator("#react-shell")); await graph.waitForReady(); - const blockState: TBlock | null = await graph.block("react-source").getState(); - expect(blockState).toMatchObject({ id: "react-source", name: "React source" }); await expect(page.getByTestId("react-block-react-source")).toHaveText("React source"); }); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.html b/packages/graph/tests/package-contract/fixtures/apps/react/index.html similarity index 74% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.html rename to packages/graph/tests/package-contract/fixtures/apps/react/index.html index 3d44421f..5fdd460f 100644 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/react.html +++ b/packages/graph/tests/package-contract/fixtures/apps/react/index.html @@ -4,12 +4,12 @@ Installed Graph React consumer - +
- + diff --git a/packages/graph/tests/package-contract/fixtures/apps/react/tsconfig.json b/packages/graph/tests/package-contract/fixtures/apps/react/tsconfig.json new file mode 100644 index 00000000..51944c0f --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/apps/react/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../shared/tsconfig.json", + "compilerOptions": { + "jsx": "react-jsx" + }, + "include": ["app.tsx", "graph.pw.ts"] +} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/app.ts b/packages/graph/tests/package-contract/fixtures/apps/vanilla/app.ts similarity index 92% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/app.ts rename to packages/graph/tests/package-contract/fixtures/apps/vanilla/app.ts index 76dda1b8..3507b54f 100644 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/app.ts +++ b/packages/graph/tests/package-contract/fixtures/apps/vanilla/app.ts @@ -1,6 +1,7 @@ import { ECanDrag, Graph, type TBlock, type TConnection } from "@gravity-ui/graph"; +import "@gravity-ui/graph/styles.css"; -import "./app.css"; +import "../../shared/base.css"; const blocks = [ { diff --git a/packages/graph/tests/package-contract/fixtures/apps/vanilla/graph.pw.ts b/packages/graph/tests/package-contract/fixtures/apps/vanilla/graph.pw.ts new file mode 100644 index 00000000..336e9664 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/apps/vanilla/graph.pw.ts @@ -0,0 +1,15 @@ +import { expect, test } from "@playwright/test"; +import { GraphPO } from "@gravity-ui/graph/playwright"; + +test("installed vanilla entrypoints load with public styles", async ({ page }) => { + await page.goto("/"); + + // Use an application-owned wrapper to make sure consumers do not need to + // depend on internal Graph markup or CSS selectors. + const graph = new GraphPO(page.locator("#graph-shell")); + await graph.waitForReady(); + + const canvas = page.locator("#graph canvas.layer").first(); + await expect(canvas).toBeVisible(); + await expect(canvas).toHaveCSS("position", "absolute"); +}); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/index.html b/packages/graph/tests/package-contract/fixtures/apps/vanilla/index.html similarity index 100% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/index.html rename to packages/graph/tests/package-contract/fixtures/apps/vanilla/index.html diff --git a/packages/graph/tests/package-contract/fixtures/apps/vanilla/tsconfig.json b/packages/graph/tests/package-contract/fixtures/apps/vanilla/tsconfig.json new file mode 100644 index 00000000..89ba35f1 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/apps/vanilla/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../shared/tsconfig.json", + "include": ["app.ts", "graph.pw.ts"] +} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/app.css b/packages/graph/tests/package-contract/fixtures/shared/base.css similarity index 100% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/app.css rename to packages/graph/tests/package-contract/fixtures/shared/base.css diff --git a/packages/graph/tests/package-contract/fixtures/shared/playwright.config.ts b/packages/graph/tests/package-contract/fixtures/shared/playwright.config.ts new file mode 100644 index 00000000..0e0fa8ac --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/shared/playwright.config.ts @@ -0,0 +1,38 @@ +import { defineConfig } from "@playwright/test"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const port = Number(process.env.PACKAGE_CONTRACT_PORT ?? "4173"); +const baseURL = `http://127.0.0.1:${port}`; +const appName = process.env.PACKAGE_CONTRACT_APP; +const sharedDirectory = path.dirname(fileURLToPath(import.meta.url)); +const consumerDirectory = path.resolve(sharedDirectory, "../.."); + +if (appName !== "vanilla" && appName !== "react") { + throw new Error('PACKAGE_CONTRACT_APP must be either "vanilla" or "react".'); +} + +export default defineConfig({ + testDir: path.resolve(sharedDirectory, "../apps", appName), + testMatch: "*.pw.ts", + fullyParallel: false, + workers: 1, + outputDir: path.join(consumerDirectory, "test-results"), + reporter: [["line"], ["html", { open: "never", outputFolder: path.join(consumerDirectory, "playwright-report") }]], + use: { + baseURL, + browserName: "chromium", + screenshot: "only-on-failure", + trace: "retain-on-failure", + }, + webServer: { + command: "node server.mjs", + cwd: sharedDirectory, + url: baseURL, + reuseExistingServer: false, + env: { + PACKAGE_CONTRACT_APP: appName, + PACKAGE_CONTRACT_PORT: String(port), + }, + }, +}); diff --git a/packages/graph/tests/package-contract/fixtures/shared/server.mjs b/packages/graph/tests/package-contract/fixtures/shared/server.mjs new file mode 100644 index 00000000..6a393163 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/shared/server.mjs @@ -0,0 +1,34 @@ +import { createReadStream } from "node:fs"; +import { createServer } from "node:http"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const sharedDirectory = path.dirname(fileURLToPath(import.meta.url)); +const fixturesDirectory = path.resolve(sharedDirectory, ".."); +const consumerDirectory = path.resolve(fixturesDirectory, ".."); +const appName = process.env.PACKAGE_CONTRACT_APP; +const port = Number(process.env.PACKAGE_CONTRACT_PORT ?? "4173"); + +if (appName !== "vanilla" && appName !== "react") { + throw new Error('PACKAGE_CONTRACT_APP must be either "vanilla" or "react".'); +} + +const appDirectory = path.join(fixturesDirectory, "apps", appName); +const files = new Map([ + ["/", { path: path.join(appDirectory, "index.html"), type: "text/html; charset=utf-8" }], + ["/app.js", { path: path.join(consumerDirectory, "dist/app.js"), type: "text/javascript; charset=utf-8" }], + ["/app.css", { path: path.join(consumerDirectory, "dist/app.css"), type: "text/css; charset=utf-8" }], +]); + +createServer((request, response) => { + const pathname = new URL(request.url ?? "/", `http://${request.headers.host}`).pathname; + const file = files.get(pathname); + + if (!file) { + response.writeHead(404).end("Not found"); + return; + } + + response.writeHead(200, { "Content-Type": file.type }); + createReadStream(file.path).pipe(response); +}).listen(port, "127.0.0.1"); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.json b/packages/graph/tests/package-contract/fixtures/shared/tsconfig.json similarity index 56% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.json rename to packages/graph/tests/package-contract/fixtures/shared/tsconfig.json index e4c72519..9acefe60 100644 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.json +++ b/packages/graph/tests/package-contract/fixtures/shared/tsconfig.json @@ -3,10 +3,10 @@ "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", - "lib": ["ES2022", "DOM"], + "lib": ["ES2022", "DOM", "ESNext.Disposable"], "strict": true, - "skipLibCheck": true, + "skipLibCheck": false, "noEmit": true, - "types": ["@playwright/test"] + "types": ["node", "@playwright/test"] } } diff --git a/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/index.cts b/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/index.cts new file mode 100644 index 00000000..c8211294 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/index.cts @@ -0,0 +1,7 @@ +import graph = require("@gravity-ui/graph/playwright"); + +const GraphPOConstructor: typeof graph.GraphPO = graph.GraphPO; +const cameraState: graph.GraphCameraState = { x: 0, y: 0, scale: 1 }; + +void GraphPOConstructor; +void cameraState; diff --git a/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/tsconfig.json b/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/tsconfig.json new file mode 100644 index 00000000..0690d0d8 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/types/node-cjs-playwright/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../shared/tsconfig.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "Node16" + }, + "include": ["index.cts"] +} diff --git a/packages/graph/tests/package-contract/fixtures/types/node-esm/index.mts b/packages/graph/tests/package-contract/fixtures/types/node-esm/index.mts new file mode 100644 index 00000000..16835a8b --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/types/node-esm/index.mts @@ -0,0 +1,6 @@ +import { Graph } from "@gravity-ui/graph"; +import { GraphCanvas, useElk } from "@gravity-ui/graph/react"; + +void Graph; +void GraphCanvas; +void useElk; diff --git a/packages/graph/tests/package-contract/fixtures/types/node-esm/tsconfig.json b/packages/graph/tests/package-contract/fixtures/types/node-esm/tsconfig.json new file mode 100644 index 00000000..0b5dc6e8 --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/types/node-esm/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../shared/tsconfig.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "Node16" + }, + "include": ["index.mts"] +} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/public-types.ts b/packages/graph/tests/package-contract/fixtures/types/playwright-bundler/index.ts similarity index 100% rename from packages/graph/tests/package-contract/playwright-installed-consumer/fixture/public-types.ts rename to packages/graph/tests/package-contract/fixtures/types/playwright-bundler/index.ts diff --git a/packages/graph/tests/package-contract/fixtures/types/playwright-bundler/tsconfig.json b/packages/graph/tests/package-contract/fixtures/types/playwright-bundler/tsconfig.json new file mode 100644 index 00000000..0e4477bc --- /dev/null +++ b/packages/graph/tests/package-contract/fixtures/types/playwright-bundler/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../shared/tsconfig.json", + "include": ["index.ts"] +} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/README.md b/packages/graph/tests/package-contract/playwright-installed-consumer/README.md deleted file mode 100644 index 1d01812c..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Installed consumer contract - -This check validates the package boundary that repository-local E2E tests cannot cover. It builds and packs -`@gravity-ui/graph`, installs the same tarball into isolated temporary vanilla and React projects, bundles one Graph -page in each project, and runs Playwright through the public `@gravity-ui/graph/playwright` entry point. - -The v1 TypeScript build emits extensionless ESM imports intended for bundlers, while Playwright loads this entry -directly in Node.js. `build:playwright-runtime` therefore rewrites only `build/playwright/index.js` as a standalone -CommonJS bundle; this test guards that installed-package boundary. - -Run it from the repository root: - -```sh -pnpm run test:package-contract -``` - -The fixture type-checks exact `Graph`, `TBlock`, and `TConnection` usage, exercises `waitForReady`, typed `evaluate`, -block click and drag, connection selection, and camera zoom, then smoke-tests the installed React entry point. The -vanilla project uses only the framework-free API, while still installing the package's currently required React -peers. Both temporary projects are deleted after the run and do not add another lockfile to the repository. Set -`KEEP_PACKAGE_CONTRACT_TMP=1` to preserve them for debugging a failure. diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/graph.pw.ts b/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/graph.pw.ts deleted file mode 100644 index e8148e63..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/graph.pw.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { expect, test } from "@playwright/test"; -import type { Graph, TBlock, TConnection } from "@gravity-ui/graph"; -import { GraphPO } from "@gravity-ui/graph/playwright"; - -test("installed package exposes working typed Graph page objects", async ({ page }) => { - await page.goto("/"); - - // Use an application-owned wrapper to make sure consumers do not need to - // depend on internal Graph markup or CSS selectors. - const graph = new GraphPO(page.locator("#graph-shell")); - await graph.waitForReady(); - - const evaluated = await graph.evaluate( - (instance: Graph, ids) => { - const block: TBlock | null = instance.blocks.getBlockState(ids.block)?.asTBlock() ?? null; - const connection: TConnection | null = instance.connections.getConnection(ids.connection) ?? null; - - return { - state: instance.state, - block, - connection, - }; - }, - { block: "source", connection: "source-to-target" } - ); - - expect(evaluated).toMatchObject({ - state: 2, - block: { id: "source", name: "Source" }, - connection: { - id: "source-to-target", - sourceBlockId: "source", - targetBlockId: "target", - }, - }); - - const block = graph.block("source"); - const blockState: TBlock | null = await block.getState(); - expect(blockState).toMatchObject({ id: "source", name: "Source" }); - - await block.click(); - expect(await block.isSelected()).toBe(true); - - const connection = graph.connection("source-to-target"); - const connectionState: TConnection | null = await connection.getState(); - expect(connectionState).toMatchObject({ - id: "source-to-target", - sourceBlockId: "source", - targetBlockId: "target", - }); - - await connection.click(); - expect(await connection.isSelected()).toBe(true); - - const initialCenter = await block.getCenter(); - const targetCenter = { x: initialCenter.x + 120, y: initialCenter.y + 80 }; - await block.dragTo(targetCenter, { steps: 5 }); - expect(await block.getCenter()).toEqual(targetCenter); - - await graph.camera().zoomToScale(0.75); - expect((await graph.camera().getState()).scale).toBeCloseTo(0.75); -}); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/playwright.config.ts b/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/playwright.config.ts deleted file mode 100644 index abd8d965..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/playwright.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { defineConfig } from "@playwright/test"; - -const port = Number(process.env.PACKAGE_CONTRACT_PORT ?? "4173"); -const baseURL = `http://127.0.0.1:${port}`; - -export default defineConfig({ - testDir: ".", - testMatch: "*.pw.ts", - fullyParallel: false, - workers: 1, - outputDir: "test-results", - reporter: [["line"], ["html", { open: "never" }]], - use: { - baseURL, - browserName: "chromium", - screenshot: "only-on-failure", - trace: "retain-on-failure", - }, - webServer: { - command: "node server.mjs", - url: baseURL, - reuseExistingServer: false, - env: { - PACKAGE_CONTRACT_PORT: String(port), - }, - }, -}); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/server.mjs b/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/server.mjs deleted file mode 100644 index 0c3d410c..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/server.mjs +++ /dev/null @@ -1,28 +0,0 @@ -import { createReadStream } from "node:fs"; -import { createServer } from "node:http"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const root = path.dirname(fileURLToPath(import.meta.url)); -const port = Number(process.env.PACKAGE_CONTRACT_PORT ?? "4173"); -const files = new Map([ - ["/", { path: path.join(root, "index.html"), type: "text/html; charset=utf-8" }], - ["/react.html", { path: path.join(root, "react.html"), type: "text/html; charset=utf-8" }], - ["/app.js", { path: path.join(root, "dist/app.js"), type: "text/javascript; charset=utf-8" }], - ["/app.css", { path: path.join(root, "dist/app.css"), type: "text/css; charset=utf-8" }], - ["/react-app.js", { path: path.join(root, "dist/react-app.js"), type: "text/javascript; charset=utf-8" }], - ["/react-app.css", { path: path.join(root, "dist/react-app.css"), type: "text/css; charset=utf-8" }], -]); - -createServer((request, response) => { - const pathname = new URL(request.url ?? "/", `http://${request.headers.host}`).pathname; - const file = files.get(pathname); - - if (!file) { - response.writeHead(404).end("Not found"); - return; - } - - response.writeHead(200, { "Content-Type": file.type }); - createReadStream(file.path).pipe(response); -}).listen(port, "127.0.0.1"); diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.react.json b/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.react.json deleted file mode 100644 index 256ee236..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.react.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "jsx": "react-jsx" - }, - "include": ["react-app.tsx", "react.pw.ts"] -} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.vanilla.json b/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.vanilla.json deleted file mode 100644 index 036e3799..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/fixture/tsconfig.vanilla.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["app.ts", "graph.pw.ts", "public-types.ts"] -} diff --git a/packages/graph/tests/package-contract/playwright-installed-consumer/run.mjs b/packages/graph/tests/package-contract/playwright-installed-consumer/run.mjs deleted file mode 100644 index 867433ce..00000000 --- a/packages/graph/tests/package-contract/playwright-installed-consumer/run.mjs +++ /dev/null @@ -1,272 +0,0 @@ -import assert from "node:assert/strict"; -import { access, cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; -import { createServer } from "node:net"; -import { tmpdir } from "node:os"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { spawn } from "node:child_process"; - -const fixtureDirectory = fileURLToPath(new URL("./fixture", import.meta.url)); -const repositoryRoot = fileURLToPath(new URL("../../../", import.meta.url)); -const temporaryDirectory = await mkdtemp(path.join(tmpdir(), "gravity-graph-playwright-consumer-")); -const tarballDirectory = path.join(temporaryDirectory, "tarballs"); -const tarballName = "gravity-ui-graph.tgz"; -const tarballPath = path.join(tarballDirectory, tarballName); -const consumerNames = ["vanilla", "react"]; - -async function getInstalledVersion(packageName) { - const manifestPath = path.join(repositoryRoot, "node_modules", ...packageName.split("/"), "package.json"); - const manifest = JSON.parse(await readFile(manifestPath, "utf8")); - - if (!manifest.version) { - throw new Error(`Installed package ${packageName} does not declare a version.`); - } - - return manifest.version; -} - -function run(command, args, options = {}) { - return new Promise((resolve, reject) => { - const child = spawn(command, args, { - cwd: options.cwd, - env: options.env ?? process.env, - stdio: ["ignore", "pipe", "pipe"], - }); - let stdout = ""; - - child.stdout.on("data", (chunk) => { - stdout += chunk; - if (options.printStdout !== false) { - process.stdout.write(chunk); - } - }); - child.stderr.on("data", (chunk) => process.stderr.write(chunk)); - child.on("error", reject); - child.on("close", (code, signal) => { - if (code === 0) { - resolve(stdout); - return; - } - - reject( - new Error( - signal - ? `${command} ${args.join(" ")} was terminated by ${signal}.` - : `${command} ${args.join(" ")} exited with code ${code}.` - ) - ); - }); - }); -} - -async function getFreePort() { - const server = createServer(); - - return new Promise((resolve, reject) => { - server.once("error", reject); - server.listen(0, "127.0.0.1", () => { - const address = server.address(); - if (!address || typeof address === "string") { - server.close(); - reject(new Error("Could not reserve a local port for the consumer test.")); - return; - } - - server.close((error) => { - if (error) { - reject(error); - } else { - resolve(address.port); - } - }); - }); - }); -} - -async function preserveCIArtifacts() { - if (!process.env.CI) { - return; - } - - for (const consumerName of consumerNames) { - for (const directory of ["playwright-report", "test-results"]) { - const source = path.join(temporaryDirectory, consumerName, directory); - const target = path.join(repositoryRoot, directory, "package-contract", consumerName); - - try { - await mkdir(path.dirname(target), { recursive: true }); - await cp(source, target, { recursive: true }); - } catch (error) { - if (error?.code !== "ENOENT") { - console.error(`[package-contract] Could not preserve ${consumerName}/${directory}:`, error); - } - } - } - } -} - -async function assertInstalledPackageContract(consumerDirectory) { - const packageRoot = path.join(consumerDirectory, "node_modules", "@gravity-ui", "graph"); - const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8")); - - assert.equal(manifest.name, "@gravity-ui/graph"); - assert.notEqual(manifest.private, true); - assert.equal(manifest.main, "build/index.js"); - assert.equal(manifest.module, "build/index.js"); - assert.equal(manifest.types, "build/index.d.ts"); - assert.deepEqual(manifest.typesVersions, { - "*": { - react: ["build/react-components/index.d.ts"], - playwright: ["build/playwright/index.d.ts"], - }, - }); - assert.deepEqual(manifest.exports, { - ".": { - types: "./build/index.d.ts", - default: "./build/index.js", - }, - "./react": { - types: "./build/react-components/index.d.ts", - default: "./build/react-components/index.js", - }, - "./playwright": { - types: "./build/playwright/index.d.ts", - default: "./build/playwright/index.js", - }, - }); - - await Promise.all( - [ - "build/index.js", - "build/index.d.ts", - "build/react-components/index.js", - "build/react-components/index.d.ts", - "build/react-components/graph-canvas.css", - "build/playwright/index.js", - "build/playwright/index.d.ts", - "build/docs/INDEX.md", - "README.md", - "LICENSE", - "tsconfig.json", - ].map((relativePath) => access(path.join(packageRoot, relativePath))) - ); -} - -async function runConsumer({ name, manifest, typecheckConfig, entryPoint, testFile }) { - const consumerDirectory = path.join(temporaryDirectory, name); - await mkdir(consumerDirectory); - await cp(fixtureDirectory, consumerDirectory, { recursive: true }); - await writeFile(path.join(consumerDirectory, "package.json"), `${JSON.stringify(manifest, null, 2)}\n`); - - console.log(`\n[package-contract:${name}] Installing the packed library...`); - await run("pnpm", ["install", "--ignore-workspace", "--ignore-scripts", "--no-lockfile", "--no-frozen-lockfile"], { - cwd: consumerDirectory, - }); - - await assertInstalledPackageContract(consumerDirectory); - - console.log(`\n[package-contract:${name}] Type-checking the public consumer API...`); - await run("pnpm", ["exec", "tsc", "-p", typecheckConfig, "--noEmit"], { cwd: consumerDirectory }); - - console.log(`\n[package-contract:${name}] Bundling ${entryPoint}...`); - await run("pnpm", ["exec", "esbuild", entryPoint, "--bundle", `--outfile=dist/${path.parse(entryPoint).name}.js`], { - cwd: consumerDirectory, - }); - - console.log(`\n[package-contract:${name}] Running ${testFile} through the installed Playwright entrypoint...`); - const port = await getFreePort(); - await run("pnpm", ["exec", "playwright", "test", testFile], { - cwd: consumerDirectory, - env: { - ...process.env, - PACKAGE_CONTRACT_PORT: String(port), - }, - }); -} - -try { - await mkdir(tarballDirectory); - - console.log("\n[package-contract] Building published files..."); - await run("pnpm", ["run", "build:publish"], { - cwd: repositoryRoot, - }); - - console.log("\n[package-contract] Packing @gravity-ui/graph..."); - await run("pnpm", ["pack", "--out", tarballPath], { - cwd: repositoryRoot, - printStdout: false, - }); - - const [ - playwrightVersion, - esbuildVersion, - typescriptVersion, - reactVersion, - reactDomVersion, - reactTypesVersion, - reactDomTypesVersion, - ] = await Promise.all( - ["@playwright/test", "esbuild", "typescript", "react", "react-dom", "@types/react", "@types/react-dom"].map( - getInstalledVersion - ) - ); - const commonManifest = { - private: true, - type: "module", - packageManager: "pnpm@10.34.5", - dependencies: { - "@gravity-ui/graph": `file:../tarballs/${tarballName}`, - react: reactVersion, - "react-dom": reactDomVersion, - }, - devDependencies: { - "@playwright/test": playwrightVersion, - esbuild: esbuildVersion, - typescript: typescriptVersion, - }, - }; - - await runConsumer({ - name: "vanilla", - manifest: { - ...commonManifest, - name: "gravity-graph-installed-vanilla-consumer", - }, - typecheckConfig: "tsconfig.vanilla.json", - entryPoint: "app.ts", - testFile: "graph.pw.ts", - }); - - await runConsumer({ - name: "react", - manifest: { - ...commonManifest, - name: "gravity-graph-installed-react-consumer", - dependencies: { - ...commonManifest.dependencies, - }, - devDependencies: { - ...commonManifest.devDependencies, - "@types/react": reactTypesVersion, - "@types/react-dom": reactDomTypesVersion, - }, - }, - typecheckConfig: "tsconfig.react.json", - entryPoint: "react-app.tsx", - testFile: "react.pw.ts", - }); - - console.log("\n[package-contract] Installed consumer contract passed."); -} catch (error) { - await preserveCIArtifacts(); - - if (process.env.KEEP_PACKAGE_CONTRACT_TMP === "1") { - console.error(`\n[package-contract] Preserving failed project at ${temporaryDirectory}`); - } - throw error; -} finally { - if (process.env.KEEP_PACKAGE_CONTRACT_TMP !== "1") { - await rm(temporaryDirectory, { recursive: true, force: true }); - } -} diff --git a/packages/graph/tests/package-contract/run.mjs b/packages/graph/tests/package-contract/run.mjs new file mode 100644 index 00000000..32758a4c --- /dev/null +++ b/packages/graph/tests/package-contract/run.mjs @@ -0,0 +1,171 @@ +import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { buildAndPackArtifact, checkInstalledArtifact } from "./checks/artifact.mjs"; +import { checkBrowserConsumer, preserveBrowserArtifacts } from "./checks/browser.mjs"; +import { checkRuntimeConsumer } from "./checks/runtime.mjs"; +import { checkConsumerTypes, checkTarballTypes } from "./checks/types.mjs"; +import { run } from "./utils.mjs"; + +const fixturesDirectory = fileURLToPath(new URL("./fixtures", import.meta.url)); +const packageRoot = fileURLToPath(new URL("../../", import.meta.url)); +const workspaceRoot = fileURLToPath(new URL("../../../../", import.meta.url)); +const temporaryDirectory = await mkdtemp(path.join(tmpdir(), "gravity-graph-package-contract-")); +const tarballDirectory = path.join(temporaryDirectory, "tarballs"); +const tarballName = "gravity-ui-graph.tgz"; +const tarballPath = path.join(tarballDirectory, tarballName); +const staleBuildSentinelPath = path.join(packageRoot, "build", "package-contract-stale-sentinel.txt"); +const consumerNames = ["vanilla", "react"]; + +async function getInstalledVersion(packageName) { + const manifestPath = path.join(packageRoot, "node_modules", ...packageName.split("/"), "package.json"); + const manifest = JSON.parse(await readFile(manifestPath, "utf8")); + + if (!manifest.version) { + throw new Error(`Installed package ${packageName} does not declare a version.`); + } + + return manifest.version; +} + +async function runConsumer({ + name, + manifest, + typecheckConfigs, + entryPoint, + nativeImports, + expectNoReact = false, + verifySignalInterop = false, +}) { + const consumerDirectory = path.join(temporaryDirectory, name); + await mkdir(consumerDirectory); + await cp(fixturesDirectory, path.join(consumerDirectory, "fixtures"), { recursive: true }); + await writeFile(path.join(consumerDirectory, "package.json"), `${JSON.stringify(manifest, null, 2)}\n`); + + console.log(`\n[package-contract:${name}] Installing the packed library...`); + await run("pnpm", ["install", "--ignore-workspace", "--ignore-scripts", "--no-lockfile", "--no-frozen-lockfile"], { + cwd: consumerDirectory, + }); + + await checkInstalledArtifact(consumerDirectory); + await checkRuntimeConsumer({ + consumerDirectory, + entrypoints: nativeImports, + expectNoReact, + verifySignalInterop, + }); + await checkConsumerTypes({ + consumerDirectory, + consumerName: name, + configs: typecheckConfigs, + }); + await checkBrowserConsumer({ + consumerDirectory, + consumerName: name, + appName: name, + entryPoint, + }); +} + +try { + await mkdir(tarballDirectory); + await buildAndPackArtifact({ packageRoot, staleBuildSentinelPath, tarballPath }); + await checkTarballTypes({ packageRoot, tarballPath }); + + const workspaceManifest = JSON.parse(await readFile(path.join(workspaceRoot, "package.json"), "utf8")); + if (!workspaceManifest.packageManager) { + throw new Error("The workspace manifest must declare packageManager for generated consumers."); + } + + const [ + playwrightVersion, + esbuildVersion, + typescriptVersion, + nodeTypesVersion, + signalsVersion, + reactVersion, + reactDomVersion, + reactTypesVersion, + reactDomTypesVersion, + ] = await Promise.all( + [ + "@playwright/test", + "esbuild", + "typescript", + "@types/node", + "@preact/signals-core", + "react", + "react-dom", + "@types/react", + "@types/react-dom", + ].map(getInstalledVersion) + ); + const commonManifest = { + private: true, + type: "module", + packageManager: workspaceManifest.packageManager, + dependencies: { + "@gravity-ui/graph": `file:../tarballs/${tarballName}`, + "@preact/signals-core": signalsVersion, + }, + devDependencies: { + "@playwright/test": playwrightVersion, + "@types/node": nodeTypesVersion, + esbuild: esbuildVersion, + typescript: typescriptVersion, + }, + }; + + await runConsumer({ + name: "vanilla", + manifest: { + ...commonManifest, + name: "gravity-graph-installed-vanilla-consumer", + }, + typecheckConfigs: [ + "fixtures/apps/vanilla/tsconfig.json", + "fixtures/types/playwright-bundler/tsconfig.json", + "fixtures/types/node-cjs-playwright/tsconfig.json", + ], + entryPoint: "fixtures/apps/vanilla/app.ts", + nativeImports: ["root", "playwright"], + expectNoReact: true, + verifySignalInterop: true, + }); + + await runConsumer({ + name: "react", + manifest: { + ...commonManifest, + name: "gravity-graph-installed-react-consumer", + dependencies: { + ...commonManifest.dependencies, + react: reactVersion, + "react-dom": reactDomVersion, + }, + devDependencies: { + ...commonManifest.devDependencies, + "@types/react": reactTypesVersion, + "@types/react-dom": reactDomTypesVersion, + }, + }, + typecheckConfigs: ["fixtures/apps/react/tsconfig.json", "fixtures/types/node-esm/tsconfig.json"], + entryPoint: "fixtures/apps/react/app.tsx", + nativeImports: ["root", "react", "playwright"], + }); + + console.log("\n[package-contract] Published package contract passed."); +} catch (error) { + await preserveBrowserArtifacts({ consumerNames, packageRoot, temporaryDirectory }); + throw error; +} finally { + await rm(staleBuildSentinelPath, { force: true }); + + if (process.env.KEEP_PACKAGE_CONTRACT_TMP === "1") { + console.log(`\n[package-contract] Preserved temporary projects at ${temporaryDirectory}`); + } else { + await rm(temporaryDirectory, { recursive: true, force: true }); + } +} diff --git a/packages/graph/tests/package-contract/utils.mjs b/packages/graph/tests/package-contract/utils.mjs new file mode 100644 index 00000000..35486972 --- /dev/null +++ b/packages/graph/tests/package-contract/utils.mjs @@ -0,0 +1,35 @@ +import { spawn } from "node:child_process"; + +export function run(command, args, options = {}) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: options.cwd, + env: options.env ?? process.env, + stdio: ["ignore", "pipe", "pipe"], + }); + let stdout = ""; + + child.stdout.on("data", (chunk) => { + stdout += chunk; + if (options.printStdout !== false) { + process.stdout.write(chunk); + } + }); + child.stderr.on("data", (chunk) => process.stderr.write(chunk)); + child.on("error", reject); + child.on("close", (code, signal) => { + if (code === 0) { + resolve(stdout); + return; + } + + reject( + new Error( + signal + ? `${command} ${args.join(" ")} was terminated by ${signal}.` + : `${command} ${args.join(" ")} exited with code ${code}.` + ) + ); + }); + }); +} diff --git a/packages/graph/tsconfig.json b/packages/graph/tsconfig.json index f6be019e..7214fc7a 100644 --- a/packages/graph/tsconfig.json +++ b/packages/graph/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "baseUrl": "./src/", "outDir": "./build/", "moduleResolution": "node", "module": "esnext", @@ -11,11 +10,7 @@ "experimentalDecorators": true, "typeRoots": ["./node_modules/@types"], "skipLibCheck": true, - "jsx": "react", - "paths": { - "@/*": ["./*"] - } + "jsx": "react" }, - "outDir": "./dist/", - "include": ["./src/**/*.ts", "src/**/*.tsx"], + "include": ["./src/**/*.ts", "src/**/*.tsx"] } diff --git a/packages/graph/tsconfig.publish.json b/packages/graph/tsconfig.publish.json index 68f612a2..5412aa9a 100644 --- a/packages/graph/tsconfig.publish.json +++ b/packages/graph/tsconfig.publish.json @@ -2,8 +2,10 @@ "extends": "./tsconfig", "compilerOptions": { "declaration": true, + "emitDeclarationOnly": true, "allowJs": true, - "paths": {} + "rootDir": "./src", + "skipLibCheck": false }, "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/**/*.spec.ts", "src/**/*.spec.tsx"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf19bcf3..40861970 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,10 +41,10 @@ importers: version: 20.19.43 '@types/react': specifier: ^18.2.14 - version: 18.2.14 + version: 18.3.31 '@types/react-dom': specifier: ^18.2.6 - version: 18.2.6 + version: 18.3.7(@types/react@18.3.31) concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -93,7 +93,7 @@ importers: version: 3.2.0(@types/eslint@9.6.1)(eslint@8.57.0)(prettier@3.3.2)(typescript@5.9.2) '@storybook/addon-docs': specifier: ^9.1.2 - version: 9.1.2(@types/react@18.2.14)(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2)) + version: 9.1.2(@types/react@18.3.31)(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2)) '@storybook/addon-styling-webpack': specifier: ^2.0.0 version: 2.0.0(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2))(webpack@5.101.1(@swc/core@1.13.3)(esbuild@0.25.12)) @@ -117,10 +117,10 @@ importers: version: 20.19.43 '@types/react': specifier: ^18.2.14 - version: 18.2.14 + version: 18.3.31 '@types/react-dom': specifier: ^18.2.6 - version: 18.2.6 + version: 18.3.7(@types/react@18.3.31) '@typescript-eslint/eslint-plugin': specifier: 5.39.0 version: 5.39.0(@typescript-eslint/parser@5.39.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) @@ -160,6 +160,9 @@ importers: '@preact/signals-core': specifier: ^1.12.2 version: 1.12.2 + elkjs: + specifier: ^0.9.3 + version: 0.9.3 intersects: specifier: ^2.7.2 version: 2.7.2 @@ -173,6 +176,9 @@ importers: specifier: ^0.1.1 version: 0.1.1 devDependencies: + '@arethetypeswrong/cli': + specifier: ^0.18.5 + version: 0.18.5 '@commitlint/cli': specifier: ^19.5.0 version: 19.8.1(@types/node@20.19.43)(typescript@5.9.2) @@ -202,7 +208,7 @@ importers: version: 0.2.39(@swc/core@1.13.3) '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/intersects': specifier: ^2.5.0 version: 2.5.0 @@ -219,11 +225,11 @@ importers: specifier: ^3.0.0 version: 3.0.0 '@types/react': - specifier: ^18.2.14 - version: 18.2.14 + specifier: ^18.3.31 + version: 18.3.31 '@types/react-dom': - specifier: ^18.2.6 - version: 18.2.6 + specifier: ^18.3.7 + version: 18.3.7(@types/react@18.3.31) '@typescript-eslint/eslint-plugin': specifier: 5.39.0 version: 5.39.0(@typescript-eslint/parser@5.39.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) @@ -233,18 +239,9 @@ importers: chokidar-cli: specifier: ^3.0.0 version: 3.0.0 - concurrently: - specifier: ^9.2.1 - version: 9.2.1 - copyfiles: - specifier: ^2.4.1 - version: 2.4.1 cross-env: specifier: ^7.0.3 version: 7.0.3 - elkjs: - specifier: ^0.9.3 - version: 0.9.3 esbuild: specifier: ^0.27.2 version: 0.27.2 @@ -281,6 +278,9 @@ importers: prettier: specifier: ^3.0.0 version: 3.3.2 + publint: + specifier: ^0.3.23 + version: 0.3.24 react: specifier: ^18.2.0 version: 18.3.1 @@ -303,6 +303,18 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@andrewbranch/untar.js@1.0.4': + resolution: {integrity: sha512-pVXSwPsLuw8IGLo2Di0EaOfsk+ntVvpkk942J/sHYIkwvtKUakEcPh7HBgZ6tuimgzKSEHgCvO4XgQ05DEbwDw==} + + '@arethetypeswrong/cli@0.18.5': + resolution: {integrity: sha512-gM+8vRsQOD/Uc7EnBedUhkG5OCsDWE4uoak5QvomGpMpaky0Eh41p04nIMgrWb8EOmqZUJGc6zz9hsP6E56R7g==} + engines: {node: '>=20'} + hasBin: true + + '@arethetypeswrong/core@0.18.5': + resolution: {integrity: sha512-9ytjzGwxjm9Uz7I9avfbt5vlQt6uk9uRRESzJjqrznl6WKvI6dwYTo+vJ3U02Wrq/mR3iql/PzhvHhKdJIAjDQ==} + engines: {node: '>=20'} + '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -519,6 +531,13 @@ packages: '@bem-react/classname@1.7.0': resolution: {integrity: sha512-WNZAJEVNHFpQ1eyR3SKxXUDHaXbTyMieFfC65tqEGvGxx9pMcaKf65v/IINdDBe6xIt6WgGu0EHgFQ5KH4lwZQ==} + '@braidai/lang@1.1.2': + resolution: {integrity: sha512-qBcknbBufNHlui137Hft8xauQMTZDKdophmLFv05r2eNmdIv/MlPuP4TdUknHG68UdWLgVZwgxVe735HzJNIwA==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + '@commitlint/cli@19.8.1': resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} engines: {node: '>=v18'} @@ -1162,6 +1181,9 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@loaderkit/resolve@1.0.6': + resolution: {integrity: sha512-G8FdIoF5CypfwmD9rl8BXod5HDn8JqB0CCNBXDTaRZ+yRYhARrrSToX1zg1zy9jX3zLqigsELwhT4gNtkdQAUg==} + '@mdx-js/react@3.0.1': resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} peerDependencies: @@ -1222,9 +1244,17 @@ packages: '@preact/signals-core@1.12.2': resolution: {integrity: sha512-5Yf8h1Ke3SMHr15xl630KtwPTW4sYDFkkxS0vQ8UiQLWwZQnrF9IKaVG1mN5VcJz52EcWs2acsc/Npjha/7ysA==} + '@publint/pack@0.1.7': + resolution: {integrity: sha512-4EDEmvxWtgsCnnVeBvtFIFZtUhPPt1+bA9JrSwU4Sa//6oKtzCSlGGXYJr44OD9aGISymbieJ4mCKHUygUDU+g==} + engines: {node: '>=18'} + '@sinclair/typebox@0.34.38': resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -1557,21 +1587,20 @@ packages: '@types/rbush@3.0.0': resolution: {integrity: sha512-W3ue/GYWXBOpkRm0VSoifrP3HV0Ni47aVJWvXyWMcbtpBy/l/K/smBRiJ+fI8f7shXRjZBiux+iJzYbh7VmcZg==} - '@types/react-dom@18.2.6': - resolution: {integrity: sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==} + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 '@types/react-redux@7.1.34': resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} - '@types/react@18.2.14': - resolution: {integrity: sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==} + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} - '@types/scheduler@0.23.0': - resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} - '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -1943,6 +1972,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-html-community@0.0.8: resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} engines: {'0': node >= 0.8.0} @@ -1960,6 +1993,10 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} + ansi-regex@6.3.0: + resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} + engines: {node: '>=12'} + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -1976,6 +2013,9 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} @@ -2176,6 +2216,10 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} @@ -2221,10 +2265,19 @@ packages: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + cliui@5.0.0: resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} @@ -2262,6 +2315,10 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2299,13 +2356,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cosmiconfig-typescript-loader@6.3.0: resolution: {integrity: sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==} engines: {node: '>=v18'} @@ -2376,8 +2426,8 @@ packages: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -2552,6 +2602,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + endent@2.1.0: resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} @@ -2570,6 +2623,10 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -2862,6 +2919,9 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3053,6 +3113,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -3320,12 +3383,6 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3671,6 +3728,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3698,6 +3759,17 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked-terminal@7.3.0: + resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} + engines: {node: '>=16.0.0'} + peerDependencies: + marked: '>=1 <16' + + marked@9.1.6: + resolution: {integrity: sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==} + engines: {node: '>= 16'} + hasBin: true + mdast-util-find-and-replace@2.2.2: resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} @@ -3874,11 +3946,6 @@ packages: mitata@1.0.34: resolution: {integrity: sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==} - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - monaco-editor@0.52.0: resolution: {integrity: sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==} @@ -3892,6 +3959,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3914,15 +3984,16 @@ packages: node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -4030,6 +4101,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} @@ -4041,6 +4115,15 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -4174,12 +4257,14 @@ packages: resolution: {integrity: sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + publint@0.3.24: + resolution: {integrity: sha512-9zS56KrKBoqi5Qt8h92uMP8TTM9AYZSgnmCo4u2priMqkOZvQnTsziZ2p5LJ2ywbYkAjoCDp2jda9u4cgFefIw==} + engines: {node: '>=18'} + hasBin: true + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4284,12 +4369,6 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -4406,9 +4485,6 @@ packages: resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} engines: {node: '>=0.4'} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -4483,6 +4559,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -4565,12 +4645,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -4631,6 +4705,10 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -4691,8 +4769,12 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -4816,6 +4898,11 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} + typescript@5.6.1-rc: + resolution: {integrity: sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} @@ -4827,6 +4914,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -4857,10 +4948,6 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -4893,6 +4980,10 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + vfile-message@3.1.4: resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} @@ -5027,10 +5118,6 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - y18n@4.0.3: resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} @@ -5091,6 +5178,29 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.30 + '@andrewbranch/untar.js@1.0.4': {} + + '@arethetypeswrong/cli@0.18.5': + dependencies: + '@arethetypeswrong/core': 0.18.5 + chalk: 4.1.2 + cli-table3: 0.6.5 + commander: 10.0.1 + marked: 9.1.6 + marked-terminal: 7.3.0(marked@9.1.6) + semver: 7.7.2 + + '@arethetypeswrong/core@0.18.5': + dependencies: + '@andrewbranch/untar.js': 1.0.4 + '@loaderkit/resolve': 1.0.6 + cjs-module-lexer: 1.4.0 + fflate: 0.8.3 + lru-cache: 11.5.2 + semver: 7.7.2 + typescript: 5.6.1-rc + validate-npm-package-name: 5.0.1 + '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -5347,6 +5457,11 @@ snapshots: '@bem-react/classname@1.7.0': {} + '@braidai/lang@1.1.2': {} + + '@colors/colors@1.5.0': + optional: true + '@commitlint/cli@19.8.1(@types/node@20.19.43)(typescript@5.9.2)': dependencies: '@commitlint/format': 19.8.1 @@ -6025,10 +6140,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@mdx-js/react@3.0.1(@types/react@18.2.14)(react@18.3.1)': + '@loaderkit/resolve@1.0.6': + dependencies: + '@braidai/lang': 1.1.2 + + '@mdx-js/react@3.0.1(@types/react@18.3.31)(react@18.3.1)': dependencies: '@types/mdx': 2.0.13 - '@types/react': 18.2.14 + '@types/react': 18.3.31 react: 18.3.1 '@monaco-editor/loader@1.4.0(monaco-editor@0.52.0)': @@ -6081,8 +6200,14 @@ snapshots: '@preact/signals-core@1.12.2': {} + '@publint/pack@0.1.7': + dependencies: + tinyexec: 1.3.0 + '@sinclair/typebox@0.34.38': {} + '@sindresorhus/is@4.6.0': {} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 @@ -6091,9 +6216,9 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@storybook/addon-docs@9.1.2(@types/react@18.2.14)(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2))': + '@storybook/addon-docs@9.1.2(@types/react@18.3.31)(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2))': dependencies: - '@mdx-js/react': 3.0.1(@types/react@18.2.14)(react@18.3.1) + '@mdx-js/react': 3.0.1(@types/react@18.3.31)(react@18.3.1) '@storybook/csf-plugin': 9.1.2(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2)) '@storybook/icons': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/react-dom-shim': 9.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.2(@testing-library/dom@10.4.1)(prettier@3.3.2)) @@ -6321,15 +6446,15 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.4 '@testing-library/dom': 10.4.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) optionalDependencies: - '@types/react': 18.2.14 - '@types/react-dom': 18.2.6 + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: @@ -6399,9 +6524,9 @@ snapshots: '@types/estree@1.0.8': {} - '@types/hoist-non-react-statics@3.3.7(@types/react@18.2.14)': + '@types/hoist-non-react-statics@3.3.7(@types/react@18.3.31)': dependencies: - '@types/react': 18.2.14 + '@types/react': 18.3.31 hoist-non-react-statics: 3.3.2 '@types/html-minifier-terser@6.1.0': {} @@ -6453,27 +6578,24 @@ snapshots: '@types/rbush@3.0.0': {} - '@types/react-dom@18.2.6': + '@types/react-dom@18.3.7(@types/react@18.3.31)': dependencies: - '@types/react': 18.2.14 + '@types/react': 18.3.31 '@types/react-redux@7.1.34': dependencies: - '@types/hoist-non-react-statics': 3.3.7(@types/react@18.2.14) - '@types/react': 18.2.14 + '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.31) + '@types/react': 18.3.31 hoist-non-react-statics: 3.3.2 redux: 4.2.1 - '@types/react@18.2.14': + '@types/react@18.3.31': dependencies: '@types/prop-types': 15.7.12 - '@types/scheduler': 0.23.0 - csstype: 3.1.3 + csstype: 3.2.3 '@types/resolve@1.20.6': {} - '@types/scheduler@0.23.0': {} - '@types/semver@7.5.8': {} '@types/stack-utils@2.0.3': {} @@ -6875,6 +6997,10 @@ snapshots: dependencies: type-fest: 0.21.3 + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-html-community@0.0.8: {} ansi-regex@4.1.1: {} @@ -6883,6 +7009,8 @@ snapshots: ansi-regex@6.0.1: {} + ansi-regex@6.3.0: {} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -6895,6 +7023,8 @@ snapshots: ansi-styles@6.2.1: {} + any-promise@1.3.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 @@ -7156,6 +7286,8 @@ snapshots: chalk@5.3.0: {} + chalk@5.6.2: {} + char-regex@1.0.2: {} character-entities@2.0.2: {} @@ -7199,8 +7331,23 @@ snapshots: dependencies: restore-cursor: 3.1.0 + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + cli-spinners@2.9.2: {} + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + cliui@5.0.0: dependencies: string-width: 3.1.0 @@ -7239,6 +7386,8 @@ snapshots: colorette@2.0.20: {} + commander@10.0.1: {} + commander@2.20.3: {} commander@8.3.0: {} @@ -7278,18 +7427,6 @@ snapshots: convert-source-map@2.0.0: {} - copyfiles@2.4.1: - dependencies: - glob: 7.2.3 - minimatch: 3.1.2 - mkdirp: 1.0.4 - noms: 0.0.0 - through2: 2.0.5 - untildify: 4.0.0 - yargs: 16.2.0 - - core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@6.3.0(@types/node@20.19.43)(cosmiconfig@9.0.2(typescript@5.9.2))(typescript@5.9.2): dependencies: '@types/node': 20.19.43 @@ -7364,7 +7501,7 @@ snapshots: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 - csstype@3.1.3: {} + csstype@3.2.3: {} damerau-levenshtein@1.0.8: {} @@ -7489,7 +7626,7 @@ snapshots: dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.25.4 - csstype: 3.1.3 + csstype: 3.2.3 dom-serializer@1.4.1: dependencies: @@ -7532,6 +7669,8 @@ snapshots: emoji-regex@9.2.2: {} + emojilib@2.4.0: {} + endent@2.1.0: dependencies: dedent: 0.7.0 @@ -7549,6 +7688,8 @@ snapshots: env-paths@2.2.1: {} + environment@1.1.0: {} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -8089,6 +8230,8 @@ snapshots: dependencies: bser: 2.1.1 + fflate@0.8.3: {} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -8298,6 +8441,8 @@ snapshots: he@1.2.0: {} + highlight.js@10.7.3: {} + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -8535,10 +8680,6 @@ snapshots: dependencies: is-docker: 2.2.1 - isarray@0.0.1: {} - - isarray@1.0.0: {} - isarray@2.0.5: {} isexe@2.0.0: {} @@ -9082,6 +9223,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.5.2: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -9108,6 +9251,19 @@ snapshots: markdown-table@3.0.4: {} + marked-terminal@7.3.0(marked@9.1.6): + dependencies: + ansi-escapes: 7.3.0 + ansi-regex: 6.3.0 + chalk: 5.6.2 + cli-highlight: 2.1.11 + cli-table3: 0.6.5 + marked: 9.1.6 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + + marked@9.1.6: {} + mdast-util-find-and-replace@2.2.2: dependencies: '@types/mdast': 3.0.15 @@ -9432,8 +9588,6 @@ snapshots: mitata@1.0.34: {} - mkdirp@1.0.4: {} - monaco-editor@0.52.0: {} moo-color@1.0.3: @@ -9444,6 +9598,12 @@ snapshots: ms@2.1.3: {} + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + nanoid@3.3.11: {} napi-postinstall@0.3.3: {} @@ -9459,15 +9619,17 @@ snapshots: node-abort-controller@3.1.1: {} + node-emoji@2.2.0: + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + node-int64@0.4.0: {} node-releases@2.0.19: {} - noms@0.0.0: - dependencies: - inherits: 2.0.4 - readable-stream: 1.0.34 - normalize-path@3.0.0: {} npm-run-path@4.0.1: @@ -9592,6 +9754,8 @@ snapshots: package-json-from-dist@1.0.1: {} + package-manager-detector@1.8.0: {} + param-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -9608,6 +9772,14 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -9719,14 +9891,19 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - process-nextick-args@2.0.1: {} - prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 + publint@0.3.24: + dependencies: + '@publint/pack': 0.1.7 + package-manager-detector: 1.8.0 + picocolors: 1.1.1 + sade: 1.8.1 + punycode@2.3.1: {} pure-rand@7.0.1: {} @@ -9844,23 +10021,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - readable-stream@1.0.34: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -9998,8 +10158,6 @@ snapshots: has-symbols: 1.0.3 isarray: 2.0.5 - safe-buffer@5.1.2: {} - safe-buffer@5.2.1: {} safe-regex-test@1.0.3: @@ -10080,6 +10238,10 @@ snapshots: signal-exit@4.1.0: {} + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + slash@3.0.0: {} source-map-js@1.2.1: {} @@ -10201,12 +10363,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - string_decoder@0.10.31: {} - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -10257,6 +10413,11 @@ snapshots: dependencies: has-flag: 4.0.0 + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + supports-preserve-symlinks-flag@1.0.0: {} swc-loader@0.2.6(@swc/core@1.13.3)(webpack@5.101.1(@swc/core@1.13.3)(esbuild@0.25.12)): @@ -10309,10 +10470,13 @@ snapshots: text-table@0.2.0: {} - through2@2.0.5: + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 + any-promise: 1.3.0 through@2.3.8: {} @@ -10438,6 +10602,8 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 + typescript@5.6.1-rc: {} + typescript@5.9.2: {} unbox-primitive@1.0.2: @@ -10449,6 +10615,8 @@ snapshots: undici-types@6.21.0: {} + unicode-emoji-modifier-base@1.0.0: {} + unicorn-magic@0.1.0: {} unified@10.1.2: @@ -10511,8 +10679,6 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - untildify@4.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.2): dependencies: browserslist: 4.25.2 @@ -10546,6 +10712,8 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + validate-npm-package-name@5.0.1: {} + vfile-message@3.1.4: dependencies: '@types/unist': 2.0.11 @@ -10719,8 +10887,6 @@ snapshots: xmlchars@2.2.0: {} - xtend@4.0.2: {} - y18n@4.0.3: {} y18n@5.0.8: {}