diff --git a/demos/linearlite/.gitignore b/demos/linearlite/.gitignore new file mode 100644 index 000000000..c05ac1359 --- /dev/null +++ b/demos/linearlite/.gitignore @@ -0,0 +1,3 @@ +dist +.env.local +db/data/ \ No newline at end of file diff --git a/demos/linearlite/.prettierrc b/demos/linearlite/.prettierrc new file mode 100644 index 000000000..f685078ff --- /dev/null +++ b/demos/linearlite/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "semi": false, + "tabWidth": 2, + "singleQuote": true +} diff --git a/demos/linearlite/README.md b/demos/linearlite/README.md new file mode 100644 index 000000000..b2a336205 --- /dev/null +++ b/demos/linearlite/README.md @@ -0,0 +1,60 @@ +# Linearlite + PGlite + ElectricSQL + +This is a demo app that shows how to build a local-first app using PGlite and the ElectricSQL sync engine. + +It's an example of a team collaboration app such as Linear built using ElectricSQL - a sync engine that synchronises little subsets of your Postgres data into local apps and services. So you can have the data you need, in-sync, wherever you need it. + +It's built on top of the excellent clone of the Linear UI built by [Tuan Nguyen](https://github.com/tuan3w). + +## Setup + +1. Make sure you've installed all dependencies for the monorepo and built all packages. + +From the root directory: + +- `pnpm i` +- `pnpm run -r build` + +2. Add a `.env` file with the following (or similar), in this directory: + +``` +DATABASE_URL=postgresql://postgres:password@localhost:54321/linearlite +VITE_ELECTRIC_URL=http://localhost:3000 +VITE_WRITE_SERVER_URL=http://localhost:3001 +``` + +3. Start the docker containers: + +`pnpm run backend:up` + +4. Start the write path server: + +`pnpm run write-server` + +5. Start the dev server: + +`pnpm run dev` + +5. When done, tear down the backend containers: + +`pnpm run backend:down` + +## How it works + +LinearLite demonstrates a local-first architecture using ElectricSQL and PGlite. Here's how the different pieces fit together: + +### Backend Components + +1. **Postgres Database**: The source of truth, containing the complete dataset. + +2. **Electric Sync Service**: Runs in front of Postgres, managing data synchronization from it to the clients. Preduces replication streams for a subset of the database called "shapes". + +3. **Write Server**: A simple HTTP server that handles write operations, applying them to the Postgres database. + +### Frontend Components + +1. **PGlite**: An in-browser database that stores a local copy of the data, enabling offline functionality and fast queries. + +2. **PGlite + Electric Sync Plugin**: Connects PGlite to the Electric sync service and loads the data into the local database. + +3. **React Frontend**: A Linear-inspired UI that interacts directly with the local database. diff --git a/demos/linearlite/backend/docker-compose.yml b/demos/linearlite/backend/docker-compose.yml new file mode 100644 index 000000000..15fd2c886 --- /dev/null +++ b/demos/linearlite/backend/docker-compose.yml @@ -0,0 +1,30 @@ +version: "3.3" +name: "pglite-linearlite" + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: linearlite + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + ports: + - 54321:5432 + volumes: + - ./postgres.conf:/etc/postgresql/postgresql.conf:ro + tmpfs: + - /var/lib/postgresql/data + - /tmp + command: + - postgres + - -c + - config_file=/etc/postgresql/postgresql.conf + + backend: + image: electricsql/electric + environment: + DATABASE_URL: postgresql://postgres:password@postgres:5432/linearlite?sslmode=disable + ports: + - 3000:3000 + depends_on: + - postgres diff --git a/demos/linearlite/backend/postgres.conf b/demos/linearlite/backend/postgres.conf new file mode 100644 index 000000000..f28083ca8 --- /dev/null +++ b/demos/linearlite/backend/postgres.conf @@ -0,0 +1,2 @@ +listen_addresses = '*' +wal_level = logical \ No newline at end of file diff --git a/demos/linearlite/db/generate_data.js b/demos/linearlite/db/generate_data.js new file mode 100644 index 000000000..a2e5240c9 --- /dev/null +++ b/demos/linearlite/db/generate_data.js @@ -0,0 +1,53 @@ +import { faker } from '@faker-js/faker' +import { generateNKeysBetween } from 'fractional-indexing' +import { v4 as uuidv4 } from 'uuid' + +export function generateIssues(numIssues) { + // generate properly spaced kanban keys and shuffle them + const kanbanKeys = faker.helpers.shuffle( + generateNKeysBetween(null, null, numIssues) + ) + return Array.from({ length: numIssues }, (_, idx) => + generateIssue(kanbanKeys[idx]) + ) +} + +function generateIssue(kanbanKey) { + const issueId = uuidv4() + const createdAt = faker.date.past() + return { + id: issueId, + title: faker.lorem.sentence({ min: 3, max: 8 }), + description: faker.lorem.sentences({ min: 2, max: 6 }, `\n`), + priority: faker.helpers.arrayElement([`none`, `low`, `medium`, `high`]), + status: faker.helpers.arrayElement([ + `backlog`, + `todo`, + `in_progress`, + `done`, + `canceled`, + ]), + created: createdAt.toISOString(), + modified: faker.date + .between({ from: createdAt, to: new Date() }) + .toISOString(), + kanbanorder: kanbanKey, + username: faker.internet.userName(), + comments: faker.helpers.multiple( + () => generateComment(issueId, createdAt), + { count: faker.number.int({ min: 0, max: 1 }) } + ), + } +} + +function generateComment(issueId, issueCreatedAt) { + const createdAt = faker.date.between({ from: issueCreatedAt, to: new Date() }) + return { + id: uuidv4(), + body: faker.lorem.text(), + username: faker.internet.userName(), + issue_id: issueId, + created: createdAt.toISOString(), + modified: createdAt.toISOString(), // comments are never modified + } +} diff --git a/demos/linearlite/db/load_data.js b/demos/linearlite/db/load_data.js new file mode 100644 index 000000000..4a4f494e6 --- /dev/null +++ b/demos/linearlite/db/load_data.js @@ -0,0 +1,72 @@ +import postgres from 'postgres' +import { generateIssues } from './generate_data.js' + +if (!process.env.DATABASE_URL) { + throw new Error(`DATABASE_URL is not set`) +} + +const DATABASE_URL = process.env.DATABASE_URL +const ISSUES_TO_LOAD = process.env.ISSUES_TO_LOAD || 512 +const BATCH_SIZE = 1000 +const issues = generateIssues(ISSUES_TO_LOAD) + +console.info(`Connecting to Postgres at ${DATABASE_URL}`) +const sql = postgres(DATABASE_URL) + +async function batchInsert(sql, table, columns, dataArray, batchSize = 1000) { + for (let i = 0; i < dataArray.length; i += batchSize) { + const batch = dataArray.slice(i, i + batchSize) + + await sql` + INSERT INTO ${sql(table)} ${sql(batch, columns)} + ` + + process.stdout.write( + `Loaded ${Math.min(i + batchSize, dataArray.length)} of ${dataArray.length} ${table}s\r` + ) + } +} + +const issueCount = issues.length +let commentCount = 0 + +try { + // Process data in batches + for (let i = 0; i < issues.length; i += BATCH_SIZE) { + const issueBatch = issues.slice(i, i + BATCH_SIZE) + + await sql.begin(async (sql) => { + // Disable FK checks + await sql`SET CONSTRAINTS ALL DEFERRED` + + // Insert issues + const issuesData = issueBatch.map(({ comments: _, ...rest }) => rest) + const issueColumns = Object.keys(issuesData[0]) + await batchInsert(sql, 'issue', issueColumns, issuesData, BATCH_SIZE) + + // Insert related comments + const batchComments = issueBatch.flatMap((issue) => issue.comments) + const commentColumns = Object.keys(batchComments[0]) + await batchInsert( + sql, + 'comment', + commentColumns, + batchComments, + BATCH_SIZE + ) + + commentCount += batchComments.length + }) + + process.stdout.write( + `\nProcessed batch ${Math.floor(i / BATCH_SIZE) + 1}: ${Math.min(i + BATCH_SIZE, issues.length)} of ${issues.length} issues\n` + ) + } + + console.info(`Loaded ${issueCount} issues with ${commentCount} comments.`) +} catch (error) { + console.error('Error loading data:', error) + throw error +} finally { + await sql.end() +} diff --git a/demos/linearlite/db/migrations-client/01-create_tables.sql b/demos/linearlite/db/migrations-client/01-create_tables.sql new file mode 100644 index 000000000..c0a5c1e4b --- /dev/null +++ b/demos/linearlite/db/migrations-client/01-create_tables.sql @@ -0,0 +1,290 @@ +-- # Tables and indexes +CREATE TABLE IF NOT EXISTS "issue" ( + "id" UUID NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "priority" TEXT NOT NULL, + "status" TEXT NOT NULL, + "modified" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "created" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "kanbanorder" TEXT NOT NULL, + "username" TEXT NOT NULL, + "deleted" BOOLEAN NOT NULL DEFAULT FALSE, -- Soft delete for local deletions + "new" BOOLEAN NOT NULL DEFAULT FALSE, -- New row flag for local inserts + "modified_columns" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], -- Columns that have been modified locally + "sent_to_server" BOOLEAN NOT NULL DEFAULT FALSE, -- Flag to track if the row has been sent to the server + "synced" BOOLEAN GENERATED ALWAYS AS (ARRAY_LENGTH(modified_columns, 1) IS NULL AND NOT deleted AND NOT new) STORED, + "backup" JSONB, -- JSONB column to store the backup of the row data for modified columns + CONSTRAINT "issue_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE IF NOT EXISTS "comment" ( + "id" UUID NOT NULL, + "body" TEXT NOT NULL, + "username" TEXT NOT NULL, + "issue_id" UUID NOT NULL, + "modified" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "created" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "deleted" BOOLEAN NOT NULL DEFAULT FALSE, -- Soft delete for local deletions + "new" BOOLEAN NOT NULL DEFAULT FALSE, -- New row flag for local inserts + "modified_columns" TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], -- Columns that have been modified locally + "sent_to_server" BOOLEAN NOT NULL DEFAULT FALSE, -- Flag to track if the row has been sent to the server + "synced" BOOLEAN GENERATED ALWAYS AS (ARRAY_LENGTH(modified_columns, 1) IS NULL AND NOT deleted AND NOT new) STORED, + "backup" JSONB, -- JSONB column to store the backup of the row data for modified columns + CONSTRAINT "comment_pkey" PRIMARY KEY ("id") +); + +CREATE INDEX IF NOT EXISTS "issue_id_idx" ON "issue" ("id"); + +CREATE INDEX IF NOT EXISTS "comment_id_idx" ON "comment" ("id"); + +-- During sync the electric.syncing config var is set to true +-- We can use this in triggers to determine the action that should be performed + +-- # Delete triggers: +-- - During sync we delete rows +-- - Otherwise we set the deleted flag to true +CREATE OR REPLACE FUNCTION handle_delete() +RETURNS TRIGGER AS $$ +DECLARE + is_syncing BOOLEAN; + bypass_triggers BOOLEAN; +BEGIN + -- Check if electric.syncing is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.syncing', true), ''), 'false')::boolean INTO is_syncing; + -- Check if electric.bypass_triggers is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.bypass_triggers', true), ''), 'false')::boolean INTO bypass_triggers; + + IF bypass_triggers THEN + RETURN OLD; + END IF; + + IF is_syncing THEN + -- If syncing we delete the row + RETURN OLD; + ELSE + -- For local deletions, check if the row is new + IF OLD.new THEN + -- If the row is new, just delete it + RETURN OLD; + ELSE + -- Otherwise, set the deleted flag instead of actually deleting + EXECUTE format('UPDATE %I SET deleted = true WHERE id = $1', TG_TABLE_NAME) USING OLD.id; + RETURN NULL; + END IF; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER issue_delete_trigger +BEFORE DELETE ON issue +FOR EACH ROW +EXECUTE FUNCTION handle_delete(); + +CREATE OR REPLACE TRIGGER comment_delete_trigger +BEFORE DELETE ON comment +FOR EACH ROW +EXECUTE FUNCTION handle_delete(); + +-- # Insert triggers: +-- - During sync we insert rows and set modified_columns = [] +-- - Otherwise we insert rows and set modified_columns to contain the names of all +-- columns that are not local-state related + +CREATE OR REPLACE FUNCTION handle_insert() +RETURNS TRIGGER AS $$ +DECLARE + is_syncing BOOLEAN; + bypass_triggers BOOLEAN; + modified_columns TEXT[] := ARRAY[]::TEXT[]; + col_name TEXT; + new_value TEXT; + old_value TEXT; +BEGIN + -- Check if electric.syncing is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.syncing', true), ''), 'false')::boolean INTO is_syncing; + -- Check if electric.bypass_triggers is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.bypass_triggers', true), ''), 'false')::boolean INTO bypass_triggers; + + IF bypass_triggers THEN + RETURN NEW; + END IF; + + IF is_syncing THEN + -- If syncing, we set modified_columns to an empty array + NEW.modified_columns := ARRAY[]::TEXT[]; + NEW.new := FALSE; + NEW.sent_to_server := FALSE; + -- If the row already exists in the database, handle it as an update + EXECUTE format('SELECT 1 FROM %I WHERE id = $1', TG_TABLE_NAME) USING NEW.id INTO old_value; + IF old_value IS NOT NULL THEN + -- Apply update logic similar to handle_update function + FOR col_name IN SELECT column_name + FROM information_schema.columns + WHERE table_name = TG_TABLE_NAME AND + table_schema = TG_TABLE_SCHEMA AND + column_name NOT IN ('id', 'synced', 'modified_columns', 'backup', 'deleted', 'new', 'sent_to_server', 'search_vector') LOOP + EXECUTE format('SELECT $1.%I', col_name) USING NEW INTO new_value; + EXECUTE format('SELECT %I FROM %I WHERE id = $1', col_name, TG_TABLE_NAME) USING NEW.id INTO old_value; + IF new_value IS DISTINCT FROM old_value THEN + EXECUTE format('UPDATE %I SET %I = $1 WHERE id = $2', TG_TABLE_NAME, col_name) USING new_value, NEW.id; + END IF; + END LOOP; + -- Update modified_columns + EXECUTE format('UPDATE %I SET modified_columns = $1 WHERE id = $2', TG_TABLE_NAME) + USING ARRAY[]::TEXT[], NEW.id; + -- Update new flag + EXECUTE format('UPDATE %I SET new = $1 WHERE id = $2', TG_TABLE_NAME) + USING FALSE, NEW.id; + -- Update sent_to_server flag + EXECUTE format('UPDATE %I SET sent_to_server = $1 WHERE id = $2', TG_TABLE_NAME) + USING FALSE, NEW.id; + RETURN NULL; -- Prevent insertion of a new row + END IF; + ELSE + -- For local inserts, we add all non-local-state columns to modified_columns + SELECT array_agg(column_name) INTO modified_columns + FROM information_schema.columns + WHERE table_name = TG_TABLE_NAME + AND column_name NOT IN ('id', 'synced', 'modified_columns', 'backup', 'deleted', 'new', 'sent_to_server', 'search_vector'); + NEW.modified_columns := modified_columns; + NEW.new := TRUE; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER issue_insert_trigger +BEFORE INSERT ON issue +FOR EACH ROW +EXECUTE FUNCTION handle_insert(); + +CREATE OR REPLACE TRIGGER comment_insert_trigger +BEFORE INSERT ON comment +FOR EACH ROW +EXECUTE FUNCTION handle_insert(); + +-- # Update triggers: +-- - During sync: +-- - If the new modified timestamp is >= the one in the database, we apply the update, +-- set modified_columns = [], and set backup = NULL +-- - Otherwise we apply the update to columns that are NOT in modified_columns and +-- - and save the values for the non-updated columns in the backup JSONB column +-- - During a non-sync transaction: +-- - If we write over a column (that are not local-state related) that was not +-- already modified, we add that column name to modified_columns, and copy the +-- current value from the column to the backup JSONB column +-- - Otherwise we just update the column + +CREATE OR REPLACE FUNCTION handle_update() +RETURNS TRIGGER AS $$ +DECLARE + is_syncing BOOLEAN; + bypass_triggers BOOLEAN; + column_name TEXT; + old_value TEXT; + new_value TEXT; +BEGIN + -- Check if electric.syncing is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.syncing', true), ''), 'false')::boolean INTO is_syncing; + -- Check if electric.bypass_triggers is true - defaults to false if not set + SELECT COALESCE(NULLIF(current_setting('electric.bypass_triggers', true), ''), 'false')::boolean INTO bypass_triggers; + + IF bypass_triggers THEN + RETURN NEW; + END IF; + + IF is_syncing THEN + -- During sync + IF (OLD.synced = TRUE) OR (OLD.sent_to_server = TRUE AND NEW.modified >= OLD.modified) THEN + -- Apply the update, reset modified_columns, backup, new, and sent_to_server flags + NEW.modified_columns := ARRAY[]::TEXT[]; + NEW.backup := NULL; + NEW.new := FALSE; + NEW.sent_to_server := FALSE; + ELSE + -- Apply update only to columns not in modified_columns + FOR column_name IN SELECT columns.column_name + FROM information_schema.columns + WHERE columns.table_name = TG_TABLE_NAME + AND columns.table_schema = TG_TABLE_SCHEMA + AND columns.column_name NOT IN ('id', 'synced', 'modified_columns', 'backup', 'deleted', 'new', 'sent_to_server', 'search_vector') LOOP + IF column_name != ANY(OLD.modified_columns) THEN + EXECUTE format('SELECT ($1).%I', column_name) USING NEW INTO new_value; + EXECUTE format('SELECT ($1).%I', column_name) USING OLD INTO old_value; + IF new_value IS DISTINCT FROM old_value THEN + EXECUTE format('UPDATE %I SET %I = $1 WHERE id = $2', TG_TABLE_NAME, column_name) USING new_value, NEW.id; + NEW.backup := jsonb_set(COALESCE(NEW.backup, '{}'::jsonb), ARRAY[column_name], to_jsonb(old_value)); + END IF; + END IF; + END LOOP; + NEW.new := FALSE; + END IF; + ELSE + -- During non-sync transaction + FOR column_name IN SELECT columns.column_name + FROM information_schema.columns + WHERE columns.table_name = TG_TABLE_NAME + AND columns.table_schema = TG_TABLE_SCHEMA + AND columns.column_name NOT IN ('id', 'synced', 'modified_columns', 'backup', 'deleted', 'new', 'sent_to_server', 'search_vector') LOOP + EXECUTE format('SELECT ($1).%I', column_name) USING NEW INTO new_value; + EXECUTE format('SELECT ($1).%I', column_name) USING OLD INTO old_value; + IF new_value IS DISTINCT FROM old_value THEN + IF NOT (column_name = ANY(OLD.modified_columns)) THEN + NEW.modified_columns := array_append(NEW.modified_columns, column_name); + NEW.backup := jsonb_set(COALESCE(NEW.backup, '{}'::jsonb), ARRAY[column_name], to_jsonb(old_value)); + END IF; + END IF; + END LOOP; + NEW.sent_to_server := FALSE; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER issue_update_trigger +BEFORE UPDATE ON issue +FOR EACH ROW +EXECUTE FUNCTION handle_update(); + +CREATE OR REPLACE TRIGGER comment_update_trigger +BEFORE UPDATE ON comment +FOR EACH ROW +EXECUTE FUNCTION handle_update(); + +-- # Functions to revert local changes using the backup column + +CREATE OR REPLACE FUNCTION revert_local_changes(table_name TEXT, row_id UUID) +RETURNS VOID AS $$ +DECLARE + backup_data JSONB; + column_name TEXT; + column_value JSONB; +BEGIN + EXECUTE format('SELECT backup FROM %I WHERE id = $1', table_name) + INTO backup_data + USING row_id; + + IF backup_data IS NOT NULL THEN + FOR column_name, column_value IN SELECT * FROM jsonb_each(backup_data) + LOOP + EXECUTE format('UPDATE %I SET %I = $1, modified_columns = array_remove(modified_columns, $2) WHERE id = $3', table_name, column_name) + USING column_value, column_name, row_id; + END LOOP; + + -- Clear the backup after reverting + EXECUTE format('UPDATE %I SET backup = NULL WHERE id = $1', table_name) + USING row_id; + END IF; +END; +$$ LANGUAGE plpgsql; + +-- Example usage: +-- SELECT revert_local_changes('issue', '123e4567-e89b-12d3-a456-426614174000'); +-- SELECT revert_local_changes('comment', '123e4567-e89b-12d3-a456-426614174001'); + + +ALTER TABLE issue DISABLE TRIGGER ALL; +ALTER TABLE comment DISABLE TRIGGER ALL; diff --git a/demos/linearlite/db/migrations-client/post-initial-sync-fts-index.sql b/demos/linearlite/db/migrations-client/post-initial-sync-fts-index.sql new file mode 100644 index 000000000..9e292c053 --- /dev/null +++ b/demos/linearlite/db/migrations-client/post-initial-sync-fts-index.sql @@ -0,0 +1 @@ +CREATE INDEX IF NOT EXISTS "issue_search_idx" ON "issue" USING GIN ((setweight(to_tsvector('simple', coalesce(title, '')), 'A') || setweight(to_tsvector('simple', coalesce(description, '')), 'B'))); \ No newline at end of file diff --git a/demos/linearlite/db/migrations-client/post-initial-sync-indexes.sql b/demos/linearlite/db/migrations-client/post-initial-sync-indexes.sql new file mode 100644 index 000000000..30328e467 --- /dev/null +++ b/demos/linearlite/db/migrations-client/post-initial-sync-indexes.sql @@ -0,0 +1,11 @@ +CREATE INDEX IF NOT EXISTS "issue_priority_idx" ON "issue" ("priority"); +CREATE INDEX IF NOT EXISTS "issue_status_idx" ON "issue" ("status"); +CREATE INDEX IF NOT EXISTS "issue_modified_idx" ON "issue" ("modified"); +CREATE INDEX IF NOT EXISTS "issue_created_idx" ON "issue" ("created"); +CREATE INDEX IF NOT EXISTS "issue_kanbanorder_idx" ON "issue" ("kanbanorder"); +CREATE INDEX IF NOT EXISTS "issue_deleted_idx" ON "issue" ("deleted"); +CREATE INDEX IF NOT EXISTS "issue_synced_idx" ON "issue" ("synced"); +CREATE INDEX IF NOT EXISTS "comment_issue_id_idxx" ON "comment" ("issue_id"); +CREATE INDEX IF NOT EXISTS "comment_created_idx" ON "comment" ("created"); +CREATE INDEX IF NOT EXISTS "comment_deleted_idx" ON "comment" ("deleted"); +CREATE INDEX IF NOT EXISTS "comment_synced_idx" ON "comment" ("synced"); \ No newline at end of file diff --git a/demos/linearlite/db/migrations-server/01-create_tables.sql b/demos/linearlite/db/migrations-server/01-create_tables.sql new file mode 100644 index 000000000..84c2cac11 --- /dev/null +++ b/demos/linearlite/db/migrations-server/01-create_tables.sql @@ -0,0 +1,24 @@ +-- Create the tables for the linearlite example +CREATE TABLE IF NOT EXISTS "issue" ( + "id" UUID NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, + "priority" TEXT NOT NULL, + "status" TEXT NOT NULL, + "modified" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "created" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "kanbanorder" TEXT NOT NULL, + "username" TEXT NOT NULL, + CONSTRAINT "issue_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE IF NOT EXISTS "comment" ( + "id" UUID NOT NULL, + "body" TEXT NOT NULL, + "username" TEXT NOT NULL, + "issue_id" UUID NOT NULL, + "modified" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + "created" TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT "comment_pkey" PRIMARY KEY ("id"), + FOREIGN KEY (issue_id) REFERENCES issue(id) ON DELETE CASCADE +); diff --git a/demos/linearlite/eslint.config.js b/demos/linearlite/eslint.config.js new file mode 100644 index 000000000..a12acf76f --- /dev/null +++ b/demos/linearlite/eslint.config.js @@ -0,0 +1,32 @@ +import rootConfig from "../../eslint.config.js"; +import pluginReact from "@eslint-react/eslint-plugin"; +// @ts-expect-error no types +import pluginReactCompiler from "eslint-plugin-react-compiler"; +// @ts-expect-error no types +import pluginReactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...rootConfig, + { + files: ["**/*.{ts,tsx}"], + ...pluginReact.configs.recommended, + }, + { + plugins: { + "react-hooks": pluginReactHooks, + "react-compiler": pluginReactCompiler, + }, + rules: { + "react-compiler/react-compiler": "error", + "react-hooks/exhaustive-deps": "error", + "react-hooks/rules-of-hooks": "error", + }, + }, + { + files: ["**/test/**"], + rules: { + "@typescript-eslint/no-unnecessary-condition": "off", + "react-compiler/react-compiler": "off", + }, + }, +]; diff --git a/demos/linearlite/index.html b/demos/linearlite/index.html new file mode 100644 index 000000000..124f0f97f --- /dev/null +++ b/demos/linearlite/index.html @@ -0,0 +1,21 @@ + + + + + + + LinearLite + + + + + + +
+
+ + + diff --git a/demos/linearlite/package.json b/demos/linearlite/package.json new file mode 100644 index 000000000..68b17d836 --- /dev/null +++ b/demos/linearlite/package.json @@ -0,0 +1,99 @@ +{ + "name": "@pglite-demos/linearlite", + "version": "0.0.1", + "license": "Apache-2.0", + "private": true, + "type": "module", + "scripts": { + "backend:up": "dotenv -- docker compose -f ./backend/docker-compose.yml up -d && pnpm db:migrate", + "backend:down": "dotenv -- docker compose -f ./backend/docker-compose.yml down --volumes", + "db:migrate": "dotenv -- pnpm exec pg-migrations apply --directory ./db/migrations-server", + "db:load-data": "dotenv -- node ./db/load_data.js", + "reset": "pnpm backend:down && pnpm backend:up && pnpm db:migrate && pnpm db:load-data", + "write-server": "dotenv -- tsx server.ts", + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "typecheck": "tsc --noEmit", + "process-data": "node ./db/process_data.js", + "format": "prettier --write ./src", + "lint": "eslint ./src", + "stylecheck": "eslint ./src && prettier --check ./src ./db" + }, + "dependencies": { + "@electric-sql/pglite": "workspace:*", + "@electric-sql/pglite-react": "workspace:*", + "@electric-sql/pglite-repl": "workspace:*", + "@electric-sql/pglite-sync": "workspace:*", + "@firefox-devtools/react-contextmenu": "^5.1.1", + "@headlessui/react": "^1.7.17", + "@hono/node-server": "^1.8.0", + "@svgr/plugin-jsx": "^8.1.0", + "@svgr/plugin-svgo": "^8.1.0", + "@tailwindcss/forms": "^0.5.6", + "@tiptap/extension-placeholder": "^2.4.0", + "@tiptap/extension-table": "^2.4.0", + "@tiptap/extension-table-cell": "^2.4.0", + "@tiptap/extension-table-header": "^2.4.0", + "@tiptap/extension-table-row": "^2.4.0", + "@tiptap/pm": "^2.4.0", + "@tiptap/react": "^2.4.0", + "@tiptap/starter-kit": "^2.4.0", + "animate.css": "^4.1.1", + "body-parser": "^1.20.3", + "classnames": "^2.5.1", + "cors": "^2.8.5", + "dayjs": "^1.11.11", + "dotenv": "^16.4.5", + "fractional-indexing": "^3.2.0", + "hono": "^4.0.0", + "jsonwebtoken": "^9.0.2", + "lodash.debounce": "^4.0.8", + "postgres": "^3.4.3", + "react": "^18.3.1", + "react-beautiful-dnd": "^13.1.1", + "react-dom": "^18.3.1", + "react-icons": "^4.10.1", + "react-markdown": "^8.0.7", + "react-router-dom": "^6.24.1", + "react-toastify": "^9.1.3", + "react-virtualized-auto-sizer": "^1.0.24", + "react-window": "^1.8.10", + "tiptap-markdown": "^0.8.2", + "uuid": "^9.0.0", + "vite-plugin-svgr": "^3.2.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@databases/pg-migrations": "^5.0.3", + "@faker-js/faker": "^8.4.1", + "@tailwindcss/typography": "^0.5.10", + "@types/body-parser": "^1.19.5", + "@types/jest": "^29.5.12", + "@types/lodash.debounce": "^4.0.9", + "@types/node": "^20.14.10", + "@types/pg": "^8.11.10", + "@types/react": "^18.3.3", + "@types/react-beautiful-dnd": "^13.1.8", + "@types/react-dom": "^18.3.0", + "@types/react-router-dom": "^5.3.3", + "@types/react-window": "^1.8.8", + "@types/uuid": "^9.0.3", + "@types/vite-plugin-react-svg": "^0.2.5", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.19", + "dotenv-cli": "^7.4.2", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-refresh": "^0.4.3", + "fs-extra": "^10.0.0", + "postcss": "^8.4.39", + "supabase": "^1.226.3", + "tailwindcss": "^3.4.4", + "tsx": "^4.19.1", + "typescript": "^5.5.3", + "vite": "^5.4.8" + } +} diff --git a/demos/linearlite/postcss.config.mjs b/demos/linearlite/postcss.config.mjs new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/demos/linearlite/postcss.config.mjs @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/demos/linearlite/public/electric-icon.png b/demos/linearlite/public/electric-icon.png new file mode 100644 index 000000000..4ed9e1e14 Binary files /dev/null and b/demos/linearlite/public/electric-icon.png differ diff --git a/demos/linearlite/public/favicon.ico b/demos/linearlite/public/favicon.ico new file mode 100644 index 000000000..d1db33bc7 Binary files /dev/null and b/demos/linearlite/public/favicon.ico differ diff --git a/demos/linearlite/public/logo192.png b/demos/linearlite/public/logo192.png new file mode 100644 index 000000000..ec14f2452 Binary files /dev/null and b/demos/linearlite/public/logo192.png differ diff --git a/demos/linearlite/public/logo512.png b/demos/linearlite/public/logo512.png new file mode 100644 index 000000000..ec14f2452 Binary files /dev/null and b/demos/linearlite/public/logo512.png differ diff --git a/demos/linearlite/public/netlify.toml b/demos/linearlite/public/netlify.toml new file mode 100644 index 000000000..ec5ebb864 --- /dev/null +++ b/demos/linearlite/public/netlify.toml @@ -0,0 +1,5 @@ + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 diff --git a/demos/linearlite/public/robots.txt b/demos/linearlite/public/robots.txt new file mode 100644 index 000000000..e9e57dc4d --- /dev/null +++ b/demos/linearlite/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/demos/linearlite/server.ts b/demos/linearlite/server.ts new file mode 100644 index 000000000..5fee4e2c5 --- /dev/null +++ b/demos/linearlite/server.ts @@ -0,0 +1,101 @@ +import { Hono } from 'hono' +import { cors } from 'hono/cors' +import postgres from 'postgres' +import { + ChangeSet, + changeSetSchema, + CommentChange, + IssueChange, +} from './src/utils/changes' +import { serve } from '@hono/node-server' + +const DATABASE_URL = + process.env.DATABASE_URL ?? + 'postgresql://postgres:password@localhost:54321/linearlite' + +// Create postgres connection +const sql = postgres(DATABASE_URL) + +const app = new Hono() + +// Middleware +app.use('/*', cors()) + +// Routes +app.get('/', async (c) => { + const result = await sql` + SELECT 'ok' as status, version() as postgres_version, now() as server_time + ` + return c.json(result[0]) +}) + +app.post('/apply-changes', async (c) => { + const content = await c.req.json() + let parsedChanges: ChangeSet + try { + parsedChanges = changeSetSchema.parse(content) + // Any additional validation of the changes can be done here! + } catch (error) { + console.error(error) + return c.json({ error: 'Invalid changes' }, 400) + } + const changeResponse = await applyChanges(parsedChanges) + return c.json(changeResponse) +}) + +// Start the server +const port = 3001 +console.log(`Server is running on port ${port}`) + +serve({ + fetch: app.fetch, + port, +}) + +async function applyChanges(changes: ChangeSet): Promise<{ success: boolean }> { + const { issues, comments } = changes + + try { + await sql.begin(async (sql) => { + for (const issue of issues) { + await applyTableChange('issue', issue, sql) + } + for (const comment of comments) { + await applyTableChange('comment', comment, sql) + } + }) + return { success: true } + } catch (error) { + throw error + } +} + +async function applyTableChange( + tableName: 'issue' | 'comment', + change: IssueChange | CommentChange, + sql: postgres.TransactionSql +): Promise { + const { + id, + modified_columns: modified_columns_raw, + new: isNew, + deleted, + } = change + const modified_columns = modified_columns_raw as (keyof typeof change)[] + + if (deleted) { + await sql` + DELETE FROM ${sql(tableName)} WHERE id = ${id} + ` + } else if (isNew) { + await sql` + INSERT INTO ${sql(tableName)} ${sql(change, 'id', ...modified_columns)} + ` + } else { + await sql` + UPDATE ${sql(tableName)} + SET ${sql(change, ...modified_columns)} + WHERE id = ${id} + ` + } +} diff --git a/demos/linearlite/src/App.tsx b/demos/linearlite/src/App.tsx new file mode 100644 index 000000000..de86fced5 --- /dev/null +++ b/demos/linearlite/src/App.tsx @@ -0,0 +1,221 @@ +import 'animate.css/animate.min.css' +import Board from './pages/Board' +import { useState, createContext, useEffect, useMemo } from 'react' +import { + createBrowserRouter, + RouterProvider, + type Params, +} from 'react-router-dom' +import 'react-toastify/dist/ReactToastify.css' +import { live, LiveNamespace, LiveQuery } from '@electric-sql/pglite/live' +import { PGliteWorker } from '@electric-sql/pglite/worker' +import { PGliteProvider } from '@electric-sql/pglite-react' +import PGWorker from './pglite-worker.js?worker' +import List from './pages/List' +import Root from './pages/root' +import Issue from './pages/Issue' +import { + getFilterStateFromSearchParams, + filterStateToSql, + FilterState, +} from './utils/filterState' +import { Issue as IssueType, Status, StatusValue } from './types/types' +import { startSync, useSyncStatus, waitForInitialSyncDone } from './sync' +import { electricSync } from '@electric-sql/pglite-sync' +import { ImSpinner8 } from 'react-icons/im' + +interface MenuContextInterface { + showMenu: boolean + setShowMenu: (show: boolean) => void +} + +export const MenuContext = createContext(null as MenuContextInterface | null) + +type PGliteWorkerWithLive = PGliteWorker & { live: LiveNamespace } + +async function createPGlite() { + return PGliteWorker.create(new PGWorker(), { + extensions: { + live, + sync: electricSync(), + }, + }) +} + +const pgPromise = createPGlite() + +let syncStarted = false +pgPromise.then(async (pg) => { + console.log('PGlite worker started') + pg.onLeaderChange(() => { + console.log('Leader changed, isLeader:', pg.isLeader) + if (pg.isLeader && !syncStarted) { + syncStarted = true + startSync(pg) + } + }) +}) + +let resolveFirstLoaderPromise: (value: void | PromiseLike) => void +const firstLoaderPromise = new Promise((resolve) => { + resolveFirstLoaderPromise = resolve +}) + +async function issueListLoader({ request }: { request: Request }) { + await waitForInitialSyncDone() + const pg = await pgPromise + const url = new URL(request.url) + const filterState = getFilterStateFromSearchParams(url.searchParams) + const { sql, sqlParams } = filterStateToSql(filterState) + const liveIssues = await pg.live.query({ + query: sql, + params: sqlParams, + signal: request.signal, + offset: 0, + limit: 100, + }) + resolveFirstLoaderPromise() + return { liveIssues, filterState } +} + +async function boardIssueListLoader({ request }: { request: Request }) { + await waitForInitialSyncDone() + const pg = await pgPromise + const url = new URL(request.url) + const filterState = getFilterStateFromSearchParams(url.searchParams) + + const columnsLiveIssues: Partial>> = + {} + + for (const status of Object.values(Status)) { + const colFilterState: FilterState = { + ...filterState, + orderBy: 'kanbanorder', + orderDirection: 'asc', + status: [status], + } + const { sql: colSql, sqlParams: colSqlParams } = + filterStateToSql(colFilterState) + const colLiveIssues = await pg.live.query({ + query: colSql, + params: colSqlParams, + signal: request.signal, + offset: 0, + limit: 10, + }) + columnsLiveIssues[status] = colLiveIssues + } + + resolveFirstLoaderPromise() + + return { + columnsLiveIssues: columnsLiveIssues as Record< + StatusValue, + LiveQuery + >, + filterState, + } +} + +async function issueLoader({ + params, + request, +}: { + params: Params + request: Request +}) { + const pg = await pgPromise + const liveIssue = await pg.live.query({ + query: `SELECT * FROM issue WHERE id = $1`, + params: [params.id], + signal: request.signal, + }) + return { liveIssue } +} + +const router = createBrowserRouter([ + { + path: `/`, + element: , + children: [ + { + path: `/`, + element: , + loader: issueListLoader, + }, + { + path: `/search`, + element: , + loader: issueListLoader, + }, + { + path: `/board`, + element: , + loader: boardIssueListLoader, + }, + { + path: `/issue/:id`, + element: , + loader: issueLoader, + }, + ], + }, +]) + +const LoadingScreen = ({ children }: { children: React.ReactNode }) => { + return ( +
+ +
+ {children} +
+
+ ) +} + +const App = () => { + const [showMenu, setShowMenu] = useState(false) + const [pgForProvider, setPgForProvider] = + useState(null) + const [syncStatus, syncMessage] = useSyncStatus() + const [firstLoaderDone, setFirstLoaderDone] = useState(false) + + useEffect(() => { + pgPromise.then(setPgForProvider) + }, []) + + useEffect(() => { + if (firstLoaderDone) return + firstLoaderPromise.then(() => { + setFirstLoaderDone(true) + }) + }, [firstLoaderDone]) + + const menuContextValue = useMemo( + () => ({ showMenu, setShowMenu }), + [showMenu] + ) + + if (!pgForProvider) return Starting PGlite... + + if (syncStatus === 'initial-sync') + return ( + + Performing initial sync... +
+ {syncMessage} +
+ ) + + if (!firstLoaderDone) return Loading... + + return ( + + + + + + ) +} + +export default App diff --git a/demos/linearlite/src/assets/fonts/27237475-28043385 b/demos/linearlite/src/assets/fonts/27237475-28043385 new file mode 100644 index 000000000..21a8f5078 Binary files /dev/null and b/demos/linearlite/src/assets/fonts/27237475-28043385 differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff b/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff new file mode 100644 index 000000000..4f61ad0ec Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff2 b/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff2 new file mode 100644 index 000000000..19b58e07c Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-ExtraBold.woff2 differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff b/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff new file mode 100644 index 000000000..860da965f Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff2 b/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff2 new file mode 100644 index 000000000..c35427fa7 Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-Medium.woff2 differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff b/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff new file mode 100644 index 000000000..dea603220 Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff2 b/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff2 new file mode 100644 index 000000000..cddb43653 Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-Regular.woff2 differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff b/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff new file mode 100644 index 000000000..ec6f74b0b Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff differ diff --git a/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff2 b/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff2 new file mode 100644 index 000000000..ec6f74b0b Binary files /dev/null and b/demos/linearlite/src/assets/fonts/Inter-UI-SemiBold.woff2 differ diff --git a/demos/linearlite/src/assets/icons/add-subissue.svg b/demos/linearlite/src/assets/icons/add-subissue.svg new file mode 100644 index 000000000..e08d380d9 --- /dev/null +++ b/demos/linearlite/src/assets/icons/add-subissue.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/add.svg b/demos/linearlite/src/assets/icons/add.svg new file mode 100644 index 000000000..77d1805c1 --- /dev/null +++ b/demos/linearlite/src/assets/icons/add.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/archive.svg b/demos/linearlite/src/assets/icons/archive.svg new file mode 100644 index 000000000..791b3b817 --- /dev/null +++ b/demos/linearlite/src/assets/icons/archive.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/assignee.svg b/demos/linearlite/src/assets/icons/assignee.svg new file mode 100644 index 000000000..0110ea5b4 --- /dev/null +++ b/demos/linearlite/src/assets/icons/assignee.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/attachment.svg b/demos/linearlite/src/assets/icons/attachment.svg new file mode 100644 index 000000000..408625211 --- /dev/null +++ b/demos/linearlite/src/assets/icons/attachment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/avatar.svg b/demos/linearlite/src/assets/icons/avatar.svg new file mode 100644 index 000000000..3021b1491 --- /dev/null +++ b/demos/linearlite/src/assets/icons/avatar.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/cancel.svg b/demos/linearlite/src/assets/icons/cancel.svg new file mode 100644 index 000000000..f5fe6e873 --- /dev/null +++ b/demos/linearlite/src/assets/icons/cancel.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/chat.svg b/demos/linearlite/src/assets/icons/chat.svg new file mode 100644 index 000000000..05882262e --- /dev/null +++ b/demos/linearlite/src/assets/icons/chat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/circle-dot.svg b/demos/linearlite/src/assets/icons/circle-dot.svg new file mode 100644 index 000000000..67568703b --- /dev/null +++ b/demos/linearlite/src/assets/icons/circle-dot.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/linearlite/src/assets/icons/circle.svg b/demos/linearlite/src/assets/icons/circle.svg new file mode 100644 index 000000000..fac3b81fc --- /dev/null +++ b/demos/linearlite/src/assets/icons/circle.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/claim.svg b/demos/linearlite/src/assets/icons/claim.svg new file mode 100644 index 000000000..6a31eb376 --- /dev/null +++ b/demos/linearlite/src/assets/icons/claim.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/close.svg b/demos/linearlite/src/assets/icons/close.svg new file mode 100644 index 000000000..f17488caa --- /dev/null +++ b/demos/linearlite/src/assets/icons/close.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/delete.svg b/demos/linearlite/src/assets/icons/delete.svg new file mode 100644 index 000000000..eb3fe397e --- /dev/null +++ b/demos/linearlite/src/assets/icons/delete.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/done.svg b/demos/linearlite/src/assets/icons/done.svg new file mode 100644 index 000000000..4904f818d --- /dev/null +++ b/demos/linearlite/src/assets/icons/done.svg @@ -0,0 +1,3 @@ + + + diff --git a/demos/linearlite/src/assets/icons/dots.svg b/demos/linearlite/src/assets/icons/dots.svg new file mode 100644 index 000000000..8193e5c16 --- /dev/null +++ b/demos/linearlite/src/assets/icons/dots.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/due-date.svg b/demos/linearlite/src/assets/icons/due-date.svg new file mode 100644 index 000000000..bf888e0fd --- /dev/null +++ b/demos/linearlite/src/assets/icons/due-date.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/dupplication.svg b/demos/linearlite/src/assets/icons/dupplication.svg new file mode 100644 index 000000000..3d5ebb62a --- /dev/null +++ b/demos/linearlite/src/assets/icons/dupplication.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/filter.svg b/demos/linearlite/src/assets/icons/filter.svg new file mode 100644 index 000000000..86ea60ed8 --- /dev/null +++ b/demos/linearlite/src/assets/icons/filter.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/git-issue.svg b/demos/linearlite/src/assets/icons/git-issue.svg new file mode 100644 index 000000000..de681ca9f --- /dev/null +++ b/demos/linearlite/src/assets/icons/git-issue.svg @@ -0,0 +1,4 @@ + + + + diff --git a/demos/linearlite/src/assets/icons/guide.svg b/demos/linearlite/src/assets/icons/guide.svg new file mode 100644 index 000000000..41f941a76 --- /dev/null +++ b/demos/linearlite/src/assets/icons/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/half-circle.svg b/demos/linearlite/src/assets/icons/half-circle.svg new file mode 100644 index 000000000..7a4f0b28d --- /dev/null +++ b/demos/linearlite/src/assets/icons/half-circle.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/help.svg b/demos/linearlite/src/assets/icons/help.svg new file mode 100644 index 000000000..88ed0a4d9 --- /dev/null +++ b/demos/linearlite/src/assets/icons/help.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/inbox.svg b/demos/linearlite/src/assets/icons/inbox.svg new file mode 100644 index 000000000..8bbd1f375 --- /dev/null +++ b/demos/linearlite/src/assets/icons/inbox.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/issue.svg b/demos/linearlite/src/assets/icons/issue.svg new file mode 100644 index 000000000..b7ac9f03d --- /dev/null +++ b/demos/linearlite/src/assets/icons/issue.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/label.svg b/demos/linearlite/src/assets/icons/label.svg new file mode 100644 index 000000000..02109d29e --- /dev/null +++ b/demos/linearlite/src/assets/icons/label.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/menu.svg b/demos/linearlite/src/assets/icons/menu.svg new file mode 100644 index 000000000..404573a3a --- /dev/null +++ b/demos/linearlite/src/assets/icons/menu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/parent-issue.svg b/demos/linearlite/src/assets/icons/parent-issue.svg new file mode 100644 index 000000000..23635e919 --- /dev/null +++ b/demos/linearlite/src/assets/icons/parent-issue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/plus.svg b/demos/linearlite/src/assets/icons/plus.svg new file mode 100644 index 000000000..d874b1c66 --- /dev/null +++ b/demos/linearlite/src/assets/icons/plus.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/project.svg b/demos/linearlite/src/assets/icons/project.svg new file mode 100644 index 000000000..89e1f8ceb --- /dev/null +++ b/demos/linearlite/src/assets/icons/project.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/question.svg b/demos/linearlite/src/assets/icons/question.svg new file mode 100644 index 000000000..c83642674 --- /dev/null +++ b/demos/linearlite/src/assets/icons/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/relationship.svg b/demos/linearlite/src/assets/icons/relationship.svg new file mode 100644 index 000000000..cb0dc32c7 --- /dev/null +++ b/demos/linearlite/src/assets/icons/relationship.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/rounded-claim.svg b/demos/linearlite/src/assets/icons/rounded-claim.svg new file mode 100644 index 000000000..0ed5d811e --- /dev/null +++ b/demos/linearlite/src/assets/icons/rounded-claim.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/search.svg b/demos/linearlite/src/assets/icons/search.svg new file mode 100644 index 000000000..01f82dfe4 --- /dev/null +++ b/demos/linearlite/src/assets/icons/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/signal-medium.svg b/demos/linearlite/src/assets/icons/signal-medium.svg new file mode 100644 index 000000000..4aac5b254 --- /dev/null +++ b/demos/linearlite/src/assets/icons/signal-medium.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/signal-strong.svg b/demos/linearlite/src/assets/icons/signal-strong.svg new file mode 100644 index 000000000..5d6f38ce7 --- /dev/null +++ b/demos/linearlite/src/assets/icons/signal-strong.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/signal-strong.xsd b/demos/linearlite/src/assets/icons/signal-strong.xsd new file mode 100644 index 000000000..da262c9b7 --- /dev/null +++ b/demos/linearlite/src/assets/icons/signal-strong.xsd @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/signal-weak.svg b/demos/linearlite/src/assets/icons/signal-weak.svg new file mode 100644 index 000000000..a5a5a6b77 --- /dev/null +++ b/demos/linearlite/src/assets/icons/signal-weak.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/slack.svg b/demos/linearlite/src/assets/icons/slack.svg new file mode 100644 index 000000000..0aec65f54 --- /dev/null +++ b/demos/linearlite/src/assets/icons/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/view.svg b/demos/linearlite/src/assets/icons/view.svg new file mode 100644 index 000000000..86bc611d4 --- /dev/null +++ b/demos/linearlite/src/assets/icons/view.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/demos/linearlite/src/assets/icons/zoom.svg b/demos/linearlite/src/assets/icons/zoom.svg new file mode 100644 index 000000000..409044326 --- /dev/null +++ b/demos/linearlite/src/assets/icons/zoom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/assets/images/icon.inverse.svg b/demos/linearlite/src/assets/images/icon.inverse.svg new file mode 100644 index 000000000..142b79bdf --- /dev/null +++ b/demos/linearlite/src/assets/images/icon.inverse.svg @@ -0,0 +1,4 @@ + + + + diff --git a/demos/linearlite/src/assets/images/logo.svg b/demos/linearlite/src/assets/images/logo.svg new file mode 100644 index 000000000..9dfc1c058 --- /dev/null +++ b/demos/linearlite/src/assets/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/demos/linearlite/src/components/AboutModal.tsx b/demos/linearlite/src/components/AboutModal.tsx new file mode 100644 index 000000000..21cdffac4 --- /dev/null +++ b/demos/linearlite/src/components/AboutModal.tsx @@ -0,0 +1,59 @@ +import Modal from './Modal' + +interface Props { + isOpen: boolean + onDismiss?: () => void +} + +export default function AboutModal({ isOpen, onDismiss }: Props) { + return ( + +
+

+ This is an example of a team collaboration app such as{` `} + + Linear + + {` `} + built using{` `} + + ElectricSQL + + {` `}- the local-first sync layer for web and mobile apps. +

+

+ This example is built on top of the excellent clone of the Linear UI + built by{` `} + + Tuan Nguyen + + . +

+

+ We have replaced the canned data with a stack running{` `} + + Electric + + {` `} + in Docker. +

+
+
+ ) +} diff --git a/demos/linearlite/src/components/Avatar.tsx b/demos/linearlite/src/components/Avatar.tsx new file mode 100644 index 000000000..88bc0cd3a --- /dev/null +++ b/demos/linearlite/src/components/Avatar.tsx @@ -0,0 +1,84 @@ +import { MouseEventHandler } from 'react' +import classnames from 'classnames' +import AvatarImg from '../assets/icons/avatar.svg' + +interface Props { + online?: boolean + showOffline?: boolean + name?: string + avatarUrl?: string + onClick?: MouseEventHandler | undefined +} + +//bg-blue-500 + +function stringToHslColor(str: string, s: number, l: number) { + let hash = 0 + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash) + } + + const h = hash % 360 + return `hsl(` + h + `, ` + s + `%, ` + l + `%)` +} + +function getAcronym(name: string) { + let acr = ((name || ``).match(/\b(\w)/g) || []) + .join(``) + .slice(0, 2) + .toUpperCase() + if (acr.length === 1) { + acr = acr + name.slice(1, 2).toLowerCase() + } + return acr +} +function Avatar({ online, showOffline, name, onClick, avatarUrl }: Props) { + let avatar, status + + // create avatar image icon + if (avatarUrl) + avatar = ( + {name} + ) + else if (name !== undefined) { + // use name as avatar + avatar = ( +
+ {getAcronym(name)} +
+ ) + } else { + // try to use default avatar + avatar = ( + avatar + ) + } + + //status icon + if (online || showOffline) + status = ( + // + + ) + else status = null + + return ( +
+ {avatar} + {status} +
+ ) +} + +export default Avatar diff --git a/demos/linearlite/src/components/IssueModal.tsx b/demos/linearlite/src/components/IssueModal.tsx new file mode 100644 index 000000000..1c09f1714 --- /dev/null +++ b/demos/linearlite/src/components/IssueModal.tsx @@ -0,0 +1,196 @@ +import { memo, useEffect, useRef, useState } from 'react' +import { generateKeyBetween } from 'fractional-indexing' + +import { BsChevronRight as ChevronRight } from 'react-icons/bs' +import { ReactComponent as CloseIcon } from '../assets/icons/close.svg' +import { ReactComponent as ElectricIcon } from '../assets/images/icon.inverse.svg' + +import Modal from '../components/Modal' +import Editor from '../components/editor/Editor' +import PriorityIcon from './PriorityIcon' +import StatusIcon from './StatusIcon' +import PriorityMenu from './contextmenu/PriorityMenu' +import StatusMenu from './contextmenu/StatusMenu' + +import { + Priority, + Status, + PriorityDisplay, + StatusValue, + PriorityValue, +} from '../types/types' +import { showInfo, showWarning } from '../utils/notification' +import { usePGlite } from '@electric-sql/pglite-react' +import { Issue } from '../types/types' +import config from '../config' + +interface Props { + isOpen: boolean + onDismiss?: () => void +} + +function IssueModal({ isOpen, onDismiss }: Props) { + const ref = useRef(null) + const [title, setTitle] = useState(``) + const [description, setDescription] = useState() + const [priority, setPriority] = useState(Priority.NONE) + const [status, setStatus] = useState(Status.BACKLOG) + const pg = usePGlite() + + const handleSubmit = async () => { + if (title === '') { + showWarning('Please enter a title before submitting', 'Title required') + return + } + + if (config.readonly) { + showWarning('This is a read-only demo', 'Read-only') + if (onDismiss) onDismiss() + reset() + return + } + + const lastIssue = ( + await pg.query(` + SELECT * FROM issue + ORDER BY kanbanorder DESC + LIMIT 1 + `) + )?.rows[0] + const kanbanorder = generateKeyBetween(lastIssue?.kanbanorder, null) + + const date = new Date() + await pg.sql` + INSERT INTO issue (id, title, username, priority, status, description, modified, created, kanbanorder) + VALUES (${crypto.randomUUID()}, ${title}, ${'testuser'}, ${priority}, ${status}, ${description ?? ''}, ${date}, ${date}, ${kanbanorder}) + ` + + if (onDismiss) onDismiss() + reset() + showInfo(`You created new issue.`, `Issue created`) + } + + const handleClickCloseBtn = () => { + if (onDismiss) onDismiss() + reset() + } + + const reset = () => { + setTimeout(() => { + setTitle(``) + setDescription(``) + setPriority(Priority.NONE) + setStatus(Status.BACKLOG) + }, 250) + } + + const timeoutRef = useRef>() + + useEffect(() => { + if (isOpen && !timeoutRef.current) { + // eslint-disable-next-line @eslint-react/web-api/no-leaked-timeout + timeoutRef.current = setTimeout(() => { + ref.current?.focus() + timeoutRef.current = undefined + }, 250) + } + }, [isOpen]) + + const body = ( +
+ {/* header */} +
+
+ + + electric + + + New Issue +
+
+ +
+
+
+ {/* Issue title */} +
+ + + + } + onSelect={(st) => { + setStatus(st as StatusValue) + }} + /> + setTitle(e.target.value)} + /> +
+ + {/* Issue description editor */} +
+ setDescription(val)} + placeholder="Add description..." + /> +
+
+ + {/* Issue labels & priority */} +
+ + + {PriorityDisplay[priority]} + + } + onSelect={(val) => { + setPriority(val as PriorityValue) + }} + /> +
+ {/* Footer */} +
+ +
+
+ ) + + return ( + + {body} + + ) +} + +export default memo(IssueModal) diff --git a/demos/linearlite/src/components/ItemGroup.tsx b/demos/linearlite/src/components/ItemGroup.tsx new file mode 100644 index 000000000..61e62384d --- /dev/null +++ b/demos/linearlite/src/components/ItemGroup.tsx @@ -0,0 +1,28 @@ +import { BsFillCaretDownFill, BsFillCaretRightFill } from 'react-icons/bs' +import * as React from 'react' +import { useState } from 'react' + +interface Props { + title: string + children: React.ReactNode +} +function ItemGroup({ title, children }: Props) { + const [showItems, setShowItems] = useState(true) + + const Icon = showItems ? BsFillCaretDownFill : BsFillCaretRightFill + return ( +
+ + {showItems && children} +
+ ) +} + +export default ItemGroup diff --git a/demos/linearlite/src/components/LeftMenu.tsx b/demos/linearlite/src/components/LeftMenu.tsx new file mode 100644 index 000000000..df167c84a --- /dev/null +++ b/demos/linearlite/src/components/LeftMenu.tsx @@ -0,0 +1,195 @@ +import { ReactComponent as HelpIcon } from '../assets/icons/help.svg' +import { ReactComponent as MenuIcon } from '../assets/icons/menu.svg' +import { ReactComponent as ElectricIcon } from '../assets/images/icon.inverse.svg' +import { ReactComponent as BacklogIcon } from '../assets/icons/circle-dot.svg' +import { MenuContext } from '../App' +import classnames from 'classnames' +import { memo, RefObject, useRef, useState, useContext } from 'react' +import { BsPencilSquare as AddIcon } from 'react-icons/bs' +import { BsSearch as SearchIcon } from 'react-icons/bs' +import { BsFillGrid3X3GapFill as BoardIcon } from 'react-icons/bs' +import { BsCollectionFill as IssuesIcon } from 'react-icons/bs' +import { MdKeyboardArrowDown as ExpandMore } from 'react-icons/md' +import { BsTerminalFill as ConsoleIcon } from 'react-icons/bs' +import { Link } from 'react-router-dom' +import Avatar from './Avatar' +import AboutModal from './AboutModal' +import IssueModal from './IssueModal' +import PGliteConsoleModal from './PGliteConsoleModal' +import ItemGroup from './ItemGroup' +import ProfileMenu from './ProfileMenu' + +function LeftMenu() { + const ref = useRef() as RefObject + const [showProfileMenu, setShowProfileMenu] = useState(false) + const [showAboutModal, setShowAboutModal] = useState(false) + const [showIssueModal, setShowIssueModal] = useState(false) + const [showPGliteConsoleModal, setShowPGliteConsoleModal] = useState(false) + const { showMenu, setShowMenu } = useContext(MenuContext)! + + const classes = classnames( + `absolute z-40 lg:static inset-0 transform duration-300 lg:relative lg:translate-x-0 bg-white flex flex-col flex-shrink-0 w-56 font-sans text-sm text-gray-700 border-r border-gray-100 lg:shadow-none justify-items-start`, + { + '-translate-x-full ease-out shadow-none': !showMenu, + 'translate-x-0 ease-in shadow-xl': showMenu, + } + ) + + return ( + <> +
+ + + {/* Top menu*/} +
+
+ {/* Project selection */} + + + {/*
+ G +
*/} + electric + + + {/* User avatar */} +
+ + setShowProfileMenu(false)} + setShowAboutModal={setShowAboutModal} + className="absolute top-10" + /> +
+
+ + {/* Create issue btn */} +
+ + + + +
+
+ +
+ + + + All Issues + + + + + + Active + + + + Backlog + + + + Board + + + + {/* extra space */} +
+ + {/* bottom group */} +
+ + + + {` `} + ElectricSQL + + +
+
+
+ {/* Modals */} + { + setShowAboutModal(false)} + /> + } + { + setShowIssueModal(false)} + /> + } + { + setShowPGliteConsoleModal(false)} + /> + } + + ) +} + +export default memo(LeftMenu) diff --git a/demos/linearlite/src/components/Modal.tsx b/demos/linearlite/src/components/Modal.tsx new file mode 100644 index 000000000..3f69409c0 --- /dev/null +++ b/demos/linearlite/src/components/Modal.tsx @@ -0,0 +1,103 @@ +import React, { + memo, + RefObject, + useCallback, + useRef, + type MouseEvent, +} from 'react' +import ReactDOM from 'react-dom' +import classnames from 'classnames' + +import { ReactComponent as CloseIcon } from '../assets/icons/close.svg' +import useLockBodyScroll from '../hooks/useLockBodyScroll' +import { Transition } from '@headlessui/react' + +interface Props { + title?: string + isOpen: boolean + center?: boolean + className?: string + /* function called when modal is closed */ + onDismiss?: () => void + children?: React.ReactNode + size?: keyof typeof sizeClasses +} +const sizeClasses = { + large: `w-175`, + normal: `w-140`, +} + +function Modal({ + title, + isOpen, + center = true, + size = `normal`, + className, + onDismiss, + children, +}: Props) { + const ref = useRef(null) as RefObject + const outerRef = useRef(null) + + const wrapperClasses = classnames( + `fixed flex flex-col items-center inset-0 z-50`, + { + 'justify-center': center, + } + ) + const modalClasses = classnames( + `flex flex-col items-center overflow-hidden transform bg-white modal shadow-large-modal rounded-xl`, + { + 'mt-20 mb-2 ': !center, + }, + sizeClasses[size], + className + ) + const handleClick = useCallback( + (event: MouseEvent) => { + event.stopPropagation() + event.preventDefault() + if (!onDismiss) return + if (ref.current && !ref.current.contains(event.target as Element)) { + onDismiss() + } + }, + [onDismiss] + ) + + useLockBodyScroll() + + const modal = ( +
+ +
+ {title && ( +
+
{title}
+
+ +
+
+ )} + {children} +
+
+
+ ) + + return ReactDOM.createPortal( + modal, + document.getElementById(`root-modal`) as Element + ) +} + +export default memo(Modal) diff --git a/demos/linearlite/src/components/PGliteConsoleModal.tsx b/demos/linearlite/src/components/PGliteConsoleModal.tsx new file mode 100644 index 000000000..f2f018420 --- /dev/null +++ b/demos/linearlite/src/components/PGliteConsoleModal.tsx @@ -0,0 +1,22 @@ +import { Repl } from '@electric-sql/pglite-repl' +import Modal from './Modal' + +interface Props { + isOpen: boolean + onDismiss?: () => void +} + +export default function PGliteConsoleModal({ isOpen, onDismiss }: Props) { + return ( + +
+ +
+
+ ) +} diff --git a/demos/linearlite/src/components/Portal.tsx b/demos/linearlite/src/components/Portal.tsx new file mode 100644 index 000000000..e6e29d484 --- /dev/null +++ b/demos/linearlite/src/components/Portal.tsx @@ -0,0 +1,15 @@ +import { ReactNode, useState } from 'react' +import { useEffect } from 'react' +import { createPortal } from 'react-dom' + +//Copied from https://github.com/tailwindlabs/headlessui/blob/71730fea1291e572ae3efda16d8644f870d87750/packages/%40headlessui-react/pages/menu/menu-with-popper.tsx#L90 +export function Portal(props: { children: ReactNode }) { + const { children } = props + const [mounted, setMounted] = useState(false) + + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect + useEffect(() => setMounted(true), []) + + if (!mounted) return null + return createPortal(children, document.body) +} diff --git a/demos/linearlite/src/components/PriorityIcon.tsx b/demos/linearlite/src/components/PriorityIcon.tsx new file mode 100644 index 000000000..c1105530c --- /dev/null +++ b/demos/linearlite/src/components/PriorityIcon.tsx @@ -0,0 +1,13 @@ +import classNames from 'classnames' +import { PriorityIcons, PriorityValue } from '../types/types' + +interface Props { + priority: string + className?: string +} + +export default function PriorityIcon({ priority, className }: Props) { + const classes = classNames(`w-4 h-4`, className) + const Icon = PriorityIcons[priority.toLowerCase() as PriorityValue] + return +} diff --git a/demos/linearlite/src/components/ProfileMenu.tsx b/demos/linearlite/src/components/ProfileMenu.tsx new file mode 100644 index 000000000..cfc5322e3 --- /dev/null +++ b/demos/linearlite/src/components/ProfileMenu.tsx @@ -0,0 +1,99 @@ +import { Transition } from '@headlessui/react' +import { useRef } from 'react' +import classnames from 'classnames' +import { useClickOutside } from '../hooks/useClickOutside' +import Toggle from './Toggle' + +interface Props { + isOpen: boolean + onDismiss?: () => void + setShowAboutModal?: (show: boolean) => void + className?: string +} +export default function ProfileMenu({ + isOpen, + className, + onDismiss, + setShowAboutModal, +}: Props) { + const connectivityState = { status: `disconnected` } + const classes = classnames( + `select-none w-53 shadow-modal z-50 flex flex-col py-1 bg-white font-normal rounded text-gray-800`, + className + ) + const ref = useRef(null) + + const connectivityConnected = connectivityState.status !== `disconnected` + const connectivityStateDisplay = + connectivityState.status[0].toUpperCase() + + connectivityState.status.slice(1) + + // const toggleConnectivityState = () => { + // if (connectivityConnected) { + // electric.disconnect() + // } else { + // electric.connect() + // } + // } + + useClickOutside(ref, () => { + if (isOpen && onDismiss) { + onDismiss() + } + }) + + return ( +
+ + + + Visit ElectricSQL + + + Documentation + + + GitHub + +
+ + {connectivityStateDisplay} + + +
+
+
+ ) +} diff --git a/demos/linearlite/src/components/Select.tsx b/demos/linearlite/src/components/Select.tsx new file mode 100644 index 000000000..e142cd8cb --- /dev/null +++ b/demos/linearlite/src/components/Select.tsx @@ -0,0 +1,29 @@ +import classNames from 'classnames' +import { ReactNode } from 'react' + +interface Props { + className?: string + children: ReactNode + defaultValue?: string | number | ReadonlyArray + value?: string | number | ReadonlyArray + onChange?: (event: React.ChangeEvent) => void +} +export default function Select(props: Props) { + const { children, defaultValue, className, value, onChange, ...rest } = props + + const classes = classNames( + `form-select text-xs focus:ring-transparent form-select text-gray-800 h-6 bg-gray-100 rounded pr-4.5 bg-right pl-2 py-0 appearance-none focus:outline-none border-none`, + className + ) + return ( + + ) +} diff --git a/demos/linearlite/src/components/StatusIcon.tsx b/demos/linearlite/src/components/StatusIcon.tsx new file mode 100644 index 000000000..d6c066a1a --- /dev/null +++ b/demos/linearlite/src/components/StatusIcon.tsx @@ -0,0 +1,15 @@ +import classNames from 'classnames' +import { StatusIcons, StatusValue } from '../types/types' + +interface Props { + status: string + className?: string +} + +export default function StatusIcon({ status, className }: Props) { + const classes = classNames(`w-3.5 h-3.5 rounded`, className) + + const Icon = StatusIcons[status.toLowerCase() as StatusValue] + + return +} diff --git a/demos/linearlite/src/components/Toggle.tsx b/demos/linearlite/src/components/Toggle.tsx new file mode 100644 index 000000000..4494b98cd --- /dev/null +++ b/demos/linearlite/src/components/Toggle.tsx @@ -0,0 +1,41 @@ +import classnames from 'classnames' + +interface Props { + onChange?: (value: boolean) => void + className?: string + value?: boolean + activeClass?: string + activeLabelClass?: string +} +export default function Toggle({ + onChange, + className, + value = false, + activeClass = `bg-indigo-600 hover:bg-indigo-700`, + activeLabelClass = `border-indigo-600`, +}: Props) { + const labelClasses = classnames( + `absolute h-3.5 w-3.5 overflow-hidden border-2 transition duration-200 ease-linear rounded-full cursor-pointer bg-white`, + { + 'left-0 border-gray-300': !value, + 'right-0': value, + [activeLabelClass]: value, + } + ) + const classes = classnames( + `group relative rounded-full w-5 h-3.5 transition duration-200 ease-linear`, + { + [activeClass]: value, + 'bg-gray-300': !value, + }, + className + ) + const onClick = () => { + if (onChange) onChange(!value) + } + return ( +
+ +
+ ) +} diff --git a/demos/linearlite/src/components/TopFilter.tsx b/demos/linearlite/src/components/TopFilter.tsx new file mode 100644 index 000000000..5724b4e2e --- /dev/null +++ b/demos/linearlite/src/components/TopFilter.tsx @@ -0,0 +1,216 @@ +import { ReactComponent as MenuIcon } from '../assets/icons/menu.svg' +import { useState, useContext, useEffect } from 'react' +import { BsSortUp, BsPlus, BsX, BsSearch as SearchIcon } from 'react-icons/bs' +import { useLiveQuery, usePGlite } from '@electric-sql/pglite-react' +import ViewOptionMenu from './ViewOptionMenu' +import { MenuContext } from '../App' +import FilterMenu from './contextmenu/FilterMenu' +import { FilterState, useFilterState } from '../utils/filterState' +import { PriorityDisplay, StatusDisplay } from '../types/types' +import debounce from 'lodash.debounce' +import { createFTSIndex } from '../migrations' + +interface Props { + filteredIssuesCount: number + hideSort?: boolean + showSearch?: boolean + title?: string + filterState?: FilterState +} + +export default function ({ + filteredIssuesCount, + hideSort, + showSearch, + title = `All issues`, + filterState, +}: Props) { + const [usedFilterState, setFilterState] = useFilterState() + const [showViewOption, setShowViewOption] = useState(false) + const { showMenu, setShowMenu } = useContext(MenuContext)! + const [searchQuery, setSearchQuery] = useState(``) + const [FTSIndexReady, setFTSIndexReady] = useState(true) + const pg = usePGlite() + + filterState ??= usedFilterState + + const totalIssuesCount = useLiveQuery<{ count: number }>( + `SELECT COUNT(id) FROM issue WHERE deleted = false` + )?.rows[0].count + + const handleSearchInner = debounce((query: string) => { + setFilterState({ + ...filterState, + query: query, + }) + }, 300) + + const handleSearch = (query: string) => { + setSearchQuery(query) + handleSearchInner(query) + } + + const eqStatuses = (statuses: string[]) => { + const statusSet = new Set(statuses) + return ( + filterState.status?.length === statusSet.size && + filterState.status.every((x) => statusSet.has(x)) + ) + } + + if (filterState.status?.length) { + if (eqStatuses([`backlog`])) { + title = `Backlog` + } else if (eqStatuses([`todo`, `in_progress`])) { + title = `Active` + } + } + + useEffect(() => { + if (!showSearch) return + const checkFTSIndex = async () => { + const res = await pg.query( + `SELECT 1 FROM pg_indexes WHERE indexname = 'issue_search_idx';` + ) + const indexReady = res.rows.length > 0 + if (!indexReady) { + setFTSIndexReady(false) + await createFTSIndex(pg) + } + setFTSIndexReady(true) + } + checkFTSIndex() + }, [showSearch, pg]) + + const showFTSIndexProgress = showSearch && !FTSIndexReady + + return ( + <> +
+ {/* left section */} +
+ + +
{title}
+ {/* {filteredIssuesCount} */} + + {filteredIssuesCount.toLocaleString()} + {totalIssuesCount !== undefined && + filteredIssuesCount !== totalIssuesCount + ? ` of ${totalIssuesCount.toLocaleString()}` + : ``} + + + + Filter + + } + id={`filter-menu`} + /> +
+ +
+ {!hideSort && ( + + )} +
+
+ + {(!!filterState.status?.length || !!filterState.priority?.length) && ( +
+ {!!filterState.priority?.length && ( +
+ Priority is + + {filterState.priority + ?.map( + (priority) => + PriorityDisplay[priority as keyof typeof PriorityDisplay] + ) + .join(`, `)} + + { + setFilterState({ + ...filterState, + priority: undefined, + }) + }} + > + + +
+ )} + {!!filterState.status?.length && ( +
+ Status is + + {filterState.status + ?.map( + (status) => + StatusDisplay[status as keyof typeof StatusDisplay] + ) + .join(`, `)} + + { + setFilterState({ + ...filterState, + status: undefined, + }) + }} + > + + +
+ )} +
+ )} + + {showSearch && ( +
+ + handleSearch(e.target.value)} + /> +
+ )} + + {showFTSIndexProgress && ( +
+
+
+ Building full text search index... (only happens once) +
+
+ )} + + setShowViewOption(false)} + /> + + ) +} diff --git a/demos/linearlite/src/components/ViewOptionMenu.tsx b/demos/linearlite/src/components/ViewOptionMenu.tsx new file mode 100644 index 000000000..a464927b4 --- /dev/null +++ b/demos/linearlite/src/components/ViewOptionMenu.tsx @@ -0,0 +1,92 @@ +import { Transition } from '@headlessui/react' +import { useClickOutside } from '../hooks/useClickOutside' +import { useRef } from 'react' +import Select from './Select' +import { useFilterState } from '../utils/filterState' + +interface Props { + isOpen: boolean + onDismiss?: () => void +} +export default function ({ isOpen, onDismiss }: Props) { + const ref = useRef(null) + const [filterState, setFilterState] = useFilterState() + + useClickOutside(ref, () => { + if (isOpen && onDismiss) onDismiss() + }) + + const handleOrderByChange = (e: React.ChangeEvent) => { + setFilterState({ + ...filterState, + orderBy: e.target.value, + }) + } + + const handleOrderDirectionChange = ( + e: React.ChangeEvent + ) => { + setFilterState({ + ...filterState, + orderDirection: e.target.value as `asc` | `desc`, + }) + } + + return ( +
+ +
+ View Options +
+ +
+ {/*
+ Grouping +
+ +
+
*/} + +
+ Ordering +
+ +
+
+ +
+
+
+
+
+ ) +} diff --git a/demos/linearlite/src/components/contextmenu/FilterMenu.tsx b/demos/linearlite/src/components/contextmenu/FilterMenu.tsx new file mode 100644 index 000000000..886235ce9 --- /dev/null +++ b/demos/linearlite/src/components/contextmenu/FilterMenu.tsx @@ -0,0 +1,121 @@ +import { Portal } from '../Portal' +import { ReactNode, useState } from 'react' +import { ContextMenuTrigger } from '@firefox-devtools/react-contextmenu' +import { BsCheck2 } from 'react-icons/bs' +import { Menu } from './menu' +import { useFilterState } from '../../utils/filterState' +import { PriorityOptions, StatusOptions } from '../../types/types' + +interface Props { + id: string + button: ReactNode + className?: string +} + +function FilterMenu({ id, button, className }: Props) { + const [filterState, setFilterState] = useFilterState() + const [keyword, setKeyword] = useState(``) + + let priorities = PriorityOptions + if (keyword !== ``) { + const normalizedKeyword = keyword.toLowerCase().trim() + priorities = priorities.filter( + ([_icon, _priority, label]) => + (label as string).toLowerCase().indexOf(normalizedKeyword) !== -1 + ) + } + + let statuses = StatusOptions + if (keyword !== ``) { + const normalizedKeyword = keyword.toLowerCase().trim() + statuses = statuses.filter( + ([_icon, _status, label]) => + label.toLowerCase().indexOf(normalizedKeyword) !== -1 + ) + } + + const priorityOptions = priorities.map(([Icon, priority, label]) => { + return ( + handlePrioritySelect(priority as string)} + > + + {label} + {filterState.priority?.includes(priority) && ( + + )} + + ) + }) + + const statusOptions = statuses.map(([Icon, status, label]) => { + return ( + handleStatusSelect(status as string)} + > + + {label} + {filterState.status?.includes(status) && ( + + )} + + ) + }) + + const handlePrioritySelect = (priority: string) => { + setKeyword(``) + const newPriority = filterState.priority || [] + if (newPriority.includes(priority)) { + newPriority.splice(newPriority.indexOf(priority), 1) + } else { + newPriority.push(priority) + } + setFilterState({ + ...filterState, + priority: newPriority, + }) + } + + const handleStatusSelect = (status: string) => { + setKeyword(``) + const newStatus = filterState.status || [] + if (newStatus.includes(status)) { + newStatus.splice(newStatus.indexOf(status), 1) + } else { + newStatus.push(status) + } + setFilterState({ + ...filterState, + status: newStatus, + }) + } + + return ( + <> + + {button} + + + + setKeyword(kw)} + > + {priorityOptions && Priority} + {priorityOptions} + {priorityOptions && statusOptions && } + {statusOptions && Status} + {statusOptions} + + + + ) +} + +export default FilterMenu diff --git a/demos/linearlite/src/components/contextmenu/PriorityMenu.tsx b/demos/linearlite/src/components/contextmenu/PriorityMenu.tsx new file mode 100644 index 000000000..f29716411 --- /dev/null +++ b/demos/linearlite/src/components/contextmenu/PriorityMenu.tsx @@ -0,0 +1,70 @@ +import { Portal } from '../Portal' +import { ReactNode, useState } from 'react' +import { ContextMenuTrigger } from '@firefox-devtools/react-contextmenu' +import { Menu } from './menu' +import { PriorityOptions } from '../../types/types' + +interface Props { + id: string + button: ReactNode + filterKeyword?: boolean + className?: string + onSelect?: (item: string) => void +} + +function PriorityMenu({ + id, + button, + filterKeyword = false, + className, + onSelect, +}: Props) { + const [keyword, setKeyword] = useState(``) + + const handleSelect = (priority: string) => { + setKeyword(``) + if (onSelect) onSelect(priority) + } + let statusOpts = PriorityOptions + if (keyword !== ``) { + const normalizedKeyword = keyword.toLowerCase().trim() + statusOpts = statusOpts.filter( + ([_Icon, _priority, label]) => + (label as string).toLowerCase().indexOf(normalizedKeyword) !== -1 + ) + } + + const options = statusOpts.map(([Icon, priority, label]) => { + return ( + handleSelect(priority as string)} + > + {label} + + ) + }) + + return ( + <> + + {button} + + + + setKeyword(kw)} + className={className} + > + {options} + + + + ) +} + +export default PriorityMenu diff --git a/demos/linearlite/src/components/contextmenu/StatusMenu.tsx b/demos/linearlite/src/components/contextmenu/StatusMenu.tsx new file mode 100644 index 000000000..8ab3faa53 --- /dev/null +++ b/demos/linearlite/src/components/contextmenu/StatusMenu.tsx @@ -0,0 +1,56 @@ +import { Portal } from '../Portal' +import { ReactNode, useState } from 'react' +import { ContextMenuTrigger } from '@firefox-devtools/react-contextmenu' +import { StatusOptions } from '../../types/types' +import { Menu } from './menu' + +interface Props { + id: string + button: ReactNode + className?: string + onSelect?: (status: string) => void +} +export default function StatusMenu({ id, button, className, onSelect }: Props) { + const [keyword, setKeyword] = useState(``) + const handleSelect = (status: string) => { + if (onSelect) onSelect(status) + } + + let statuses = StatusOptions + if (keyword !== ``) { + const normalizedKeyword = keyword.toLowerCase().trim() + statuses = statuses.filter( + ([_icon, _id, l]) => l.toLowerCase().indexOf(normalizedKeyword) !== -1 + ) + } + + const options = statuses.map(([Icon, id, label]) => { + return ( + handleSelect(id)}> + +
{label}
+
+ ) + }) + + return ( + <> + + {button} + + + + setKeyword(kw)} + > + {options} + + + + ) +} diff --git a/demos/linearlite/src/components/contextmenu/menu.tsx b/demos/linearlite/src/components/contextmenu/menu.tsx new file mode 100644 index 000000000..f4eff45a5 --- /dev/null +++ b/demos/linearlite/src/components/contextmenu/menu.tsx @@ -0,0 +1,103 @@ +import classnames from 'classnames' +import { ReactNode, useRef } from 'react' +import { + ContextMenu, + MenuItem, + type MenuItemProps as CMMenuItemProps, +} from '@firefox-devtools/react-contextmenu' + +const sizeClasses = { + small: `w-34`, + normal: `w-72`, +} + +export interface MenuProps { + id: string + size: keyof typeof sizeClasses + className?: string + onKeywordChange?: (kw: string) => void + filterKeyword: boolean + children: ReactNode + searchPlaceholder?: string +} + +interface MenuItemProps { + children: ReactNode + onClick?: CMMenuItemProps[`onClick`] +} +const Item = function ({ onClick, children }: MenuItemProps) { + return ( + + {children} + + ) +} + +const Divider = function () { + return +} + +const Header = function ({ children }: MenuItemProps) { + return ( + + {children} + + ) +} + +export const Menu = (props: MenuProps) => { + const { + id, + size = `small`, + onKeywordChange, + children, + className, + filterKeyword, + searchPlaceholder, + } = props + const ref = useRef(null) + + const classes = classnames( + `cursor-default bg-white rounded shadow-modal z-100`, + sizeClasses[size], + className + ) + + return ( + { + if (ref.current) ref.current.focus() + }} + > +
{ + e.stopPropagation() + }} + > + {filterKeyword && ( + { + if (onKeywordChange) onKeywordChange(e.target.value) + }} + onClick={(e) => { + e.stopPropagation() + }} + placeholder={searchPlaceholder} + /> + )} + {children} +
+
+ ) +} + +Menu.Item = Item +Menu.Divider = Divider +Menu.Header = Header diff --git a/demos/linearlite/src/components/editor/Editor.tsx b/demos/linearlite/src/components/editor/Editor.tsx new file mode 100644 index 000000000..bf5b2bb93 --- /dev/null +++ b/demos/linearlite/src/components/editor/Editor.tsx @@ -0,0 +1,82 @@ +import { + useEditor, + EditorContent, + BubbleMenu, + type Extensions, +} from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import Placeholder from '@tiptap/extension-placeholder' +import Table from '@tiptap/extension-table' +import TableCell from '@tiptap/extension-table-cell' +import TableHeader from '@tiptap/extension-table-header' +import TableRow from '@tiptap/extension-table-row' +import { Markdown } from 'tiptap-markdown' +import EditorMenu from './EditorMenu' +import { useEffect, useRef } from 'react' + +interface EditorProps { + value: string + onChange: (value: string) => void + className?: string + placeholder?: string +} + +const Editor = ({ + value, + onChange, + className = ``, + placeholder, +}: EditorProps) => { + const editorProps = { + attributes: { + class: className, + }, + } + const markdownValue = useRef(null) + + const extensions: Extensions = [ + StarterKit, + Markdown, + Table, + TableRow, + TableHeader, + TableCell, + ] + + const editor = useEditor({ + extensions, + editorProps, + content: value || undefined, + onUpdate: ({ editor }) => { + markdownValue.current = editor.storage.markdown.getMarkdown() + onChange(markdownValue.current || ``) + }, + }) + + useEffect(() => { + if (editor && markdownValue.current !== value) { + editor.commands.setContent(value) + } + }, [editor, value]) + + if (placeholder) { + extensions.push( + Placeholder.configure({ + placeholder, + }) + ) + } + + return ( + <> + + {editor && ( + + + + )} + + ) +} + +export default Editor diff --git a/demos/linearlite/src/components/editor/EditorMenu.tsx b/demos/linearlite/src/components/editor/EditorMenu.tsx new file mode 100644 index 000000000..de8bd22e6 --- /dev/null +++ b/demos/linearlite/src/components/editor/EditorMenu.tsx @@ -0,0 +1,125 @@ +import type { Editor as TipTapEditor } from '@tiptap/react' +import classNames from 'classnames' + +import { BsTypeBold as BoldIcon } from 'react-icons/bs' +import { BsTypeItalic as ItalicIcon } from 'react-icons/bs' +import { BsTypeStrikethrough as StrikeIcon } from 'react-icons/bs' +import { BsCode as CodeIcon } from 'react-icons/bs' +import { BsListUl as BulletListIcon } from 'react-icons/bs' +import { BsListOl as OrderedListIcon } from 'react-icons/bs' +import { BsCodeSlash as CodeBlockIcon } from 'react-icons/bs' +import { BsChatQuote as BlockquoteIcon } from 'react-icons/bs' + +export interface EditorMenuProps { + editor: TipTapEditor +} + +const EditorMenu = ({ editor }: EditorMenuProps) => { + return ( +
+ + + + +
+ + + + +
+ ) +} + +export default EditorMenu diff --git a/demos/linearlite/src/config.ts b/demos/linearlite/src/config.ts new file mode 100644 index 000000000..9e8fcb4e6 --- /dev/null +++ b/demos/linearlite/src/config.ts @@ -0,0 +1,3 @@ +export default { + readonly: false, +} diff --git a/demos/linearlite/src/electric.tsx b/demos/linearlite/src/electric.tsx new file mode 100644 index 000000000..5c85f02c5 --- /dev/null +++ b/demos/linearlite/src/electric.tsx @@ -0,0 +1 @@ +export const baseUrl = import.meta.env.ELECTRIC_URL ?? `http://localhost:3000` diff --git a/demos/linearlite/src/hooks/useClickOutside.ts b/demos/linearlite/src/hooks/useClickOutside.ts new file mode 100644 index 000000000..a7f6a94ac --- /dev/null +++ b/demos/linearlite/src/hooks/useClickOutside.ts @@ -0,0 +1,28 @@ +import { RefObject, useCallback, useEffect } from 'react' + +export const useClickOutside = ( + ref: RefObject, + callback: (event: MouseEvent | TouchEvent) => void, + outerRef?: RefObject +): void => { + const handleClick = useCallback( + (event: MouseEvent | TouchEvent) => { + if (!event.target || outerRef?.current?.contains(event.target as Node)) { + return + } + if (ref.current && !ref.current.contains(event.target as Node)) { + callback(event) + } + }, + [callback, ref, outerRef] + ) + useEffect(() => { + document.addEventListener(`mousedown`, handleClick) + document.addEventListener(`touchstart`, handleClick) + + return () => { + document.removeEventListener(`mousedown`, handleClick) + document.removeEventListener(`touchstart`, handleClick) + } + }) +} diff --git a/demos/linearlite/src/hooks/useLockBodyScroll.ts b/demos/linearlite/src/hooks/useLockBodyScroll.ts new file mode 100644 index 000000000..4bd0a1933 --- /dev/null +++ b/demos/linearlite/src/hooks/useLockBodyScroll.ts @@ -0,0 +1,14 @@ +import { useLayoutEffect } from 'react' + +export default function useLockBodyScroll() { + useLayoutEffect(() => { + // Get original value of body overflow + const originalStyle = window.getComputedStyle(document.body).overflow + // Prevent scrolling on mount + document.body.style.overflow = `hidden` + // Re-enable scrolling when component unmounts + return () => { + document.body.style.overflow = originalStyle + } + }, []) // Empty array ensures effect is only run on mount and unmount +} diff --git a/demos/linearlite/src/main.tsx b/demos/linearlite/src/main.tsx new file mode 100644 index 000000000..45cb8857d --- /dev/null +++ b/demos/linearlite/src/main.tsx @@ -0,0 +1,18 @@ +import { createRoot } from 'react-dom/client' +import './style.css' + +import App from './App' + +const container = document.getElementById(`root`)! +const root = createRoot(container) +root.render() + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +//reportWebVitals(); + +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://cra.link/PWA +//serviceWorkerRegistration.register(); diff --git a/demos/linearlite/src/migrations.ts b/demos/linearlite/src/migrations.ts new file mode 100644 index 000000000..143a6fb48 --- /dev/null +++ b/demos/linearlite/src/migrations.ts @@ -0,0 +1,30 @@ +import type { PGlite, PGliteInterface } from '@electric-sql/pglite' +import m1 from '../db/migrations-client/01-create_tables.sql?raw' +import postInitialSyncIndexes from '../db/migrations-client/post-initial-sync-indexes.sql?raw' +import postInitialSyncFtsIndex from '../db/migrations-client/post-initial-sync-fts-index.sql?raw' + +export async function migrate(pg: PGlite) { + const tables = await pg.query( + `SELECT table_name FROM information_schema.tables WHERE table_schema='public'` + ) + if (tables.rows.length === 0) { + await pg.exec(m1) + } +} + +export async function postInitialSync(pg: PGliteInterface) { + const commands = postInitialSyncIndexes + .split('\n') + .map((c) => c.trim()) + .filter((c) => c.length > 0) + for (const command of commands) { + // wait 100ms between commands + console.time(`command: ${command}`) + await pg.exec(command) + console.timeEnd(`command: ${command}`) + } +} + +export async function createFTSIndex(pg: PGliteInterface) { + await pg.exec(postInitialSyncFtsIndex) +} diff --git a/demos/linearlite/src/pages/Board/IssueBoard.tsx b/demos/linearlite/src/pages/Board/IssueBoard.tsx new file mode 100644 index 000000000..919b88d97 --- /dev/null +++ b/demos/linearlite/src/pages/Board/IssueBoard.tsx @@ -0,0 +1,248 @@ +import { DragDropContext, DropResult } from 'react-beautiful-dnd' +import { useState, useEffect } from 'react' +import { generateKeyBetween } from 'fractional-indexing' +import { Issue, Status, StatusDisplay, StatusValue } from '../../types/types' +import { useLiveQuery, usePGlite } from '@electric-sql/pglite-react' +import IssueCol from './IssueCol' +import { LiveQuery, LiveQueryResults } from '@electric-sql/pglite/live' + +export interface IssueBoardProps { + columnsLiveIssues: Record> +} + +interface MovedIssues { + [id: string]: { + status?: StatusValue + kanbanorder?: string + } +} + +export default function IssueBoard({ columnsLiveIssues }: IssueBoardProps) { + const pg = usePGlite() + const [movedIssues, setMovedIssues] = useState({}) + + // Issues are coming from a live query, this may not have updated before we rerender + // after a drag and drop. So we keep track of moved issues and use that to override + // the status of the issue when sorting the issues into columns. + + useEffect(() => { + // Reset moved issues when issues change + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect + setMovedIssues({}) + }, [columnsLiveIssues]) + + const issuesByStatus: Record = {} + const issuesResByStatus: Record> = {} + Object.entries(columnsLiveIssues).forEach(([status, liveQuery]) => { + // eslint-disable-next-line react-compiler/react-compiler + // eslint-disable-next-line react-hooks/rules-of-hooks + const issuesRes = useLiveQuery(liveQuery) + issuesResByStatus[status] = issuesRes + issuesRes.rows.forEach((issue) => { + // If the issue has been moved, patch with new status and kanbanorder for sorting + if (movedIssues[issue.id]) { + issue = { + ...issue, + ...movedIssues[issue.id], + } + } + + const status = issue.status.toLowerCase() + if (!issuesByStatus[status]) { + issuesByStatus[status] = [] + } + issuesByStatus[status].push(issue) + }) + }) + + // Sort issues in each column by kanbanorder and issue id + Object.keys(issuesByStatus).forEach((status) => { + issuesByStatus[status].sort((a, b) => { + if (a.kanbanorder < b.kanbanorder) { + return -1 + } + if (a.kanbanorder > b.kanbanorder) { + return 1 + } + // Use unique issue id to break ties + if (a.id < b.id) { + return -1 + } else { + return 1 + } + }) + }) + + // Fill in undefined to create the leader and tail for the offset and limit + Object.keys(issuesByStatus).forEach((status) => { + // new array of lenth issuesResByStatus[status].total_count + const issues = new Array(issuesResByStatus[status]?.totalCount || 0) + const offset = issuesResByStatus[status]?.offset || 0 + issuesByStatus[status].forEach((issue, index) => { + issues[index + offset] = issue + }) + issuesByStatus[status] = issues + }) + + const adjacentIssues = ( + column: string, + index: number, + sameColumn = true, + currentIndex: number + ) => { + const columnIssues = issuesByStatus[column] || [] + let prevIssue: Issue | undefined + let nextIssue: Issue | undefined + if (sameColumn) { + if (currentIndex < index) { + prevIssue = columnIssues[index] + nextIssue = columnIssues[index + 1] + } else { + prevIssue = columnIssues[index - 1] + nextIssue = columnIssues[index] + } + } else { + prevIssue = columnIssues[index - 1] + nextIssue = columnIssues[index] + } + console.log(`sameColumn`, sameColumn) + console.log(`prevIssue`, prevIssue) + console.log(`nextIssue`, nextIssue) + return { prevIssue, nextIssue } + } + + // /** + // * Fix duplicate kanbanorder, this is recursive so we can fix multiple consecutive + // * issues with the same kanbanorder. + // * @param issue The issue to fix the kanbanorder for + // * @param issueBefore The issue immediately before one that needs fixing + // * @returns The new kanbanorder that was set for the issue + // */ + // const fixKanbanOrder = (issue: Issue, issueBefore: Issue) => { + // // First we find the issue immediately after the issue that needs fixing. + // const issueIndex = issuesByStatus[issue.status].indexOf(issue) + // const issueAfter = issuesByStatus[issue.status][issueIndex + 1] + + // // The kanbanorder of the issue before the issue that needs fixing + // const prevKanbanOrder = issueBefore?.kanbanorder + + // // The kanbanorder of the issue after the issue that needs fixing + // let nextKanbanOrder = issueAfter?.kanbanorder + + // // If the next issue has the same kanbanorder the next issue needs fixing too, + // // we recursively call fixKanbanOrder for that issue to fix it's kanbanorder. + // if (issueAfter && nextKanbanOrder && nextKanbanOrder === prevKanbanOrder) { + // nextKanbanOrder = fixKanbanOrder(issueAfter, issueBefore) + // } + + // // Generate a new kanbanorder between the previous and next issues + // const kanbanorder = generateKeyBetween(prevKanbanOrder, nextKanbanOrder) + + // // Keep track of moved issues so we can override the kanbanorder when sorting + // // We do this due to the momentary lag between updating the database and the live + // // query updating the issues. + // setMovedIssues((prev) => ({ + // ...prev, + // [issue.id]: { + // kanbanorder: kanbanorder, + // }, + // })) + + // pg.sql` + // UPDATE issue + // SET kanbanorder = ${kanbanorder} + // WHERE id = ${issue.id} + // ` + + // // Return the new kanbanorder + // return kanbanorder + // } + + /** + * Get a new kanbanorder that sits between two other issues. + * Used to generate a new kanbanorder when moving an issue. + * @param issueBefore The issue immediately before the issue being moved + * @param issueAfter The issue immediately after the issue being moved + * @returns The new kanbanorder + */ + const getNewKanbanOrder = (issueBefore: Issue, issueAfter: Issue) => { + const prevKanbanOrder = issueBefore?.kanbanorder + // eslint-disable-next-line prefer-const + let nextKanbanOrder = issueAfter?.kanbanorder + // if (nextKanbanOrder && nextKanbanOrder === prevKanbanOrder) { + // // If the next issue has the same kanbanorder as the previous issue, + // // we need to fix the kanbanorder of the next issue. + // // This can happen when two users move issues into the same position at the same + // // time. + // nextKanbanOrder = fixKanbanOrder(issueAfter, issueBefore) + // } + return generateKeyBetween(prevKanbanOrder, nextKanbanOrder) + } + + const onDragEnd = ({ source, destination, draggableId }: DropResult) => { + console.log(source, destination, draggableId) + if (destination && destination.droppableId) { + const { prevIssue, nextIssue } = adjacentIssues( + destination.droppableId, + destination.index, + destination.droppableId === source.droppableId, + source.index + ) + // Get a new kanbanorder between the previous and next issues + const kanbanorder = getNewKanbanOrder(prevIssue, nextIssue) + // Keep track of moved issues so we can override the status and kanbanorder when + // sorting issues into columns. + const modified = new Date() + setMovedIssues((prev) => ({ + ...prev, + [draggableId]: { + status: destination.droppableId as StatusValue, + kanbanorder, + modified, + }, + })) + pg.sql` + UPDATE issue + SET status = ${destination.droppableId}, kanbanorder = ${kanbanorder}, modified = ${modified} + WHERE id = ${draggableId} + ` + } + } + + return ( + +
+ + + + + +
+
+ ) +} diff --git a/demos/linearlite/src/pages/Board/IssueCol.tsx b/demos/linearlite/src/pages/Board/IssueCol.tsx new file mode 100644 index 000000000..e0c37d5e3 --- /dev/null +++ b/demos/linearlite/src/pages/Board/IssueCol.tsx @@ -0,0 +1,155 @@ +import { CSSProperties } from 'react' +import StatusIcon from '../../components/StatusIcon' +import { + Droppable, + DroppableProvided, + DroppableStateSnapshot, + Draggable, + DraggableProvided, + DraggableStateSnapshot, +} from 'react-beautiful-dnd' +import { FixedSizeList as List, ListOnItemsRenderedProps } from 'react-window' +import AutoSizer from 'react-virtualized-auto-sizer' +import IssueItem, { itemHeight } from './IssueItem' +import { Issue } from '../../types/types' +import { LiveQuery } from '@electric-sql/pglite/live' +import { useLiveQuery } from '@electric-sql/pglite-react' + +const CHUNK_SIZE = 25 + +function calculateWindow( + startIndex: number, + stopIndex: number +): { offset: number; limit: number } { + const offset = Math.max( + 0, + Math.floor(startIndex / CHUNK_SIZE) * CHUNK_SIZE - CHUNK_SIZE + ) + const endOffset = Math.ceil(stopIndex / CHUNK_SIZE) * CHUNK_SIZE + CHUNK_SIZE + const limit = endOffset - offset + return { offset, limit } +} + +interface Props { + status: string + title: string + issues: Array | undefined + liveQuery: LiveQuery +} + +const itemSpacing = 8 + +function IssueCol({ title, status, issues, liveQuery }: Props) { + issues = issues || [] + const statusIcon = + + const issuesRes = useLiveQuery(liveQuery) + + const offset = liveQuery.initialResults.offset ?? issuesRes.offset ?? 0 + const limit = liveQuery.initialResults.limit ?? issuesRes.limit ?? CHUNK_SIZE + + const handleOnItemsRendered = (props: ListOnItemsRenderedProps) => { + const { offset: newOffset, limit: newLimit } = calculateWindow( + props.overscanStartIndex, + props.overscanStopIndex + ) + if (newOffset !== offset || newLimit !== limit) { + liveQuery.refresh({ offset: newOffset, limit: newLimit }) + } + } + + return ( +
+
+
+ {statusIcon} + {title} + + {issues?.length || 0} + +
+
+ { + const issue = issues[rubric.source.index] + return ( + + ) + }} + > + {( + droppableProvided: DroppableProvided, + snapshot: DroppableStateSnapshot + ) => { + // Add an extra item to our list to make space for a dragging item + // Usually the DroppableProvided.placeholder does this, but that won't + // work in a virtual list + const itemCount: number = snapshot.isUsingPlaceholder + ? issues.length + 1 + : issues.length + + return ( +
+ + {({ height, width }) => ( + + {Row} + + )} + +
+ ) + }} +
+
+ ) +} + +const Row = ({ + data: issues, + index, + style, +}: { + data: Issue[] + index: number + style: CSSProperties | undefined +}) => { + const issue = issues[index] + if (!issue) return null + return ( + + {(provided: DraggableProvided, snapshot: DraggableStateSnapshot) => ( + + )} + + ) +} + +export default IssueCol diff --git a/demos/linearlite/src/pages/Board/IssueItem.tsx b/demos/linearlite/src/pages/Board/IssueItem.tsx new file mode 100644 index 000000000..aaa32b5a1 --- /dev/null +++ b/demos/linearlite/src/pages/Board/IssueItem.tsx @@ -0,0 +1,92 @@ +import { type CSSProperties } from 'react' +import classNames from 'classnames' +import { useNavigate } from 'react-router-dom' +import { DraggableProvided } from 'react-beautiful-dnd' +import { BsCloudCheck as SyncedIcon } from 'react-icons/bs' +import { BsCloudSlash as UnsyncedIcon } from 'react-icons/bs' +import { usePGlite } from '@electric-sql/pglite-react' +import Avatar from '../../components/Avatar' +import PriorityMenu from '../../components/contextmenu/PriorityMenu' +import PriorityIcon from '../../components/PriorityIcon' +import { Issue } from '../../types/types' + +interface IssueProps { + issue: Issue + index: number + isDragging?: boolean + provided: DraggableProvided + style?: CSSProperties +} + +export const itemHeight = 100 + +function getStyle( + provided: DraggableProvided, + style?: CSSProperties +): CSSProperties { + return { + ...provided.draggableProps.style, + ...(style || {}), + height: `${itemHeight}px`, + } +} + +const IssueItem = ({ issue, style, isDragging, provided }: IssueProps) => { + const pg = usePGlite() + const navigate = useNavigate() + const priorityIcon = ( + + + + ) + + const updatePriority = (priority: string) => { + pg.sql` + UPDATE issue + SET priority = ${priority}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + } + + return ( +
navigate(`/issue/${issue.id}`)} + > +
+
+ + {issue.title} + +
+
+ +
+
+
+ updatePriority(p)} + /> + {issue.synced ? ( + + ) : ( + + )} +
+
+ ) +} + +export default IssueItem diff --git a/demos/linearlite/src/pages/Board/index.tsx b/demos/linearlite/src/pages/Board/index.tsx new file mode 100644 index 000000000..41dd82653 --- /dev/null +++ b/demos/linearlite/src/pages/Board/index.tsx @@ -0,0 +1,40 @@ +/* eslint-disable react-compiler/react-compiler */ +import TopFilter from '../../components/TopFilter' +import IssueBoard from './IssueBoard' +import { FilterState } from '../../utils/filterState' +import { Issue, StatusValue } from '../../types/types' +import { useLiveQuery } from '@electric-sql/pglite-react' +import { useLoaderData } from 'react-router-dom' +import { LiveQuery } from '@electric-sql/pglite/live' + +export interface BoardLoaderData { + filterState: FilterState + columnsLiveIssues: Record> +} + +function Board() { + const { columnsLiveIssues, filterState } = useLoaderData() as BoardLoaderData + + const totalIssuesCount = Object.values(columnsLiveIssues).reduce( + (total, liveQuery) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const issuesRes = useLiveQuery(liveQuery) + return total + (issuesRes?.totalCount ?? 0) + }, + 0 + ) + + return ( +
+ + +
+ ) +} + +export default Board diff --git a/demos/linearlite/src/pages/Issue/Comments.tsx b/demos/linearlite/src/pages/Issue/Comments.tsx new file mode 100644 index 000000000..8245ecc6b --- /dev/null +++ b/demos/linearlite/src/pages/Issue/Comments.tsx @@ -0,0 +1,103 @@ +import { useState } from 'react' +import ReactMarkdown from 'react-markdown' +import Editor from '../../components/editor/Editor' +import Avatar from '../../components/Avatar' +import { formatDate } from '../../utils/date' +import { showWarning } from '../../utils/notification' +import { Comment, Issue } from '../../types/types' +import { BsCloudCheck as SyncedIcon } from 'react-icons/bs' +import { BsCloudSlash as UnsyncedIcon } from 'react-icons/bs' +import { useLiveQuery, usePGlite } from '@electric-sql/pglite-react' + +export interface CommentsProps { + issue: Issue +} + +function Comments({ issue }: CommentsProps) { + const pg = usePGlite() + const [newCommentBody, setNewCommentBody] = useState(``) + const commentsResults = useLiveQuery.sql` + SELECT * FROM comment WHERE issue_id = ${issue.id} + ` + const comments = commentsResults?.rows + + const commentList = () => { + if (comments && comments.length > 0) { + return comments.map((comment) => ( +
+
+ + + {comment.username} + + + {formatDate(comment.created)} + + + {/* Synced status */} + {comment.synced ? ( + + ) : ( + + )} + +
+
+ {comment.body} +
+
+ )) + } + } + + const handlePost = () => { + if (!newCommentBody) { + showWarning( + `Please enter a comment before submitting`, + `Comment required` + ) + return + } + + pg.sql` + INSERT INTO comment (id, issue_id, body, created, username) + VALUES ( + ${crypto.randomUUID()}, + ${issue.id}, + ${newCommentBody}, + ${new Date()}, + ${'testuser'} + ) + ` + + setNewCommentBody(``) + } + + return ( + <> + {commentList()} +
+ setNewCommentBody(val)} + placeholder="Add a comment..." + /> +
+
+ +
+ + ) +} + +export default Comments diff --git a/demos/linearlite/src/pages/Issue/DeleteModal.tsx b/demos/linearlite/src/pages/Issue/DeleteModal.tsx new file mode 100644 index 000000000..6fcd50b83 --- /dev/null +++ b/demos/linearlite/src/pages/Issue/DeleteModal.tsx @@ -0,0 +1,48 @@ +import Modal from '../../components/Modal' + +interface Props { + isOpen: boolean + setIsOpen: (isOpen: boolean) => void + onDismiss?: () => void + deleteIssue: () => void +} + +export default function AboutModal({ + isOpen, + setIsOpen, + onDismiss, + deleteIssue, +}: Props) { + const handleDelete = () => { + setIsOpen(false) + if (onDismiss) onDismiss() + deleteIssue() + } + + return ( + +
+ Are you sure you want to delete this issue? +
+
+ + +
+
+ ) +} diff --git a/demos/linearlite/src/pages/Issue/index.tsx b/demos/linearlite/src/pages/Issue/index.tsx new file mode 100644 index 000000000..414438345 --- /dev/null +++ b/demos/linearlite/src/pages/Issue/index.tsx @@ -0,0 +1,272 @@ +/* eslint-disable react-compiler/react-compiler */ +import { useNavigate, useLoaderData } from 'react-router-dom' +import { useState, useRef, useCallback } from 'react' +import { BsCloudCheck as SyncedIcon } from 'react-icons/bs' +import { BsCloudSlash as UnsyncedIcon } from 'react-icons/bs' +import { LiveQuery } from '@electric-sql/pglite/live' +import { usePGlite, useLiveQuery } from '@electric-sql/pglite-react' +import { BsTrash3 as DeleteIcon } from 'react-icons/bs' +import { BsXLg as CloseIcon } from 'react-icons/bs' +import PriorityMenu from '../../components/contextmenu/PriorityMenu' +import StatusMenu from '../../components/contextmenu/StatusMenu' +import PriorityIcon from '../../components/PriorityIcon' +import StatusIcon from '../../components/StatusIcon' +import Avatar from '../../components/Avatar' +import { Issue, PriorityDisplay, StatusDisplay } from '../../types/types' +import Editor from '../../components/editor/Editor' +import DeleteModal from './DeleteModal' +import Comments from './Comments' +import debounce from 'lodash.debounce' + +const debounceTime = 500 + +function IssuePage() { + const { liveIssue } = useLoaderData() as { liveIssue: LiveQuery } + const issue = useLiveQuery(liveIssue).rows[0] + const navigate = useNavigate() + const pg = usePGlite() + + const [showDeleteModal, setShowDeleteModal] = useState(false) + + const [dirtyTitle, setDirtyTitle] = useState(null) + const titleIsDirty = useRef(false) + const [dirtyDescription, setDirtyDescription] = useState(null) + const descriptionIsDirty = useRef(false) + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleTitleChangeDebounced = useCallback( + debounce(async (title: string) => { + console.log(`handleTitleChangeDebounced`, title) + pg.sql` + UPDATE issue + SET title = ${title}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + // We can't set titleIsDirty.current = false here because we haven't yet received + // the updated issue from the db + }, debounceTime), + [pg] + ) + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleDescriptionChangeDebounced = useCallback( + debounce(async (description: string) => { + pg.sql` + UPDATE issue + SET description = ${description}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + // We can't set descriptionIsDirty.current = false here because we haven't yet received + // the updated issue from the db + }, debounceTime), + [pg] + ) + + if (issue === undefined) { + return
Loading...
+ } else if (issue === null) { + return
Issue not found
+ } + + // We check if the dirty title or description is the same as the actual title or + // description, and if so, we can switch back to the non-dirty version + if (dirtyTitle === issue.title) { + setDirtyTitle(null) + titleIsDirty.current = false + } + if (dirtyDescription === issue.description) { + setDirtyDescription(null) + descriptionIsDirty.current = false + } + + const handleStatusChange = (status: string) => { + pg.sql` + UPDATE issue + SET status = ${status}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + } + + const handlePriorityChange = (priority: string) => { + pg.sql` + UPDATE issue + SET priority = ${priority}, modified = ${new Date()} + WHERE id = ${issue.id} + ` + } + + const handleTitleChange = (title: string) => { + setDirtyTitle(title) + titleIsDirty.current = true + // We debounce the title change so that we don't spam the db with updates + handleTitleChangeDebounced(title) + } + + const handleDescriptionChange = (description: string) => { + setDirtyDescription(description) + descriptionIsDirty.current = true + // We debounce the description change so that we don't spam the db with updates + handleDescriptionChangeDebounced(description) + } + + const handleDelete = () => { + pg.sql` + DELETE FROM issue WHERE id = ${issue.id} + ` + // Comments will be deleted automatically because of the ON DELETE CASCADE + handleClose() + } + + const handleClose = () => { + if (window.history.length > 2) { + navigate(-1) + } + navigate(`/`) + } + + const shortId = () => { + if (issue.id.includes(`-`)) { + return issue.id.slice(issue.id.length - 8) + } else { + return issue.id + } + } + + return ( + <> +
+
+
+
+ Issue + + {shortId()} + +
+ +
+ {issue.synced ? ( + + ) : ( + + )} + + +
+
+
+ + {/*
issue info
*/} +
+
+
+
+
+ Opened by +
+
+ +
+
+
+
+ Status +
+
+ + + {StatusDisplay[issue.status]} + + } + onSelect={handleStatusChange} + /> +
+
+
+
+ Priority +
+
+ + + {PriorityDisplay[issue.priority]} + + } + onSelect={handlePriorityChange} + /> +
+
+
+
+
+ handleTitleChange(e.target.value)} + /> + +
+ handleDescriptionChange(val)} + placeholder="Add description..." + /> +
+
+

