├── .gitignore
├── postcss.config.js
├── public
├── icons
│ ├── icon128.png
│ ├── icon16.png
│ ├── icon32.png
│ ├── icon48.png
│ └── icon96.png
├── manifest.json
├── popup.html
└── options.html
├── src
├── utils
│ └── tabs.ts
├── popup.ts
├── actions
│ ├── closeBlankPages.ts
│ ├── deduplicateTabs.ts
│ ├── groupDomains.ts
│ ├── organizeViaAi.ts
│ └── sortTabs.ts
├── main.css
├── background.ts
├── options.ts
├── categories.ts
└── openai.ts
├── .editorconfig
├── tailwind.config.js
├── tsconfig.json
├── package.json
├── LICENSE
├── README.md
├── tsup.config.ts
├── .github
└── workflows
│ └── release.yml
└── pnpm-lock.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 |
4 | /node_modules
5 | /dist
6 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/public/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spaceemotion/ai-tab-organizer/HEAD/public/icons/icon128.png
--------------------------------------------------------------------------------
/public/icons/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spaceemotion/ai-tab-organizer/HEAD/public/icons/icon16.png
--------------------------------------------------------------------------------
/public/icons/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spaceemotion/ai-tab-organizer/HEAD/public/icons/icon32.png
--------------------------------------------------------------------------------
/public/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spaceemotion/ai-tab-organizer/HEAD/public/icons/icon48.png
--------------------------------------------------------------------------------
/public/icons/icon96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spaceemotion/ai-tab-organizer/HEAD/public/icons/icon96.png
--------------------------------------------------------------------------------
/src/utils/tabs.ts:
--------------------------------------------------------------------------------
1 | export const mapTabIds = (tabs: { id?: number | undefined }[]) => (
2 | tabs
3 | .map((tab) => tab.id)
4 | .filter((id): id is number => !!id)
5 | );
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | charset = utf-8
7 |
8 | [*.{js,ts,vue,css,html}]
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | './public/*.html',
5 | ],
6 | theme: {
7 | extend: {
8 | fontFamily: {
9 | 'sans': ['Inter', 'sans-serif'],
10 | },
11 | },
12 | },
13 | plugins: [],
14 | }
15 |
--------------------------------------------------------------------------------
/src/popup.ts:
--------------------------------------------------------------------------------
1 | const registerAction = (id: string) => {
2 | document.getElementById(id)?.addEventListener('click', () => {
3 | chrome.runtime.sendMessage({ action: id });
4 | });
5 | };
6 |
7 | registerAction('action-ai');
8 | registerAction('action-deduplicate');
9 | registerAction('action-closeblanks');
10 | registerAction('action-sort');
11 | registerAction('action-group-tlds');
12 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "module": "esnext",
5 | "target": "ES2022",
6 | "allowJs": true,
7 | "esModuleInterop": true,
8 | "jsx": "react-jsx",
9 | "noEmit": true,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "moduleResolution": "node",
13 | "baseUrl": ".",
14 | },
15 | "exclude": ["node_modules", "build", "dist"]
16 | }
17 |
--------------------------------------------------------------------------------
/src/actions/closeBlankPages.ts:
--------------------------------------------------------------------------------
1 | import { mapTabIds } from "../utils/tabs";
2 |
3 | export default async function closeBlankPages() {
4 | const tabs = await chrome.tabs.query({});
5 | const blankTabs = tabs.filter((tab) => (
6 | tab.url === 'chrome://newtab/' ||
7 | tab.url === 'about:blank' ||
8 | tab.url === 'edge://newtab/'
9 | ));
10 |
11 | console.log(
12 | `Found ${blankTabs.length} blank tabs, closing`,
13 | blankTabs.map((tab) => tab.url),
14 | );
15 |
16 | chrome.tabs.remove(mapTabIds(blankTabs));
17 | }
18 |
--------------------------------------------------------------------------------
/src/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer components {
6 | .form-button {
7 | @apply px-4 py-2 text-sm font-medium text-white bg-zinc-700 border border-zinc-800 rounded shadow hover:bg-zinc-800 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-zinc-500;
8 | }
9 |
10 | .form-input {
11 | @apply block w-full px-3 py-2 text-sm border bg-white border-zinc-400 rounded shadow-sm focus:outline-none focus:ring-zinc-500 focus:border-zinc-500;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ai-tab-organizer",
3 | "version": "1.4",
4 | "scripts": {
5 | "build": "tsup",
6 | "watch": "tsup -- --dev"
7 | },
8 | "dependencies": {
9 | "ky": "^0.33.3",
10 | "lodash-es": "^4.17.21",
11 | "zod": "^3.21.4",
12 | "zod-to-json-schema": "^3.21.4"
13 | },
14 | "devDependencies": {
15 | "@types/chrome": "^0.0.243",
16 | "@types/lodash-es": "^4.17.8",
17 | "@types/node": "^20.4.8",
18 | "tailwindcss": "^3.3.3",
19 | "tsup": "^7.2.0",
20 | "typescript": "^5.1.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/actions/deduplicateTabs.ts:
--------------------------------------------------------------------------------
1 | import { mapTabIds } from "../utils/tabs";
2 |
3 | // Keep these URLs open, as they don't keep their state in the URl
4 | const keepUrlsRegex = /(chat\.openai\.com)/i;
5 |
6 | export default async function deduplicateTabs() {
7 | const tabs = await chrome.tabs.query({});
8 | const urls = tabs.map((tab) => tab.url);
9 |
10 | const duplicates = urls.filter((url, index) => urls.indexOf(url) !== index);
11 | const duplicateTabs = tabs.filter((tab) => (
12 | duplicates.includes(tab.url) && !keepUrlsRegex.test(tab.url!)
13 | ));
14 |
15 | console.log(
16 | `Found ${duplicateTabs.length} duplicate tabs, closing`,
17 | duplicateTabs.map((tab) => tab.url),
18 | );
19 |
20 | chrome.tabs.remove(mapTabIds(duplicateTabs));
21 | }
22 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "manifest_version": 3,
4 | "author": "spaceemotion",
5 | "name": "AI Tab Organizer",
6 | "description": "Use AI to organize all your tabs into categorized groups!",
7 | "homepage_url": "https://github.com/spaceemotion/ai-tab-organizer",
8 | "version": "1.4",
9 | "permissions": [
10 | "tabs",
11 | "tabGroups",
12 | "storage"
13 | ],
14 | "incognito": "split",
15 | "background": {
16 | "service_worker": "background.js"
17 | },
18 | "action": {
19 | "default_popup": "popup.html"
20 | },
21 | "options_ui": {
22 | "page": "options.html",
23 | "open_in_tab": false
24 | },
25 | "host_permissions": ["https://api.openai.com/*"],
26 | "icons": {
27 | "16": "icons/icon16.png",
28 | "32": "icons/icon32.png",
29 | "48": "icons/icon48.png",
30 | "128": "icons/icon128.png"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/public/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AI Tab Organizer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/background.ts:
--------------------------------------------------------------------------------
1 |
2 | import deduplicateTabs from './actions/deduplicateTabs';
3 | import closeBlankPages from './actions/closeBlankPages';
4 | import organizeViaAi from './actions/organizeViaAi';
5 | import sortTabs from './actions/sortTabs';
6 | import groupDomains from './actions/groupDomains';
7 |
8 | enum Actions {
9 | CloseBlanks = 'action-closeblanks',
10 | Deduplicate = 'action-deduplicate',
11 | Ai = 'action-ai',
12 | Sort = 'action-sort',
13 | GroupTLDs = 'action-group-tlds',
14 | }
15 |
16 | const actionMap: Record = {
17 | [Actions.CloseBlanks]: closeBlankPages,
18 | [Actions.Deduplicate]: deduplicateTabs,
19 | [Actions.Ai]: organizeViaAi,
20 | [Actions.Sort]: sortTabs,
21 | [Actions.GroupTLDs]: groupDomains,
22 | };
23 |
24 | chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
25 | if (!message.action) {
26 | return;
27 | }
28 |
29 | await actionMap[message.action as Actions]?.();
30 | });
31 |
--------------------------------------------------------------------------------
/public/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AI Tab Organizer Extension Options
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/options.ts:
--------------------------------------------------------------------------------
1 | const getAPIKeyElement = () => document.getElementById('api-key') as HTMLInputElement;
2 | const getAIModelElement = () => document.getElementById('ai-model') as HTMLInputElement;
3 |
4 | // Saves options to chrome.storage
5 | const saveOptions = async () => {
6 | const key = getAPIKeyElement().value;
7 | const model = getAIModelElement().value;
8 |
9 | await chrome.storage.sync.set({
10 | apiKey: key,
11 | aiModel: model,
12 | });
13 | };
14 |
15 | // Restores select box and checkbox state using the preferences
16 | // stored in chrome.storage.
17 | const restoreOptions = async () => {
18 | const options = await chrome.storage.sync.get({
19 | apiKey: '',
20 | aiModel: '',
21 | });
22 |
23 | getAPIKeyElement().value = options.apiKey;
24 | getAIModelElement().value = options.aiModel;
25 | };
26 |
27 | document.addEventListener('DOMContentLoaded', restoreOptions);
28 | document.getElementById('save')?.addEventListener('click', saveOptions);
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 spaceemotion
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # AI Tab Organizer
4 | Organizes all tabs across windows into AI-generated groups. Uses your own OpenAI API.
5 |
6 | > [!NOTE]
7 | > **This is a fun, working experiment with Chrome and OpenAI.**
8 | > Not only is the OpenAI API kind of slow, it sometimes groups tabs in a weird way.
9 | > Use at your own risk.
10 |
11 | ## Features
12 | - **AI Organize:** Organizes all tabs across windows into AI-generated groups
13 | - **Deduplicate Tabs:** Closes all duplicate tabs
14 | - **Close Blank Pages:** Closes all tabs that are blank pages (e.g. `about:blank`)
15 | - **Sort Tabs by URL:** Sorts all tabs by URL, even inside groups
16 | - **Group Tabs by URL:** Groups all tabs by hostname when they have a minimum of 10 tabs
17 |
18 | ## Local Install
19 | 1. Download `.zip` file from https://github.com/spaceemotion/ai-tab-organizer/releases/latest
20 | 2. Unpack to a folder of your choosing
21 | 3. Enable developer mode in Chrome/Edge
22 | 4. Load from unpacked extension
23 |
24 | ## Settings
25 | Open up the extension settings to enter your [OpenAI API Key]
26 | or specify the [AI model] to be used.
27 |
28 | [OpenAI API Key]: "Generate a new API token"
29 | [AI model]: "View all available models"
30 |
--------------------------------------------------------------------------------
/tsup.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'tsup'
2 | import { existsSync, statSync, mkdirSync, readdirSync, copyFileSync } from "node:fs";
3 | import { join } from 'node:path';
4 |
5 | const isDev = process.argv.includes('--dev');
6 |
7 | function copyRecursiveSync(src: string, dest: string) {
8 | const exists = existsSync(src);
9 | const stats = exists && statSync(src);
10 | const isDirectory = stats && stats.isDirectory();
11 |
12 | if (exists && isDirectory) {
13 | mkdirSync(dest, { recursive: true });
14 | readdirSync(src).forEach(childItemName => {
15 | copyRecursiveSync(join(src, childItemName), join(dest, childItemName));
16 | });
17 | } else {
18 | copyFileSync(src, dest);
19 | }
20 | }
21 |
22 | export default defineConfig({
23 | entry: [
24 | 'src/background.ts',
25 | 'src/popup.ts',
26 | 'src/options.ts',
27 | 'src/main.css',
28 | ],
29 |
30 | watch: isDev,
31 |
32 | publicDir: 'public',
33 |
34 | noExternal: [
35 | 'zod',
36 | 'zod-to-json-schema',
37 | 'lodash-es',
38 | 'ky',
39 | ],
40 |
41 | platform: 'browser',
42 |
43 | treeshake: true,
44 | splitting: false,
45 | clean: true,
46 |
47 | sourcemap: false,
48 | minify: !isDev,
49 |
50 |
51 | // WORKAROUND for https://github.com/egoist/tsup/issues/831
52 | onSuccess: isDev ? async () => {
53 | copyRecursiveSync('public', 'dist');
54 | } : undefined,
55 | })
56 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | jobs:
9 | build:
10 | name: Publish release artifact
11 | runs-on: ubuntu-latest
12 |
13 | permissions: write-all
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v3
18 |
19 | - name: Install Node.js
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: 20
23 |
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v2
26 | with:
27 | version: 8
28 | run_install: false
29 |
30 | - name: Get pnpm store directory
31 | shell: bash
32 | run: |
33 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
34 |
35 | - uses: actions/cache@v3
36 | name: Setup pnpm cache
37 | with:
38 | path: ${{ env.STORE_PATH }}
39 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
40 | restore-keys: |
41 | ${{ runner.os }}-pnpm-store-
42 |
43 | - name: Install dependencies
44 | run: pnpm install
45 |
46 | - name: Build extension
47 | run: pnpm run build
48 |
49 | - name: Archive Release
50 | uses: thedoctor0/zip-release@0.7.1
51 | with:
52 | type: 'zip'
53 | filename: 'release.zip'
54 | directory: 'dist'
55 |
56 | - name: Upload Release
57 | uses: ncipollo/release-action@v1.12.0
58 | with:
59 | artifacts: "dist/release.zip"
60 | generateReleaseNotes: true
61 |
--------------------------------------------------------------------------------
/src/actions/groupDomains.ts:
--------------------------------------------------------------------------------
1 | import { organizeTabs } from "src/categories";
2 |
3 | export default async function groupDomains({
4 | minTabs = 10,
5 | }: {
6 | minTabs?: number
7 | } = {}) {
8 | // Get all tabs we care about
9 | const ungroupedTabs = await chrome.tabs.query({
10 | pinned: false,
11 | groupId: chrome.tabGroups.TAB_GROUP_ID_NONE,
12 | });
13 |
14 | console.log(`Found ${ungroupedTabs.length} tabs`);
15 |
16 | // Build a map of domains to tabs, group them by TLD (e.g. example.com)
17 | const domainMap = new Map();
18 |
19 | for (const tab of ungroupedTabs) {
20 | const url = new URL(tab.url!);
21 | const domain = url.hostname
22 | .replace(/^www\./, ''); // Remove common prefixes
23 |
24 | if (!domain) {
25 | // Might be a local file
26 | continue;
27 | }
28 |
29 | if (!domainMap.has(domain)) {
30 | domainMap.set(domain, []);
31 | }
32 |
33 | domainMap.get(domain)!.push(tab);
34 | }
35 |
36 | console.log(`Found ${domainMap.size} domains`);
37 |
38 | const domains = Array.from(domainMap.entries())
39 | // Filter out domains that don't have enough tabs
40 | .filter(([_, tabs]) => tabs.length >= minTabs)
41 | // Map to domain name and tab IDs
42 | .map(([domain, tabs]) => [domain, tabs.map((tab) => tab.id)] as [string, number[]])
43 | // Sort by domain name
44 | .sort((a, b) => a[0].localeCompare(b[0]));
45 |
46 | console.log(`Found ${domains.length} domains with at least ${minTabs} tabs`);
47 |
48 | // Create a group for each domain, but in the current window
49 | await organizeTabs(
50 | Object.fromEntries(domains),
51 | await chrome.windows.getCurrent(),
52 | );
53 | };
54 |
--------------------------------------------------------------------------------
/src/actions/organizeViaAi.ts:
--------------------------------------------------------------------------------
1 | import { chunk } from 'lodash-es';
2 |
3 | import { collectTabs, mergeCategories, organizeTabs } from '../categories';
4 | import { analyzeTabs } from '../openai';
5 |
6 | export default async function organizeViaAi() {
7 | const { apiKey, aiModel } = await chrome.storage.sync.get({
8 | apiKey: '',
9 | aiModel: '',
10 | });
11 |
12 | if (!apiKey) {
13 | console.error('No API key found');
14 | return;
15 | }
16 |
17 | const tabs = await collectTabs();
18 | const chunks = chunk(tabs, 60);
19 |
20 | const categories = await Promise.all(
21 | chunks.map((chunk) => analyzeTabs({
22 | model: aiModel,
23 | apiKey,
24 | }, chunk)),
25 | );
26 |
27 | console.log("Got all results, now merging");
28 |
29 | const allMatchedTabIds = categories.flatMap((category) => category.tabs);
30 | const allTabIds = tabs.map((tab) => tab.id);
31 |
32 | // Make sure the returned Tab IDs are actually valid
33 | const invalidTabIds = allMatchedTabIds.filter((id) => !allTabIds.includes(id));
34 |
35 | if (invalidTabIds.length > 0) {
36 | console.warn(`Got invalid Tab IDs: ${invalidTabIds.join(', ')}`);
37 | categories.push({
38 | "Other": invalidTabIds,
39 | });
40 | }
41 |
42 | // Find out if we have any tabs that have not been categorized
43 | const leftovers = allTabIds.filter((id) => !allMatchedTabIds.includes(id));
44 |
45 | if (leftovers.length > 0) {
46 | console.warn(`Got leftover Tab IDs: ${leftovers.join(', ')}`);
47 | categories.push({
48 | "Other": leftovers,
49 | });
50 | }
51 |
52 | // Merge shared categories
53 | const merged = mergeCategories(categories);
54 | console.log(merged);
55 |
56 | organizeTabs(merged);
57 | console.log("Done");
58 | };
59 |
--------------------------------------------------------------------------------
/src/categories.ts:
--------------------------------------------------------------------------------
1 | export interface TabItem {
2 | id: number;
3 | title: string;
4 | domain: string;
5 | }
6 |
7 | const extractTabInfo = (tab: chrome.tabs.Tab): TabItem => {
8 | const url = new URL(tab.url ?? '');
9 |
10 | return {
11 | id: tab.id ?? -1,
12 | title: tab.title ?? '',
13 | domain: url.hostname.replace(/^www\./, ''),
14 | };
15 | };
16 |
17 | export const collectTabs = async (): Promise => {
18 | return new Promise((resolve) => {
19 | chrome.tabs.query({}, (tabs) => {
20 | resolve(tabs.map(extractTabInfo));
21 | });
22 | });
23 | };
24 |
25 | export const mergeCategories = (categories: Record[]): Record => {
26 | const merged: Record = {};
27 |
28 | for (const category of categories) {
29 | for (const [name, tabs] of Object.entries(category)) {
30 | const validIds = tabs.filter((id) => Number.isSafeInteger(id) && id >= 0);
31 |
32 | if (validIds.length === 0) {
33 | continue;
34 | }
35 |
36 | if (!merged[name]) {
37 | merged[name] = validIds;
38 | } else {
39 | merged[name].push(...validIds);
40 | }
41 | }
42 | }
43 |
44 | return merged;
45 | };
46 |
47 | export const organizeTabs = async (categorizedTabs: Record, window?: chrome.windows.Window): Promise => {
48 | const newWindow = window ?? await chrome.windows.create();
49 |
50 | await Promise.all(Object.entries(categorizedTabs).map(async ([name, tabIds]) => {
51 | // Create a new tab group for each category
52 | const group = await chrome.tabs.group({
53 | tabIds,
54 | createProperties: {
55 | windowId: newWindow.id,
56 | },
57 | });
58 |
59 | // Set the title of the group to the category name
60 | await chrome.tabGroups.update(group, {
61 | title: name,
62 | collapsed: true,
63 | });
64 | }));
65 | };
66 |
--------------------------------------------------------------------------------
/src/openai.ts:
--------------------------------------------------------------------------------
1 | import { omit } from "lodash-es";
2 | import { z } from "zod";
3 | import zodToJsonSchemaImpl from 'zod-to-json-schema';
4 | import ky from 'ky';
5 |
6 | import type { TabItem } from "./categories";
7 |
8 | function zodToJsonSchema(schema: z.ZodType) {
9 | return omit(
10 | zodToJsonSchemaImpl(schema, { $refStrategy: 'none' }),
11 | '$ref',
12 | '$schema',
13 | 'default',
14 | 'definitions',
15 | 'description',
16 | 'markdownDescription',
17 | );
18 | }
19 |
20 | const SetTabCategoriesSchema = z.object({
21 | categories: z.array(z.object({
22 | name: z.string().describe('The name or title of the category.'),
23 | tabs: z.array(z.number()).describe('A list of all Tab IDs that belong to this category.'),
24 | })),
25 | });
26 |
27 | interface AiOptions {
28 | model?: string;
29 | apiKey: string;
30 | }
31 |
32 | export const analyzeTabs = async (options: AiOptions, tabs: TabItem[]): Promise> => {
33 | try {
34 | // send the request containing the messages to the OpenAI API
35 | const response = await ky.post('https://api.openai.com/v1/chat/completions', {
36 | retry: {
37 | limit: 3,
38 | maxRetryAfter: 500,
39 | },
40 | timeout: 1000 * 59, // almost a minute
41 | headers: {
42 | 'Content-Type': 'application/json',
43 | 'Authorization': `Bearer ${options.apiKey}`
44 | },
45 | json: {
46 | model: options.model ?? 'gpt-3.5-turbo',
47 | temperature: 0.7,
48 | messages: [
49 | {
50 | "role": "system",
51 | "content": `
52 | You are a browser tab categorizer.
53 | You will be given a list of tabs, each with a domain and their title.
54 | Organize the whole list of tabs into groups based on their topic.
55 | Weigh the tab domains more into consideration than their title.
56 | In case many tabs share the same domain, find individual topics.
57 | Do not be too broad or generic.
58 | In case a tab is too miscellaneous, use "Other".
59 | Limit yourself to six categories maximum.
60 | At least 3 tabs have to be in each category.
61 | `.trim(),
62 | },
63 | {
64 | "role": "user",
65 | "content": JSON.stringify(tabs),
66 | }
67 | ],
68 | function_call: {
69 | name: 'set_tab_categories',
70 | },
71 | functions: [
72 | {
73 | name: 'set_tab_categories',
74 | description: 'Moves all tabs into given categories.',
75 | parameters: zodToJsonSchema(SetTabCategoriesSchema),
76 | },
77 | ],
78 | },
79 | });
80 |
81 | // Get the data from the API response as parsed schema
82 | const data: any = await response.json();
83 | const calledFuntionRaw = data.choices[0]?.message?.function_call?.arguments ?? '';
84 | const calledFunction = SetTabCategoriesSchema.parse(JSON.parse(calledFuntionRaw));
85 |
86 | // Return a mapping of category name to tab ID list
87 | return calledFunction.categories.reduce((acc, category) => {
88 | acc[category.name] = category.tabs;
89 | return acc;
90 | }, {} as Record);
91 | } catch (error) {
92 | throw error;
93 | }
94 | };
95 |
--------------------------------------------------------------------------------
/src/actions/sortTabs.ts:
--------------------------------------------------------------------------------
1 | import { mapTabIds } from "src/utils/tabs";
2 |
3 | type Entry = {
4 | id: number;
5 | groupId: number;
6 | index: number;
7 | url: string;
8 | }
9 |
10 | const isGroupedTab = (id: number) => (
11 | id !== chrome.tabGroups.TAB_GROUP_ID_NONE
12 | );
13 |
14 | /**
15 | * Sorts tabs by their URL, using a custom strategy,
16 | * so we have the following order:
17 | *
18 | * 1. bar.com
19 | * 2. foo.com
20 | * 3. baz.foo.com
21 | * 4. example.foo.com
22 | * 5. a.sub.xyz.com
23 | * 6. b.sub.xyz.com
24 | *
25 | * Afterwards the URL gets ordered by the rest (/subdirector/file.html?query#hash)
26 | */
27 | const urlTabSort = (a: Entry, b: Entry): number => {
28 | const aUrl = new URL(a.url);
29 | const bUrl = new URL(b.url);
30 |
31 | const aParts = aUrl.hostname.split('.').reverse();
32 | const bParts = bUrl.hostname.split('.').reverse();
33 |
34 | for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
35 | if (aParts[i] !== bParts[i]) {
36 | // If one URL has fewer parts, it goes first
37 | if (!aParts[i]) return -1;
38 | if (!bParts[i]) return 1;
39 |
40 | // Otherwise, compare the parts
41 | return aParts[i].localeCompare(bParts[i], 'en');
42 | }
43 | }
44 |
45 | // If the hostnames are identical, sort by their full URL
46 | return aUrl.href.localeCompare(bUrl.href, 'en');
47 | }
48 |
49 | export default async function sortTabs() {
50 | const tabs = await chrome.tabs.query({
51 | currentWindow: true,
52 | });
53 |
54 | // 1. Group the entries by groupId
55 | const grouped: { [groupId: number]: Entry[] } = {};
56 | const groupOrder: number[] = []; // Keep track of the order of the groupIds
57 |
58 | for (const tab of tabs) {
59 | if (!grouped.hasOwnProperty(tab.groupId)) {
60 | grouped[tab.groupId] = [];
61 |
62 | // Remember the order of appearance of groupIds
63 | if (isGroupedTab(tab.groupId)) {
64 | groupOrder.push(tab.groupId);
65 | }
66 | }
67 |
68 | grouped[tab.groupId].push({
69 | id: tab.id!,
70 | groupId: tab.groupId,
71 | index: tab.index,
72 | url: tab.url ?? '',
73 | });
74 | }
75 |
76 |
77 | // 2. Sort each group individually
78 | for (let groupId in grouped) {
79 | grouped[groupId].sort(urlTabSort);
80 | console.log(`Sorted group ${groupId}`, grouped[groupId]);
81 | }
82 |
83 | // 3. Move ungrouped tabs to the end
84 | const validGroups = groupOrder.filter((groupId) => isGroupedTab(groupId));
85 | // validGroups.push(chrome.tabGroups.TAB_GROUP_ID_NONE);
86 |
87 | // 4. Re-apply the sort in the same group order as before and
88 | // make sure that every entry has a continuous index
89 | const sortedTabIds: number[] = [];
90 |
91 | for (let groupId of groupOrder) {
92 | for (let entry of grouped[groupId]) {
93 | sortedTabIds.push(entry.id);
94 | }
95 | }
96 |
97 | // Move each tab group individually, so they are preserved
98 | // https://developer.chrome.com/docs/extensions/reference/tabs/#method-move
99 | let offset = 0;
100 |
101 | for (const groupId of validGroups) {
102 | console.log(`Moving group ${groupId}`, grouped[groupId]);
103 | const [_, ...rest] = grouped[groupId];
104 |
105 | for (let index = 0; index < rest.length; index++) {
106 | const tab = grouped[groupId][index];
107 | await chrome.tabs.move(tab.id, { index: offset + index });
108 | }
109 |
110 | await chrome.tabs.group({
111 | groupId,
112 | tabIds: mapTabIds(grouped[groupId]),
113 | });
114 |
115 | offset += grouped[groupId].length;
116 | }
117 |
118 | // Move ungrouped tabs to the end
119 | const ungroupedIds = mapTabIds(grouped[chrome.tabGroups.TAB_GROUP_ID_NONE] ?? []);
120 |
121 | if (ungroupedIds.length > 0) {
122 | console.log('Moving ungrouped tabs', grouped[chrome.tabGroups.TAB_GROUP_ID_NONE]);
123 | await chrome.tabs.move(ungroupedIds, { index: -1 });
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | ky:
9 | specifier: ^0.33.3
10 | version: 0.33.3
11 | lodash-es:
12 | specifier: ^4.17.21
13 | version: 4.17.21
14 | zod:
15 | specifier: ^3.21.4
16 | version: 3.21.4
17 | zod-to-json-schema:
18 | specifier: ^3.21.4
19 | version: 3.21.4(zod@3.21.4)
20 |
21 | devDependencies:
22 | '@types/chrome':
23 | specifier: ^0.0.243
24 | version: 0.0.243
25 | '@types/lodash-es':
26 | specifier: ^4.17.8
27 | version: 4.17.8
28 | '@types/node':
29 | specifier: ^20.4.8
30 | version: 20.4.8
31 | tailwindcss:
32 | specifier: ^3.3.3
33 | version: 3.3.3
34 | tsup:
35 | specifier: ^7.2.0
36 | version: 7.2.0(postcss@8.4.27)(typescript@5.1.3)
37 | typescript:
38 | specifier: ^5.1.0
39 | version: 5.1.3
40 |
41 | packages:
42 |
43 | /@alloc/quick-lru@5.2.0:
44 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
45 | engines: {node: '>=10'}
46 | dev: true
47 |
48 | /@esbuild/android-arm64@0.18.17:
49 | resolution: {integrity: sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==}
50 | engines: {node: '>=12'}
51 | cpu: [arm64]
52 | os: [android]
53 | requiresBuild: true
54 | dev: true
55 | optional: true
56 |
57 | /@esbuild/android-arm@0.18.17:
58 | resolution: {integrity: sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==}
59 | engines: {node: '>=12'}
60 | cpu: [arm]
61 | os: [android]
62 | requiresBuild: true
63 | dev: true
64 | optional: true
65 |
66 | /@esbuild/android-x64@0.18.17:
67 | resolution: {integrity: sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==}
68 | engines: {node: '>=12'}
69 | cpu: [x64]
70 | os: [android]
71 | requiresBuild: true
72 | dev: true
73 | optional: true
74 |
75 | /@esbuild/darwin-arm64@0.18.17:
76 | resolution: {integrity: sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==}
77 | engines: {node: '>=12'}
78 | cpu: [arm64]
79 | os: [darwin]
80 | requiresBuild: true
81 | dev: true
82 | optional: true
83 |
84 | /@esbuild/darwin-x64@0.18.17:
85 | resolution: {integrity: sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==}
86 | engines: {node: '>=12'}
87 | cpu: [x64]
88 | os: [darwin]
89 | requiresBuild: true
90 | dev: true
91 | optional: true
92 |
93 | /@esbuild/freebsd-arm64@0.18.17:
94 | resolution: {integrity: sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==}
95 | engines: {node: '>=12'}
96 | cpu: [arm64]
97 | os: [freebsd]
98 | requiresBuild: true
99 | dev: true
100 | optional: true
101 |
102 | /@esbuild/freebsd-x64@0.18.17:
103 | resolution: {integrity: sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==}
104 | engines: {node: '>=12'}
105 | cpu: [x64]
106 | os: [freebsd]
107 | requiresBuild: true
108 | dev: true
109 | optional: true
110 |
111 | /@esbuild/linux-arm64@0.18.17:
112 | resolution: {integrity: sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==}
113 | engines: {node: '>=12'}
114 | cpu: [arm64]
115 | os: [linux]
116 | requiresBuild: true
117 | dev: true
118 | optional: true
119 |
120 | /@esbuild/linux-arm@0.18.17:
121 | resolution: {integrity: sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==}
122 | engines: {node: '>=12'}
123 | cpu: [arm]
124 | os: [linux]
125 | requiresBuild: true
126 | dev: true
127 | optional: true
128 |
129 | /@esbuild/linux-ia32@0.18.17:
130 | resolution: {integrity: sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==}
131 | engines: {node: '>=12'}
132 | cpu: [ia32]
133 | os: [linux]
134 | requiresBuild: true
135 | dev: true
136 | optional: true
137 |
138 | /@esbuild/linux-loong64@0.18.17:
139 | resolution: {integrity: sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==}
140 | engines: {node: '>=12'}
141 | cpu: [loong64]
142 | os: [linux]
143 | requiresBuild: true
144 | dev: true
145 | optional: true
146 |
147 | /@esbuild/linux-mips64el@0.18.17:
148 | resolution: {integrity: sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==}
149 | engines: {node: '>=12'}
150 | cpu: [mips64el]
151 | os: [linux]
152 | requiresBuild: true
153 | dev: true
154 | optional: true
155 |
156 | /@esbuild/linux-ppc64@0.18.17:
157 | resolution: {integrity: sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==}
158 | engines: {node: '>=12'}
159 | cpu: [ppc64]
160 | os: [linux]
161 | requiresBuild: true
162 | dev: true
163 | optional: true
164 |
165 | /@esbuild/linux-riscv64@0.18.17:
166 | resolution: {integrity: sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==}
167 | engines: {node: '>=12'}
168 | cpu: [riscv64]
169 | os: [linux]
170 | requiresBuild: true
171 | dev: true
172 | optional: true
173 |
174 | /@esbuild/linux-s390x@0.18.17:
175 | resolution: {integrity: sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==}
176 | engines: {node: '>=12'}
177 | cpu: [s390x]
178 | os: [linux]
179 | requiresBuild: true
180 | dev: true
181 | optional: true
182 |
183 | /@esbuild/linux-x64@0.18.17:
184 | resolution: {integrity: sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==}
185 | engines: {node: '>=12'}
186 | cpu: [x64]
187 | os: [linux]
188 | requiresBuild: true
189 | dev: true
190 | optional: true
191 |
192 | /@esbuild/netbsd-x64@0.18.17:
193 | resolution: {integrity: sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==}
194 | engines: {node: '>=12'}
195 | cpu: [x64]
196 | os: [netbsd]
197 | requiresBuild: true
198 | dev: true
199 | optional: true
200 |
201 | /@esbuild/openbsd-x64@0.18.17:
202 | resolution: {integrity: sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==}
203 | engines: {node: '>=12'}
204 | cpu: [x64]
205 | os: [openbsd]
206 | requiresBuild: true
207 | dev: true
208 | optional: true
209 |
210 | /@esbuild/sunos-x64@0.18.17:
211 | resolution: {integrity: sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==}
212 | engines: {node: '>=12'}
213 | cpu: [x64]
214 | os: [sunos]
215 | requiresBuild: true
216 | dev: true
217 | optional: true
218 |
219 | /@esbuild/win32-arm64@0.18.17:
220 | resolution: {integrity: sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==}
221 | engines: {node: '>=12'}
222 | cpu: [arm64]
223 | os: [win32]
224 | requiresBuild: true
225 | dev: true
226 | optional: true
227 |
228 | /@esbuild/win32-ia32@0.18.17:
229 | resolution: {integrity: sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==}
230 | engines: {node: '>=12'}
231 | cpu: [ia32]
232 | os: [win32]
233 | requiresBuild: true
234 | dev: true
235 | optional: true
236 |
237 | /@esbuild/win32-x64@0.18.17:
238 | resolution: {integrity: sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==}
239 | engines: {node: '>=12'}
240 | cpu: [x64]
241 | os: [win32]
242 | requiresBuild: true
243 | dev: true
244 | optional: true
245 |
246 | /@jridgewell/gen-mapping@0.3.3:
247 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
248 | engines: {node: '>=6.0.0'}
249 | dependencies:
250 | '@jridgewell/set-array': 1.1.2
251 | '@jridgewell/sourcemap-codec': 1.4.15
252 | '@jridgewell/trace-mapping': 0.3.18
253 | dev: true
254 |
255 | /@jridgewell/resolve-uri@3.1.0:
256 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
257 | engines: {node: '>=6.0.0'}
258 | dev: true
259 |
260 | /@jridgewell/set-array@1.1.2:
261 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
262 | engines: {node: '>=6.0.0'}
263 | dev: true
264 |
265 | /@jridgewell/sourcemap-codec@1.4.14:
266 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
267 | dev: true
268 |
269 | /@jridgewell/sourcemap-codec@1.4.15:
270 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
271 | dev: true
272 |
273 | /@jridgewell/trace-mapping@0.3.18:
274 | resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
275 | dependencies:
276 | '@jridgewell/resolve-uri': 3.1.0
277 | '@jridgewell/sourcemap-codec': 1.4.14
278 | dev: true
279 |
280 | /@nodelib/fs.scandir@2.1.5:
281 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
282 | engines: {node: '>= 8'}
283 | dependencies:
284 | '@nodelib/fs.stat': 2.0.5
285 | run-parallel: 1.2.0
286 | dev: true
287 |
288 | /@nodelib/fs.stat@2.0.5:
289 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
290 | engines: {node: '>= 8'}
291 | dev: true
292 |
293 | /@nodelib/fs.walk@1.2.8:
294 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
295 | engines: {node: '>= 8'}
296 | dependencies:
297 | '@nodelib/fs.scandir': 2.1.5
298 | fastq: 1.15.0
299 | dev: true
300 |
301 | /@types/chrome@0.0.243:
302 | resolution: {integrity: sha512-4PHv0kxxxpZFHWPBiJJ9TWH8kbx0567j1b2djnhpJjpiSGNI7UKkz7dSEECBtQ0B3N5nQTMwSB/5IopkWGAbEA==}
303 | dependencies:
304 | '@types/filesystem': 0.0.32
305 | '@types/har-format': 1.2.11
306 | dev: true
307 |
308 | /@types/filesystem@0.0.32:
309 | resolution: {integrity: sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==}
310 | dependencies:
311 | '@types/filewriter': 0.0.29
312 | dev: true
313 |
314 | /@types/filewriter@0.0.29:
315 | resolution: {integrity: sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==}
316 | dev: true
317 |
318 | /@types/har-format@1.2.11:
319 | resolution: {integrity: sha512-T232/TneofqK30AD1LRrrf8KnjLvzrjWDp7eWST5KoiSzrBfRsLrWDPk4STQPW4NZG6v2MltnduBVmakbZOBIQ==}
320 | dev: true
321 |
322 | /@types/lodash-es@4.17.8:
323 | resolution: {integrity: sha512-euY3XQcZmIzSy7YH5+Unb3b2X12Wtk54YWINBvvGQ5SmMvwb11JQskGsfkH/5HXK77Kr8GF0wkVDIxzAisWtog==}
324 | dependencies:
325 | '@types/lodash': 4.14.196
326 | dev: true
327 |
328 | /@types/lodash@4.14.196:
329 | resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==}
330 | dev: true
331 |
332 | /@types/node@20.4.8:
333 | resolution: {integrity: sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==}
334 | dev: true
335 |
336 | /any-promise@1.3.0:
337 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
338 | dev: true
339 |
340 | /anymatch@3.1.3:
341 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
342 | engines: {node: '>= 8'}
343 | dependencies:
344 | normalize-path: 3.0.0
345 | picomatch: 2.3.1
346 | dev: true
347 |
348 | /arg@5.0.2:
349 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
350 | dev: true
351 |
352 | /array-union@2.1.0:
353 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
354 | engines: {node: '>=8'}
355 | dev: true
356 |
357 | /balanced-match@1.0.2:
358 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
359 | dev: true
360 |
361 | /binary-extensions@2.2.0:
362 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
363 | engines: {node: '>=8'}
364 | dev: true
365 |
366 | /brace-expansion@1.1.11:
367 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
368 | dependencies:
369 | balanced-match: 1.0.2
370 | concat-map: 0.0.1
371 | dev: true
372 |
373 | /braces@3.0.2:
374 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
375 | engines: {node: '>=8'}
376 | dependencies:
377 | fill-range: 7.0.1
378 | dev: true
379 |
380 | /bundle-require@4.0.1(esbuild@0.18.17):
381 | resolution: {integrity: sha512-9NQkRHlNdNpDBGmLpngF3EFDcwodhMUuLz9PaWYciVcQF9SE4LFjM2DB/xV1Li5JiuDMv7ZUWuC3rGbqR0MAXQ==}
382 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
383 | peerDependencies:
384 | esbuild: '>=0.17'
385 | dependencies:
386 | esbuild: 0.18.17
387 | load-tsconfig: 0.2.5
388 | dev: true
389 |
390 | /cac@6.7.14:
391 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
392 | engines: {node: '>=8'}
393 | dev: true
394 |
395 | /camelcase-css@2.0.1:
396 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
397 | engines: {node: '>= 6'}
398 | dev: true
399 |
400 | /chokidar@3.5.3:
401 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
402 | engines: {node: '>= 8.10.0'}
403 | dependencies:
404 | anymatch: 3.1.3
405 | braces: 3.0.2
406 | glob-parent: 5.1.2
407 | is-binary-path: 2.1.0
408 | is-glob: 4.0.3
409 | normalize-path: 3.0.0
410 | readdirp: 3.6.0
411 | optionalDependencies:
412 | fsevents: 2.3.2
413 | dev: true
414 |
415 | /commander@4.1.1:
416 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
417 | engines: {node: '>= 6'}
418 | dev: true
419 |
420 | /concat-map@0.0.1:
421 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
422 | dev: true
423 |
424 | /cross-spawn@7.0.3:
425 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
426 | engines: {node: '>= 8'}
427 | dependencies:
428 | path-key: 3.1.1
429 | shebang-command: 2.0.0
430 | which: 2.0.2
431 | dev: true
432 |
433 | /cssesc@3.0.0:
434 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
435 | engines: {node: '>=4'}
436 | hasBin: true
437 | dev: true
438 |
439 | /debug@4.3.4:
440 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
441 | engines: {node: '>=6.0'}
442 | peerDependencies:
443 | supports-color: '*'
444 | peerDependenciesMeta:
445 | supports-color:
446 | optional: true
447 | dependencies:
448 | ms: 2.1.2
449 | dev: true
450 |
451 | /didyoumean@1.2.2:
452 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
453 | dev: true
454 |
455 | /dir-glob@3.0.1:
456 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
457 | engines: {node: '>=8'}
458 | dependencies:
459 | path-type: 4.0.0
460 | dev: true
461 |
462 | /dlv@1.1.3:
463 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
464 | dev: true
465 |
466 | /esbuild@0.18.17:
467 | resolution: {integrity: sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==}
468 | engines: {node: '>=12'}
469 | hasBin: true
470 | requiresBuild: true
471 | optionalDependencies:
472 | '@esbuild/android-arm': 0.18.17
473 | '@esbuild/android-arm64': 0.18.17
474 | '@esbuild/android-x64': 0.18.17
475 | '@esbuild/darwin-arm64': 0.18.17
476 | '@esbuild/darwin-x64': 0.18.17
477 | '@esbuild/freebsd-arm64': 0.18.17
478 | '@esbuild/freebsd-x64': 0.18.17
479 | '@esbuild/linux-arm': 0.18.17
480 | '@esbuild/linux-arm64': 0.18.17
481 | '@esbuild/linux-ia32': 0.18.17
482 | '@esbuild/linux-loong64': 0.18.17
483 | '@esbuild/linux-mips64el': 0.18.17
484 | '@esbuild/linux-ppc64': 0.18.17
485 | '@esbuild/linux-riscv64': 0.18.17
486 | '@esbuild/linux-s390x': 0.18.17
487 | '@esbuild/linux-x64': 0.18.17
488 | '@esbuild/netbsd-x64': 0.18.17
489 | '@esbuild/openbsd-x64': 0.18.17
490 | '@esbuild/sunos-x64': 0.18.17
491 | '@esbuild/win32-arm64': 0.18.17
492 | '@esbuild/win32-ia32': 0.18.17
493 | '@esbuild/win32-x64': 0.18.17
494 | dev: true
495 |
496 | /execa@5.1.1:
497 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
498 | engines: {node: '>=10'}
499 | dependencies:
500 | cross-spawn: 7.0.3
501 | get-stream: 6.0.1
502 | human-signals: 2.1.0
503 | is-stream: 2.0.1
504 | merge-stream: 2.0.0
505 | npm-run-path: 4.0.1
506 | onetime: 5.1.2
507 | signal-exit: 3.0.7
508 | strip-final-newline: 2.0.0
509 | dev: true
510 |
511 | /fast-glob@3.3.1:
512 | resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
513 | engines: {node: '>=8.6.0'}
514 | dependencies:
515 | '@nodelib/fs.stat': 2.0.5
516 | '@nodelib/fs.walk': 1.2.8
517 | glob-parent: 5.1.2
518 | merge2: 1.4.1
519 | micromatch: 4.0.5
520 | dev: true
521 |
522 | /fastq@1.15.0:
523 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
524 | dependencies:
525 | reusify: 1.0.4
526 | dev: true
527 |
528 | /fill-range@7.0.1:
529 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
530 | engines: {node: '>=8'}
531 | dependencies:
532 | to-regex-range: 5.0.1
533 | dev: true
534 |
535 | /fs.realpath@1.0.0:
536 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
537 | dev: true
538 |
539 | /fsevents@2.3.2:
540 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
541 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
542 | os: [darwin]
543 | requiresBuild: true
544 | dev: true
545 | optional: true
546 |
547 | /function-bind@1.1.1:
548 | resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
549 | dev: true
550 |
551 | /get-stream@6.0.1:
552 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
553 | engines: {node: '>=10'}
554 | dev: true
555 |
556 | /glob-parent@5.1.2:
557 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
558 | engines: {node: '>= 6'}
559 | dependencies:
560 | is-glob: 4.0.3
561 | dev: true
562 |
563 | /glob-parent@6.0.2:
564 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
565 | engines: {node: '>=10.13.0'}
566 | dependencies:
567 | is-glob: 4.0.3
568 | dev: true
569 |
570 | /glob@7.1.6:
571 | resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
572 | dependencies:
573 | fs.realpath: 1.0.0
574 | inflight: 1.0.6
575 | inherits: 2.0.4
576 | minimatch: 3.1.2
577 | once: 1.4.0
578 | path-is-absolute: 1.0.1
579 | dev: true
580 |
581 | /globby@11.1.0:
582 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
583 | engines: {node: '>=10'}
584 | dependencies:
585 | array-union: 2.1.0
586 | dir-glob: 3.0.1
587 | fast-glob: 3.3.1
588 | ignore: 5.2.4
589 | merge2: 1.4.1
590 | slash: 3.0.0
591 | dev: true
592 |
593 | /has@1.0.3:
594 | resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
595 | engines: {node: '>= 0.4.0'}
596 | dependencies:
597 | function-bind: 1.1.1
598 | dev: true
599 |
600 | /human-signals@2.1.0:
601 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
602 | engines: {node: '>=10.17.0'}
603 | dev: true
604 |
605 | /ignore@5.2.4:
606 | resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
607 | engines: {node: '>= 4'}
608 | dev: true
609 |
610 | /inflight@1.0.6:
611 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
612 | dependencies:
613 | once: 1.4.0
614 | wrappy: 1.0.2
615 | dev: true
616 |
617 | /inherits@2.0.4:
618 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
619 | dev: true
620 |
621 | /is-binary-path@2.1.0:
622 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
623 | engines: {node: '>=8'}
624 | dependencies:
625 | binary-extensions: 2.2.0
626 | dev: true
627 |
628 | /is-core-module@2.13.0:
629 | resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
630 | dependencies:
631 | has: 1.0.3
632 | dev: true
633 |
634 | /is-extglob@2.1.1:
635 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
636 | engines: {node: '>=0.10.0'}
637 | dev: true
638 |
639 | /is-glob@4.0.3:
640 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
641 | engines: {node: '>=0.10.0'}
642 | dependencies:
643 | is-extglob: 2.1.1
644 | dev: true
645 |
646 | /is-number@7.0.0:
647 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
648 | engines: {node: '>=0.12.0'}
649 | dev: true
650 |
651 | /is-stream@2.0.1:
652 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
653 | engines: {node: '>=8'}
654 | dev: true
655 |
656 | /isexe@2.0.0:
657 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
658 | dev: true
659 |
660 | /jiti@1.19.1:
661 | resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==}
662 | hasBin: true
663 | dev: true
664 |
665 | /joycon@3.1.1:
666 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
667 | engines: {node: '>=10'}
668 | dev: true
669 |
670 | /ky@0.33.3:
671 | resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==}
672 | engines: {node: '>=14.16'}
673 | dev: false
674 |
675 | /lilconfig@2.1.0:
676 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
677 | engines: {node: '>=10'}
678 | dev: true
679 |
680 | /lines-and-columns@1.2.4:
681 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
682 | dev: true
683 |
684 | /load-tsconfig@0.2.5:
685 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
686 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
687 | dev: true
688 |
689 | /lodash-es@4.17.21:
690 | resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
691 | dev: false
692 |
693 | /lodash.sortby@4.7.0:
694 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
695 | dev: true
696 |
697 | /merge-stream@2.0.0:
698 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
699 | dev: true
700 |
701 | /merge2@1.4.1:
702 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
703 | engines: {node: '>= 8'}
704 | dev: true
705 |
706 | /micromatch@4.0.5:
707 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
708 | engines: {node: '>=8.6'}
709 | dependencies:
710 | braces: 3.0.2
711 | picomatch: 2.3.1
712 | dev: true
713 |
714 | /mimic-fn@2.1.0:
715 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
716 | engines: {node: '>=6'}
717 | dev: true
718 |
719 | /minimatch@3.1.2:
720 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
721 | dependencies:
722 | brace-expansion: 1.1.11
723 | dev: true
724 |
725 | /ms@2.1.2:
726 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
727 | dev: true
728 |
729 | /mz@2.7.0:
730 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
731 | dependencies:
732 | any-promise: 1.3.0
733 | object-assign: 4.1.1
734 | thenify-all: 1.6.0
735 | dev: true
736 |
737 | /nanoid@3.3.6:
738 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
739 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
740 | hasBin: true
741 | dev: true
742 |
743 | /normalize-path@3.0.0:
744 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
745 | engines: {node: '>=0.10.0'}
746 | dev: true
747 |
748 | /npm-run-path@4.0.1:
749 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
750 | engines: {node: '>=8'}
751 | dependencies:
752 | path-key: 3.1.1
753 | dev: true
754 |
755 | /object-assign@4.1.1:
756 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
757 | engines: {node: '>=0.10.0'}
758 | dev: true
759 |
760 | /object-hash@3.0.0:
761 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
762 | engines: {node: '>= 6'}
763 | dev: true
764 |
765 | /once@1.4.0:
766 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
767 | dependencies:
768 | wrappy: 1.0.2
769 | dev: true
770 |
771 | /onetime@5.1.2:
772 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
773 | engines: {node: '>=6'}
774 | dependencies:
775 | mimic-fn: 2.1.0
776 | dev: true
777 |
778 | /path-is-absolute@1.0.1:
779 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
780 | engines: {node: '>=0.10.0'}
781 | dev: true
782 |
783 | /path-key@3.1.1:
784 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
785 | engines: {node: '>=8'}
786 | dev: true
787 |
788 | /path-parse@1.0.7:
789 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
790 | dev: true
791 |
792 | /path-type@4.0.0:
793 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
794 | engines: {node: '>=8'}
795 | dev: true
796 |
797 | /picocolors@1.0.0:
798 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
799 | dev: true
800 |
801 | /picomatch@2.3.1:
802 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
803 | engines: {node: '>=8.6'}
804 | dev: true
805 |
806 | /pify@2.3.0:
807 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
808 | engines: {node: '>=0.10.0'}
809 | dev: true
810 |
811 | /pirates@4.0.6:
812 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
813 | engines: {node: '>= 6'}
814 | dev: true
815 |
816 | /postcss-import@15.1.0(postcss@8.4.27):
817 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
818 | engines: {node: '>=14.0.0'}
819 | peerDependencies:
820 | postcss: ^8.0.0
821 | dependencies:
822 | postcss: 8.4.27
823 | postcss-value-parser: 4.2.0
824 | read-cache: 1.0.0
825 | resolve: 1.22.4
826 | dev: true
827 |
828 | /postcss-js@4.0.1(postcss@8.4.27):
829 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
830 | engines: {node: ^12 || ^14 || >= 16}
831 | peerDependencies:
832 | postcss: ^8.4.21
833 | dependencies:
834 | camelcase-css: 2.0.1
835 | postcss: 8.4.27
836 | dev: true
837 |
838 | /postcss-load-config@4.0.1(postcss@8.4.27):
839 | resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
840 | engines: {node: '>= 14'}
841 | peerDependencies:
842 | postcss: '>=8.0.9'
843 | ts-node: '>=9.0.0'
844 | peerDependenciesMeta:
845 | postcss:
846 | optional: true
847 | ts-node:
848 | optional: true
849 | dependencies:
850 | lilconfig: 2.1.0
851 | postcss: 8.4.27
852 | yaml: 2.3.1
853 | dev: true
854 |
855 | /postcss-nested@6.0.1(postcss@8.4.27):
856 | resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
857 | engines: {node: '>=12.0'}
858 | peerDependencies:
859 | postcss: ^8.2.14
860 | dependencies:
861 | postcss: 8.4.27
862 | postcss-selector-parser: 6.0.13
863 | dev: true
864 |
865 | /postcss-selector-parser@6.0.13:
866 | resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
867 | engines: {node: '>=4'}
868 | dependencies:
869 | cssesc: 3.0.0
870 | util-deprecate: 1.0.2
871 | dev: true
872 |
873 | /postcss-value-parser@4.2.0:
874 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
875 | dev: true
876 |
877 | /postcss@8.4.27:
878 | resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
879 | engines: {node: ^10 || ^12 || >=14}
880 | dependencies:
881 | nanoid: 3.3.6
882 | picocolors: 1.0.0
883 | source-map-js: 1.0.2
884 | dev: true
885 |
886 | /punycode@2.3.0:
887 | resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
888 | engines: {node: '>=6'}
889 | dev: true
890 |
891 | /queue-microtask@1.2.3:
892 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
893 | dev: true
894 |
895 | /read-cache@1.0.0:
896 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
897 | dependencies:
898 | pify: 2.3.0
899 | dev: true
900 |
901 | /readdirp@3.6.0:
902 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
903 | engines: {node: '>=8.10.0'}
904 | dependencies:
905 | picomatch: 2.3.1
906 | dev: true
907 |
908 | /resolve-from@5.0.0:
909 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
910 | engines: {node: '>=8'}
911 | dev: true
912 |
913 | /resolve@1.22.4:
914 | resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==}
915 | hasBin: true
916 | dependencies:
917 | is-core-module: 2.13.0
918 | path-parse: 1.0.7
919 | supports-preserve-symlinks-flag: 1.0.0
920 | dev: true
921 |
922 | /reusify@1.0.4:
923 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
924 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
925 | dev: true
926 |
927 | /rollup@3.27.2:
928 | resolution: {integrity: sha512-YGwmHf7h2oUHkVBT248x0yt6vZkYQ3/rvE5iQuVBh3WO8GcJ6BNeOkpoX1yMHIiBm18EMLjBPIoUDkhgnyxGOQ==}
929 | engines: {node: '>=14.18.0', npm: '>=8.0.0'}
930 | hasBin: true
931 | optionalDependencies:
932 | fsevents: 2.3.2
933 | dev: true
934 |
935 | /run-parallel@1.2.0:
936 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
937 | dependencies:
938 | queue-microtask: 1.2.3
939 | dev: true
940 |
941 | /shebang-command@2.0.0:
942 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
943 | engines: {node: '>=8'}
944 | dependencies:
945 | shebang-regex: 3.0.0
946 | dev: true
947 |
948 | /shebang-regex@3.0.0:
949 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
950 | engines: {node: '>=8'}
951 | dev: true
952 |
953 | /signal-exit@3.0.7:
954 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
955 | dev: true
956 |
957 | /slash@3.0.0:
958 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
959 | engines: {node: '>=8'}
960 | dev: true
961 |
962 | /source-map-js@1.0.2:
963 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
964 | engines: {node: '>=0.10.0'}
965 | dev: true
966 |
967 | /source-map@0.8.0-beta.0:
968 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
969 | engines: {node: '>= 8'}
970 | dependencies:
971 | whatwg-url: 7.1.0
972 | dev: true
973 |
974 | /strip-final-newline@2.0.0:
975 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
976 | engines: {node: '>=6'}
977 | dev: true
978 |
979 | /sucrase@3.34.0:
980 | resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==}
981 | engines: {node: '>=8'}
982 | hasBin: true
983 | dependencies:
984 | '@jridgewell/gen-mapping': 0.3.3
985 | commander: 4.1.1
986 | glob: 7.1.6
987 | lines-and-columns: 1.2.4
988 | mz: 2.7.0
989 | pirates: 4.0.6
990 | ts-interface-checker: 0.1.13
991 | dev: true
992 |
993 | /supports-preserve-symlinks-flag@1.0.0:
994 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
995 | engines: {node: '>= 0.4'}
996 | dev: true
997 |
998 | /tailwindcss@3.3.3:
999 | resolution: {integrity: sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==}
1000 | engines: {node: '>=14.0.0'}
1001 | hasBin: true
1002 | dependencies:
1003 | '@alloc/quick-lru': 5.2.0
1004 | arg: 5.0.2
1005 | chokidar: 3.5.3
1006 | didyoumean: 1.2.2
1007 | dlv: 1.1.3
1008 | fast-glob: 3.3.1
1009 | glob-parent: 6.0.2
1010 | is-glob: 4.0.3
1011 | jiti: 1.19.1
1012 | lilconfig: 2.1.0
1013 | micromatch: 4.0.5
1014 | normalize-path: 3.0.0
1015 | object-hash: 3.0.0
1016 | picocolors: 1.0.0
1017 | postcss: 8.4.27
1018 | postcss-import: 15.1.0(postcss@8.4.27)
1019 | postcss-js: 4.0.1(postcss@8.4.27)
1020 | postcss-load-config: 4.0.1(postcss@8.4.27)
1021 | postcss-nested: 6.0.1(postcss@8.4.27)
1022 | postcss-selector-parser: 6.0.13
1023 | resolve: 1.22.4
1024 | sucrase: 3.34.0
1025 | transitivePeerDependencies:
1026 | - ts-node
1027 | dev: true
1028 |
1029 | /thenify-all@1.6.0:
1030 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
1031 | engines: {node: '>=0.8'}
1032 | dependencies:
1033 | thenify: 3.3.1
1034 | dev: true
1035 |
1036 | /thenify@3.3.1:
1037 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
1038 | dependencies:
1039 | any-promise: 1.3.0
1040 | dev: true
1041 |
1042 | /to-regex-range@5.0.1:
1043 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
1044 | engines: {node: '>=8.0'}
1045 | dependencies:
1046 | is-number: 7.0.0
1047 | dev: true
1048 |
1049 | /tr46@1.0.1:
1050 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
1051 | dependencies:
1052 | punycode: 2.3.0
1053 | dev: true
1054 |
1055 | /tree-kill@1.2.2:
1056 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
1057 | hasBin: true
1058 | dev: true
1059 |
1060 | /ts-interface-checker@0.1.13:
1061 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
1062 | dev: true
1063 |
1064 | /tsup@7.2.0(postcss@8.4.27)(typescript@5.1.3):
1065 | resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==}
1066 | engines: {node: '>=16.14'}
1067 | hasBin: true
1068 | peerDependencies:
1069 | '@swc/core': ^1
1070 | postcss: ^8.4.12
1071 | typescript: '>=4.1.0'
1072 | peerDependenciesMeta:
1073 | '@swc/core':
1074 | optional: true
1075 | postcss:
1076 | optional: true
1077 | typescript:
1078 | optional: true
1079 | dependencies:
1080 | bundle-require: 4.0.1(esbuild@0.18.17)
1081 | cac: 6.7.14
1082 | chokidar: 3.5.3
1083 | debug: 4.3.4
1084 | esbuild: 0.18.17
1085 | execa: 5.1.1
1086 | globby: 11.1.0
1087 | joycon: 3.1.1
1088 | postcss: 8.4.27
1089 | postcss-load-config: 4.0.1(postcss@8.4.27)
1090 | resolve-from: 5.0.0
1091 | rollup: 3.27.2
1092 | source-map: 0.8.0-beta.0
1093 | sucrase: 3.34.0
1094 | tree-kill: 1.2.2
1095 | typescript: 5.1.3
1096 | transitivePeerDependencies:
1097 | - supports-color
1098 | - ts-node
1099 | dev: true
1100 |
1101 | /typescript@5.1.3:
1102 | resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
1103 | engines: {node: '>=14.17'}
1104 | hasBin: true
1105 | dev: true
1106 |
1107 | /util-deprecate@1.0.2:
1108 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
1109 | dev: true
1110 |
1111 | /webidl-conversions@4.0.2:
1112 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
1113 | dev: true
1114 |
1115 | /whatwg-url@7.1.0:
1116 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
1117 | dependencies:
1118 | lodash.sortby: 4.7.0
1119 | tr46: 1.0.1
1120 | webidl-conversions: 4.0.2
1121 | dev: true
1122 |
1123 | /which@2.0.2:
1124 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1125 | engines: {node: '>= 8'}
1126 | hasBin: true
1127 | dependencies:
1128 | isexe: 2.0.0
1129 | dev: true
1130 |
1131 | /wrappy@1.0.2:
1132 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
1133 | dev: true
1134 |
1135 | /yaml@2.3.1:
1136 | resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
1137 | engines: {node: '>= 14'}
1138 | dev: true
1139 |
1140 | /zod-to-json-schema@3.21.4(zod@3.21.4):
1141 | resolution: {integrity: sha512-fjUZh4nQ1s6HMccgIeE0VP4QG/YRGPmyjO9sAh890aQKPEk3nqbfUXhMFaC+Dr5KvYBm8BCyvfpZf2jY9aGSsw==}
1142 | peerDependencies:
1143 | zod: ^3.21.4
1144 | dependencies:
1145 | zod: 3.21.4
1146 | dev: false
1147 |
1148 | /zod@3.21.4:
1149 | resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
1150 | dev: false
1151 |
--------------------------------------------------------------------------------