diff --git a/.changeset/odd-rocks-burn.md b/.changeset/odd-rocks-burn.md new file mode 100644 index 0000000000..260453ecbf --- /dev/null +++ b/.changeset/odd-rocks-burn.md @@ -0,0 +1,5 @@ +--- +"@inlang/cross-sell-ninja": patch +--- + +create @inlang/cross-sell-ninja diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/README.md b/inlang/source-code/cross-sell/cross-sell-ninja/README.md new file mode 100644 index 0000000000..30e8d51919 --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/README.md @@ -0,0 +1,67 @@ +# Cross-sell Ninja 🥷 package + +## Features + +- **Workflow Verification**: Ensures that the GitHub Actions workflow incorporating the Ninja i18n lint action is present in a project's `.github/workflows` directory. +- **Automated Workflow Addition**: If not present, the package can automatically add a workflow to incorporate Ninja i18n linting into the project. + +## Installation + +Add this package to your `dependencies` in `package.json` & install it using `pnpm install`: + +```bash +"@inlang/cross-sell-ninja": "workspace:*" +``` + +## Usage + +This module exports two main asynchronous functions: + +### `isAdopted(fs: NodeishFilesystem): Promise` + +Verifies if the Ninja i18n GitHub Action is adopted within the GitHub workflow files of your project. + +#### Parameters + +- `fs`: A `NodeishFilesystem` object for file system interactions. + +#### Returns + +- `Promise`: `true` if the Ninja i18n GitHub Action workflow is found, `false` otherwise. + +### `add(fs: NodeishFilesystem): Promise` + +Adds the Ninja i18n GitHub Action workflow to the repository's `.github/workflows` directory if it's not already present. + +#### Parameters + +- `fs`: A `NodeishFilesystem` object for file system interactions. + +## Example + +```typescript +import { isAdopted, add } from '@inlang/cross-sell-ninja'; +import { NodeishFilesystem } from '@lix-js/fs'; + +async function ensureNinjaAdoption(fs: NodeishFilesystem) { + const isWorkflowAdopted = await isAdopted(fs); + + if (!isWorkflowAdopted) { + // Optionally prompt for user confirmation + const userConfirmed = await promptUser("Do you want to add the Ninja i18n workflow?"); + + if (userConfirmed) { + await add(fs); + console.log('Ninja i18n workflow added.'); + } else { + console.log('User declined to add Ninja i18n workflow.'); + } + } else { + console.log('Ninja i18n workflow is already adopted.'); + } +} +``` + +## Contributing + +Contributions are highly appreciated! Whether it's feature requests, bug reports, or pull requests, please feel free to contribute. Check out our [Discord](https://discord.gg/CNPfhWpcAa) for community discussions and updates. \ No newline at end of file diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/package.json b/inlang/source-code/cross-sell/cross-sell-ninja/package.json new file mode 100644 index 0000000000..d5b22fea22 --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/package.json @@ -0,0 +1,35 @@ +{ + "name": "@inlang/cross-sell-ninja", + "description": "A package to cross-sell Ninja", + "version": "0.0.0", + "type": "module", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "./dist", + "./src" + ], + "scripts": { + "build": "tsc --build", + "dev": "tsc --watch", + "test": "tsc --noEmit && vitest run --passWithNoTests --coverage", + "lint": "eslint ./src --fix", + "format": "prettier ./src --write", + "clean": "rm -rf ./dist ./node_modules" + }, + "devDependencies": { + "@inlang/sdk": "workspace:*", + "@lix-js/fs": "workspace:*", + "@types/js-yaml": "^4.0.9", + "@sinclair/typebox": "^0.31.17", + "@vitest/coverage-v8": "0.33.0", + "js-yaml": "^4.1.0", + "patch-package": "6.5.1", + "typescript": "^5.1.3", + "vitest": "0.33.0" + } +} diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/src/index.test.ts b/inlang/source-code/cross-sell/cross-sell-ninja/src/index.test.ts new file mode 100644 index 0000000000..131c0fe170 --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/src/index.test.ts @@ -0,0 +1,194 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import * as yaml from "js-yaml" +import { add, isAdopted } from "./index.js" +import type { NodeishFilesystem } from "@lix-js/fs" + +vi.mock("js-yaml", async () => { + const actual = (await vi.importActual("js-yaml")) as any + return { + ...actual, + load: vi.fn(actual.load), + dump: vi.fn(actual.dump), + } +}) + +describe("GitHub Actions Workflow Adoption Checks", () => { + let fsMock: NodeishFilesystem + + beforeEach(() => { + fsMock = { + exists: vi.fn(), + readdir: vi.fn(), + // @ts-expect-error + readFile: vi.fn(), + stat: vi.fn(), + writeFile: vi.fn(), + mkdir: vi.fn(), + } + }) + + it("detects adoption of Ninja i18n GitHub Action", async () => { + fsMock.exists.mockResolvedValue(true) + // @ts-expect-error + fsMock.readdir.mockResolvedValue(["ninja_i18n.yml"]) + // @ts-expect-error + fsMock.readFile.mockResolvedValue( + yaml.dump({ + name: "Ninja i18n action", + on: { pull_request_target: {} }, + jobs: { + "ninja-i18n": { + name: "Ninja i18n - GitHub Lint Action", + "runs-on": "ubuntu-latest", + steps: [ + { + name: "Checkout", + id: "checkout", + uses: "actions/checkout@v4", + }, + { + name: "Run Ninja i18n", + id: "ninja-i18n", + uses: "opral/ninja-i18n-action@main", + env: { + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}", + }, + }, + ], + }, + }, + }) + ) + // @ts-expect-error + fsMock.stat.mockResolvedValue({ isDirectory: () => false }) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(true) + }) + + it("correctly adds the Ninja i18n GitHub Action workflow", async () => { + fsMock.exists.mockResolvedValue(false) + + await add({ fs: fsMock }) + + expect(fsMock.mkdir).toHaveBeenCalledWith(".github/workflows", { recursive: true }) + // @ts-expect-error + const writtenContent = fsMock.writeFile.mock.calls[0][1] + expect(writtenContent).toContain("name: Ninja i18n action") + expect(writtenContent).toContain("uses: opral/ninja-i18n-action@main") + }) + + it("does not find the action in deep nested directories beyond level 3", async () => { + // Simulate deep directory structure + fsMock.exists.mockResolvedValue(true) + // @ts-expect-error + fsMock.readdir.mockImplementation((path) => { + if (path.endsWith("level1")) return Promise.resolve(["level2"]) + if (path.endsWith("level2")) return Promise.resolve(["level3"]) + if (path.endsWith("level3")) return Promise.resolve([]) + return Promise.resolve(["level1"]) + }) + // @ts-expect-error + fsMock.stat.mockImplementation((path) => + Promise.resolve({ + isDirectory: () => path.includes("level"), + }) + ) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(false) + }) + + it("does not search beyond a depth of 3", async () => { + // @ts-expect-error + fsMock.readdir.mockImplementation((path) => { + // Implement logic to simulate depth, based on the path argument + if (path.endsWith("level3")) return Promise.resolve(["tooDeepDirectory"]) + return Promise.resolve(["level1"]) + }) + // @ts-expect-error + fsMock.stat.mockImplementation((path) => + Promise.resolve({ + isDirectory: () => !path.endsWith(".yml"), + }) + ) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(false) + // Ensure readdir was called the correct number of times to validate depth control + }) + + it("returns false if checking directory existence throws an error", async () => { + fsMock.exists.mockRejectedValue(new Error("Filesystem error")) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(false) + }) + + it("returns true when the action is found in a nested directory within depth limit", async () => { + fsMock.exists.mockResolvedValue(true) + // @ts-expect-error + fsMock.readdir.mockImplementation((path) => { + if (path === ".github/workflows") return Promise.resolve(["level1"]) + if (path === ".github/workflows/level1") return Promise.resolve(["level2"]) + if (path === ".github/workflows/level1/level2") return Promise.resolve(["ninja_i18n.yml"]) + return Promise.resolve([]) + }) + + // @ts-expect-error + fsMock.readFile.mockImplementation((path) => { + if (path === ".github/workflows/level1/level2/ninja_i18n.yml") { + return Promise.resolve( + yaml.dump({ + name: "Ninja i18n action", + on: { pull_request_target: {} }, + jobs: { + "ninja-i18n": { + "runs-on": "ubuntu-latest", + steps: [ + { + name: "Run Ninja i18n", + uses: "opral/ninja-i18n-action@main", + env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }, + }, + ], + }, + }, + }) + ) + } + return Promise.reject(new Error("File not found")) + }) + + // @ts-expect-error + fsMock.stat.mockImplementation((path) => + Promise.resolve({ + isDirectory: () => !path.endsWith(".yml"), + }) + ) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(true) + }) + + it("returns false and logs an error for malformed YAML content", async () => { + fsMock.exists.mockResolvedValue(true) + // @ts-expect-error + fsMock.readdir.mockResolvedValue(["ninja_i18n.yml"]) + // @ts-expect-error + fsMock.readFile.mockResolvedValue("malformed yaml content") + // @ts-expect-error + fsMock.stat.mockResolvedValue({ isDirectory: () => false }) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(false) + }) + + it("handles filesystem errors gracefully in isAdopted function", async () => { + fsMock.exists.mockRejectedValue(new Error("Filesystem error")) + + await expect(isAdopted({ fs: fsMock })).resolves.toBe(false) + }) + + it("handles errors when creating the workflow directory in add function", async () => { + fsMock.exists.mockResolvedValue(false) + // @ts-expect-error + fsMock.mkdir.mockRejectedValue(new Error("Filesystem error")) + + await expect(add({ fs: fsMock })).rejects.toThrow("Filesystem error") + }) +}) diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/src/index.ts b/inlang/source-code/cross-sell/cross-sell-ninja/src/index.ts new file mode 100644 index 0000000000..512676d44f --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/src/index.ts @@ -0,0 +1,110 @@ +import type { NodeishFilesystem } from "@lix-js/fs" +import { type Static } from "@sinclair/typebox" +import { Value } from "@sinclair/typebox/value" +import * as yaml from "js-yaml" // Assuming you have a YAML parsing library +import { GitHubActionsWorkflow } from "./types.js" + +/** + * Checks if the Ninja i18n GitHub Action is adopted within the GitHub workflow files. + * @param {Object} args - The arguments object. + * @param {NodeishFilesystem} args.fs - The filesystem to use for operations. + * @returns {Promise} - A promise that resolves to true if the action is adopted, otherwise false. + */ +export async function isAdopted(args: { fs: NodeishFilesystem }): Promise { + // Helper function for recursive search + async function searchWorkflowFiles(directoryPath: string, depth: number = 0): Promise { + if (depth > 3) { + // Limit recursion depth to 3 levels + return false + } + + try { + await args.fs.exists(directoryPath) + } catch (error) { + return false + } + + const items = await args.fs.readdir(directoryPath) + for (const item of items) { + const itemPath = `${directoryPath}/${item}` + const stats = await args.fs.stat(itemPath) + if (stats.isDirectory()) { + // Recursive call to search within the directory + if (await searchWorkflowFiles(itemPath, depth + 1)) { + return true // Found in a deeper level + } + } else if (item.endsWith(".yml") || item.endsWith(".yaml")) { + const fileContents = await args.fs.readFile(itemPath, { encoding: "utf-8" }) + const workflow = yaml.load(fileContents) as Static + if (Value.Check(GitHubActionsWorkflow, workflow)) { + if (workflow && workflow.jobs) { + for (const jobKey in workflow.jobs) { + const job = workflow.jobs[jobKey] + if (job && job.steps) { + for (const step of job.steps) { + if (step.uses && step.uses.includes("opral/ninja-i18n-action")) { + return true // Found the action + } + } + } + } + } + } else { + // Ignore invalid YAML files + console.error("Failed to parse YAML", [...Value.Errors(GitHubActionsWorkflow, workflow)]) + return false // Malformed YAML should result in a negative check + } + } + } + + // Not found at this level or any deeper level + return false + } + + // Start the search from the workflow directory + return await searchWorkflowFiles(".github/workflows") +} + +/** + * Adds the Ninja i18n GitHub Action workflow to the repository. + * @param {Object} args - The arguments object. + * @param {NodeishFilesystem} args.fs - The filesystem to use for operations. + * @returns {Promise} - A promise that resolves when the action workflow is successfully added. + */ +export async function add(args: { fs: NodeishFilesystem }): Promise { + const workflowDirPath = ".github/workflows" + const workflowFilePath = `${workflowDirPath}/ninja_i18n.yml` + const ninjaI18nWorkflowYaml = ` +name: Ninja i18n action + +on: + pull_request_target: + +permissions: + pull-requests: write + +jobs: + ninja-i18n: + name: Ninja i18n - GitHub Lint Action + runs-on: ubuntu-latest + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Run Ninja i18n + id: ninja-i18n + uses: opral/ninja-i18n-action@main + env: + GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }} + ` + + // Ensure the workflow directory exists + if (!(await args.fs.exists(workflowDirPath))) { + await args.fs.mkdir(workflowDirPath, { recursive: true }) + } + + // Write the Ninja i18n workflow YAML to the file + await args.fs.writeFile(workflowFilePath, ninjaI18nWorkflowYaml) +} diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/src/types.ts b/inlang/source-code/cross-sell/cross-sell-ninja/src/types.ts new file mode 100644 index 0000000000..b369209179 --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/src/types.ts @@ -0,0 +1,57 @@ +import { Type, type Static } from "@sinclair/typebox" + +const GitHubActionStep = Type.Object({ + name: Type.Optional(Type.String()), + id: Type.Optional(Type.String()), + uses: Type.Optional(Type.String()), + with: Type.Optional(Type.Record(Type.String(), Type.String())), + env: Type.Optional(Type.Record(Type.String(), Type.String())), +}) + +const GitHubActionJob = Type.Object({ + name: Type.Optional(Type.String()), + "runs-on": Type.String(), + steps: Type.Array(GitHubActionStep), +}) + +const GitHubActionEvent = Type.Object({ + pull_request_target: Type.Optional(Type.Object({})), + // Define other events as necessary, ensuring correct property names +}) + +export const GitHubActionsWorkflow = Type.Object({ + name: Type.String(), + on: GitHubActionEvent, + jobs: Type.Record(Type.String(), GitHubActionJob), + // Use Record to define an object with string keys and GitHubActionJob values +}) + +// Example usage +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const ninjaI18nWorkflow: Static = { + name: "Ninja i18n action", + on: { + pull_request_target: {}, + }, + jobs: { + "ninja-i18n": { + name: "Ninja i18n - GitHub Lint Action", + "runs-on": "ubuntu-latest", + steps: [ + { + name: "Checkout", + id: "checkout", + uses: "actions/checkout@v4", + }, + { + name: "Run Ninja i18n", + id: "ninja-i18n", + uses: "opral/ninja-i18n-action@main", + env: { + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}", + }, + }, + ], + }, + }, +} diff --git a/inlang/source-code/cross-sell/cross-sell-ninja/tsconfig.json b/inlang/source-code/cross-sell/cross-sell-ninja/tsconfig.json new file mode 100644 index 0000000000..f135cfd1a0 --- /dev/null +++ b/inlang/source-code/cross-sell/cross-sell-ninja/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/*", "./build.js"], + "compilerOptions": { + "resolveJsonModule": true, + "outDir": "./dist", + "rootDir": "./src" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e87a967509..31c3b3cdfc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,6 +201,36 @@ importers: specifier: 0.34.6 version: 0.34.6(jsdom@22.1.0) + inlang/source-code/cross-sell/cross-sell-ninja: + devDependencies: + '@inlang/sdk': + specifier: workspace:* + version: link:../../sdk + '@lix-js/fs': + specifier: workspace:* + version: link:../../../../lix/source-code/fs + '@sinclair/typebox': + specifier: ^0.31.17 + version: 0.31.28 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + '@vitest/coverage-v8': + specifier: 0.34.6 + version: 0.34.6(vitest@0.34.6) + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + patch-package: + specifier: 6.5.1 + version: 6.5.1 + typescript: + specifier: ^5.1.3 + version: 5.3.3 + vitest: + specifier: 0.34.6 + version: 0.34.6(jsdom@22.1.0) + inlang/source-code/cross-sell/cross-sell-sherlock: devDependencies: '@inlang/sdk': @@ -7417,6 +7447,7 @@ packages: resolution: {integrity: sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==} engines: {node: '>=16.3.0'} hasBin: true + requiresBuild: true peerDependencies: typescript: '>= 4.7.4' peerDependenciesMeta: @@ -10243,6 +10274,10 @@ packages: '@types/istanbul-lib-report': 3.0.3 dev: true + /@types/js-yaml@4.0.9: + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -11632,6 +11667,7 @@ packages: /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + requiresBuild: true dependencies: event-target-shim: 5.0.1 dev: true @@ -11870,6 +11906,7 @@ packages: /archiver-utils@5.0.2: resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} engines: {node: '>= 14'} + requiresBuild: true dependencies: glob: 10.3.10 graceful-fs: 4.2.11 @@ -11896,6 +11933,7 @@ packages: /archiver@7.0.1: resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} engines: {node: '>= 14'} + requiresBuild: true dependencies: archiver-utils: 5.0.2 async: 3.2.5 @@ -12346,6 +12384,7 @@ packages: /b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + requiresBuild: true /babel-core@7.0.0-bridge.0(@babel/core@7.24.1): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} @@ -12821,6 +12860,7 @@ packages: /buffer-crc32@1.0.0: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} + requiresBuild: true dev: true /buffer-equal-constant-time@1.0.1: @@ -13160,6 +13200,7 @@ packages: /chromium-bidi@0.4.16(devtools-protocol@0.0.1147663): resolution: {integrity: sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==} + requiresBuild: true peerDependencies: devtools-protocol: '*' dependencies: @@ -13472,6 +13513,7 @@ packages: /compress-commons@6.0.2: resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} engines: {node: '>= 14'} + requiresBuild: true dependencies: crc-32: 1.2.2 crc32-stream: 6.0.0 @@ -13636,6 +13678,7 @@ packages: /crc32-stream@6.0.0: resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} engines: {node: '>= 14'} + requiresBuild: true dependencies: crc-32: 1.2.2 readable-stream: 4.5.2 @@ -13683,6 +13726,7 @@ packages: /cross-fetch@4.0.0: resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + requiresBuild: true dependencies: node-fetch: 2.7.0 transitivePeerDependencies: @@ -13777,6 +13821,7 @@ packages: /css-shorthand-properties@1.1.1: resolution: {integrity: sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==} + requiresBuild: true dev: true /css-to-react-native@3.2.0: @@ -13796,6 +13841,7 @@ packages: /css-value@0.0.1: resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} + requiresBuild: true dev: true /css-what@6.1.0: @@ -14270,10 +14316,12 @@ packages: /devtools-protocol@0.0.1147663: resolution: {integrity: sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==} + requiresBuild: true dev: true /devtools-protocol@0.0.1263784: resolution: {integrity: sha512-k0SCZMwj587w4F8QYbP5iIbSonL6sd3q8aVJch036r9Tv2t9b5/Oq7AiJ/FJvRuORm/pJNXZtrdNNWlpRnl56A==} + requiresBuild: true dev: true /didyoumean@1.2.2: @@ -15562,6 +15610,7 @@ packages: /event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + requiresBuild: true dev: true /eventemitter3@4.0.7: @@ -15776,6 +15825,7 @@ packages: /fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + requiresBuild: true dev: true /fast-deep-equal@3.1.3: @@ -15788,6 +15838,7 @@ packages: /fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + requiresBuild: true /fast-glob@3.2.7: resolution: {integrity: sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==} @@ -18726,6 +18777,7 @@ packages: /lodash.zip@4.2.0: resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + requiresBuild: true dev: true /lodash@4.17.21: @@ -20138,6 +20190,7 @@ packages: /mitt@3.0.0: resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==} + requiresBuild: true dev: true /mixme@0.5.10: @@ -22094,6 +22147,7 @@ packages: /proxy-agent@6.3.0: resolution: {integrity: sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==} engines: {node: '>= 14'} + requiresBuild: true dependencies: agent-base: 7.1.0 debug: 4.3.4(supports-color@8.1.1) @@ -22200,6 +22254,7 @@ packages: /puppeteer-core@20.9.0(typescript@5.3.2): resolution: {integrity: sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==} engines: {node: '>=16.3.0'} + requiresBuild: true peerDependencies: typescript: '>= 4.7.4' peerDependenciesMeta: @@ -22247,6 +22302,7 @@ packages: /query-selector-shadow-dom@1.0.1: resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + requiresBuild: true dev: true /query-string@5.1.1: @@ -22270,6 +22326,7 @@ packages: /queue-tick@1.0.1: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + requiresBuild: true /quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -22937,6 +22994,7 @@ packages: /resq@1.11.0: resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} + requiresBuild: true dependencies: fast-deep-equal: 2.0.1 dev: true @@ -23007,6 +23065,7 @@ packages: /rgb2hex@0.2.5: resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} + requiresBuild: true dev: true /rimraf@2.6.3: @@ -23323,6 +23382,7 @@ packages: /serialize-error@11.0.3: resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==} engines: {node: '>=14.16'} + requiresBuild: true dependencies: type-fest: 2.19.0 dev: true @@ -23954,6 +24014,7 @@ packages: /streamx@2.16.1: resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==} + requiresBuild: true dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -27054,6 +27115,7 @@ packages: /ws@8.13.0: resolution: {integrity: sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==} engines: {node: '>=10.0.0'} + requiresBuild: true peerDependencies: bufferutil: ^4.0.1 utf-8-validate: '>=5.0.2' @@ -27193,6 +27255,7 @@ packages: /yargs@17.7.1: resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} engines: {node: '>=12'} + requiresBuild: true dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -27257,6 +27320,7 @@ packages: /zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} + requiresBuild: true dependencies: archiver-utils: 5.0.2 compress-commons: 6.0.2