Comments

+ +
+
+
+
+ + setShowDeleteModal(false)} + deleteIssue={handleDelete} + /> + + ) +} + +export default IssuePage diff --git a/demos/linearlite/src/pages/List/IssueList.tsx b/demos/linearlite/src/pages/List/IssueList.tsx new file mode 100644 index 000000000..7248ed8fe --- /dev/null +++ b/demos/linearlite/src/pages/List/IssueList.tsx @@ -0,0 +1,60 @@ +import { CSSProperties } from 'react' +import { + FixedSizeList as List, + areEqual, + type ListOnItemsRenderedProps, +} from 'react-window' +import { memo } from 'react' +import AutoSizer from 'react-virtualized-auto-sizer' +import IssueRow from './IssueRow' +import { Issue } from '../../types/types' + +export interface IssueListProps { + issues: (Issue | undefined)[] + onItemsRendered?: (props: ListOnItemsRenderedProps) => void +} + +function IssueList({ issues, onItemsRendered }: IssueListProps) { + return ( +
+ + {({ height, width }) => ( + + {VirtualIssueRow} + + )} + +
+ ) +} + +const VirtualIssueRow = memo( + ({ + data: issues, + index, + style, + }: { + data: (Issue | undefined)[] + index: number + style: CSSProperties + }) => { + const issue = issues[index] + return ( + + ) + }, + areEqual +) + +export default memo(IssueList) diff --git a/demos/linearlite/src/pages/List/IssueRow.tsx b/demos/linearlite/src/pages/List/IssueRow.tsx new file mode 100644 index 000000000..05d5f45b5 --- /dev/null +++ b/demos/linearlite/src/pages/List/IssueRow.tsx @@ -0,0 +1,93 @@ +import type { CSSProperties } from 'react' +import { usePGlite } from '@electric-sql/pglite-react' +import { BsCloudCheck as SyncedIcon } from 'react-icons/bs' +import { BsCloudSlash as UnsyncedIcon } from 'react-icons/bs' +import PriorityMenu from '../../components/contextmenu/PriorityMenu' +import StatusMenu from '../../components/contextmenu/StatusMenu' +import PriorityIcon from '../../components/PriorityIcon' +import StatusIcon from '../../components/StatusIcon' +import Avatar from '../../components/Avatar' +import { memo } from 'react' +import { useNavigate } from 'react-router-dom' +import { formatDate } from '../../utils/date' +import { Issue } from '../../types/types' + +interface Props { + issue: Issue | undefined + style: CSSProperties +} + +function IssueRow({ issue, style }: Props) { + const pg = usePGlite() + const navigate = useNavigate() + + const handleChangeStatus = (status: string) => { + pg.sql` + UPDATE issue + SET status = ${status}, modified = ${new Date()} + WHERE id = ${issue!.id} + ` + } + + const handleChangePriority = (priority: string) => { + pg.sql` + UPDATE issue + SET priority = ${priority}, modified = ${new Date()} + WHERE id = ${issue!.id} + ` + } + + if (!issue?.id) { + return ( +
+
+
+ ) + } + + return ( +
navigate(`/issue/${issue.id}`)} + style={style} + > +
+ } + onSelect={handleChangePriority} + /> +
+
+ } + onSelect={handleChangeStatus} + /> +
+
+ {issue.title.slice(0, 3000) || ``} +
+
+ {formatDate(issue.created)} +
+
+ +
+
+ {issue.synced ? ( + + ) : ( + + )} +
+
+ ) +} + +export default memo(IssueRow) diff --git a/demos/linearlite/src/pages/List/index.tsx b/demos/linearlite/src/pages/List/index.tsx new file mode 100644 index 000000000..b2a497001 --- /dev/null +++ b/demos/linearlite/src/pages/List/index.tsx @@ -0,0 +1,67 @@ +import { useLiveQuery } from '@electric-sql/pglite-react' +import { LiveQuery } from '@electric-sql/pglite/live' +import { useLoaderData } from 'react-router-dom' +import { type ListOnItemsRenderedProps } from 'react-window' +import TopFilter from '../../components/TopFilter' +import IssueList from './IssueList' +import { Issue } from '../../types/types' +import { FilterState } from '../../utils/filterState' +const CHUNK_SIZE = 50 + +function calculateWindow( + startIndex: number, + stopIndex: number +): { offset: number; limit: number } { + const offset = Math.max( + 0, + Math.floor(startIndex / CHUNK_SIZE) * CHUNK_SIZE - CHUNK_SIZE + ) + const endOffset = Math.ceil(stopIndex / CHUNK_SIZE) * CHUNK_SIZE + CHUNK_SIZE + const limit = endOffset - offset + return { offset, limit } +} + +function List({ showSearch = false }) { + const { liveIssues, filterState } = useLoaderData() as { + liveIssues: LiveQuery + filterState: FilterState + } + + const issuesRes = useLiveQuery(liveIssues) + const offset = liveIssues.initialResults.offset ?? issuesRes.offset ?? 0 + const limit = liveIssues.initialResults.limit ?? issuesRes.limit ?? CHUNK_SIZE + const issues = issuesRes?.rows + + const updateOffsetAndLimit = (itemsRendered: ListOnItemsRenderedProps) => { + const { offset: newOffset, limit: newLimit } = calculateWindow( + itemsRendered.overscanStartIndex, + itemsRendered.overscanStopIndex + ) + if (newOffset !== offset || newLimit !== limit) { + liveIssues.refresh({ offset: newOffset, limit: newLimit }) + } + } + + const currentTotalCount = issuesRes.totalCount ?? issuesRes.rows.length + const currentOffset = issuesRes.offset ?? 0 + const filledItems = new Array(currentTotalCount).fill(null) + issues.forEach((issue, index) => { + filledItems[index + currentOffset] = issue + }) + + return ( +
+ + updateOffsetAndLimit(itemsRendered)} + issues={filledItems} + /> +
+ ) +} + +export default List diff --git a/demos/linearlite/src/pages/root.tsx b/demos/linearlite/src/pages/root.tsx new file mode 100644 index 000000000..77cd6c5e1 --- /dev/null +++ b/demos/linearlite/src/pages/root.tsx @@ -0,0 +1,31 @@ +import { Outlet } from 'react-router-dom' +import LeftMenu from '../components/LeftMenu' +import { cssTransition, ToastContainer } from 'react-toastify' + +const slideUp = cssTransition({ + enter: `animate__animated animate__slideInUp`, + exit: `animate__animated animate__slideOutDown`, +}) + +export default function Root() { + return ( +
+
+ + +
+ +
+ ) +} diff --git a/demos/linearlite/src/pglite-worker.ts b/demos/linearlite/src/pglite-worker.ts new file mode 100644 index 000000000..598424e24 --- /dev/null +++ b/demos/linearlite/src/pglite-worker.ts @@ -0,0 +1,15 @@ +import { worker } from '@electric-sql/pglite/worker' +import { PGlite } from '@electric-sql/pglite' +import { migrate } from './migrations' + +worker({ + async init() { + const pg = await PGlite.create({ + dataDir: 'idb://linearlite2', + relaxedDurability: true, + }) + // Migrate the database to the latest schema + await migrate(pg) + return pg + }, +}) diff --git a/demos/linearlite/src/pglite.ts b/demos/linearlite/src/pglite.ts new file mode 100644 index 000000000..e69de29bb diff --git a/demos/linearlite/src/shims/react-contextmenu.d.ts b/demos/linearlite/src/shims/react-contextmenu.d.ts new file mode 100644 index 000000000..c580d66e6 --- /dev/null +++ b/demos/linearlite/src/shims/react-contextmenu.d.ts @@ -0,0 +1,107 @@ +// Copied here from the unreleased master branch of github.com/firefox-devtools/react-contextmenu +/* eslint-disable */ + +declare module '@firefox-devtools/react-contextmenu' { + import * as React from 'react' + + export interface ContextMenuProps { + id: string + data?: any + className?: string + hideOnLeave?: boolean + rtl?: boolean + onHide?: { (event: any): void } + onMouseLeave?: + | { + ( + event: React.MouseEvent, + data: Object, + target: HTMLElement + ): void + } + | Function + onShow?: { (event: any): void } + preventHideOnContextMenu?: boolean + preventHideOnResize?: boolean + preventHideOnScroll?: boolean + style?: React.CSSProperties + children?: React.ReactNode + } + + export interface ContextMenuTriggerProps { + id: string + attributes?: React.HTMLAttributes + collect?: { (data: any): any } + disable?: boolean + holdToDisplay?: number + renderTag?: React.ElementType + triggerOnLeftClick?: boolean + disableIfShiftIsPressed?: boolean + [key: string]: any + children?: React.ReactNode + } + + export interface MenuItemProps { + attributes?: React.HTMLAttributes + className?: string + data?: Object + disabled?: boolean + divider?: boolean + preventClose?: boolean + onClick?: + | { + ( + event: + | React.TouchEvent + | React.MouseEvent, + data: Object, + target: HTMLElement + ): void + } + | Function + children?: React.ReactNode + } + + export interface SubMenuProps { + title: React.ReactElement | React.ReactText + className?: string + disabled?: boolean + hoverDelay?: number + rtl?: boolean + preventCloseOnClick?: boolean + onClick?: + | { + ( + event: + | React.TouchEvent + | React.MouseEvent, + data: Object, + target: HTMLElement + ): void + } + | Function + children?: React.ReactNode + } + + export interface ConnectMenuProps { + id: string + trigger: any + } + + export const ContextMenu: React.ComponentClass + export const ContextMenuTrigger: React.ComponentClass + export const MenuItem: React.ComponentClass + export const SubMenu: React.ComponentClass + export function connectMenu

( + menuId: string + ): ( + Child: React.ComponentType

+ ) => React.ComponentType

+ export function showMenu(opts?: any, target?: HTMLElement): void + export function hideMenu(opts?: any, target?: HTMLElement): void +} + +declare module '@firefox-devtools/react-contextmenu/modules/actions' { + export function showMenu(opts?: any, target?: HTMLElement): void + export function hideMenu(opts?: any, target?: HTMLElement): void +} diff --git a/demos/linearlite/src/style.css b/demos/linearlite/src/style.css new file mode 100644 index 000000000..9b39c3c85 --- /dev/null +++ b/demos/linearlite/src/style.css @@ -0,0 +1,73 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; +body { + font-size: 12px; + @apply font-medium text-gray-600; +} + +@font-face { + font-family: 'Inter UI'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: + url('assets/fonts/Inter-UI-Regular.woff2') format('woff2'), + url('assets/fonts/Inter-UI-Regular.woff') format('woff'); +} + +@font-face { + font-family: 'Inter UI'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: + url('assets/fonts/Inter-UI-Medium.woff2') format('woff2'), + url('assets/fonts/Inter-UI-Medium.woff') format('woff'); +} + +@font-face { + font-family: 'Inter UI'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: + url('assets/fonts/Inter-UI-SemiBold.woff2') format('woff2'), + url('assets/fonts/Inter-UI-SemiBold.woff') format('woff'); +} + +@font-face { + font-family: 'Inter UI'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: + url('assets/fonts/Inter-UI-ExtraBold.woff2') format('woff2'), + url('assets/fonts/Inter-UI-ExtraBold.woff') format('woff'); +} + +.modal { + max-width: calc(100vw - 32px); + max-height: calc(100vh - 32px); +} + +.editor ul { + list-style-type: circle; +} +.editor ol { + list-style-type: decimal; +} + +#root, +body, +html { + height: 100%; +} + +.tiptap p.is-editor-empty:first-child::before { + color: #adb5bd; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} diff --git a/demos/linearlite/src/sync.ts b/demos/linearlite/src/sync.ts new file mode 100644 index 000000000..756bd1c5c --- /dev/null +++ b/demos/linearlite/src/sync.ts @@ -0,0 +1,314 @@ +import { Mutex } from '@electric-sql/pglite' +import { type PGliteWithLive } from '@electric-sql/pglite/live' +import { type PGliteWithSync } from '@electric-sql/pglite-sync' +import type { IssueChange, CommentChange, ChangeSet } from './utils/changes' +import { postInitialSync } from './migrations' +import { useEffect, useState } from 'react' + +const WRITE_SERVER_URL = import.meta.env.VITE_WRITE_SERVER_URL +const ELECTRIC_URL = import.meta.env.VITE_ELECTRIC_URL +const ELECTRIC_DATABASE_ID = import.meta.env.VITE_ELECTRIC_DATABASE_ID +const ELECTRIC_TOKEN = import.meta.env.VITE_ELECTRIC_TOKEN +const APPLY_CHANGES_URL = `${WRITE_SERVER_URL}/apply-changes` + +type SyncStatus = 'initial-sync' | 'done' + +type PGliteWithExtensions = PGliteWithLive & PGliteWithSync + +export async function startSync(pg: PGliteWithExtensions) { + await startSyncToDatabase(pg) + startWritePath(pg) +} + +async function startSyncToDatabase(pg: PGliteWithExtensions) { + // Check if there are any issues already in the database + const issues = await pg.query(`SELECT 1 FROM issue LIMIT 1`) + const hasIssuesAtStart = issues.rows.length > 0 + + let issueShapeInitialSyncDone = false + let commentShapeInitialSyncDone = false + let postInitialSyncDone = false + + if (!hasIssuesAtStart && !postInitialSyncDone) { + updateSyncStatus('initial-sync', 'Downloading shape data...') + } + + let postInitialSyncDoneResolver: () => void + const postInitialSyncDonePromise = new Promise((resolve) => { + postInitialSyncDoneResolver = resolve + }) + + const doPostInitialSync = async () => { + if ( + !hasIssuesAtStart && + issueShapeInitialSyncDone && + commentShapeInitialSyncDone && + !postInitialSyncDone + ) { + postInitialSyncDone = true + updateSyncStatus('initial-sync', 'Creating indexes...') + await postInitialSync(pg) + postInitialSyncDoneResolver() + } + } + + const issueUrl = new URL(`${ELECTRIC_URL}/v1/shape`) + if (ELECTRIC_TOKEN) { + issueUrl.searchParams.set('token', ELECTRIC_TOKEN) + } + + // Issues Sync + const issuesSync = await pg.sync.syncShapeToTable({ + shape: { + url: issueUrl.toString(), + params: { + table: 'issue', + database_id: ELECTRIC_DATABASE_ID, + }, + }, + table: 'issue', + primaryKey: ['id'], + shapeKey: 'issues', + commitGranularity: 'up-to-date', + useCopy: true, + onInitialSync: async () => { + issueShapeInitialSyncDone = true + await pg.exec(`ALTER TABLE issue ENABLE TRIGGER ALL`) + doPostInitialSync() + }, + }) + issuesSync.subscribe( + () => { + if (!hasIssuesAtStart && !postInitialSyncDone) { + updateSyncStatus('initial-sync', 'Inserting issues...') + } + }, + (error) => { + console.error('issuesSync error', error) + } + ) + + const commentUrl = new URL(`${ELECTRIC_URL}/v1/shape`) + if (ELECTRIC_TOKEN) { + commentUrl.searchParams.set('token', ELECTRIC_TOKEN) + } + + // Comments Sync + const commentsSync = await pg.sync.syncShapeToTable({ + shape: { + url: commentUrl.toString(), + params: { + table: 'comment', + database_id: ELECTRIC_DATABASE_ID, + }, + }, + table: 'comment', + primaryKey: ['id'], + shapeKey: 'comments', + commitGranularity: 'up-to-date', + useCopy: true, + onInitialSync: async () => { + commentShapeInitialSyncDone = true + await pg.exec(`ALTER TABLE comment ENABLE TRIGGER ALL`) + doPostInitialSync() + }, + }) + commentsSync.subscribe( + () => { + if (!hasIssuesAtStart && !postInitialSyncDone) { + updateSyncStatus('initial-sync', 'Inserting comments...') + } + }, + (error) => { + console.error('commentsSync error', error) + } + ) + + if (!hasIssuesAtStart) { + await postInitialSyncDonePromise + await pg.query(`SELECT 1;`) // Do a query to ensure PGlite is idle + } + updateSyncStatus('done') +} + +const syncMutex = new Mutex() + +async function startWritePath(pg: PGliteWithExtensions) { + // Use a live query to watch for changes to the local tables that need to be synced + pg.live.query<{ + issue_count: number + comment_count: number + }>( + ` + SELECT * FROM + (SELECT count(id) as issue_count FROM issue WHERE synced = false), + (SELECT count(id) as comment_count FROM comment WHERE synced = false) + `, + [], + async (results) => { + const { issue_count, comment_count } = results.rows[0] + if (issue_count > 0 || comment_count > 0) { + await syncMutex.acquire() + try { + doSyncToServer(pg) + } finally { + syncMutex.release() + } + } + } + ) +} + +// Call wrapped in mutex to prevent multiple syncs from happening at the same time +async function doSyncToServer(pg: PGliteWithExtensions) { + let issueChanges: IssueChange[] + let commentChanges: CommentChange[] + await pg.transaction(async (tx) => { + const issueRes = await tx.query(` + SELECT + id, + title, + description, + priority, + status, + modified, + created, + kanbanorder, + username, + modified_columns, + deleted, + new + FROM issue + WHERE synced = false AND sent_to_server = false + `) + const commentRes = await tx.query(` + SELECT + id, + body, + username, + issue_id, + modified, + created, + modified_columns, + deleted, + new + FROM comment + WHERE synced = false AND sent_to_server = false + `) + issueChanges = issueRes.rows + commentChanges = commentRes.rows + }) + const changeSet: ChangeSet = { + issues: issueChanges!, + comments: commentChanges!, + } + const response = await fetch(APPLY_CHANGES_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(changeSet), + }) + if (!response.ok) { + throw new Error('Failed to apply changes') + } + await pg.transaction(async (tx) => { + // Mark all changes as sent to server, but check that the modified timestamp + // has not changed in the meantime + + tx.exec('SET LOCAL electric.bypass_triggers = true') + + for (const issue of issueChanges!) { + await tx.query( + ` + UPDATE issue + SET sent_to_server = true + WHERE id = $1 AND modified = $2 + `, + [issue.id, issue.modified] + ) + } + + for (const comment of commentChanges!) { + await tx.query( + ` + UPDATE comment + SET sent_to_server = true + WHERE id = $1 AND modified = $2 + `, + [comment.id, comment.modified] + ) + } + }) +} + +export function updateSyncStatus(newStatus: SyncStatus, message?: string) { + localStorage.setItem('syncStatus', JSON.stringify([newStatus, message])) + // Fire a storage event on this tab as this doesn't happen automatically + window.dispatchEvent( + new StorageEvent('storage', { + key: 'syncStatus', + newValue: JSON.stringify([newStatus, message]), + }) + ) +} + +export function useSyncStatus() { + const currentSyncStatusJson = localStorage.getItem('syncStatus') + const currentSyncStatus: [SyncStatus, string] = currentSyncStatusJson + ? JSON.parse(currentSyncStatusJson) + : ['initial-sync', 'Starting sync...'] + const [syncStatus, setSyncStatus] = + useState<[SyncStatus, string]>(currentSyncStatus) + + useEffect(() => { + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'syncStatus' && e.newValue) { + const [newStatus, message] = JSON.parse(e.newValue) + setSyncStatus([newStatus, message]) + } + } + + window.addEventListener('storage', handleStorageChange) + + return () => { + window.removeEventListener('storage', handleStorageChange) + } + }, []) + + return syncStatus +} + +let initialSyncDone = false + +export function waitForInitialSyncDone() { + return new Promise((resolve) => { + if (initialSyncDone) { + resolve() + return + } + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'syncStatus' && e.newValue) { + const [newStatus] = JSON.parse(e.newValue) + if (newStatus === 'done') { + window.removeEventListener('storage', handleStorageChange) + initialSyncDone = true + resolve() + } + } + } + + // Check current status first + const currentSyncStatusJson = localStorage.getItem('syncStatus') + const [currentStatus] = currentSyncStatusJson + ? JSON.parse(currentSyncStatusJson) + : ['initial-sync'] + + if (currentStatus === 'done') { + initialSyncDone = true + resolve() + } else { + window.addEventListener('storage', handleStorageChange) + } + }) +} diff --git a/demos/linearlite/src/types/types.ts b/demos/linearlite/src/types/types.ts new file mode 100644 index 000000000..6f173d355 --- /dev/null +++ b/demos/linearlite/src/types/types.ts @@ -0,0 +1,119 @@ +import type React from 'react' + +import { ReactComponent as CancelIcon } from '../assets/icons/cancel.svg' +import { ReactComponent as BacklogIcon } from '../assets/icons/circle-dot.svg' +import { ReactComponent as TodoIcon } from '../assets/icons/circle.svg' +import { ReactComponent as DoneIcon } from '../assets/icons/done.svg' +import { ReactComponent as InProgressIcon } from '../assets/icons/half-circle.svg' + +import { ReactComponent as HighPriorityIcon } from '../assets/icons/signal-strong.svg' +import { ReactComponent as LowPriorityIcon } from '../assets/icons/signal-weak.svg' +import { ReactComponent as MediumPriorityIcon } from '../assets/icons/signal-medium.svg' +import { ReactComponent as NoPriorityIcon } from '../assets/icons/dots.svg' +import { ReactComponent as UrgentPriorityIcon } from '../assets/icons/rounded-claim.svg' + +export type Issue = { + id: string + title: string + description: string + priority: (typeof Priority)[keyof typeof Priority] + status: (typeof Status)[keyof typeof Status] + modified: Date + created: Date + kanbanorder: string + username: string + synced: boolean +} + +export type Comment = { + id: string + body: string + username: string + issue_id: string + created: Date + synced: boolean +} + +export const Priority = { + NONE: `none`, + URGENT: `urgent`, + HIGH: `high`, + LOW: `low`, + MEDIUM: `medium`, +} as const + +export type PriorityValue = (typeof Priority)[keyof typeof Priority] + +export const PriorityDisplay = { + [Priority.NONE]: `None`, + [Priority.URGENT]: `Urgent`, + [Priority.HIGH]: `High`, + [Priority.LOW]: `Low`, + [Priority.MEDIUM]: `Medium`, +} + +export const PriorityIcons = { + [Priority.NONE]: NoPriorityIcon, + [Priority.URGENT]: UrgentPriorityIcon, + [Priority.HIGH]: HighPriorityIcon, + [Priority.MEDIUM]: MediumPriorityIcon, + [Priority.LOW]: LowPriorityIcon, +} + +export const PriorityOptions: [ + React.FunctionComponent>, + PriorityValue, + string, +][] = [ + [PriorityIcons[Priority.NONE], Priority.NONE, `None`], + [PriorityIcons[Priority.URGENT], Priority.URGENT, `Urgent`], + [PriorityIcons[Priority.HIGH], Priority.HIGH, `High`], + [PriorityIcons[Priority.MEDIUM], Priority.MEDIUM, `Medium`], + [PriorityIcons[Priority.LOW], Priority.LOW, `Low`], +] + +export const Status = { + BACKLOG: `backlog`, + TODO: `todo`, + IN_PROGRESS: `in_progress`, + DONE: `done`, + CANCELED: `canceled`, +} as const + +export type StatusValue = (typeof Status)[keyof typeof Status] + +export const StatusDisplay = { + [Status.BACKLOG]: `Backlog`, + [Status.TODO]: `To Do`, + [Status.IN_PROGRESS]: `In Progress`, + [Status.DONE]: `Done`, + [Status.CANCELED]: `Canceled`, +} + +export const StatusIcons = { + [Status.BACKLOG]: BacklogIcon, + [Status.TODO]: TodoIcon, + [Status.IN_PROGRESS]: InProgressIcon, + [Status.DONE]: DoneIcon, + [Status.CANCELED]: CancelIcon, +} + +export const StatusOptions: [ + React.FunctionComponent>, + StatusValue, + string, +][] = [ + [StatusIcons[Status.BACKLOG], Status.BACKLOG, StatusDisplay[Status.BACKLOG]], + [StatusIcons[Status.TODO], Status.TODO, StatusDisplay[Status.TODO]], + [ + StatusIcons[Status.IN_PROGRESS], + Status.IN_PROGRESS, + StatusDisplay[Status.IN_PROGRESS], + ], + [StatusIcons[Status.DONE], Status.DONE, StatusDisplay[Status.DONE]], + [ + StatusIcons[Status.CANCELED], + Status.CANCELED, + StatusDisplay[Status.CANCELED], + ], +] diff --git a/demos/linearlite/src/utils/changes.ts b/demos/linearlite/src/utils/changes.ts new file mode 100644 index 000000000..c0a6aa48a --- /dev/null +++ b/demos/linearlite/src/utils/changes.ts @@ -0,0 +1,39 @@ +import { z } from 'zod' + +export const issueChangeSchema = z.object({ + id: z.string(), + title: z.string().nullable().optional(), + description: z.string().nullable().optional(), + priority: z.string().nullable().optional(), + status: z.string().nullable().optional(), + modified: z.string().nullable().optional(), + created: z.string().nullable().optional(), + kanbanorder: z.string().nullable().optional(), + username: z.string().nullable().optional(), + modified_columns: z.array(z.string()).nullable().optional(), + deleted: z.boolean().nullable().optional(), + new: z.boolean().nullable().optional(), +}) + +export type IssueChange = z.infer + +export const commentChangeSchema = z.object({ + id: z.string(), + body: z.string().nullable().optional(), + username: z.string().nullable().optional(), + issue_id: z.string().nullable().optional(), + modified: z.string().nullable().optional(), + created: z.string().nullable().optional(), + modified_columns: z.array(z.string()).nullable().optional(), + deleted: z.boolean().nullable().optional(), + new: z.boolean().nullable().optional(), +}) + +export type CommentChange = z.infer + +export const changeSetSchema = z.object({ + issues: z.array(issueChangeSchema), + comments: z.array(commentChangeSchema), +}) + +export type ChangeSet = z.infer diff --git a/demos/linearlite/src/utils/date.ts b/demos/linearlite/src/utils/date.ts new file mode 100644 index 000000000..367909624 --- /dev/null +++ b/demos/linearlite/src/utils/date.ts @@ -0,0 +1,6 @@ +import dayjs from 'dayjs' + +export function formatDate(date?: Date): string { + if (!date) return `` + return dayjs(date).format(`D MMM`) +} diff --git a/demos/linearlite/src/utils/filterState.ts b/demos/linearlite/src/utils/filterState.ts new file mode 100644 index 000000000..490909c61 --- /dev/null +++ b/demos/linearlite/src/utils/filterState.ts @@ -0,0 +1,115 @@ +import { useSearchParams } from 'react-router-dom' + +export interface FilterState { + orderBy: string + orderDirection: `asc` | `desc` + status?: string[] + priority?: string[] + query?: string +} + +export function getFilterStateFromSearchParams( + searchParams: URLSearchParams +): FilterState { + const orderBy = searchParams.get(`orderBy`) ?? `created` + const orderDirection = + (searchParams.get(`orderDirection`) as `asc` | `desc`) ?? `desc` + const status = searchParams + .getAll(`status`) + .map((status) => status.toLocaleLowerCase().split(`,`)) + .flat() + const priority = searchParams + .getAll(`priority`) + .map((status) => status.toLocaleLowerCase().split(`,`)) + .flat() + const query = searchParams.get(`query`) + + const state = { + orderBy, + orderDirection, + status, + priority, + query: query || undefined, + } + + return state +} + +export function useFilterState(): [ + FilterState, + (state: Partial) => void, +] { + const [searchParams, setSearchParams] = useSearchParams() + const state = getFilterStateFromSearchParams(searchParams) + + const setState = (state: Partial) => { + const { orderBy, orderDirection, status, priority, query } = state + setSearchParams((searchParams) => { + if (orderBy) { + searchParams.set(`orderBy`, orderBy) + } else { + searchParams.delete(`orderBy`) + } + if (orderDirection) { + searchParams.set(`orderDirection`, orderDirection) + } else { + searchParams.delete(`orderDirection`) + } + if (status && status.length > 0) { + searchParams.set(`status`, status.join(`,`)) + } else { + searchParams.delete(`status`) + } + if (priority && priority.length > 0) { + searchParams.set(`priority`, priority.join(`,`)) + } else { + searchParams.delete(`priority`) + } + if (query) { + searchParams.set(`query`, query) + } else { + searchParams.delete(`query`) + } + return searchParams + }) + } + + return [state, setState] +} + +export function filterStateToSql(filterState: FilterState) { + let i = 1 + const sqlWhere = [] + const sqlParams = [] + if (filterState.status?.length) { + sqlWhere.push( + `status IN (${filterState.status.map(() => `$${i++}`).join(' ,')})` + ) + sqlParams.push(...filterState.status) + } + if (filterState.priority?.length) { + sqlWhere.push( + `priority IN (${filterState.priority.map(() => `$${i++}`).join(' ,')})` + ) + sqlParams.push(...filterState.priority) + } + if (filterState.query) { + sqlWhere.push(` + (setweight(to_tsvector('simple', coalesce(title, '')), 'A') || + setweight(to_tsvector('simple', coalesce(description, '')), 'B')) + @@ plainto_tsquery('simple', $${i++}) + `) + sqlParams.push(filterState.query) + } + const sql = ` + SELECT id, title, priority, status, modified, created, kanbanorder, username, synced + FROM issue + WHERE + ${sqlWhere.length ? `${sqlWhere.join(' AND ')} AND ` : ''} + deleted = false + ORDER BY + ${filterState.orderBy} ${filterState.orderDirection}, + id ASC + ` + return { sql, sqlParams } +} diff --git a/demos/linearlite/src/utils/notification.tsx b/demos/linearlite/src/utils/notification.tsx new file mode 100644 index 000000000..cab562711 --- /dev/null +++ b/demos/linearlite/src/utils/notification.tsx @@ -0,0 +1,49 @@ +import { toast } from 'react-toastify' + +export function showWarning(msg: string, title: string = ``) { + //TODO: make notification showing from bottom + const content = ( +

+ {title !== `` && ( +
+ + + + + + {title} +
+ )} +
{msg}
+
+ ) + toast(content, { + position: `bottom-right`, + }) +} + +export function showInfo(msg: string, title: string = ``) { + //TODO: make notification showing from bottom + const content = ( +
+ {title !== `` && ( +
+ + + + + + {title} +
+ )} +
{msg}
+
+ ) + toast(content, { + position: `bottom-right`, + }) +} diff --git a/demos/linearlite/src/vite-env.d.ts b/demos/linearlite/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/demos/linearlite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/demos/linearlite/supabase/.gitignore b/demos/linearlite/supabase/.gitignore new file mode 100644 index 000000000..a3ad88055 --- /dev/null +++ b/demos/linearlite/supabase/.gitignore @@ -0,0 +1,4 @@ +# Supabase +.branches +.temp +.env diff --git a/demos/linearlite/supabase/config.toml b/demos/linearlite/supabase/config.toml new file mode 100644 index 000000000..63fcd1ab3 --- /dev/null +++ b/demos/linearlite/supabase/config.toml @@ -0,0 +1,275 @@ +# For detailed configuration reference documentation, visit: +# https://supabase.com/docs/guides/local-development/cli/config +# A string used to distinguish different Supabase projects on the same host. Defaults to the +# working directory name when running `supabase init`. +project_id = "linearlite" + +[api] +enabled = true +# Port to use for the API URL. +port = 54321 +# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API +# endpoints. `public` is always included. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. `public` is always included. +extra_search_path = ["public", "extensions"] +# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size +# for accidental or malicious requests. +max_rows = 1000 + +[api.tls] +enabled = false + +[db] +# Port to use for the local database URL. +port = 54322 +# Port used by db diff command to initialize the shadow database. +shadow_port = 54320 +# The database major version to use. This has to be the same as your remote database's. Run `SHOW +# server_version;` on the remote database to check. +major_version = 15 + +[db.pooler] +enabled = false +# Port to use for the local connection pooler. +port = 54329 +# Specifies when a server connection can be reused by other clients. +# Configure one of the supported pooler modes: `transaction`, `session`. +pool_mode = "transaction" +# How many server connections to allow per user/database pair. +default_pool_size = 20 +# Maximum number of client connections allowed. +max_client_conn = 100 + +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory. For example: +# sql_paths = ['./seeds/*.sql', '../project-src/seeds/*-load-testing.sql'] +sql_paths = ['./seed.sql'] + +[realtime] +enabled = true +# Bind realtime via either IPv4 or IPv6. (default: IPv4) +# ip_version = "IPv6" +# The maximum length in bytes of HTTP request headers. (default: 4096) +# max_header_length = 4096 + +[studio] +enabled = true +# Port to use for Supabase Studio. +port = 54323 +# External URL of the API server that frontend connects to. +api_url = "http://127.0.0.1" +# OpenAI API Key to use for Supabase AI in the Supabase Studio. +openai_api_key = "env(OPENAI_API_KEY)" + +# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they +# are monitored, and you can view the emails that would have been sent from the web interface. +[inbucket] +enabled = true +# Port to use for the email testing server web interface. +port = 54324 +# Uncomment to expose additional ports for testing user applications that send emails. +# smtp_port = 54325 +# pop3_port = 54326 +# admin_email = "admin@email.com" +# sender_name = "Admin" + +[storage] +enabled = true +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[storage.image_transformation] +enabled = true + +# Uncomment to configure local storage buckets +# [storage.buckets.images] +# public = false +# file_size_limit = "50MiB" +# allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" + +[auth] +enabled = true +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://127.0.0.1:3000" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://127.0.0.1:3000"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). +jwt_expiry = 3600 +# If disabled, the refresh token will never expire. +enable_refresh_token_rotation = true +# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. +# Requires enable_refresh_token_rotation = true. +refresh_token_reuse_interval = 10 +# Allow/disallow new user signups to your project. +enable_signup = true +# Allow/disallow anonymous sign-ins to your project. +enable_anonymous_sign_ins = false +# Allow/disallow testing manual linking of accounts +enable_manual_linking = false +# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more. +minimum_password_length = 6 +# Passwords that do not meet the following requirements will be rejected as weak. Supported values +# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols` +password_requirements = "" + +[auth.email] +# Allow/disallow new user signups via email to your project. +enable_signup = true +# If enabled, a user will be required to confirm any email change on both the old, and new email +# addresses. If disabled, only the new email is required to confirm. +double_confirm_changes = true +# If enabled, users need to confirm their email address before signing in. +enable_confirmations = false +# If enabled, users will need to reauthenticate or have logged in recently to change their password. +secure_password_change = false +# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email. +max_frequency = "1s" +# Number of characters used in the email OTP. +otp_length = 6 +# Number of seconds before the email OTP expires (defaults to 1 hour). +otp_expiry = 3600 + +# Use a production-ready SMTP server +# [auth.email.smtp] +# host = "smtp.sendgrid.net" +# port = 587 +# user = "apikey" +# pass = "env(SENDGRID_API_KEY)" +# admin_email = "admin@email.com" +# sender_name = "Admin" + +# Uncomment to customize email template +# [auth.email.template.invite] +# subject = "You have been invited" +# content_path = "./supabase/templates/invite.html" + +[auth.sms] +# Allow/disallow new user signups via SMS to your project. +enable_signup = false +# If enabled, users need to confirm their phone number before signing in. +enable_confirmations = false +# Template for sending OTP to users +template = "Your code is {{ .Code }}" +# Controls the minimum amount of time that must pass before sending another sms otp. +max_frequency = "5s" + +# Use pre-defined map of phone number to OTP for testing. +# [auth.sms.test_otp] +# 4152127777 = "123456" + +# Configure logged in session timeouts. +# [auth.sessions] +# Force log out after the specified duration. +# timebox = "24h" +# Force log out if the user has been inactive longer than the specified duration. +# inactivity_timeout = "8h" + +# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. +# [auth.hook.custom_access_token] +# enabled = true +# uri = "pg-functions:////" + +# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. +[auth.sms.twilio] +enabled = false +account_sid = "" +message_service_sid = "" +# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: +auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" + +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control use of MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = true +verify_enabled = true + +# Configure Multi-factor-authentication via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ .Code }}" +max_frequency = "5s" + +# Configure Multi-factor-authentication via WebAuthn +# [auth.mfa.web_authn] +# enroll_enabled = true +# verify_enabled = true + +# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, +# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, +# `twitter`, `slack`, `spotify`, `workos`, `zoom`. +[auth.external.apple] +enabled = false +client_id = "" +# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: +secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +# Overrides the default auth redirectUrl. +redirect_uri = "" +# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, +# or any other third-party OIDC providers. +url = "" +# If enabled, the nonce check will be skipped. Required for local sign in with Google auth. +skip_nonce_check = false + +# Use Firebase Auth as a third-party provider alongside Supabase Auth. +[auth.third_party.firebase] +enabled = false +# project_id = "my-firebase-project" + +# Use Auth0 as a third-party provider alongside Supabase Auth. +[auth.third_party.auth0] +enabled = false +# tenant = "my-auth0-tenant" +# tenant_region = "us" + +# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth. +[auth.third_party.aws_cognito] +enabled = false +# user_pool_id = "my-user-pool-id" +# user_pool_region = "us-east-1" + +[edge_runtime] +enabled = true +# Configure one of the supported request policies: `oneshot`, `per_worker`. +# Use `oneshot` for hot reload, or `per_worker` for load testing. +policy = "oneshot" +# Port to attach the Chrome inspector for debugging edge functions. +inspector_port = 8083 + +# Use these configurations to customize your Edge Function. +# [functions.MY_FUNCTION_NAME] +# enabled = true +# verify_jwt = true +# import_map = "./functions/MY_FUNCTION_NAME/deno.json" +# Uncomment to specify a custom file path to the entrypoint. +# Supported file extensions are: .ts, .js, .mjs, .jsx, .tsx +# entrypoint = "./functions/MY_FUNCTION_NAME/index.ts" + +[analytics] +enabled = true +port = 54327 +# Configure one of the supported backends: `postgres`, `bigquery`. +backend = "postgres" + +# Experimental features may be deprecated any time +[experimental] +# Configures Postgres storage engine to use OrioleDB (S3) +orioledb_version = "" +# Configures S3 bucket URL, eg. .s3-.amazonaws.com +s3_host = "env(S3_HOST)" +# Configures S3 bucket region, eg. us-east-1 +s3_region = "env(S3_REGION)" +# Configures AWS_ACCESS_KEY_ID for S3 bucket +s3_access_key = "env(S3_ACCESS_KEY)" +# Configures AWS_SECRET_ACCESS_KEY for S3 bucket +s3_secret_key = "env(S3_SECRET_KEY)" diff --git a/demos/linearlite/supabase/functions/write-server/index.ts b/demos/linearlite/supabase/functions/write-server/index.ts new file mode 100644 index 000000000..6554c8730 --- /dev/null +++ b/demos/linearlite/supabase/functions/write-server/index.ts @@ -0,0 +1,130 @@ +// Follow this setup guide to integrate the Deno language server with your editor: +// https://deno.land/manual/getting_started/setup_your_environment +// This enables autocomplete, go to definition, etc. + +// Setup type definitions for built-in Supabase Runtime APIs +import 'jsr:@supabase/functions-js/edge-runtime.d.ts' +import { Hono } from 'jsr:@hono/hono' +import { cors } from 'jsr:@hono/hono/cors' +import postgres from 'https://deno.land/x/postgresjs/mod.js' +import { z } from 'https://deno.land/x/zod/mod.ts' + +const issueChangeSchema = z.object({ + id: z.string(), + title: z.string().nullable().optional(), + description: z.string().nullable().optional(), + priority: z.string().nullable().optional(), + status: z.string().nullable().optional(), + modified: z.string().nullable().optional(), + created: z.string().nullable().optional(), + kanbanorder: z.string().nullable().optional(), + username: z.string().nullable().optional(), + modified_columns: z.array(z.string()).nullable().optional(), + deleted: z.boolean().nullable().optional(), + new: z.boolean().nullable().optional(), +}) + +type IssueChange = z.infer + +const commentChangeSchema = z.object({ + id: z.string(), + body: z.string().nullable().optional(), + username: z.string().nullable().optional(), + issue_id: z.string().nullable().optional(), + modified: z.string().nullable().optional(), + created: z.string().nullable().optional(), + modified_columns: z.array(z.string()).nullable().optional(), + deleted: z.boolean().nullable().optional(), + new: z.boolean().nullable().optional(), +}) + +type CommentChange = z.infer + +const changeSetSchema = z.object({ + issues: z.array(issueChangeSchema), + comments: z.array(commentChangeSchema), +}) + +type ChangeSet = z.infer + +const DATABASE_URL = Deno.env.get('SUPABASE_DB_URL')! + +// Create postgres connection +const sql = postgres(DATABASE_URL) + +const app = new Hono() + +// Middleware +app.use('/write-server/*', cors()) + +// Routes +app.get('/write-server/', async (c) => { + const result = await sql` + SELECT 'ok' as status, version() as postgres_version, now() as server_time + ` + return c.json(result[0]) +}) + +app.post('/write-server/apply-changes', async (c) => { + const content = await c.req.json() + let parsedChanges: ChangeSet + try { + parsedChanges = changeSetSchema.parse(content) + // Any additional validation of the changes can be done here! + } catch (error) { + console.error(error) + return c.json({ error: 'Invalid changes' }, 400) + } + const changeResponse = await applyChanges(parsedChanges) + return c.json(changeResponse) +}) + +async function applyChanges(changes: ChangeSet): Promise<{ success: boolean }> { + const { issues, comments } = changes + + try { + await sql.begin(async (sql) => { + for (const issue of issues) { + await applyTableChange('issue', issue, sql) + } + for (const comment of comments) { + await applyTableChange('comment', comment, sql) + } + }) + return { success: true } + } catch (error) { + throw error + } +} + +async function applyTableChange( + tableName: 'issue' | 'comment', + change: IssueChange | CommentChange, + sql: postgres.TransactionSql +): Promise { + const { + id, + modified_columns: modified_columns_raw, + new: isNew, + deleted, + } = change + const modified_columns = modified_columns_raw as (keyof typeof change)[] + + if (deleted) { + await sql` + DELETE FROM ${sql(tableName)} WHERE id = ${id} + ` + } else if (isNew) { + await sql` + INSERT INTO ${sql(tableName)} ${sql(change, 'id', ...modified_columns)} + ` + } else { + await sql` + UPDATE ${sql(tableName)} + SET ${sql(change, ...modified_columns)} + WHERE id = ${id} + ` + } +} + +Deno.serve(app.fetch) diff --git a/demos/linearlite/tailwind.config.js b/demos/linearlite/tailwind.config.js new file mode 100644 index 000000000..04d9eb03d --- /dev/null +++ b/demos/linearlite/tailwind.config.js @@ -0,0 +1,88 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], + darkMode: ['class', '[data-theme="dark"]'], + theme: { + screens: { + sm: '640px', + // => @media (min-width: 640px) { ... } + + md: '768px', + // => @media (min-width: 768px) { ... } + + lg: '1024px', + // => @media (min-width: 1024px) { ... } + + xl: '1280px', + // => @media (min-width: 1280px) { ... } + + '2xl': '1536px', + // => @media (min-width: 1536px) { ... } + }, + // color: { + // // gray: colors.trueGray, + // }, + fontFamily: { + sans: [ + 'Inter\\ UI', + 'SF\\ Pro\\ Display', + '-apple-system', + 'BlinkMacSystemFont', + 'Segoe\\ UI', + 'Roboto', + 'Oxygen', + 'Ubuntu', + 'Cantarell', + 'Open\\ Sans', + 'Helvetica\\ Neue', + 'sans-serif', + ], + }, + borderWidth: { + DEFAULT: '1px', + 0: '0', + 2: '2px', + 3: '3px', + 4: '4px', + 6: '6px', + 8: '8px', + }, + extend: { + boxShadow: { + modal: 'rgb(0 0 0 / 9%) 0px 3px 12px', + 'large-modal': 'rgb(0 0 0 / 50%) 0px 16px 70px', + }, + spacing: { + 2.5: '10px', + 4.5: '18px', + 3.5: '14px', + 34: '136px', + + 70: '280px', + 140: '560px', + 100: '400px', + 175: '700px', + 53: '212px', + 90: '360px', + }, + fontSize: { + xxs: '0.5rem', + xs: '0.75rem', // 12px + sm: '0.8125rem', // 13px + md: '0.9357rem', //15px + 14: '0.875rem', + base: '1.0rem', // 16px + }, + zIndex: { + 100: 100, + }, + }, + }, + variants: { + extend: { + backgroundColor: ['checked'], + borderColor: ['checked'], + }, + }, + plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')], +} diff --git a/demos/linearlite/tsconfig.json b/demos/linearlite/tsconfig.json new file mode 100644 index 000000000..a462695a9 --- /dev/null +++ b/demos/linearlite/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "types": ["vite/client", "vite-plugin-svgr/client"], + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/demos/linearlite/vite.config.ts b/demos/linearlite/vite.config.ts new file mode 100644 index 000000000..d77861d2b --- /dev/null +++ b/demos/linearlite/vite.config.ts @@ -0,0 +1,31 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' +import svgr from 'vite-plugin-svgr' + +// https://vitejs.dev/config/ +export default defineConfig({ + optimizeDeps: { + exclude: ['@electric-sql/pglite'], + }, + worker: { + format: 'es', + }, + plugins: [ + react(), + svgr({ + svgrOptions: { + svgo: true, + plugins: [`@svgr/plugin-svgo`, `@svgr/plugin-jsx`], + svgoConfig: { + plugins: [ + `preset-default`, + `removeTitle`, + `removeDesc`, + `removeDoctype`, + `cleanupIds`, + ], + }, + }, + }), + ], +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fe4984df..d3ed0ef42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 3.3.3 tsup: specifier: ^8.3.0 - version: 8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3) + version: 8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.6.0) tsx: specifier: ^4.19.1 version: 4.19.1 @@ -39,6 +39,226 @@ importers: specifier: ^5.6.3 version: 5.6.3 + demos/linearlite: + dependencies: + '@electric-sql/pglite': + specifier: workspace:* + version: link:../../packages/pglite + '@electric-sql/pglite-react': + specifier: workspace:* + version: link:../../packages/pglite-react + '@electric-sql/pglite-repl': + specifier: workspace:* + version: link:../../packages/pglite-repl + '@electric-sql/pglite-sync': + specifier: workspace:* + version: link:../../packages/pglite-sync + '@firefox-devtools/react-contextmenu': + specifier: ^5.1.1 + version: 5.1.1(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@headlessui/react': + specifier: ^1.7.17 + version: 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@hono/node-server': + specifier: ^1.8.0 + version: 1.13.7(hono@4.6.12) + '@svgr/plugin-jsx': + specifier: ^8.1.0 + version: 8.1.0(@svgr/core@8.1.0(typescript@5.6.3)) + '@svgr/plugin-svgo': + specifier: ^8.1.0 + version: 8.1.0(@svgr/core@8.1.0(typescript@5.6.3))(typescript@5.6.3) + '@tailwindcss/forms': + specifier: ^0.5.6 + version: 0.5.9(tailwindcss@3.4.13) + '@tiptap/extension-placeholder': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-table': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-table-cell': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-table-header': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-table-row': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/pm': + specifier: ^2.4.0 + version: 2.8.0 + '@tiptap/react': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tiptap/starter-kit': + specifier: ^2.4.0 + version: 2.8.0(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))) + animate.css: + specifier: ^4.1.1 + version: 4.1.1 + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + classnames: + specifier: ^2.5.1 + version: 2.5.1 + cors: + specifier: ^2.8.5 + version: 2.8.5 + dayjs: + specifier: ^1.11.11 + version: 1.11.13 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + fractional-indexing: + specifier: ^3.2.0 + version: 3.2.0 + hono: + specifier: ^4.0.0 + version: 4.6.12 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 + lodash.debounce: + specifier: ^4.0.8 + version: 4.0.8 + postgres: + specifier: ^3.4.3 + version: 3.4.5 + react: + specifier: ^18.3.1 + version: 18.3.1 + react-beautiful-dnd: + specifier: ^13.1.1 + version: 13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-icons: + specifier: ^4.10.1 + version: 4.12.0(react@18.3.1) + react-markdown: + specifier: ^8.0.7 + version: 8.0.7(@types/react@18.3.11)(react@18.3.1) + react-router-dom: + specifier: ^6.24.1 + version: 6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-toastify: + specifier: ^9.1.3 + version: 9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-virtualized-auto-sizer: + specifier: ^1.0.24 + version: 1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-window: + specifier: ^1.8.10 + version: 1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tiptap-markdown: + specifier: ^0.8.2 + version: 0.8.10(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + uuid: + specifier: ^9.0.0 + version: 9.0.1 + vite-plugin-svgr: + specifier: ^3.2.0 + version: 3.3.0(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@databases/pg-migrations': + specifier: ^5.0.3 + version: 5.0.3(typescript@5.6.3) + '@faker-js/faker': + specifier: ^8.4.1 + version: 8.4.1 + '@tailwindcss/typography': + specifier: ^0.5.10 + version: 0.5.15(tailwindcss@3.4.13) + '@types/body-parser': + specifier: ^1.19.5 + version: 1.19.5 + '@types/jest': + specifier: ^29.5.12 + version: 29.5.13 + '@types/lodash.debounce': + specifier: ^4.0.9 + version: 4.0.9 + '@types/node': + specifier: ^20.14.10 + version: 20.16.11 + '@types/pg': + specifier: ^8.11.10 + version: 8.11.10 + '@types/react': + specifier: ^18.3.3 + version: 18.3.11 + '@types/react-beautiful-dnd': + specifier: ^13.1.8 + version: 13.1.8 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.0 + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 + '@types/react-window': + specifier: ^1.8.8 + version: 1.8.8 + '@types/uuid': + specifier: ^9.0.3 + version: 9.0.8 + '@types/vite-plugin-react-svg': + specifier: ^0.2.5 + version: 0.2.5 + '@vitejs/plugin-react': + specifier: ^4.3.1 + version: 4.3.2(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)) + autoprefixer: + specifier: ^10.4.19 + version: 10.4.20(postcss@8.4.47) + dotenv-cli: + specifier: ^7.4.2 + version: 7.4.2 + eslint: + specifier: ^8.57.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.1) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.3.3) + eslint-plugin-react-hooks: + specifier: ^4.6.2 + version: 4.6.2(eslint@8.57.1) + eslint-plugin-react-refresh: + specifier: ^0.4.3 + version: 0.4.12(eslint@8.57.1) + fs-extra: + specifier: ^10.0.0 + version: 10.1.0 + postcss: + specifier: ^8.4.39 + version: 8.4.47 + supabase: + specifier: ^1.226.3 + version: 1.226.3 + tailwindcss: + specifier: ^3.4.4 + version: 3.4.13 + tsx: + specifier: ^4.19.1 + version: 4.19.1 + typescript: + specifier: ^5.5.3 + version: 5.6.3 + vite: + specifier: ^5.4.8 + version: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + docs: dependencies: '@electric-sql/pglite': @@ -396,6 +616,10 @@ packages: '@algolia/transporter@4.24.0': resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==} + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -613,6 +837,57 @@ packages: '@codemirror/view@6.34.1': resolution: {integrity: sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==} + '@databases/connection-pool@1.1.0': + resolution: {integrity: sha512-/12/SNgl0V77mJTo5SX3yGPz4c9XGQwAlCfA0vlfs/0HcaErNpYXpmhj0StET07w6TmTJTnaUgX2EPcQK9ez5A==} + + '@databases/escape-identifier@1.0.3': + resolution: {integrity: sha512-Su36iSVzaHxpVdISVMViUX/32sLvzxVgjZpYhzhotxZUuLo11GVWsiHwqkvUZijTLUxcDmUqEwGJO3O/soLuZA==} + + '@databases/lock@2.1.0': + resolution: {integrity: sha512-ReWnFE5qeCuO2SA5h5fDh/hE/vMolA+Epe6xkAQP1FL2nhnsTCYwN2JACk/kWctR4OQoh0njBjPZ0yfIptclcA==} + + '@databases/migrations-base@3.0.1': + resolution: {integrity: sha512-CutCQ1AjsEWqSuXInD8KwaZYa3/InYGFu3uZ/2pu0Ku4MHRab14+sKNXLk/dxHaJLplngLtCraBo8rL7/21Vhg==} + + '@databases/pg-config@3.2.0': + resolution: {integrity: sha512-hoPAK/F8gLcLgEJ8mLSnNvRlKqShwx5+GglDHIIfQhKF+Zz6M6QceiOefckS4WSjA0x2HClPvpercnXp9i24ag==} + + '@databases/pg-connection-string@1.0.0': + resolution: {integrity: sha512-8czOF9jlv7PlS7BPjnL82ynpDs1t8cu+C2jvdtMr37e8daPKMS7n1KfNE9xtr2Gq4QYKjynep097eYa5yIwcLA==} + + '@databases/pg-data-type-id@3.0.0': + resolution: {integrity: sha512-VqW1csN8pRsWJxjPsGIC9FQ8wyenfmGv0P//BaeDMAu/giM3IXKxKM8fkScUSQ00uqFK/L1iHS5g6dgodF3XzA==} + + '@databases/pg-errors@1.0.0': + resolution: {integrity: sha512-Yz3exbptZwOn4ZD/MSwY6z++XVyOFsMh5DERvSw3awRwJFnfdaqdeiIxxX0MVjM6KPihF0xxp8lPO7vTc5ydpw==} + + '@databases/pg-migrations@5.0.3': + resolution: {integrity: sha512-mUKbVYiACRZJOhE96Y5Fr2IIJ2iBNPVM6L3LgBypH9zIQMOLixGpJ6ZS0oabtNIFJhjncXl5FVEtYaa8x5KkoA==} + hasBin: true + + '@databases/pg@5.5.0': + resolution: {integrity: sha512-WIojK9AYIlNi5YRfc5YUOow3PQ82ClmwT9HG3nEsKLUERYieoVmHMYDQLS0ry6FjgJx+2yFs7LCw4kZpWu1TBw==} + + '@databases/push-to-async-iterable@3.0.0': + resolution: {integrity: sha512-xwu/yNgINdMU+fn6UwFsxh+pa6UrVPafY+0qm0RK0/nKyjllfDqSbwK4gSmdmLEwPYxKwch9CAE3P8NxN1hPSg==} + + '@databases/queue@1.0.1': + resolution: {integrity: sha512-dqRU+/aQ4lhFzjPIkIhjB0+UEKMb76FoBgHOJUTcEblgatr/IhdhHliT3VVwcImXh35Mz297PAXE4yFM4eYWUQ==} + + '@databases/shared@3.1.0': + resolution: {integrity: sha512-bO1DIYAYDiWOCqVPvBio1JqZQYh4dph2M1av2w/REeFT6WBd64mTrOFlcxKV0CUAYT0UiJsDfPqEfw0/APRzWg==} + + '@databases/split-sql-query@1.0.4': + resolution: {integrity: sha512-lDqDQvH34NNjLs0knaDvL6HKgPtishQlDYHfOkvbAd5VQOEhcDvvmG2zbBuFvS2HQAz5NsyLj5erGaxibkxhvQ==} + peerDependencies: + '@databases/sql': '*' + + '@databases/sql@3.3.0': + resolution: {integrity: sha512-vj9huEy4mjJ48GS1Z8yvtMm4BYAnFYACUds25ym6Gd/gsnngkJ17fo62a6mmbNNwCBS/8467PmZR01Zs/06TjA==} + + '@databases/validate-unicode@1.0.0': + resolution: {integrity: sha512-dLKqxGcymeVwEb/6c44KjOnzaAafFf0Wxa8xcfEjx/qOl3rdijsKYBAtIGhtVtOlpPf/PFKfgTuFurSPn/3B/g==} + '@docsearch/css@3.6.2': resolution: {integrity: sha512-vKNZepO2j7MrYBTZIGXvlUOIR+v9KRf70FApRgovWrj3GTs1EITz/Xb0AOlm1xsQBp16clVZj1SY/qaOJbQtZw==} @@ -819,6 +1094,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.14.54': + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -1022,6 +1303,30 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@firefox-devtools/react-contextmenu@5.1.1': + resolution: {integrity: sha512-I4/hSZPE6A9Whu9Xut80gJbyz0fWCggNiDJOM0Bhdl9RJa4gBiFj5Q5MDjQSP1vpI3InP33lS747Z3LUnivw9w==} + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.1 || ^17.0.0 || ^18.0.0 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.1 || ^17.0.0 || ^18.0.0 + + '@headlessui/react@1.7.19': + resolution: {integrity: sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==} + engines: {node: '>=10'} + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + + '@hono/node-server@1.13.7': + resolution: {integrity: sha512-kTfUMsoloVKtRA2fLiGSd9qBddmru9KadNyhJCwgKBxTiNkaAJEwkVN9KV/rS4HtmmNRtUh6P+YpmjRMl0d9vQ==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@humanwhocodes/config-array@0.13.0': resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} engines: {node: '>=10.10.0'} @@ -1035,14 +1340,30 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead + '@inquirer/figures@1.0.7': + resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + engines: {node: '>=18'} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -1151,6 +1472,20 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@remirror/core-constants@3.0.0': + resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} + + '@remix-run/router@1.20.0': + resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==} + engines: {node: '>=14.0.0'} + '@rollup/pluginutils@5.1.2': resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} engines: {node: '>=14.0.0'} @@ -1283,6 +1618,99 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==} + engines: {node: '>=14'} + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==} + engines: {node: '>=14'} + + '@svgr/plugin-jsx@8.1.0': + resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==} + engines: {node: '>=14'} + peerDependencies: + '@svgr/core': '*' + + '@tailwindcss/forms@0.5.9': + resolution: {integrity: sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20' + + '@tailwindcss/typography@0.5.15': + resolution: {integrity: sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20' + + '@tanstack/react-virtual@3.10.8': + resolution: {integrity: sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@tanstack/virtual-core@3.10.8': + resolution: {integrity: sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -1316,6 +1744,172 @@ packages: '@vue/compiler-sfc': optional: true + '@tiptap/core@2.8.0': + resolution: {integrity: sha512-xsqDI4BNzYRWRtBq7+/38ThhqEr7uG9Njip1x+9/wgR3vWPBFnBkYJTz6jSxS35NRE6BSnERm4/B/vrLuY1Hdw==} + peerDependencies: + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-blockquote@2.8.0': + resolution: {integrity: sha512-m3CKrOIvV7fY1Ak2gYf5LkKiz6AHxHpg6wxfVaJvdBqXgLyVtHo552N+A4oSHOSRbB4AG9EBQ2NeBM8cdEQ4MA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-bold@2.8.0': + resolution: {integrity: sha512-U1YkZBxDkSLNvPNiqxB5g42IeJHr27C7zDb/yGQN2xL4UBeg4O9xVhCFfe32f6tLwivSL0dar4ScElpaCJuqow==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-bubble-menu@2.8.0': + resolution: {integrity: sha512-swg+myJPN60LduQvLMF4hVBqP5LOIN01INZBzBI8egz8QufqtSyRCgXl7Xcma0RT5xIXnZSG9XOqNFf2rtkjKA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-bullet-list@2.8.0': + resolution: {integrity: sha512-H4O2X0ozbc/ce9/XF1H98sqWVUdtt7jzy7hMBunwmY8ZxI4dHtcRkeg81CZbpKTqOqRrMCLWjE3M2tgiDXrDkA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/extension-list-item': ^2.7.0 + '@tiptap/extension-text-style': ^2.7.0 + + '@tiptap/extension-code-block@2.8.0': + resolution: {integrity: sha512-POuA5Igx+Dto0DTazoBFAQTj/M/FCdkqRVD9Uhsxhv49swPyANTJRr05vgbgtHB+NDDsZfCawVh7pI0IAD/O0w==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-code@2.8.0': + resolution: {integrity: sha512-VSFn3sFF6qPpOGkXFhik8oYRH5iByVJpFEFd/duIEftmS0MdPzkbSItOpN3mc9xsJ5dCX80LYaResSj5hr5zkA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-document@2.8.0': + resolution: {integrity: sha512-mp7Isx1sVc/ifeW4uW/PexGQ9exN3NRUOebSpnLfqXeWYk4y1RS1PA/3+IHkOPVetbnapgPjFx/DswlCP3XLjA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-dropcursor@2.8.0': + resolution: {integrity: sha512-rAFvx44YuT6dtS1c+ALw0ROAGI16l5L1HxquL4hR1gtxDcTieST5xhw5bkshXlmrlfotZXPrhokzqA7qjhZtJw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-floating-menu@2.8.0': + resolution: {integrity: sha512-H4QT61CrkLqisnGGC7zgiYmsl2jXPHl89yQCbdlkQN7aw11H7PltcJS2PJguL0OrRVJS/Mv/VTTUiMslmsEV5g==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-gapcursor@2.8.0': + resolution: {integrity: sha512-Be1LWCmvteQInOnNVN+HTqc1XWsj1bCl+Q7et8qqNjtGtTaCbdCp8ppcH1SKJxNTM/RLUtPyJ8FDgOTj51ixCA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-hard-break@2.8.0': + resolution: {integrity: sha512-vqiIfviNiCmy/pJTHuDSCAGL2O4QDEdDmAvGJu8oRmElUrnlg8DbJUfKvn6DWQHNSQwRb+LDrwWlzAYj1K9u6A==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-heading@2.8.0': + resolution: {integrity: sha512-4inWgrTPiqlivPmEHFOM5ck2UsmOsbKKPtqga6bALvWPmCv24S6/EBwFp8Jz4YABabXDnkviihmGu0LpP9D69w==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-history@2.8.0': + resolution: {integrity: sha512-u5YS0J5Egsxt8TUWMMAC3QhPZaak+IzQeyHch4gtqxftx96tprItY7AD/A3pGDF2uCSnN+SZrk6yVexm6EncDw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-horizontal-rule@2.8.0': + resolution: {integrity: sha512-Sn/MI8WVFBoIYSIHA9NJryJIyCEzZdRysau8pC5TFnfifre0QV1ksPz2bgF+DyCD69ozQiRdBBHDEwKe47ZbfQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-italic@2.8.0': + resolution: {integrity: sha512-PwwSE2LTYiHI47NJnsfhBmPiLE8IXZYqaSoNPU6flPrk1KxEzqvRI1joKZBmD9wuqzmHJ93VFIeZcC+kfwi8ZA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-list-item@2.8.0': + resolution: {integrity: sha512-o7OGymGxB0B9x3x2prp3KBDYFuBYGc5sW69O672jk8G52DqhzzndgPnkk0qUn8nXAUKuDGbJmpmHVA2kagqnRg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-ordered-list@2.8.0': + resolution: {integrity: sha512-sCvNbcTS1+5QTTXwUPFa10vf5I1pr8sGcOTIh0G+a5ZkS5+6FxT12k7VLzPt39QyNbOi+77U2o4Xr4XyaEkfSg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/extension-list-item': ^2.7.0 + '@tiptap/extension-text-style': ^2.7.0 + + '@tiptap/extension-paragraph@2.8.0': + resolution: {integrity: sha512-XgxxNNbuBF48rAGwv7/s6as92/xjm/lTZIGTq9aG13ClUKFtgdel7C33SpUCcxg3cO2WkEyllXVyKUiauFZw/A==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-placeholder@2.8.0': + resolution: {integrity: sha512-BMqv/C9Tcjd7L1/OphUAJTZhWfpWs0rTQJ0bs3RRGsC8L+K20Fg+li45vw7M0teojpfrw57zwJogJd/m23Zr1Q==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-strike@2.8.0': + resolution: {integrity: sha512-ezkDiXxQ3ME/dDMMM7tAMkKRi6UWw7tIu+Mx7Os0z8HCGpVBk1gFhLlhEd8I5rJaPZr4tK1wtSehMA9bscFGQw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-table-cell@2.8.0': + resolution: {integrity: sha512-IZpxONWyOd474L8+k4bHrFNRhbsl9eRwbNs5O877JkVFItc2WUz1DIhbJzjmBRsqExtWQJuOsiqWFab1kpiwGQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-table-header@2.8.0': + resolution: {integrity: sha512-B67A96yMQlG96IFzZBc7D5dnn7O29hcjuDLtjyZkKvU5D/RlFKPMmC9nVphCV3CnbkvEOZUdK9pNaOpen64naw==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-table-row@2.8.0': + resolution: {integrity: sha512-Iezej6l7X+WqKzGLmCgAwmpL+QsfjFv1g8yVH5d0/3Pkcj3G9nDn+GSm4bZnbfYFyqInHG94PZ5PMReiALrJtA==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-table@2.8.0': + resolution: {integrity: sha512-dm9CitjacXyJuE5SZfV2lUc3uOiP2sxo6fygIzMz7iuxHqQueyONWG+TBkK7HjqzXOiMPsvOf/25NazzIG8HMg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + + '@tiptap/extension-text-style@2.8.0': + resolution: {integrity: sha512-jJp0vcZ2Ty7RvIL0VU6dm1y+fTfXq1lN2GwtYzYM0ueFuESa+Qo8ticYOImyWZ3wGJGVrjn7OV9r0ReW0/NYkQ==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/extension-text@2.8.0': + resolution: {integrity: sha512-EDAdFFzWOvQfVy7j3qkKhBpOeE5thkJaBemSWfXI93/gMVc0ZCdLi24mDvNNgUHlT+RjlIoQq908jZaaxLKN2A==} + peerDependencies: + '@tiptap/core': ^2.7.0 + + '@tiptap/pm@2.8.0': + resolution: {integrity: sha512-eMGpRooUMvKz/vOpnKKppApMSoNM325HxTdAJvTlVAmuHp5bOY5kyY1kfUlePRiVx1t1UlFcXs3kecFwkkBD3Q==} + + '@tiptap/react@2.8.0': + resolution: {integrity: sha512-o/aSCjO5Nu4MsNpTF+N1SzYzVQvvBiclmTOZX2E6usZ8jre5zmKfXHDSZnjGSRTK6z6kw5KW8wpjRQha03f9mg==} + peerDependencies: + '@tiptap/core': ^2.7.0 + '@tiptap/pm': ^2.7.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + + '@tiptap/starter-kit@2.8.0': + resolution: {integrity: sha512-r7UwaTrECkQoheWVZKFDqtL5tBx07x7IFT+prfgnsVlYFutGWskVVqzCDvD3BDmrg5PzeCWYZrQGlPaLib7tjg==} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} @@ -1337,27 +1931,78 @@ packages: '@types/better-sqlite3@7.6.11': resolution: {integrity: sha512-i8KcD3PgGtGBLl3+mMYA8PdKkButvPyARxA7IQAd6qeslht13qxb1zzO8dRCtE7U3IoJS782zDBAeoKiM695kg==} + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/emscripten@1.39.13': resolution: {integrity: sha512-cFq+fO/isvhvmuP/+Sl4K4jtU6E23DoivtbO4r50e3odaxAiVdbfSYRDdJ4gCdxx+3aRjhphS5ZMwIH4hFy/Cw==} '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/history@4.7.11': + resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + + '@types/hoist-non-react-statics@3.3.5': + resolution: {integrity: sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.13': + resolution: {integrity: sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==} + + '@types/linkify-it@3.0.5': + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash.debounce@4.0.9': + resolution: {integrity: sha512-Ma5JcgTREwpLRwMM+XwBR7DaWe96nC38uCBDFKZWbNKD+osjVzdpnUSwBcqCptrp16sSOLBAUb50Car5I0TCsQ==} + + '@types/lodash@4.17.10': + resolution: {integrity: sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==} + + '@types/markdown-it@13.0.9': + resolution: {integrity: sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==} + '@types/markdown-it@14.1.2': resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + '@types/mdast@3.0.15': + resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mdurl@1.0.5': + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/node-fetch@2.6.11': resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} @@ -1367,21 +2012,63 @@ packages: '@types/node@20.16.11': resolution: {integrity: sha512-y+cTCACu92FyA5fgQSAI8A1H429g7aSK2HsO7K4XYUWc4dY5IUz55JSDIYT6/VsOLfGy8vmvQYC2hfb0iF16Uw==} + '@types/pg@8.11.10': + resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} + '@types/prop-types@15.7.13': resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==} + '@types/react-beautiful-dnd@13.1.8': + resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==} + '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-redux@7.1.34': + resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==} + + '@types/react-router-dom@5.3.3': + resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + + '@types/react-router@5.1.20': + resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} + + '@types/react-window@1.8.8': + resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} + '@types/react@18.3.11': resolution: {integrity: sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==} + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/svgo@2.6.4': + resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/vite-plugin-react-svg@0.2.5': + resolution: {integrity: sha512-p2yB779GM9G1bxgKPO+YRi+NJE/U8Di9cZacYGfjtl3XEO62mBbj54tFXCssDyhNuJc9iGUAfJipoMZvzZNPmw==} + '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -1741,10 +2428,17 @@ packages: algoliasearch@4.24.0: resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==} + animate.css@4.1.1: + resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1776,6 +2470,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -1800,6 +2497,9 @@ packages: resolution: {integrity: sha512-/JcvVCQRTVQHwLI8TCxSeOS9AcCV01MbCJC4plSP5ulygJH+D30lz85nvMcER5k+qoX2fJ1C/i13Zo1/eoMGTw==} engines: {node: '>=11.14.0'} + assert-never@1.3.0: + resolution: {integrity: sha512-9Z3vxQ+berkL/JJo0dK+EY3Lp0s3NtSnP3VCLsh5HDcZPrh0M+KQRK5sWhUeyPPH+/RCxZqOxLMR+YC6vlviEQ==} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -1820,10 +2520,20 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1841,6 +2551,10 @@ packages: better-sqlite3@11.3.0: resolution: {integrity: sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==} + bin-links@5.0.0: + resolution: {integrity: sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==} + engines: {node: ^18.17.0 || >=20.5.0} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1857,6 +2571,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -1875,6 +2593,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1896,6 +2617,10 @@ packages: peerDependencies: esbuild: '>=0.18' + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1908,6 +2633,14 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + caniuse-lite@1.0.30001668: resolution: {integrity: sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==} @@ -1936,6 +2669,9 @@ packages: character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} @@ -1953,14 +2689,48 @@ packages: chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + cmd-shim@7.0.0: + resolution: {integrity: sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==} + engines: {node: ^18.17.0 || >=20.5.0} + codemirror@6.0.1: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} @@ -1995,6 +2765,10 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -2019,6 +2793,10 @@ packages: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -2026,10 +2804,23 @@ packages: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + corser@2.0.1: resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} engines: {node: '>= 0.4.0'} + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -2040,11 +2831,33 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-box-model@1.2.1: + resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + cssstyle@4.1.0: resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==} engines: {node: '>=18'} @@ -2052,6 +2865,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -2060,9 +2877,20 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -2083,6 +2911,9 @@ packages: decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + decode-named-character-reference@1.0.2: + resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2114,6 +2945,13 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -2126,10 +2964,18 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2141,14 +2987,24 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -2156,14 +3012,48 @@ packages: dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv-cli@7.4.2: + resolution: {integrity: sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==} + hasBin: true + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + editorconfig@1.0.4: resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} engines: {node: '>=14'} hasBin: true + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.36: resolution: {integrity: sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==} @@ -2187,6 +3077,9 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -2198,6 +3091,131 @@ packages: es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + esbuild-android-64@0.14.54: + resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + esbuild-android-arm64@0.14.54: + resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + esbuild-darwin-64@0.14.54: + resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + esbuild-darwin-arm64@0.14.54: + resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + esbuild-freebsd-64@0.14.54: + resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + esbuild-freebsd-arm64@0.14.54: + resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + esbuild-linux-32@0.14.54: + resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + esbuild-linux-64@0.14.54: + resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + esbuild-linux-arm64@0.14.54: + resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + esbuild-linux-arm@0.14.54: + resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + esbuild-linux-mips64le@0.14.54: + resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + esbuild-linux-ppc64le@0.14.54: + resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + esbuild-linux-riscv64@0.14.54: + resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + esbuild-linux-s390x@0.14.54: + resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + esbuild-netbsd-64@0.14.54: + resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + esbuild-openbsd-64@0.14.54: + resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + esbuild-sunos-64@0.14.54: + resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + esbuild-windows-32@0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + esbuild-windows-64@0.14.54: + resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + esbuild-windows-arm64@0.14.54: + resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + esbuild@0.14.54: + resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -2216,10 +3234,34 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + eslint-plugin-react-compiler@0.0.0-experimental-9ed098e-20240725: resolution: {integrity: sha512-Xv2iD8kU6R4Wdjdh1WhdP8UnSqSV+/XcadxwBCmMr836fQUoXGuw/uVGc01v9opZs9SwKzo+8My6ayVCgAinPA==} engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0} @@ -2371,6 +3413,13 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -2381,6 +3430,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -2402,6 +3454,10 @@ packages: picomatch: optional: true + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2451,9 +3507,24 @@ packages: resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} engines: {node: '>= 6'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fractional-indexing@3.2.0: + resolution: {integrity: sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==} + engines: {node: ^14.13.1 || >=16.0.0} + fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -2481,6 +3552,9 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + funtypes@4.2.0: + resolution: {integrity: sha512-DvOtjiKvkeuXGV0O8LQh9quUP3bSOTEQPGv537Sao8kDq2rDbg48UsSJ7wlBLPzR2Mn0pV7cyAiq5pYG1oUyCQ==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2588,6 +3662,9 @@ packages: hast-util-to-html@9.0.3: resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==} + hast-util-whitespace@2.0.1: + resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} + hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -2601,6 +3678,13 @@ packages: hermes-parser@0.20.1: resolution: {integrity: sha512-BL5P83cwCogI8D7rrDCgsFY0tdYUtmFP9XaXtl2IQjC+2Xo+4okjfXintlTxcIwl4qeGddEl28Z11kbVIw0aNA==} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hono@4.6.12: + resolution: {integrity: sha512-eHtf4kSDNw6VVrdbd5IQi16r22m3s7mWPLd7xOMhg1a/Yyb1A0qpUFq8xYMX4FMuDe1nTKeMX5rTx7Nmw+a+Ag==} + engines: {node: '>=16.9.0'} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -2615,6 +3699,10 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -2680,10 +3768,20 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + inline-style-parser@0.1.1: + resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} + + inquirer@9.3.7: + resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} + engines: {node: '>=18'} + internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + interrogator@2.0.1: + resolution: {integrity: sha512-HPilaDW0ZSPEKhhj6NcklQi7jhYyad1r8l6tS9hYCxvVnlrrJAUMZ7GuGa5PFK3RmquLSk+iml2geBJjC+Yc9g==} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -2692,6 +3790,9 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -2703,6 +3804,10 @@ packages: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -2733,6 +3838,10 @@ packages: eslint: '*' typescript: '>=4.7.4' + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} @@ -2749,9 +3858,16 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -2784,6 +3900,10 @@ packages: resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -2809,6 +3929,30 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} @@ -2856,6 +4000,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -2873,9 +4020,26 @@ packages: jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} @@ -2883,6 +4047,10 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + lilconfig@3.1.2: resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} engines: {node: '>=14'} @@ -2890,6 +4058,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2906,9 +4077,36 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} @@ -2918,6 +4116,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2928,6 +4130,9 @@ packages: loupe@3.1.2: resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2951,9 +4156,44 @@ packages: mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-it-task-lists@2.1.1: + resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + mdast-util-definitions@5.1.2: + resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} + + mdast-util-from-markdown@1.3.1: + resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} + + mdast-util-to-hast@12.3.0: + resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} + mdast-util-to-hast@13.2.0: resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-string@3.2.0: + resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memoize-one@5.2.1: + resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -2961,21 +4201,84 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + micromark-core-commonmark@1.1.0: + resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} + + micromark-factory-destination@1.1.0: + resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} + + micromark-factory-label@1.1.0: + resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} + + micromark-factory-space@1.1.0: + resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} + + micromark-factory-title@1.1.0: + resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} + + micromark-factory-whitespace@1.1.0: + resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} + + micromark-util-character@1.2.0: + resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} + micromark-util-character@2.1.0: resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==} + micromark-util-chunked@1.1.0: + resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} + + micromark-util-classify-character@1.1.0: + resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} + + micromark-util-combine-extensions@1.1.0: + resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} + + micromark-util-decode-numeric-character-reference@1.1.0: + resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} + + micromark-util-decode-string@1.1.0: + resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} + + micromark-util-encode@1.1.0: + resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} + micromark-util-encode@2.0.0: resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==} + micromark-util-html-tag-name@1.2.0: + resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} + + micromark-util-normalize-identifier@1.1.0: + resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} + + micromark-util-resolve-all@1.1.0: + resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} + + micromark-util-sanitize-uri@1.2.0: + resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} + micromark-util-sanitize-uri@2.0.0: resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==} + micromark-util-subtokenize@1.1.0: + resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} + + micromark-util-symbol@1.1.0: + resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} + micromark-util-symbol@2.0.0: resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==} + micromark-util-types@1.1.0: + resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} + micromark-util-types@2.0.0: resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==} + micromark@3.2.0: + resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3005,6 +4308,10 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + mini-svg-data-uri@1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -3033,6 +4340,10 @@ packages: minisearch@7.1.0: resolution: {integrity: sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==} + minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} @@ -3043,6 +4354,11 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.7.2: resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} @@ -3050,12 +4366,19 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} muggle-string@0.4.1: resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3070,10 +4393,21 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-abi@3.68.0: resolution: {integrity: sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==} engines: {node: '>=10'} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -3086,6 +4420,14 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} + npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3104,6 +4446,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + object-inspect@1.13.2: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} @@ -3120,6 +4466,13 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -3142,6 +4495,13 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -3187,10 +4547,17 @@ packages: package-manager-detector@0.2.2: resolution: {integrity: sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==} + parameter-reducers@2.1.0: + resolution: {integrity: sha512-aj9V6DUnNbj4YEmVxloPLX9duhklIC+SIOVUrVdaT3WfgEownET+TYg/JsjANQUNGe46dmOCHEKiuycL36cOnw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} @@ -3243,10 +4610,19 @@ packages: pg-connection-string@2.7.0: resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + pg-cursor@2.12.0: + resolution: {integrity: sha512-rppw54OnuYZfMUjiJI2zJMwAjjt2V9EtLUb+t7V5tqwSE5Jxod+7vA7Y0FI6Nq976jNLciA0hoVkwvjjB8qzEw==} + peerDependencies: + pg: ^8 + pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + pg-pool@3.7.0: resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} peerDependencies: @@ -3259,6 +4635,10 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + pg@8.13.0: resolution: {integrity: sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==} engines: {node: '>= 8.0.0'} @@ -3282,6 +4662,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -3311,6 +4695,30 @@ packages: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + postcss-load-config@6.0.1: resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} @@ -3329,10 +4737,23 @@ packages: yaml: optional: true + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + postcss-selector-parser@6.1.2: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.4.47: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} @@ -3341,18 +4762,41 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + postgres-bytea@1.0.0: resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} engines: {node: '>=0.10.0'} + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + postgres-date@1.0.7: resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} engines: {node: '>=0.10.0'} + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + postgres-interval@1.2.0: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + + postgres@3.4.5: + resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} + engines: {node: '>=12'} + preact@10.24.2: resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} @@ -3365,6 +4809,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -3386,9 +4834,74 @@ packages: printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@6.5.0: resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + prosemirror-changeset@2.2.1: + resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==} + + prosemirror-collab@1.3.1: + resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} + + prosemirror-commands@1.6.1: + resolution: {integrity: sha512-tNy4uaGWzvuUYXDke7B28krndIrdQJhSh0OLpubtwtEwFbjItOj/eoAfPvstBJyyV0S2+b5t4G+4XPXdxar6pg==} + + prosemirror-dropcursor@1.8.1: + resolution: {integrity: sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==} + + prosemirror-gapcursor@1.3.2: + resolution: {integrity: sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==} + + prosemirror-history@1.4.1: + resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==} + + prosemirror-inputrules@1.4.0: + resolution: {integrity: sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==} + + prosemirror-keymap@1.2.2: + resolution: {integrity: sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==} + + prosemirror-markdown@1.13.1: + resolution: {integrity: sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==} + + prosemirror-menu@1.2.4: + resolution: {integrity: sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==} + + prosemirror-model@1.23.0: + resolution: {integrity: sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==} + + prosemirror-schema-basic@1.2.3: + resolution: {integrity: sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==} + + prosemirror-schema-list@1.4.1: + resolution: {integrity: sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==} + + prosemirror-state@1.4.3: + resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==} + + prosemirror-tables@1.5.0: + resolution: {integrity: sha512-VMx4zlYWm7aBlZ5xtfJHpqa3Xgu3b7srV54fXYnXgsAcIGRqKSrhiK3f89omzzgaAgAtDOV4ImXnLKhVfheVNQ==} + + prosemirror-trailing-node@3.0.0: + resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + + prosemirror-transform@1.10.2: + resolution: {integrity: sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==} + + prosemirror-view@1.34.3: + resolution: {integrity: sha512-mKZ54PrX19sSaQye+sef+YjBbNu2voNwLS1ivb6aD2IRmxRGW64HU9B644+7OfJStGLyxvOreKqEgfvXa91WIA==} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -3404,6 +4917,10 @@ packages: pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3418,29 +4935,108 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + raf-schd@4.0.3: + resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-beautiful-dnd@13.1.1: + resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} + deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672' + peerDependencies: + react: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-dom@18.3.1: resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: react: ^18.3.1 + react-icons@4.12.0: + resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-markdown@8.0.7: + resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==} + peerDependencies: + '@types/react': '>=16' + react: '>=16' + + react-redux@7.2.9: + resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} + peerDependencies: + react: ^16.8.3 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-router-dom@6.27.0: + resolution: {integrity: sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.27.0: + resolution: {integrity: sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-toastify@9.1.3: + resolution: {integrity: sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + + react-virtualized-auto-sizer@1.0.24: + resolution: {integrity: sha512-3kCn7N9NEb3FlvJrSHWGQ4iVl+ydQObq2fHMn12i5wbtm74zHOPhz/i64OL3c1S1vi9i2GXtZqNqUJTQ+BnNfg==} + peerDependencies: + react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 + + react-window@1.8.10: + resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} + engines: {node: '>8.0.0'} + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-cmd-shim@5.0.0: + resolution: {integrity: sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==} + engines: {node: ^18.17.0 || >=20.5.0} + read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -3453,6 +5049,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -3463,6 +5062,12 @@ packages: resolution: {integrity: sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==} engines: {node: '>= 0.4'} + remark-parse@10.0.2: + resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} + + remark-rehype@10.1.0: + resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3489,6 +5094,10 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3501,20 +5110,40 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rollup@2.77.3: + resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==} + engines: {node: '>=10.0.0'} + hasBin: true + rollup@4.24.0: resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} @@ -3559,6 +5188,9 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -3609,6 +5241,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -3644,9 +5279,17 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} @@ -3709,11 +5352,19 @@ packages: style-mod@4.1.2: resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + style-to-object@0.4.4: + resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true + supabase@1.226.3: + resolution: {integrity: sha512-+KytrqHMQTg1J71E26Og1lMOsUG7MW+Fvcdlu9ZpUCz2dQG96gm0HEjUlL9/PV01DmhNIUZ1+EUaf8QMco4BLg==} + engines: {npm: '>=8'} + hasBin: true + superjson@2.2.1: resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==} engines: {node: '>=16'} @@ -3734,12 +5385,29 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.9.2: + resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + engines: {node: ^14.18.0 || >=16.0.0} + tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwindcss@3.4.13: + resolution: {integrity: sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==} + engines: {node: '>=14.0.0'} + hasBin: true + tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} @@ -3747,6 +5415,10 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -3766,6 +5438,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -3800,6 +5475,14 @@ packages: resolution: {integrity: sha512-Ko2BT2VtuszvlWz3fC1y5EJu2pTZ1bf4HvDRh9RhVJbeXXRtoSrWj7ml1RrWp5dA+JRKSlTcYDwFXlm/0fqDuw==} engines: {node: ^4.0.0, npm: ^2.0.0} + tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + + tiptap-markdown@0.8.10: + resolution: {integrity: sha512-iDVkR2BjAqkTDtFX0h94yVvE2AihCXlF0Q7RIXSJPRSR5I0PA1TMuAg6FHFpmqTn4tPxJ0by0CK7PUMlnFLGEQ==} + peerDependencies: + '@tiptap/core': ^2.0.3 + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -3812,6 +5495,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -3830,6 +5517,9 @@ packages: trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -3894,6 +5584,14 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + typescript@5.4.2: resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} engines: {node: '>=14.17'} @@ -3904,28 +5602,52 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unified@10.1.2: + resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} + union@0.5.0: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} + unist-util-generated@2.0.1: + resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} + + unist-util-is@5.2.1: + resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} + unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-position@4.0.4: + resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} + unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-stringify-position@3.0.3: + resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} + unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-parents@5.1.3: + resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} + unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit@4.1.2: + resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} + unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -3937,6 +5659,14 @@ packages: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + update-browserslist-db@1.1.1: resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} hasBin: true @@ -3952,12 +5682,41 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-memo-one@1.1.3: + resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + uvu@0.5.6: + resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} + engines: {node: '>=8'} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-message@3.1.4: + resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} + vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + vfile@5.3.7: + resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} + vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} @@ -3986,6 +5745,27 @@ packages: peerDependencies: vite: '*' + vite-plugin-svgr@3.3.0: + resolution: {integrity: sha512-vWZMCcGNdPqgziYFKQ3Y95XP0d0YGp28+MM3Dp9cTa/px5CKcHHrIoPl2Jw81rgVm6/ZUNONzjXbZQZ7Kw66og==} + peerDependencies: + vite: ^2.6.0 || 3 || 4 + + vite@2.9.18: + resolution: {integrity: sha512-sAOqI5wNM9QvSEE70W3UGMdT8cyEn0+PmJMTFvTB8wB0YbYUWw3gUbY62AOyrXosGieF2htmeLATvNxpv/zNyQ==} + engines: {node: '>=12.2.0'} + hasBin: true + peerDependencies: + less: '*' + sass: '*' + stylus: '*' + peerDependenciesMeta: + less: + optional: true + sass: + optional: true + stylus: + optional: true + vite@5.4.8: resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4129,6 +5909,13 @@ packages: resolution: {tarball: https://codeload.github.com/rhashimoto/wa-sqlite/tar.gz/6dbe4f044502a7a285e815896f56304a808fe25b} version: 0.9.14 + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -4184,6 +5971,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4195,6 +5986,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@6.0.0: + resolution: {integrity: sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==} + engines: {node: ^18.17.0 || >=20.5.0} + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -4235,6 +6030,15 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@2.6.0: + resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -4251,6 +6055,10 @@ packages: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + zod-validation-error@3.4.0: resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} engines: {node: '>=18.0.0'} @@ -4369,6 +6177,8 @@ snapshots: '@algolia/logger-common': 4.24.0 '@algolia/requester-common': 4.24.0 + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -4757,42 +6567,135 @@ snapshots: style-mod: 4.1.2 w3c-keyname: 2.2.8 - '@docsearch/css@3.6.2': {} + '@databases/connection-pool@1.1.0': + dependencies: + '@databases/queue': 1.0.1 + is-promise: 4.0.0 - '@docsearch/js@3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2)': + '@databases/escape-identifier@1.0.3': dependencies: - '@docsearch/react': 3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2) - preact: 10.24.2 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights + '@databases/validate-unicode': 1.0.0 - '@docsearch/react@3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2)': + '@databases/lock@2.1.0': dependencies: - '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2) - '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) - '@docsearch/css': 3.6.2 - algoliasearch: 4.24.0 - optionalDependencies: - '@types/react': 18.3.11 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - search-insights: 2.17.2 + '@databases/queue': 1.0.1 + + '@databases/migrations-base@3.0.1': + dependencies: + assert-never: 1.3.0 + chalk: 4.1.2 + deep-equal: 2.2.3 + interrogator: 2.0.1 + is-interactive: 1.0.0 + parameter-reducers: 2.1.0 + semver: 7.6.3 + + '@databases/pg-config@3.2.0(typescript@5.6.3)': + dependencies: + cosmiconfig: 8.3.6(typescript@5.6.3) + funtypes: 4.2.0 transitivePeerDependencies: - - '@algolia/client-search' + - typescript - '@electric-sql/client@0.9.0': - optionalDependencies: - '@rollup/rollup-darwin-arm64': 4.24.0 + '@databases/pg-connection-string@1.0.0': {} - '@embedded-postgres/darwin-arm64@15.5.1-beta.11': - optional: true + '@databases/pg-data-type-id@3.0.0': {} - '@embedded-postgres/darwin-x64@15.5.1-beta.11': - optional: true + '@databases/pg-errors@1.0.0': {} + + '@databases/pg-migrations@5.0.3(typescript@5.6.3)': + dependencies: + '@databases/migrations-base': 3.0.1 + '@databases/pg': 5.5.0(typescript@5.6.3) + '@databases/pg-config': 3.2.0(typescript@5.6.3) + assert-never: 1.3.0 + chalk: 4.1.2 + interrogator: 2.0.1 + is-interactive: 1.0.0 + parameter-reducers: 2.1.0 + semver: 7.6.3 + sucrase: 3.35.0 + transitivePeerDependencies: + - pg-native + - typescript + + '@databases/pg@5.5.0(typescript@5.6.3)': + dependencies: + '@babel/code-frame': 7.25.7 + '@databases/escape-identifier': 1.0.3 + '@databases/pg-config': 3.2.0(typescript@5.6.3) + '@databases/pg-connection-string': 1.0.0 + '@databases/pg-data-type-id': 3.0.0 + '@databases/pg-errors': 1.0.0 + '@databases/push-to-async-iterable': 3.0.0 + '@databases/shared': 3.1.0 + '@databases/split-sql-query': 1.0.4(@databases/sql@3.3.0) + '@databases/sql': 3.3.0 + assert-never: 1.3.0 + pg: 8.13.0 + pg-cursor: 2.12.0(pg@8.13.0) + transitivePeerDependencies: + - pg-native + - typescript + + '@databases/push-to-async-iterable@3.0.0': + dependencies: + '@databases/queue': 1.0.1 + + '@databases/queue@1.0.1': {} + + '@databases/shared@3.1.0': + dependencies: + '@databases/connection-pool': 1.1.0 + '@databases/lock': 2.1.0 + '@databases/queue': 1.0.1 + '@databases/split-sql-query': 1.0.4(@databases/sql@3.3.0) + '@databases/sql': 3.3.0 + + '@databases/split-sql-query@1.0.4(@databases/sql@3.3.0)': + dependencies: + '@databases/sql': 3.3.0 + + '@databases/sql@3.3.0': {} + + '@databases/validate-unicode@1.0.0': {} + + '@docsearch/css@3.6.2': {} + + '@docsearch/js@3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2)': + dependencies: + '@docsearch/react': 3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2) + preact: 10.24.2 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.6.2(@algolia/client-search@4.24.0)(@types/react@18.3.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.2)': + dependencies: + '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.17.2) + '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0) + '@docsearch/css': 3.6.2 + algoliasearch: 4.24.0 + optionalDependencies: + '@types/react': 18.3.11 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + search-insights: 2.17.2 + transitivePeerDependencies: + - '@algolia/client-search' + + '@electric-sql/client@0.9.0': + optionalDependencies: + '@rollup/rollup-darwin-arm64': 4.24.0 + + '@embedded-postgres/darwin-arm64@15.5.1-beta.11': + optional: true + + '@embedded-postgres/darwin-x64@15.5.1-beta.11': + optional: true '@embedded-postgres/linux-arm64@15.5.1-beta.11': optional: true @@ -4878,6 +6781,9 @@ snapshots: '@esbuild/linux-ia32@0.23.1': optional: true + '@esbuild/linux-loong64@0.14.54': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true @@ -5097,6 +7003,27 @@ snapshots: '@eslint/js@8.57.1': {} + '@faker-js/faker@8.4.1': {} + + '@firefox-devtools/react-contextmenu@5.1.1(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + classnames: 2.5.1 + object-assign: 4.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@headlessui/react@1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/react-virtual': 3.10.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + client-only: 0.0.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@hono/node-server@1.13.7(hono@4.6.12)': + dependencies: + hono: 4.6.12 + '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -5109,6 +7036,8 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} + '@inquirer/figures@1.0.7': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -5118,10 +7047,27 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 20.16.11 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -5246,6 +7192,14 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true + '@pkgr/core@0.1.1': {} + + '@popperjs/core@2.11.8': {} + + '@remirror/core-constants@3.0.0': {} + + '@remix-run/router@1.20.0': {} + '@rollup/pluginutils@5.1.2(rollup@4.24.0)': dependencies: '@types/estree': 1.0.6 @@ -5369,6 +7323,106 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + + '@svgr/babel-preset@8.1.0(@babel/core@7.25.8)': + dependencies: + '@babel/core': 7.25.8 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.25.8) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.25.8) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.25.8) + + '@svgr/core@8.1.0(typescript@5.6.3)': + dependencies: + '@babel/core': 7.25.8 + '@svgr/babel-preset': 8.1.0(@babel/core@7.25.8) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.6.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.25.8 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.6.3))': + dependencies: + '@babel/core': 7.25.8 + '@svgr/babel-preset': 8.1.0(@babel/core@7.25.8) + '@svgr/core': 8.1.0(typescript@5.6.3) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.6.3))(typescript@5.6.3)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.6.3) + cosmiconfig: 8.3.6(typescript@5.6.3) + deepmerge: 4.3.1 + svgo: 3.3.2 + transitivePeerDependencies: + - typescript + + '@tailwindcss/forms@0.5.9(tailwindcss@3.4.13)': + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.4.13 + + '@tailwindcss/typography@0.5.15(tailwindcss@3.4.13)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.13 + + '@tanstack/react-virtual@3.10.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/virtual-core': 3.10.8 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@tanstack/virtual-core@3.10.8': {} + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.25.7 @@ -5410,6 +7464,189 @@ snapshots: optionalDependencies: '@vue/compiler-sfc': 3.5.11 + '@tiptap/core@2.8.0(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-blockquote@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-bold@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-bubble-menu@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + tippy.js: 6.3.7 + + '@tiptap/extension-bullet-list@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + + '@tiptap/extension-code-block@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-code@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-document@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-dropcursor@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-floating-menu@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + tippy.js: 6.3.7 + + '@tiptap/extension-gapcursor@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-hard-break@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-heading@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-history@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-horizontal-rule@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-italic@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-ordered-list@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-text-style': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + + '@tiptap/extension-paragraph@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-placeholder@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-strike@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-table-cell@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-table-header@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-table-row@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-table@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + + '@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/extension-text@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + + '@tiptap/pm@2.8.0': + dependencies: + prosemirror-changeset: 2.2.1 + prosemirror-collab: 1.3.1 + prosemirror-commands: 1.6.1 + prosemirror-dropcursor: 1.8.1 + prosemirror-gapcursor: 1.3.2 + prosemirror-history: 1.4.1 + prosemirror-inputrules: 1.4.0 + prosemirror-keymap: 1.2.2 + prosemirror-markdown: 1.13.1 + prosemirror-menu: 1.2.4 + prosemirror-model: 1.23.0 + prosemirror-schema-basic: 1.2.3 + prosemirror-schema-list: 1.4.1 + prosemirror-state: 1.4.3 + prosemirror-tables: 1.5.0 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3) + prosemirror-transform: 1.10.2 + prosemirror-view: 1.34.3 + + '@tiptap/react@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/extension-bubble-menu': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-floating-menu': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/pm': 2.8.0 + '@types/use-sync-external-store': 0.0.6 + fast-deep-equal: 3.1.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + use-sync-external-store: 1.2.2(react@18.3.1) + + '@tiptap/starter-kit@2.8.0(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))': + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@tiptap/extension-blockquote': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-bold': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-bullet-list': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))) + '@tiptap/extension-code': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-code-block': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-document': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-dropcursor': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-gapcursor': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-hard-break': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-heading': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-history': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-horizontal-rule': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/pm@2.8.0) + '@tiptap/extension-italic': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-list-item': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-ordered-list': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))(@tiptap/extension-list-item@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)))(@tiptap/extension-text-style@2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0))) + '@tiptap/extension-paragraph': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-strike': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/extension-text': 2.8.0(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)) + '@tiptap/pm': 2.8.0 + transitivePeerDependencies: + - '@tiptap/extension-text-style' + + '@trysound/sax@0.2.0': {} + '@types/argparse@1.0.38': {} '@types/aria-query@5.0.4': {} @@ -5439,27 +7676,87 @@ snapshots: dependencies: '@types/node': 20.16.11 + '@types/body-parser@1.19.5': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.16.11 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 20.16.11 + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 0.7.34 + '@types/emscripten@1.39.13': {} '@types/estree@1.0.6': {} + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 + '@types/history@4.7.11': {} + + '@types/hoist-non-react-statics@3.3.5': + dependencies: + '@types/react': 18.3.11 + hoist-non-react-statics: 3.3.2 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.13': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/linkify-it@3.0.5': {} + '@types/linkify-it@5.0.0': {} + '@types/lodash.debounce@4.0.9': + dependencies: + '@types/lodash': 4.17.10 + + '@types/lodash@4.17.10': {} + + '@types/markdown-it@13.0.9': + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + '@types/markdown-it@14.1.2': dependencies: '@types/linkify-it': 5.0.0 '@types/mdurl': 2.0.0 + '@types/mdast@3.0.15': + dependencies: + '@types/unist': 2.0.11 + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 + '@types/mdurl@1.0.5': {} + '@types/mdurl@2.0.0': {} + '@types/ms@0.7.34': {} + '@types/node-fetch@2.6.11': dependencies: '@types/node': 20.16.11 @@ -5471,21 +7768,82 @@ snapshots: dependencies: undici-types: 6.19.8 + '@types/pg@8.11.10': + dependencies: + '@types/node': 20.16.11 + pg-protocol: 1.7.0 + pg-types: 4.0.2 + '@types/prop-types@15.7.13': {} + '@types/react-beautiful-dnd@13.1.8': + dependencies: + '@types/react': 18.3.11 + '@types/react-dom@18.3.0': dependencies: '@types/react': 18.3.11 + '@types/react-redux@7.1.34': + dependencies: + '@types/hoist-non-react-statics': 3.3.5 + '@types/react': 18.3.11 + hoist-non-react-statics: 3.3.2 + redux: 4.2.1 + + '@types/react-router-dom@5.3.3': + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.3.11 + '@types/react-router': 5.1.20 + + '@types/react-router@5.1.20': + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.3.11 + + '@types/react-window@1.8.8': + dependencies: + '@types/react': 18.3.11 + '@types/react@18.3.11': dependencies: '@types/prop-types': 15.7.13 csstype: 3.1.3 + '@types/stack-utils@2.0.3': {} + + '@types/svgo@2.6.4': + dependencies: + '@types/node': 20.16.11 + + '@types/unist@2.0.11': {} + '@types/unist@3.0.3': {} + '@types/use-sync-external-store@0.0.6': {} + + '@types/uuid@9.0.8': {} + + '@types/vite-plugin-react-svg@0.2.5': + dependencies: + '@types/node': 20.16.11 + '@types/react': 18.3.11 + '@types/svgo': 2.6.4 + vite: 2.9.18 + transitivePeerDependencies: + - less + - sass + - stylus + '@types/web-bluetooth@0.0.20': {} + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.11.1 @@ -5967,8 +8325,14 @@ snapshots: '@algolia/requester-node-http': 4.24.0 '@algolia/transporter': 4.24.0 + animate.css@4.1.1: {} + ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -5992,6 +8356,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + arg@5.0.2: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -6017,6 +8383,8 @@ snapshots: dependencies: printable-characters: 1.0.42 + assert-never@1.3.0: {} + assertion-error@1.1.0: {} assertion-error@2.0.1: {} @@ -6033,10 +8401,22 @@ snapshots: asynckit@0.4.0: {} + autoprefixer@10.4.20(postcss@8.4.47): + dependencies: + browserslist: 4.24.0 + caniuse-lite: 1.0.30001668 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 + bail@2.0.2: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -6054,6 +8434,14 @@ snapshots: bindings: 1.5.0 prebuild-install: 7.1.2 + bin-links@5.0.0: + dependencies: + cmd-shim: 7.0.0 + npm-normalize-package-bin: 4.0.0 + proc-log: 5.0.0 + read-cmd-shim: 5.0.0 + write-file-atomic: 6.0.0 + binary-extensions@2.3.0: {} bindings@1.5.0: @@ -6070,6 +8458,23 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + boolbase@1.0.0: {} brace-expansion@1.1.11: @@ -6092,6 +8497,8 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.0) + buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} buffer@5.7.1: @@ -6120,6 +8527,8 @@ snapshots: esbuild: 0.23.1 load-tsconfig: 0.2.5 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind@1.0.7: @@ -6132,6 +8541,10 @@ snapshots: callsites@3.1.0: {} + camelcase-css@2.0.1: {} + + camelcase@6.3.0: {} + caniuse-lite@1.0.30001668: {} ccount@2.0.1: {} @@ -6169,6 +8582,8 @@ snapshots: character-entities-legacy@3.0.0: {} + character-entities@2.0.2: {} + chardet@0.7.0: {} check-error@1.0.3: @@ -6191,14 +8606,34 @@ snapshots: chownr@1.1.4: {} + chownr@3.0.0: {} + ci-info@3.9.0: {} + classnames@2.5.1: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + + client-only@0.0.1: {} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + clone@1.0.4: {} + + clsx@1.2.1: {} + + cmd-shim@7.0.0: {} + codemirror@6.0.1(@lezer/common@1.2.2): dependencies: '@codemirror/autocomplete': 6.18.1(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)(@lezer/common@1.2.2) @@ -6235,6 +8670,8 @@ snapshots: commander@4.1.1: {} + commander@7.2.0: {} + compare-versions@6.1.1: {} computeds@0.0.1: {} @@ -6262,14 +8699,30 @@ snapshots: consola@3.2.3: {} + content-type@1.0.5: {} + convert-source-map@2.0.0: {} copy-anything@3.0.5: dependencies: is-what: 4.1.16 + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + corser@2.0.1: {} + cosmiconfig@8.3.6(typescript@5.6.3): + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.6.3 + crelt@1.0.6: {} cross-spawn@5.1.0: @@ -6284,14 +8737,44 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-box-model@1.2.1: + dependencies: + tiny-invariant: 1.3.3 + + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + + css-what@6.1.0: {} + cssesc@3.0.0: {} + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + cssstyle@4.1.0: dependencies: rrweb-cssom: 0.7.1 csstype@3.1.3: {} + data-uri-to-buffer@4.0.1: {} + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -6301,8 +8784,14 @@ snapshots: dependencies: '@babel/runtime': 7.25.7 + dayjs@1.11.13: {} + de-indent@1.0.2: {} + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@3.2.7: dependencies: ms: 2.1.3 @@ -6313,6 +8802,10 @@ snapshots: decimal.js@10.4.3: {} + decode-named-character-reference@1.0.2: + dependencies: + character-entities: 2.0.2 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -6350,6 +8843,12 @@ snapshots: deep-is@0.1.4: {} + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 @@ -6364,8 +8863,12 @@ snapshots: delayed-stream@1.0.0: {} + depd@2.0.0: {} + dequal@2.0.3: {} + destroy@1.2.0: {} + detect-indent@6.1.0: {} detect-libc@2.0.3: {} @@ -6374,20 +8877,64 @@ snapshots: dependencies: dequal: 2.0.3 + didyoumean@1.2.2: {} + diff-sequences@29.6.3: {} + diff@5.2.0: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dlv@1.1.3: {} + doctrine@3.0.0: dependencies: esutils: 2.0.3 dom-accessibility-api@0.5.16: {} + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.1.0: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.7.0 + + dotenv-cli@7.4.2: + dependencies: + cross-spawn: 7.0.3 + dotenv: 16.4.5 + dotenv-expand: 10.0.0 + minimist: 1.2.8 + + dotenv-expand@10.0.0: {} + + dotenv@16.4.5: {} + eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + editorconfig@1.0.4: dependencies: '@one-ini/wasm': 0.1.1 @@ -6395,6 +8942,8 @@ snapshots: minimatch: 9.0.1 semver: 7.6.3 + ee-first@1.1.1: {} + electron-to-chromium@1.5.36: {} embedded-postgres@15.5.1-beta.9: @@ -6428,6 +8977,10 @@ snapshots: entities@4.5.0: {} + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 @@ -6446,6 +8999,90 @@ snapshots: isarray: 2.0.5 stop-iteration-iterator: 1.0.0 + esbuild-android-64@0.14.54: + optional: true + + esbuild-android-arm64@0.14.54: + optional: true + + esbuild-darwin-64@0.14.54: + optional: true + + esbuild-darwin-arm64@0.14.54: + optional: true + + esbuild-freebsd-64@0.14.54: + optional: true + + esbuild-freebsd-arm64@0.14.54: + optional: true + + esbuild-linux-32@0.14.54: + optional: true + + esbuild-linux-64@0.14.54: + optional: true + + esbuild-linux-arm64@0.14.54: + optional: true + + esbuild-linux-arm@0.14.54: + optional: true + + esbuild-linux-mips64le@0.14.54: + optional: true + + esbuild-linux-ppc64le@0.14.54: + optional: true + + esbuild-linux-riscv64@0.14.54: + optional: true + + esbuild-linux-s390x@0.14.54: + optional: true + + esbuild-netbsd-64@0.14.54: + optional: true + + esbuild-openbsd-64@0.14.54: + optional: true + + esbuild-sunos-64@0.14.54: + optional: true + + esbuild-windows-32@0.14.54: + optional: true + + esbuild-windows-64@0.14.54: + optional: true + + esbuild-windows-arm64@0.14.54: + optional: true + + esbuild@0.14.54: + optionalDependencies: + '@esbuild/linux-loong64': 0.14.54 + esbuild-android-64: 0.14.54 + esbuild-android-arm64: 0.14.54 + esbuild-darwin-64: 0.14.54 + esbuild-darwin-arm64: 0.14.54 + esbuild-freebsd-64: 0.14.54 + esbuild-freebsd-arm64: 0.14.54 + esbuild-linux-32: 0.14.54 + esbuild-linux-64: 0.14.54 + esbuild-linux-arm: 0.14.54 + esbuild-linux-arm64: 0.14.54 + esbuild-linux-mips64le: 0.14.54 + esbuild-linux-ppc64le: 0.14.54 + esbuild-linux-riscv64: 0.14.54 + esbuild-linux-s390x: 0.14.54 + esbuild-netbsd-64: 0.14.54 + esbuild-openbsd-64: 0.14.54 + esbuild-sunos-64: 0.14.54 + esbuild-windows-32: 0.14.54 + esbuild-windows-64: 0.14.54 + esbuild-windows-arm64: 0.14.54 + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -6503,8 +9140,23 @@ snapshots: escape-string-regexp@1.0.5: {} + escape-string-regexp@2.0.0: {} + escape-string-regexp@4.0.0: {} + eslint-config-prettier@9.1.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.3.3): + dependencies: + eslint: 8.57.1 + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.2 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.57.1) + eslint-plugin-react-compiler@0.0.0-experimental-9ed098e-20240725(eslint@8.57.1): dependencies: '@babel/core': 7.25.8 @@ -6771,6 +9423,16 @@ snapshots: expand-template@2.0.3: {} + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + extend@3.0.2: {} + extendable-error@0.1.7: {} external-editor@3.1.0: @@ -6781,6 +9443,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-diff@1.3.0: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6801,6 +9465,11 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -6850,8 +9519,22 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + fraction.js@4.3.7: {} + + fractional-indexing@3.2.0: {} + fs-constants@1.0.0: {} + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -6876,6 +9559,8 @@ snapshots: functions-have-names@1.2.3: {} + funtypes@4.2.0: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -6989,6 +9674,8 @@ snapshots: stringify-entities: 4.0.4 zwitch: 2.0.4 + hast-util-whitespace@2.0.1: {} + hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 @@ -7001,6 +9688,12 @@ snapshots: dependencies: hermes-estree: 0.20.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + hono@4.6.12: {} + hookable@5.5.3: {} html-encoding-sniffer@3.0.0: @@ -7013,6 +9706,14 @@ snapshots: html-void-elements@3.0.0: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 @@ -7090,12 +9791,33 @@ snapshots: ini@1.3.8: {} + inline-style-parser@0.1.1: {} + + inquirer@9.3.7: + dependencies: + '@inquirer/figures': 1.0.7 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + external-editor: 3.1.0 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 + interrogator@2.0.1: + dependencies: + inquirer: 9.3.7 + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 @@ -7106,6 +9828,8 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-arrayish@0.2.1: {} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 @@ -7119,6 +9843,8 @@ snapshots: call-bind: 1.0.7 has-tostringtag: 1.0.2 + is-buffer@2.0.5: {} + is-callable@1.2.7: {} is-core-module@2.15.1: @@ -7147,6 +9873,8 @@ snapshots: transitivePeerDependencies: - supports-color + is-interactive@1.0.0: {} + is-map@2.0.3: {} is-number-object@1.0.7: @@ -7157,8 +9885,12 @@ snapshots: is-path-inside@3.0.3: {} + is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -7186,6 +9918,8 @@ snapshots: dependencies: has-symbols: 1.0.3 + is-unicode-supported@0.1.0: {} + is-weakmap@2.0.2: {} is-weakset@2.0.3: @@ -7207,6 +9941,45 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.25.7 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 20.16.11 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jiti@1.21.6: {} + jju@1.4.0: {} joycon@3.1.1: {} @@ -7266,6 +10039,8 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -7278,10 +10053,42 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.6.3 + + jwa@1.4.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.1 + safe-buffer: 5.2.1 + keyv@4.5.4: dependencies: json-buffer: 3.0.1 + kleur@4.1.5: {} + kolorist@1.8.0: {} levn@0.4.1: @@ -7289,10 +10096,16 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lilconfig@2.1.0: {} + lilconfig@3.1.2: {} lines-and-columns@1.2.4: {} + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + load-tsconfig@0.2.5: {} local-pkg@0.5.0: @@ -7308,14 +10121,37 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.castarray@4.4.0: {} + + lodash.debounce@4.0.8: {} + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + lodash.merge@4.6.2: {} + lodash.once@4.1.1: {} + lodash.sortby@4.7.0: {} lodash.startcase@4.4.0: {} lodash@4.17.21: {} + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -7326,6 +10162,10 @@ snapshots: loupe@3.1.2: {} + lower-case@2.0.2: + dependencies: + tslib: 2.7.0 + lru-cache@10.4.3: {} lru-cache@4.1.5: @@ -7349,6 +10189,51 @@ snapshots: mark.js@8.11.1: {} + markdown-it-task-lists@2.1.1: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + mdast-util-definitions@5.1.2: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + unist-util-visit: 4.1.2 + + mdast-util-from-markdown@1.3.1: + dependencies: + '@types/mdast': 3.0.15 + '@types/unist': 2.0.11 + decode-named-character-reference: 1.0.2 + mdast-util-to-string: 3.2.0 + micromark: 3.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-decode-string: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + unist-util-stringify-position: 3.0.3 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + + mdast-util-to-hast@12.3.0: + dependencies: + '@types/hast': 2.3.10 + '@types/mdast': 3.0.15 + mdast-util-definitions: 5.1.2 + micromark-util-sanitize-uri: 1.2.0 + trim-lines: 3.0.1 + unist-util-generated: 2.0.1 + unist-util-position: 4.0.4 + unist-util-visit: 4.1.2 + mdast-util-to-hast@13.2.0: dependencies: '@types/hast': 3.0.4 @@ -7361,27 +10246,174 @@ snapshots: unist-util-visit: 5.0.0 vfile: 6.0.3 + mdast-util-to-string@3.2.0: + dependencies: + '@types/mdast': 3.0.15 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + mdurl@2.0.0: {} + + media-typer@0.3.0: {} + + memoize-one@5.2.1: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} + micromark-core-commonmark@1.1.0: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-factory-destination: 1.1.0 + micromark-factory-label: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-factory-title: 1.1.0 + micromark-factory-whitespace: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-classify-character: 1.1.0 + micromark-util-html-tag-name: 1.2.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-destination@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-label@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-factory-space@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-types: 1.1.0 + + micromark-factory-title@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-factory-whitespace@1.1.0: + dependencies: + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-character@1.2.0: + dependencies: + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + micromark-util-character@2.1.0: dependencies: micromark-util-symbol: 2.0.0 micromark-util-types: 2.0.0 + micromark-util-chunked@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-classify-character@1.1.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-combine-extensions@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-types: 1.1.0 + + micromark-util-decode-numeric-character-reference@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-decode-string@1.1.0: + dependencies: + decode-named-character-reference: 1.0.2 + micromark-util-character: 1.2.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-symbol: 1.1.0 + + micromark-util-encode@1.1.0: {} + micromark-util-encode@2.0.0: {} + micromark-util-html-tag-name@1.2.0: {} + + micromark-util-normalize-identifier@1.1.0: + dependencies: + micromark-util-symbol: 1.1.0 + + micromark-util-resolve-all@1.1.0: + dependencies: + micromark-util-types: 1.1.0 + + micromark-util-sanitize-uri@1.2.0: + dependencies: + micromark-util-character: 1.2.0 + micromark-util-encode: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-sanitize-uri@2.0.0: dependencies: micromark-util-character: 2.1.0 micromark-util-encode: 2.0.0 micromark-util-symbol: 2.0.0 + micromark-util-subtokenize@1.1.0: + dependencies: + micromark-util-chunked: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + + micromark-util-symbol@1.1.0: {} + micromark-util-symbol@2.0.0: {} + micromark-util-types@1.1.0: {} + micromark-util-types@2.0.0: {} + micromark@3.2.0: + dependencies: + '@types/debug': 4.1.12 + debug: 4.3.7 + decode-named-character-reference: 1.0.2 + micromark-core-commonmark: 1.1.0 + micromark-factory-space: 1.1.0 + micromark-util-character: 1.2.0 + micromark-util-chunked: 1.1.0 + micromark-util-combine-extensions: 1.1.0 + micromark-util-decode-numeric-character-reference: 1.1.0 + micromark-util-encode: 1.1.0 + micromark-util-normalize-identifier: 1.1.0 + micromark-util-resolve-all: 1.1.0 + micromark-util-sanitize-uri: 1.2.0 + micromark-util-subtokenize: 1.1.0 + micromark-util-symbol: 1.1.0 + micromark-util-types: 1.1.0 + uvu: 0.5.6 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -7401,6 +10433,8 @@ snapshots: mimic-response@3.1.0: {} + mini-svg-data-uri@1.4.4: {} + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -7427,6 +10461,11 @@ snapshots: minisearch@7.1.0: {} + minizlib@3.0.1: + dependencies: + minipass: 7.1.2 + rimraf: 5.0.10 + mitt@3.0.1: {} mkdirp-classic@0.5.3: {} @@ -7435,6 +10474,8 @@ snapshots: dependencies: minimist: 1.2.8 + mkdirp@3.0.1: {} + mlly@1.7.2: dependencies: acorn: 8.12.1 @@ -7444,10 +10485,14 @@ snapshots: mri@1.2.0: {} + ms@2.0.0: {} + ms@2.1.3: {} muggle-string@0.4.1: {} + mute-stream@1.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -7460,10 +10505,23 @@ snapshots: natural-compare@1.4.0: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.7.0 + node-abi@3.68.0: dependencies: semver: 7.6.3 + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-releases@2.0.18: {} nopt@7.2.1: @@ -7472,6 +10530,10 @@ snapshots: normalize-path@3.0.0: {} + normalize-range@0.1.2: {} + + npm-normalize-package-bin@4.0.0: {} + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 @@ -7488,6 +10550,8 @@ snapshots: object-assign@4.1.1: {} + object-hash@3.0.0: {} + object-inspect@1.13.2: {} object-is@1.1.6: @@ -7504,6 +10568,12 @@ snapshots: has-symbols: 1.0.3 object-keys: 1.1.1 + obuf@1.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -7531,6 +10601,20 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + orderedmap@2.1.1: {} + os-tmpdir@1.0.2: {} outdent@0.5.0: {} @@ -7567,10 +10651,19 @@ snapshots: package-manager-detector@0.2.2: {} + parameter-reducers@2.1.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.25.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse5@7.1.2: dependencies: entities: 4.5.0 @@ -7607,8 +10700,14 @@ snapshots: pg-connection-string@2.7.0: {} + pg-cursor@2.12.0(pg@8.13.0): + dependencies: + pg: 8.13.0 + pg-int8@1.0.1: {} + pg-numeric@1.0.2: {} + pg-pool@3.7.0(pg@8.13.0): dependencies: pg: 8.13.0 @@ -7623,6 +10722,16 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + pg@8.13.0: dependencies: pg-connection-string: 2.7.0 @@ -7643,6 +10752,8 @@ snapshots: picomatch@4.0.2: {} + pify@2.3.0: {} + pify@4.0.1: {} pirates@4.0.6: {} @@ -7671,18 +10782,51 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-load-config@6.0.1(postcss@8.4.47)(tsx@4.19.1): + postcss-import@15.1.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.47): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.47 + + postcss-load-config@4.0.2(postcss@8.4.47): + dependencies: + lilconfig: 3.1.2 + yaml: 2.6.0 + optionalDependencies: + postcss: 8.4.47 + + postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0): dependencies: lilconfig: 3.1.2 optionalDependencies: + jiti: 1.21.6 postcss: 8.4.47 tsx: 4.19.1 + yaml: 2.6.0 + + postcss-nested@6.2.0(postcss@8.4.47): + dependencies: + postcss: 8.4.47 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} + postcss@8.4.47: dependencies: nanoid: 3.3.7 @@ -7691,14 +10835,28 @@ snapshots: postgres-array@2.0.0: {} + postgres-array@3.0.2: {} + postgres-bytea@1.0.0: {} + postgres-bytea@3.0.0: + dependencies: + obuf: 1.1.2 + postgres-date@1.0.7: {} + postgres-date@2.1.0: {} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} + + postgres@3.4.5: {} + preact@10.24.2: {} prebuild-install@7.1.2: @@ -7718,6 +10876,10 @@ snapshots: prelude-ls@1.2.1: {} + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + prettier@2.8.8: {} prettier@3.3.3: {} @@ -7736,8 +10898,119 @@ snapshots: printable-characters@1.0.42: {} + proc-log@5.0.0: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + property-information@6.5.0: {} + prosemirror-changeset@2.2.1: + dependencies: + prosemirror-transform: 1.10.2 + + prosemirror-collab@1.3.1: + dependencies: + prosemirror-state: 1.4.3 + + prosemirror-commands@1.6.1: + dependencies: + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + + prosemirror-dropcursor@1.8.1: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.34.3 + + prosemirror-gapcursor@1.3.2: + dependencies: + prosemirror-keymap: 1.2.2 + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-view: 1.34.3 + + prosemirror-history@1.4.1: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.34.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.4.0: + dependencies: + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + + prosemirror-keymap@1.2.2: + dependencies: + prosemirror-state: 1.4.3 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.1: + dependencies: + '@types/markdown-it': 14.1.2 + markdown-it: 14.1.0 + prosemirror-model: 1.23.0 + + prosemirror-menu@1.2.4: + dependencies: + crelt: 1.0.6 + prosemirror-commands: 1.6.1 + prosemirror-history: 1.4.1 + prosemirror-state: 1.4.3 + + prosemirror-model@1.23.0: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-basic@1.2.3: + dependencies: + prosemirror-model: 1.23.0 + + prosemirror-schema-list@1.4.1: + dependencies: + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + + prosemirror-state@1.4.3: + dependencies: + prosemirror-model: 1.23.0 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.34.3 + + prosemirror-tables@1.5.0: + dependencies: + prosemirror-keymap: 1.2.2 + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + prosemirror-view: 1.34.3 + + prosemirror-trailing-node@3.0.0(prosemirror-model@1.23.0)(prosemirror-state@1.4.3)(prosemirror-view@1.34.3): + dependencies: + '@remirror/core-constants': 3.0.0 + escape-string-regexp: 4.0.0 + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-view: 1.34.3 + + prosemirror-transform@1.10.2: + dependencies: + prosemirror-model: 1.23.0 + + prosemirror-view@1.34.3: + dependencies: + prosemirror-model: 1.23.0 + prosemirror-state: 1.4.3 + prosemirror-transform: 1.10.2 + proto-list@1.2.4: {} pseudomap@1.0.2: {} @@ -7751,6 +11024,8 @@ snapshots: end-of-stream: 1.4.4 once: 1.4.0 + punycode.js@2.3.1: {} + punycode@2.3.1: {} qs@6.13.0: @@ -7761,6 +11036,15 @@ snapshots: queue-microtask@1.2.3: {} + raf-schd@4.0.3: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -7768,22 +11052,112 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-beautiful-dnd@13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.7 + css-box-model: 1.2.1 + memoize-one: 5.2.1 + raf-schd: 4.0.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + redux: 4.2.1 + use-memo-one: 1.1.3(react@18.3.1) + transitivePeerDependencies: + - react-native + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 + react-icons@4.12.0(react@18.3.1): + dependencies: + react: 18.3.1 + + react-is@16.13.1: {} + react-is@17.0.2: {} react-is@18.3.1: {} + react-markdown@8.0.7(@types/react@18.3.11)(react@18.3.1): + dependencies: + '@types/hast': 2.3.10 + '@types/prop-types': 15.7.13 + '@types/react': 18.3.11 + '@types/unist': 2.0.11 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 2.0.1 + prop-types: 15.8.1 + property-information: 6.5.0 + react: 18.3.1 + react-is: 18.3.1 + remark-parse: 10.0.2 + remark-rehype: 10.1.0 + space-separated-tokens: 2.0.2 + style-to-object: 0.4.4 + unified: 10.1.2 + unist-util-visit: 4.1.2 + vfile: 5.3.7 + transitivePeerDependencies: + - supports-color + + react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.7 + '@types/react-redux': 7.1.34 + hoist-non-react-statics: 3.3.2 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 17.0.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-refresh@0.14.2: {} + react-router-dom@6.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@remix-run/router': 1.20.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 6.27.0(react@18.3.1) + + react-router@6.27.0(react@18.3.1): + dependencies: + '@remix-run/router': 1.20.0 + react: 18.3.1 + + react-toastify@9.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 1.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-virtualized-auto-sizer@1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-window@1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.7 + memoize-one: 5.2.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-cmd-shim@5.0.0: {} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -7801,6 +11175,10 @@ snapshots: dependencies: picomatch: 2.3.1 + redux@4.2.1: + dependencies: + '@babel/runtime': 7.25.7 + regenerator-runtime@0.14.1: {} regex@4.3.3: {} @@ -7812,6 +11190,21 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 + remark-parse@10.0.2: + dependencies: + '@types/mdast': 3.0.15 + mdast-util-from-markdown: 1.3.1 + unified: 10.1.2 + transitivePeerDependencies: + - supports-color + + remark-rehype@10.1.0: + dependencies: + '@types/hast': 2.3.10 + '@types/mdast': 3.0.15 + mdast-util-to-hast: 12.3.0 + unified: 10.1.2 + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -7830,6 +11223,11 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + reusify@1.0.4: {} rfdc@1.4.1: {} @@ -7838,6 +11236,14 @@ snapshots: dependencies: glob: 7.2.3 + rimraf@5.0.10: + dependencies: + glob: 10.4.5 + + rollup@2.77.3: + optionalDependencies: + fsevents: 2.3.3 + rollup@4.24.0: dependencies: '@types/estree': 1.0.6 @@ -7860,8 +11266,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 + rope-sequence@1.3.4: {} + rrweb-cssom@0.7.1: {} + run-async@3.0.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -7870,6 +11280,10 @@ snapshots: dependencies: tslib: 2.7.0 + sade@1.8.1: + dependencies: + mri: 1.2.0 + safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} @@ -7912,6 +11326,8 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + setprototypeof@1.2.0: {} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -7960,6 +11376,11 @@ snapshots: slash@3.0.0: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.7.0 + source-map-js@1.2.1: {} source-map-support@0.5.21: @@ -7988,8 +11409,14 @@ snapshots: sprintf-js@1.0.3: {} + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + stackback@0.0.2: {} + statuses@2.0.1: {} + std-env@3.7.0: {} stop-iteration-iterator@1.0.0: @@ -8045,6 +11472,10 @@ snapshots: style-mod@4.1.2: {} + style-to-object@0.4.4: + dependencies: + inline-style-parser: 0.1.1 + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -8055,6 +11486,15 @@ snapshots: pirates: 4.0.6 ts-interface-checker: 0.1.13 + supabase@1.226.3: + dependencies: + bin-links: 5.0.0 + https-proxy-agent: 7.0.5 + node-fetch: 3.3.2 + tar: 7.4.3 + transitivePeerDependencies: + - supports-color + superjson@2.2.1: dependencies: copy-anything: 3.0.5 @@ -8073,10 +11513,54 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-parser@2.0.4: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.1.0 + css-tree: 2.3.1 + css-what: 6.1.0 + csso: 5.0.5 + picocolors: 1.1.0 + symbol-tree@3.2.4: {} + synckit@0.9.2: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.7.0 + tabbable@6.2.0: {} + tailwindcss@3.4.13: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.0 + postcss: 8.4.47 + postcss-import: 15.1.0(postcss@8.4.47) + postcss-js: 4.0.1(postcss@8.4.47) + postcss-load-config: 4.0.2(postcss@8.4.47) + postcss-nested: 6.2.0(postcss@8.4.47) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + tar-fs@2.1.1: dependencies: chownr: 1.1.4 @@ -8092,6 +11576,15 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.1 + mkdirp: 3.0.1 + yallist: 5.0.0 + term-size@2.2.1: {} terser@5.34.1: @@ -8111,6 +11604,8 @@ snapshots: dependencies: any-promise: 1.3.0 + tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} tinyexec@0.3.0: {} @@ -8132,6 +11627,18 @@ snapshots: tinytar@0.1.0: {} + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + + tiptap-markdown@0.8.10(@tiptap/core@2.8.0(@tiptap/pm@2.8.0)): + dependencies: + '@tiptap/core': 2.8.0(@tiptap/pm@2.8.0) + '@types/markdown-it': 13.0.9 + markdown-it: 14.1.0 + markdown-it-task-lists: 2.1.1 + prosemirror-markdown: 1.13.1 + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -8142,6 +11649,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + tough-cookie@4.1.4: dependencies: psl: 1.9.0 @@ -8161,6 +11670,8 @@ snapshots: trim-lines@3.0.1: {} + trough@2.2.0: {} + ts-api-utils@1.3.0(typescript@5.6.3): dependencies: typescript: 5.6.3 @@ -8176,7 +11687,7 @@ snapshots: tslib@2.7.0: {} - tsup@8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3): + tsup@8.3.0(@microsoft/api-extractor@7.47.7(@types/node@20.16.11))(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.3)(yaml@2.6.0): dependencies: bundle-require: 5.0.0(esbuild@0.23.1) cac: 6.7.14 @@ -8187,7 +11698,7 @@ snapshots: execa: 5.1.1 joycon: 3.1.1 picocolors: 1.1.0 - postcss-load-config: 6.0.1(postcss@8.4.47)(tsx@4.19.1) + postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(yaml@2.6.0) resolve-from: 5.0.0 rollup: 4.24.0 source-map: 0.8.0-beta.0 @@ -8230,35 +11741,79 @@ snapshots: type-fest@0.20.2: {} + type-fest@0.21.3: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + typescript@5.4.2: {} typescript@5.6.3: {} + uc.micro@2.1.0: {} + ufo@1.5.4: {} undici-types@6.19.8: {} + unified@10.1.2: + dependencies: + '@types/unist': 2.0.11 + bail: 2.0.2 + extend: 3.0.2 + is-buffer: 2.0.5 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 5.3.7 + union@0.5.0: dependencies: qs: 6.13.0 + unist-util-generated@2.0.1: {} + + unist-util-is@5.2.1: + dependencies: + '@types/unist': 2.0.11 + unist-util-is@6.0.0: dependencies: '@types/unist': 3.0.3 + unist-util-position@4.0.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.3 + unist-util-stringify-position@3.0.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 + unist-util-visit-parents@5.1.3: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 5.2.1 + unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.3 unist-util-is: 6.0.0 + unist-util-visit@4.1.2: + dependencies: + '@types/unist': 2.0.11 + unist-util-is: 5.2.1 + unist-util-visit-parents: 5.1.3 + unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -8269,6 +11824,10 @@ snapshots: universalify@0.2.0: {} + universalify@2.0.1: {} + + unpipe@1.0.0: {} + update-browserslist-db@1.1.1(browserslist@4.24.0): dependencies: browserslist: 4.24.0 @@ -8286,13 +11845,44 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-memo-one@1.1.3(react@18.3.1): + dependencies: + react: 18.3.1 + + use-sync-external-store@1.2.2(react@18.3.1): + dependencies: + react: 18.3.1 + util-deprecate@1.0.2: {} + uuid@9.0.1: {} + + uvu@0.5.6: + dependencies: + dequal: 2.0.3 + diff: 5.2.0 + kleur: 4.1.5 + sade: 1.8.1 + + vary@1.1.2: {} + + vfile-message@3.1.4: + dependencies: + '@types/unist': 2.0.11 + unist-util-stringify-position: 3.0.3 + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 + vfile@5.3.7: + dependencies: + '@types/unist': 2.0.11 + is-buffer: 2.0.5 + unist-util-stringify-position: 3.0.3 + vfile-message: 3.1.4 + vfile@6.0.3: dependencies: '@types/unist': 3.0.3 @@ -8357,6 +11947,26 @@ snapshots: minimatch: 9.0.5 vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + vite-plugin-svgr@3.3.0(rollup@4.24.0)(typescript@5.6.3)(vite@5.4.8(@types/node@20.16.11)(terser@5.34.1)): + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + '@svgr/core': 8.1.0(typescript@5.6.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.6.3)) + vite: 5.4.8(@types/node@20.16.11)(terser@5.34.1) + transitivePeerDependencies: + - rollup + - supports-color + - typescript + + vite@2.9.18: + dependencies: + esbuild: 0.14.54 + postcss: 8.4.47 + resolve: 1.22.8 + rollup: 2.77.3 + optionalDependencies: + fsevents: 2.3.3 + vite@5.4.8(@types/node@20.16.11)(terser@5.34.1): dependencies: esbuild: 0.21.5 @@ -8534,6 +12144,12 @@ snapshots: wa-sqlite@https://codeload.github.com/rhashimoto/wa-sqlite/tar.gz/6dbe4f044502a7a285e815896f56304a808fe25b: {} + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + web-streams-polyfill@3.3.3: {} + webidl-conversions@4.0.2: {} webidl-conversions@7.0.0: {} @@ -8597,6 +12213,12 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -8611,6 +12233,11 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@6.0.0: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + ws@8.18.0: {} xml-name-validator@4.0.0: {} @@ -8629,6 +12256,10 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + + yaml@2.6.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: @@ -8645,6 +12276,8 @@ snapshots: yocto-queue@1.1.1: {} + yoctocolors-cjs@2.1.2: {} + zod-validation-error@3.4.0(zod@3.23.8): dependencies: zod: 3.23.8 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 59a60bdc2..2f51cc673 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,4 @@ packages: + - 'demos/*' - 'packages/*' - 'docs'