From 4e47108ac828727a1c74ada4c571b651a6e2cbc9 Mon Sep 17 00:00:00 2001 From: Luk Date: Fri, 23 May 2025 21:01:48 +0200 Subject: [PATCH 1/6] local supabase vol 1 --- README.md | 69 ++++++++++++ angular.json | 14 +++ package.json | 7 +- reset-password.js | 83 +++++++++++++++ scripts/setup-local-supabase.ps1 | 73 +++++++++++++ scripts/setup-local-supabase.sh | 67 ++++++++++++ scripts/sync-from-cloud-supabase.ps1 | 93 +++++++++++++++++ scripts/sync-from-cloud-supabase.sh | 79 ++++++++++++++ src/environments/environment.local.ts | 5 + supabase/config.toml | 72 +++++++++++++ supabase/migrations/schema.sql | 144 ++++++++++++++++++++++++++ supabase/migrations/seed.sql | 26 +++++ 12 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 reset-password.js create mode 100644 scripts/setup-local-supabase.ps1 create mode 100755 scripts/setup-local-supabase.sh create mode 100644 scripts/sync-from-cloud-supabase.ps1 create mode 100755 scripts/sync-from-cloud-supabase.sh create mode 100644 src/environments/environment.local.ts create mode 100644 supabase/config.toml create mode 100644 supabase/migrations/schema.sql create mode 100644 supabase/migrations/seed.sql diff --git a/README.md b/README.md index 627a112..c9c18e0 100755 --- a/README.md +++ b/README.md @@ -58,9 +58,78 @@ Add screenshots or GIFs here to showcase the reader and admin interfaces. - Node.js (v18 or later) - npm (v10 or later) - Angular CLI (v19 or later) +- Docker (for local Supabase setup) ### Installation Steps 1. Clone the repository: ```bash git clone https://github.com/Lukecsharpwalker/angularBlog.git cd angularBlog + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Set up local Supabase (optional, but recommended for development): + + **For Unix/macOS users:** + ```bash + npm run setup:local-supabase + ``` + + **For Windows users:** + ```powershell + npm run setup:local-supabase:win + ``` + + This command will: + - Initialize a local Supabase instance using Docker + - Create the necessary database tables + - Create an admin user for testing (email: admin@example.com, password: admin123) + +4. Start the application with local Supabase: + ```bash + npm run start:local + ``` + Or use the cloud Supabase instance: + ```bash + npm start + ``` + +### Supabase Management +- To stop the local Supabase instance: + ```bash + supabase stop + ``` +- To start it again: + ```bash + supabase start + ``` +- To access Supabase Studio (admin interface): + Open http://localhost:54323 in your browser + +### Syncing from Cloud Supabase +You can sync your local Supabase instance with the cloud instance to get the latest schema, policies, and data: + +**For Unix/macOS users:** +```bash +npm run sync:cloud-supabase +``` + +**For Windows users:** +```powershell +npm run sync:cloud-supabase:win +``` + +This command will: +- Link your local Supabase project to the remote project +- Pull the database schema and policies from the cloud +- Dump data from the remote database +- Apply the schema and data to your local instance + +This is useful for: +- Getting the latest database structure during development +- Testing with real data from the production environment +- Ensuring your local environment matches the cloud environment diff --git a/angular.json b/angular.json index d57ab7e..59fcc52 100755 --- a/angular.json +++ b/angular.json @@ -74,6 +74,17 @@ "with": "src/environments/environment.development.ts" } ] + }, + "local": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.local.ts" + } + ] } }, "defaultConfiguration": "production" @@ -86,6 +97,9 @@ }, "development": { "buildTarget": "angularblogapp:build:development" + }, + "local": { + "buildTarget": "angularblogapp:build:local" } }, "defaultConfiguration": "development" diff --git a/package.json b/package.json index c7eacac..7e18238 100755 --- a/package.json +++ b/package.json @@ -9,7 +9,12 @@ "test": "ng test", "serve:ssr:AngularBlogApp": "node dist/angular-blog-app/server/server.mjs", "build:stats": "ng build --stats-json", - "analyze": "webpack-bundle-analyzer dist/angular-blog-app/stats.json" + "analyze": "webpack-bundle-analyzer dist/angular-blog-app/stats.json", + "setup:local-supabase": "bash scripts/setup-local-supabase.sh", + "setup:local-supabase:win": "powershell -ExecutionPolicy Bypass -File scripts/setup-local-supabase.ps1", + "sync:cloud-supabase": "bash scripts/sync-from-cloud-supabase.sh", + "sync:cloud-supabase:win": "powershell -ExecutionPolicy Bypass -File scripts/sync-from-cloud-supabase.ps1", + "start:local": "ng serve --configuration=local" }, "private": true, "dependencies": { diff --git a/reset-password.js b/reset-password.js new file mode 100644 index 0000000..60f9097 --- /dev/null +++ b/reset-password.js @@ -0,0 +1,83 @@ +"use strict"; +/********************************************************************** + * reset-password.ts + * ------------------------------------------------- + * Resets a Supabase user’s password via the Admin API. + * ------------------------------------------------- + * USAGE (bash): + * export SUPABASE_URL="https://aqdbdmepncxxuanlymwr.supabase.co" + * export SERVICE_ROLE_KEY="YOUR_SERVICE_ROLE_KEY_HERE" + * npx ts-node reset-password.ts \ + * a3b3cf93-6956-41c7-bec0-1qwqwd42f26 'N3w-Pa$$w0rd!' + *********************************************************************/ +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var supabase_js_1 = require("@supabase/supabase-js"); +var _a = process.argv, userId = _a[2], newPassword = _a[3]; +if (!userId || !newPassword) { + console.error('Usage: ts-node reset-password.ts '); + process.exit(1); +} +var SUPABASE_URL = process.env.SUPABASE_URL; +var SERVICE_ROLE_KEY = process.env.SERVICE_ROLE_KEY; +if (!SUPABASE_URL || !SERVICE_ROLE_KEY) { + console.error('Set SUPABASE_URL and SERVICE_ROLE_KEY environment variables.'); + process.exit(1); +} +(function () { return __awaiter(void 0, void 0, void 0, function () { + var admin, _a, data, error; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + admin = (0, supabase_js_1.createClient)(SUPABASE_URL, SERVICE_ROLE_KEY, { + auth: { autoRefreshToken: false, persistSession: false }, + }); + return [4 /*yield*/, admin.auth.admin.updateUserById(userId, { + password: newPassword, + })]; + case 1: + _a = _b.sent(), data = _a.data, error = _a.error; + if (error) { + console.error('❌ Failed to reset password:', error.message); + process.exit(1); + } + console.log("\u2705 Password updated for user ".concat(data === null || data === void 0 ? void 0 : data.id)); + return [2 /*return*/]; + } + }); +}); })(); diff --git a/scripts/setup-local-supabase.ps1 b/scripts/setup-local-supabase.ps1 new file mode 100644 index 0000000..cfd5d60 --- /dev/null +++ b/scripts/setup-local-supabase.ps1 @@ -0,0 +1,73 @@ +# PowerShell script for setting up local Supabase instance for Angular Blog App +# Windows-compatible version of setup-local-supabase.sh +# +# Note: If running this script directly (not through npm), you may need to change the execution policy: +# Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force + +# Stop on error +$ErrorActionPreference = "Stop" + +Write-Host "Setting up local Supabase instance for Angular Blog App..." + +# Check if Supabase CLI is installed +try { + $null = Get-Command supabase -ErrorAction Stop + Write-Host "Supabase CLI is already installed." +} catch { + Write-Host "Supabase CLI is not installed. Installing..." + npm install -g supabase +} + +# Check if Docker is running +try { + $null = docker info +} catch { + Write-Host "Docker is not running. Please start Docker and try again." + exit 1 +} + +# Initialize Supabase if not already initialized +if (-not (Test-Path "supabase\.branches")) { + Write-Host "Initializing Supabase..." + supabase init +} + +# Start Supabase +Write-Host "Starting Supabase..." +supabase start + +# Wait for Supabase to be ready +Write-Host "Waiting for Supabase to be ready..." +Start-Sleep -Seconds 5 + +# Apply schema +Write-Host "Applying database schema..." +supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres + +# Create a local environment file for development +if (-not (Test-Path "src\environments\environment.local.ts")) { + Write-Host "Creating local environment file..." + $envContent = @" +export const environment = { + production: false, + supabaseUrl: 'http://localhost:54321', + supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', +}; +"@ + Set-Content -Path "src\environments\environment.local.ts" -Value $envContent +} + +# Print success message +Write-Host "Local Supabase instance is now running!" +Write-Host "Admin user credentials:" +Write-Host " Email: admin@example.com" +Write-Host " Password: admin123" +Write-Host "" +Write-Host "Supabase Studio: http://localhost:54323" +Write-Host "API URL: http://localhost:54321" +Write-Host "" +Write-Host "To use the local Supabase instance in your Angular app, run:" +Write-Host " ng serve --configuration=local" +Write-Host "" +Write-Host "To stop Supabase, run:" +Write-Host " supabase stop" diff --git a/scripts/setup-local-supabase.sh b/scripts/setup-local-supabase.sh new file mode 100755 index 0000000..f6e2260 --- /dev/null +++ b/scripts/setup-local-supabase.sh @@ -0,0 +1,67 @@ + +#!/bin/bash + +# Exit on error +set -e + +echo "Setting up local Supabase instance for Angular Blog App..." + +# Check if Supabase CLI is installed +if ! command -v supabase &> /dev/null; then + echo "Supabase CLI is not installed. Installing..." + npm install -g supabase +fi + +# Check if Docker is running +if ! docker info &> /dev/null; then + echo "Docker is not running. Please start Docker and try again." + exit 1 +fi + +# Initialize Supabase if not already initialized +if [ ! -f "supabase/.branches" ]; then + echo "Initializing Supabase..." + supabase init +fi + +# Start Supabase +echo "Starting Supabase..." +supabase start + +# Wait for Supabase to be ready +echo "Waiting for Supabase to be ready..." +sleep 5 + +# Apply schema +echo "Applying database schema..." +supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres + +# Create a local environment file for development +if [ ! -f "src/environments/environment.local.ts" ]; then + echo "Creating local environment file..." + cat > src/environments/environment.local.ts << EOL +export const environment = { + production: false, + supabaseUrl: 'http://localhost:54321', + supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', +}; +EOL +fi + +# Print success message +echo "Local Supabase instance is now running!" +echo "Admin user credentials:" +echo " Email: admin@example.com" +echo " Password: admin123" +echo "" +echo "Supabase Studio: http://localhost:54323" +echo "API URL: http://localhost:54321" +echo "" +echo "To use the local Supabase instance in your Angular app, run:" +echo " ng serve --configuration=local" +echo "" +echo "To stop Supabase, run:" +echo " supabase stop" + +# Make the script executable +chmod +x scripts/setup-local-supabase.sh diff --git a/scripts/sync-from-cloud-supabase.ps1 b/scripts/sync-from-cloud-supabase.ps1 new file mode 100644 index 0000000..04fe331 --- /dev/null +++ b/scripts/sync-from-cloud-supabase.ps1 @@ -0,0 +1,93 @@ +# PowerShell script for syncing from cloud Supabase instance to local Supabase +# Windows-compatible version of sync-from-cloud-supabase.sh +# +# Note: If running this script directly (not through npm), you may need to change the execution policy: +# Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force + +# Stop on error +$ErrorActionPreference = "Stop" + +Write-Host "Syncing from cloud Supabase instance to local Supabase..." + +# Check if Supabase CLI is installed +try { + $null = Get-Command supabase -ErrorAction Stop + Write-Host "Supabase CLI is already installed." +} catch { + Write-Host "Supabase CLI is not installed. Installing..." + npm install -g supabase +} + +# Check if Docker is running +try { + $null = docker info +} catch { + Write-Host "Docker is not running. Please start Docker and try again." + exit 1 +} + +# Initialize Supabase if not already initialized +if (-not (Test-Path "supabase\.branches")) { + Write-Host "Initializing Supabase..." + supabase init +} + +# Extract Supabase URL and key from environment.ts +$envContent = Get-Content "src\environments\environment.ts" -Raw +$supabaseUrlMatch = [regex]::Match($envContent, "supabaseUrl: '([^']*)'") +$supabaseKeyMatch = [regex]::Match($envContent, "supabaseKey:\s*'([^']*)'") + +if (-not $supabaseUrlMatch.Success -or -not $supabaseKeyMatch.Success) { + Write-Host "Could not extract Supabase URL or key from environment.ts" + exit 1 +} + +$SUPABASE_URL = $supabaseUrlMatch.Groups[1].Value +$SUPABASE_KEY = $supabaseKeyMatch.Groups[1].Value + +# Extract project reference from URL +$PROJECT_REF = $SUPABASE_URL.Split('/')[2].Split('.')[0] + +Write-Host "Found Supabase project reference: $PROJECT_REF" + +# Start Supabase if not already running +try { + $null = supabase status +} catch { + Write-Host "Starting Supabase..." + supabase start +} + +# Wait for Supabase to be ready +Write-Host "Waiting for Supabase to be ready..." +Start-Sleep -Seconds 5 + +# Link to the remote project +Write-Host "Linking to remote Supabase project..." +supabase link --project-ref $PROJECT_REF --password $SUPABASE_KEY + +# Pull database schema and policies +Write-Host "Pulling database schema and policies from cloud..." +supabase db pull + +# Dump data from remote database (optional, can be commented out if not needed) +Write-Host "Dumping data from remote database..." +if (-not (Test-Path "supabase\seed")) { + New-Item -Path "supabase\seed" -ItemType Directory -Force | Out-Null +} +supabase db dump -f supabase/seed/remote_data.sql + +# Apply schema and data to local instance +Write-Host "Applying schema and data to local instance..." +supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres + +# Print success message +Write-Host "Local Supabase instance is now synced with cloud!" +Write-Host "Supabase Studio: http://localhost:54323" +Write-Host "API URL: http://localhost:54321" +Write-Host "" +Write-Host "To use the local Supabase instance in your Angular app, run:" +Write-Host " ng serve --configuration=local" +Write-Host "" +Write-Host "To stop Supabase, run:" +Write-Host " supabase stop" diff --git a/scripts/sync-from-cloud-supabase.sh b/scripts/sync-from-cloud-supabase.sh new file mode 100755 index 0000000..1210c9e --- /dev/null +++ b/scripts/sync-from-cloud-supabase.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Exit on error +set -e + +echo "Syncing from cloud Supabase instance to local Supabase..." + +# Check if Supabase CLI is installed +if ! command -v supabase &> /dev/null; then + echo "Supabase CLI is not installed. Installing..." + npm install -g supabase +fi + +# Check if Docker is running +if ! docker info &> /dev/null; then + echo "Docker is not running. Please start Docker and try again." + exit 1 +fi + +# Initialize Supabase if not already initialized +if [ ! -f "supabase/.branches" ]; then + echo "Initializing Supabase..." + supabase init +fi + +# Extract Supabase URL and key from environment.ts +SUPABASE_URL=$(grep -o "supabaseUrl: '[^']*'" src/environments/environment.ts | cut -d "'" -f 2) +SUPABASE_KEY=$(grep -o "supabaseKey: '[^']*'" src/environments/environment.ts | sed "s/supabaseKey: '//" | sed "s/',//") + +if [ -z "$SUPABASE_URL" ] || [ -z "$SUPABASE_KEY" ]; then + echo "Could not extract Supabase URL or key from environment.ts" + exit 1 +fi + +# Extract project reference from URL +PROJECT_REF=$(echo $SUPABASE_URL | cut -d '/' -f 3 | cut -d '.' -f 1) + +echo "Found Supabase project reference: $PROJECT_REF" + +# Start Supabase if not already running +if ! supabase status &> /dev/null; then + echo "Starting Supabase..." + supabase start +fi + +# Wait for Supabase to be ready +echo "Waiting for Supabase to be ready..." +sleep 5 + +# Link to the remote project +echo "Linking to remote Supabase project..." +supabase link --project-ref $PROJECT_REF --password $SUPABASE_KEY + +# Pull database schema and policies +echo "Pulling database schema and policies from cloud..." +supabase db pull + +# Dump data from remote database (optional, can be commented out if not needed) +echo "Dumping data from remote database..." +mkdir -p supabase/seed +supabase db dump -f supabase/seed/remote_data.sql + +# Apply schema and data to local instance +echo "Applying schema and data to local instance..." +supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres + +# Print success message +echo "Local Supabase instance is now synced with cloud!" +echo "Supabase Studio: http://localhost:54323" +echo "API URL: http://localhost:54321" +echo "" +echo "To use the local Supabase instance in your Angular app, run:" +echo " ng serve --configuration=local" +echo "" +echo "To stop Supabase, run:" +echo " supabase stop" + +# Make the script executable +chmod +x scripts/sync-from-cloud-supabase.sh diff --git a/src/environments/environment.local.ts b/src/environments/environment.local.ts new file mode 100644 index 0000000..17b6eb5 --- /dev/null +++ b/src/environments/environment.local.ts @@ -0,0 +1,5 @@ +export const environment = { + production: false, + supabaseUrl: 'http://localhost:54321', + supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', +}; diff --git a/supabase/config.toml b/supabase/config.toml new file mode 100644 index 0000000..155c40d --- /dev/null +++ b/supabase/config.toml @@ -0,0 +1,72 @@ +# A string used to distinguish different Supabase projects on the same host. Defaults to the working +# directory name when running `supabase init`. +project_id = "angular-blog-app" + +[api] +# 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 and storage are always included. +schemas = ["public", "storage", "auth"] +# 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 + +[db] +# Port to use for the local database URL. +port = 54322 +# 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 + +[studio] +# Port to use for Supabase Studio. +port = 54323 + +# 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] +# Port to use for the email testing server web interface. +port = 54324 +smtp_port = 54325 +pop3_port = 54326 + +[storage] +# The maximum file size allowed (e.g. "5MB", "500KB"). +file_size_limit = "50MiB" + +[auth] +# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used +# in emails. +site_url = "http://localhost:4200" +# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. +additional_redirect_urls = ["https://localhost:4200"] +# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (1 +# week). +jwt_expiry = 3600 +# Allow/disallow new user signups to your project. +enable_signup = true + +[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 + +# Use an external OAuth provider. The full list of providers are: "apple", "azure", "bitbucket", +# "discord", "facebook", "github", "gitlab", "google", "keycloak", "linkedin", "notion", "twitch", +# "twitter", "slack", "spotify", "workos", "zoom". +[auth.external.google] +enabled = true +client_id = "env(SUPABASE_AUTH_GOOGLE_CLIENT_ID)" +secret = "env(SUPABASE_AUTH_GOOGLE_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 = "" diff --git a/supabase/migrations/schema.sql b/supabase/migrations/schema.sql new file mode 100644 index 0000000..46f36e1 --- /dev/null +++ b/supabase/migrations/schema.sql @@ -0,0 +1,144 @@ +-- Create tables for the blog application + +-- Enable RLS (Row Level Security) +ALTER DATABASE postgres SET "app.settings.jwt_secret" TO 'super-secret-jwt-token-with-at-least-32-characters-long'; + +-- Create profiles table +CREATE TABLE IF NOT EXISTS public.profiles ( + id UUID PRIMARY KEY REFERENCES auth.users ON DELETE CASCADE, + username TEXT NOT NULL, + avatar_url TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() +); + +-- Create posts table +CREATE TABLE IF NOT EXISTS public.posts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + title TEXT NOT NULL, + description TEXT NOT NULL, + content TEXT NOT NULL, + is_draft BOOLEAN DEFAULT true, + user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), + category TEXT, + updated TIMESTAMP WITH TIME ZONE, + reading_time INTEGER +); + +-- Create tags table +CREATE TABLE IF NOT EXISTS public.tags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL UNIQUE +); + +-- Create post_tags table (junction table for many-to-many relationship) +CREATE TABLE IF NOT EXISTS public.post_tags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + post_id UUID NOT NULL REFERENCES public.posts(id) ON DELETE CASCADE, + tag_id UUID NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE, + UNIQUE(post_id, tag_id) +); + +-- Create comments table +CREATE TABLE IF NOT EXISTS public.comments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + content TEXT NOT NULL, + user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, + post_id UUID NOT NULL REFERENCES public.posts(id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() +); + +-- Set up Row Level Security (RLS) policies +-- Profiles table policies +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public profiles are viewable by everyone" + ON public.profiles FOR SELECT + USING (true); + +CREATE POLICY "Users can insert their own profile" + ON public.profiles FOR INSERT + WITH CHECK (auth.uid() = id); + +CREATE POLICY "Users can update their own profile" + ON public.profiles FOR UPDATE + USING (auth.uid() = id); + +-- Posts table policies +ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public posts are viewable by everyone" + ON public.posts FOR SELECT + USING (NOT is_draft OR auth.uid() = user_id); + +CREATE POLICY "Users can insert their own posts" + ON public.posts FOR INSERT + WITH CHECK (auth.uid() = user_id); + +CREATE POLICY "Users can update their own posts" + ON public.posts FOR UPDATE + USING (auth.uid() = user_id); + +CREATE POLICY "Users can delete their own posts" + ON public.posts FOR DELETE + USING (auth.uid() = user_id); + +-- Comments table policies +ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Public comments are viewable by everyone" + ON public.comments FOR SELECT + USING (true); + +CREATE POLICY "Users can insert their own comments" + ON public.comments FOR INSERT + WITH CHECK (auth.uid() = user_id); + +CREATE POLICY "Users can update their own comments" + ON public.comments FOR UPDATE + USING (auth.uid() = user_id); + +CREATE POLICY "Users can delete their own comments" + ON public.comments FOR DELETE + USING (auth.uid() = user_id); + +-- Tags table policies +ALTER TABLE public.tags ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Tags are viewable by everyone" + ON public.tags FOR SELECT + USING (true); + +CREATE POLICY "Only authenticated users can insert tags" + ON public.tags FOR INSERT + WITH CHECK (auth.role() = 'authenticated'); + +-- Post_tags table policies +ALTER TABLE public.post_tags ENABLE ROW LEVEL SECURITY; + +CREATE POLICY "Post tags are viewable by everyone" + ON public.post_tags FOR SELECT + USING (true); + +CREATE POLICY "Only post owners can add tags to posts" + ON public.post_tags FOR INSERT + WITH CHECK ( + EXISTS ( + SELECT 1 FROM public.posts + WHERE id = post_id AND user_id = auth.uid() + ) + ); + +-- Create functions and triggers +CREATE OR REPLACE FUNCTION public.handle_new_user() +RETURNS TRIGGER AS $$ +BEGIN + INSERT INTO public.profiles (id, username, avatar_url) + VALUES (new.id, new.email, 'https://www.gravatar.com/avatar/' || md5(lower(trim(new.email))) || '?d=mp'); + RETURN new; +END; +$$ LANGUAGE plpgsql SECURITY DEFINER; + +CREATE OR REPLACE TRIGGER on_auth_user_created + AFTER INSERT ON auth.users + FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); diff --git a/supabase/migrations/seed.sql b/supabase/migrations/seed.sql new file mode 100644 index 0000000..6be25a9 --- /dev/null +++ b/supabase/migrations/seed.sql @@ -0,0 +1,26 @@ +-- Seed data for the blog application + +-- Create a test admin user +-- Note: In a real application, you would use a secure password +INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, raw_app_meta_data, raw_user_meta_data) +VALUES ( + gen_random_uuid(), + 'admin@example.com', + crypt('admin123', gen_salt('bf')), + now(), + '{"provider":"email","providers":["email"],"role":"Admin"}', + '{"name":"Admin User"}' +) ON CONFLICT DO NOTHING; + +-- Create some sample tags +INSERT INTO public.tags (name) VALUES + ('Angular'), + ('TypeScript'), + ('Supabase'), + ('Web Development'), + ('Frontend') +ON CONFLICT (name) DO NOTHING; + +-- Note: The profiles table will be populated automatically by the trigger +-- when users are created. The posts, comments, and post_tags tables +-- can be populated through the application interface by the admin user. From 81e7323806c5756d0c84c3547f7f01ebf9c9d882 Mon Sep 17 00:00:00 2001 From: Luk Date: Sun, 8 Jun 2025 18:21:51 +0200 Subject: [PATCH 2/6] local supabase vol 2 --- .vscode/extensions.json | 5 +- articlesAsMarkDown/part-5.md | 228 ++ package-lock.json | 2929 ++++++++++++++++- package.json | 15 +- .../reset-password.js | 0 .../reset-password.ts | 0 scripts/set-passwords.sh | 64 + scripts/setup-local-supabase.ps1 | 73 - scripts/setup-local-supabase.sh | 67 - scripts/sync-from-cloud-supabase.ps1 | 93 - scripts/sync-from-cloud-supabase.sh | 79 - .../add-post/add-post.component.ts | 7 + src/app/admin/_models/post-from.inteface.ts | 1 + src/app/admin/_services/admin-api.service.ts | 8 +- .../main-page/post/post.component.ts | 2 +- .../_components/main-page/post/post.store.ts | 4 +- .../reader/_services/reader-api.service.ts | 9 +- supabase/config.tmp | 0 supabase/config.toml | 273 +- supabase/migrations/schema.sql | 144 - supabase/migrations/seed.sql | 26 - 21 files changed, 3340 insertions(+), 687 deletions(-) create mode 100644 articlesAsMarkDown/part-5.md rename reset-password.js => scripts/reset-password.js (100%) rename reset-password.ts => scripts/reset-password.ts (100%) create mode 100755 scripts/set-passwords.sh delete mode 100644 scripts/setup-local-supabase.ps1 delete mode 100755 scripts/setup-local-supabase.sh delete mode 100644 scripts/sync-from-cloud-supabase.ps1 delete mode 100755 scripts/sync-from-cloud-supabase.sh create mode 100644 supabase/config.tmp delete mode 100644 supabase/migrations/schema.sql delete mode 100644 supabase/migrations/seed.sql diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 77b3745..09cf720 100755 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,4 +1,5 @@ { - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 - "recommendations": ["angular.ng-template"] + "recommendations": [ + "denoland.vscode-deno" + ] } diff --git a/articlesAsMarkDown/part-5.md b/articlesAsMarkDown/part-5.md new file mode 100644 index 0000000..5214a49 --- /dev/null +++ b/articlesAsMarkDown/part-5.md @@ -0,0 +1,228 @@ +Hello. + +Code on the public repo is available here: + +https://github.com/Lukecsharpwalker/angularBlog + +Currently working on the scripts that will help to set up the local Supabase instance. + +## Creating new Supabase project + +It is not my first Supabase project, so we'll skip creating a new account. Also creating a new project is straightforward, so I will not cover it here. + +### Create DB and tables + +Just in case, Supabase provides a tutorial for migrating from Firestore to Supabase + +https://supabase.com/docs/guides/platform/migrating-to-supabase/firestore-data\\n + +In our case, we have only two collections; one is for the script that populates articles id's for SSG. So we got one; there is no point in struggling with the migration scripts. They never work on the happy path. + +For me SQL is one of the best programming languages; it's mature and intuitive, and there are a lot of good learning materials and examples. So LLMs are fed by good data, so we can rely on them and use AI as leverage. +Here is the SQL code, AI-generated, that will create the tables: + +``` +-- Enable required extensions\ncreate extension if not exists \"uuid-ossp\";\n\n\ncreate table profiles (\n id uuid primary key references auth.users(id) on delete cascade,\n username text unique not null,\n avatar_url text,\n created_at timestamp with time zone default now()\n);\n\n\ncreate table tags (\n id serial primary key,\n name text unique not null,\n color text not null,\n icon text not null\n);\n\n\ncreate table posts (\n id uuid primary key default uuid_generate_v4(),\n user_id uuid references profiles(id) on delete cascade,\n title text not null,\n description text,\n content text,\n is_draft boolean default false,\n created_at timestamp with time zone default now()\n);\n\n\ncreate table comments (\n id uuid primary key default uuid_generate_v4(),\n post_id uuid references posts(id) on delete cascade,\n user_id uuid references profiles(id) on delete cascade,\n content text,\n is_deleted boolean default false,\n is_reported boolean default false,\n created_at timestamp with time zone default now()\n);\n\n\ncreate table post_tags (\n post_id uuid references posts(id) on delete cascade,\n tag_id int references tags(id) on delete cascade,\n primary key (post_id, tag_id)\n);\n +``` + +Let's try it. + +Success. No rows returned\\nSuccess. No rows returned\\n + +Double success! + +![\"supabase-visualizer\"](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//Xnapper-2025-04-21-10.16.39.jpg\") + +The table looks nice! Now we have more tables than we had documents. For me, it looks a lot cleaner! + +The issue here is that when a new user creates an account, it'll not be bound with the Profiles table. We, of course, can achieve that with some triggers on DB, or in this case, in the code. + +I have only a few posts now, so the best option is to just copy data. I hate manual work, but sometimes it is the fastest way. + +### Setting up admin + +As you may remember, at Firestore we need to set the admin role. It was quite a struggle because we needed to create a function for that. Supabase is better because we can manipulate the DB directly from the GUI. + +![](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//addUser.jpg\") + +In the raw information about the user, we can see roles, etc. Now the user has only the authenticated role. + +![\"user](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//Xnapper-2025-04-22-10.20.26.jpg\") + +Now let run SQL command + +``` +update auth.users\nset raw_app_meta_data = jsonb_set(raw_app_meta_data, '{role}', '\"Admin\"', true),\n updated_at = now()\nwhere id = 'User Id';\n +``` + +And here we have the admin role; it should work as a role in the JS SDK, and we will try it later. + +![\"admin](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//admin.jpg.jpg\") + +Ok, let's set RLS: + +``` +-- ENABLE RLS ON TABLES\nalter table profiles enable row level security;\nalter table comments enable row level security;\nalter table posts enable row level security;\n\n-- RLS FOR PROFILES\nUPDATE, DELETE: by profile owner or Admin\n\ncreate policy \"Users can edit their own profile or Admin\"\non profiles for update\nusing (auth.uid() = id or auth.jwt()->>'role' = 'Admin');\n\ncreate policy \"Users can delete their own profile or Admin\"\non profiles for delete\nusing (auth.uid() = id or auth.jwt()->>'role' = 'Admin');\n\n-- INSERT: only authenticated users\ncreate policy \"Authenticated users can create profiles\"\non profiles for insert\nwith check (auth.role() = 'authenticated');\n\n-- RLS FOR COMMENTS\n-- SELECT: everyone (no restrictions)\ncreate policy \"Public can read comments\"\non comments for select\nusing (true);\n\n-- INSERT: only authenticated users\ncreate policy \"Authenticated users can add comments\"\non comments for insert\nwith check (auth.role() = 'authenticated');\n\n-- DELETE: Admin only\ncreate policy \"Only Admin can delete comments\"\non comments for delete\nusing (auth.jwt()->>'role' = 'Admin');\n\n-- RLS FOR POSTS\n-- SELECT: everyone\ncreate policy \"Public can read posts\"\non posts for select\nusing (true);\n\n-- INSERT, UPDATE, DELETE: Admin only\ncreate policy \"Only Admin can create posts\"\non posts for insert\nwith check (auth.jwt()->>'role' = 'Admin');\n\ncreate policy \"Only Admin can edit posts\"\non posts for update\nusing (auth.jwt()->>'role' = 'Admin');\n\ncreate policy \"Only Admin can delete posts\"\non posts for delete\nusing (auth.jwt()->>'role' = 'Admin');\n +``` + +I think we can get rid of the Select Policy for Profile. It's my mistake. + +![\"RLS](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//description.jpg\") + +Success. No rows returned\\n + +Meanwhile, I'll manually copy data and set tags, which are not set yet on the current version, and we'll jump to code! + +## Setup and use Supabase + +Installing is easy as always: + +npm install @supabase/supabase-js\\n + +In the env file: + +``` +export const environment = {\n production: false,\n supabaseUrl: 'YOUR_SUPABASE_URL',\n supabaseKey: 'YOUR_SUPABASE_KEY',\n}\n +``` + +In the Supabase docs we have a great service example, but we will tweak it a little bit: + +``` +import { Injectable } from '@angular/core';\nimport {\n AuthChangeEvent,\n AuthSession,\n createClient,\n Provider,\n Session,\n SupabaseClient,\n} from '@supabase/supabase-js';\nimport { environment } from '../../environments/environment';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class SupabaseService {\n private readonly supabase: SupabaseClient;\n public session: AuthSession | null = null;\n\n constructor() {\n this.supabase = createClient(\n environment.supabaseUrl,\n environment.supabaseKey,\n );\n }\n\n getSession(): AuthSession | null {\n this.supabase.auth.getSession().then(({ data }) => {\n this.session = data.session;\n });\n return this.session;\n }\n\n authChanges(\n callback: (event: AuthChangeEvent, session: Session | null) => void,\n ) {\n return this.supabase.auth.onAuthStateChange(callback);\n }\n\n getClient(): SupabaseClient {\n return this.supabase;\n }\n\n signInWithEmail(email: string) {\n return this.supabase.auth.signInWithOtp({ email });\n }\n\n signInWithPassword(email: string, password: string) {\n return this.supabase.auth.signInWithPassword({ email, password });\n }\n\n signUp(email: string, password: string) {\n return this.supabase.auth.signUp({ email, password });\n }\n\n signInWithProvider(provider: Provider) {\n return this.supabase.auth.signInWithOAuth({\n provider,\n options: {\n redirectTo: window.location.origin,\n },\n });\n }\n\n signOut() {\n return this.supabase.auth.signOut();\n }\n}\n\n +``` + +Ok, now we can initialize it: + +``` +{\n provide: APP_INITIALIZER,\n useFactory: supabaseInitializer,\n deps: [SupabaseService],\n multi: true,\n}\n +``` + +**APP\_INITIALIZER** is deprecated, but we can still use it! + +and the initializer code: + +``` +import { SupabaseService } from '../services/supabase.service';\nimport { Subscription } from '@supabase/supabase-js';\n\nexport function supabaseInitializer(\n supabase: SupabaseService,\n): () => { data: { subscription: Subscription } } {\n return () =>\n supabase.authChanges((_, session) => (supabase.session = session));\n}\n\n +``` + +Quick test, and it fails. I had some issues with SSR, so I disabled it on dev for now. Hope that connecting to the DB will help! + +### Create (or not) Interfaces + +Supabase has another great feature: it creates interfaces for us! + +npm install supabase --save-dev\\nnpx supabase gen types typescript --project-id aqdbdmepncxxuanlymwr > src/app/supabase-types.ts\\n + +![\"Database](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//wtf.jpg) + +WTF?! Yeah, not perfect, but let's bring some magic to life. IntelliJ provides some free credits each month! + +![\"AI](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//magic.jpg\") + +Much better! + +![\"types\"](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//better.jpg\") + +Our call will look like this: + +async getPosts(): Promise {\\n let { data: posts } = await this.supabaseService.getClient\\n .from('posts')\\n .select('\*')\\n .order('created\_at', { ascending: false });\\n\\n return posts;\\n }\\n + +Of course we will need to add a relation later. + +## ![](\"\")Install NGRX SignalStore + +ng add @ngrx/signals@latest\\n + +Lets create a store file **posts.store.ts** + +``` +import { Post } from '../../../../types/supabase';\nimport {\n patchState,\n signalStore,\n withMethods,\n withState,\n} from '@ngrx/signals';\nimport { inject } from '@angular/core';\nimport { ReaderApiService } from '../../../_services/reader-api.service';\n\ntype PostsState = {\n posts: Post[];\n loading: boolean;\n error: string | null;\n};\n\nconst initialState: PostsState = {\n posts: [],\n loading: false,\n error: null,\n};\n\nexport const PostsStore = signalStore(\n { providedIn: 'root' },\n withState(initialState),\n\n withMethods((state, postService = inject(ReaderApiService)) => ({\n async getPosts() {\n patchState(state, { loading: true, error: null });\n try {\n const posts = await postService.getPosts();\n if (posts) {\n patchState(state, { posts, loading: false });\n } else {\n patchState(state, { error: 'No posts found', loading: false });\n }\n } catch (error) {\n patchState(state, { error: 'Failed to fetch posts', loading: false });\n }\n },\n })),\n withHooks({\n onInit(store) {\n store.getPosts();\n },\n }),\n);\n +``` + +Remember the onInit hook; without this, you need to call it manually on init. + +Usage is super simple: + +``` +postStore = inject(PostsStore);\nposts = this.postStore.posts;\n +``` + +``` +
\n
\n
\n @for (post of posts(); track post.id; let f = $first) {\n \n }\n
\n
\n
\n +``` + +That's all! + +![\"post](\"https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//thatall.jpg\") + +Ok, now let's tweak query to get all data We need posts with author tags and also only posts that are not in the **draft**. + +async getPosts(): Promise {\\n const { data: posts } = await this.supabaseService.getClient\\n .from('posts')\\n .select(\`\\n \*,\\n author:profiles ( id, username, avatar\_url ),\\n post\_tags (\\n tags ( id, name, color, icon )\\n )\\n \`,\\n )\\n .eq('is\_draft', false)\\n .order('created\_at', { ascending: false });\\n\\n return posts;\\n}\\n + +There was an issue with the author because **Read** was available for the auth user; this was a mistake. + +Nice, it's working. Now I'll adjust all other places, and then we'll check if issues with the SSR are gone! + +They are not gone; to be honest, it was very hard to spot where the issue was. I've tried to cut all possibilities; everything and nothing helps, so the issue needs to be in the root. + +I have found that Supabase got some glitch at init. + +In the **Supabase service,** I changed the client creation: + +``` +constructor() {\n this.supabase = this.ngZone.runOutsideAngular(() =>\n createClient(environment.supabaseUrl, environment.supabaseKey),\n );\n}\n +``` + +It helps to build and run an app, but the content didn't prerender. XD WTF, mate, it's supposed to be easy, just one time xD + +I'm not sure if it's Supabase issue or SignalStore issue but + +setTimeout(() => {}, 0);\\n + +helps to prerender content. I will investigate this later because it's a lame approach. + +## Setup env on the GitHub Actions + +Yeah, I pushed envs, but there are add-ons for Git that revert whole history and remove specific files; it took less than a second, so fair enough for that kind of a mistake. + +Keys will be exposed anyway in the bundle; it's just how FE works, but we have a server app for our static file, and maybe it will be used for some password recovery, so admin credentials should be confidential! + +I've added this step to jobs + +``` +- name: Generate environment.ts from secrets\n shell: bash\n run: |\n cat > src/environments/environment.ts <<'EOF'\n export const environment = {\n production: true,\n supabaseUrl: '${{ secrets.SUPABASE_URL }}',\n supabaseKey: '${{ secrets.SUPABASE_ANON_KEY }}',\n };\n EOF\n\n - run: npm ci\n - run: npm run test -- --browsers=ChromeHeadless --watch=false --no-progress\n +``` + +PR action passed! So that's all; thank you! Have a lovely day! + +## Errata + +So Supabase is not working great for SSG/SSR. + +There was an issue with initializing Supabase then; for prerender, I fixed prerender, but then, something went wrong with hydration: + +https://aqdbdmepncxxuanlymwr.supabase.co/storage/v1/object/public/post-images//flicking.mov + +### Leveraging SSG by Supabase autogenerated Rest API + +Supabase has an autogenerated API; I'm quite sure that is handled by **PostgREST.** + +``` + getPosts(): Observable {\n const selectQuery = `\n *,\n author:profiles(id,username,avatar_url),\n post_tags(tags(id,name,color,icon))\n `\n .replace(/\\s+/g, ' ')\n .trim();\n\n const params = new HttpParams()\n .set('select', selectQuery)\n .set('is_draft', 'eq.false')\n .set('order', 'created_at.desc');\n\n const headers = new HttpHeaders({\n apikey: this.apiKey,\n Authorization: `Bearer ${this.apiKey}`,\n Accept: 'application/json',\n });\n\n return this.http.get(`${this.baseUrl}posts`, { headers, params });\n }\n +``` + +Looks bad, works great - fair enough!. + +It could be refactored and will be! For sure this **Bearer** should be in the interceptor. + +Also, SignalStore should be adjusted. + +``` +withMethods((store, postService = inject(ReaderApiService)) => ({\n getPost: rxMethod(\n pipe(\n tap(() => patchState(store, { loading: true, error: null })),\n switchMap((id) =>\n postService.getPost(id).pipe(\n tapResponse({\n next: (post: Post) => patchState(store, { post, loading: false }),\n error: (err: string) =>\n patchState(store, {\n error: err ?? 'Failed to fetch post',\n loading: false,\n }),\n }),\n ),\n ),\n ),\n ),\n})),\n +``` + +**rxMethod** is provided by NGRX SignalStore and handles subscriptions inside. How cool is that? + +Now SSG and SSR work like a charm! + +" diff --git a/package-lock.json b/package-lock.json index 76965ab..ffdf6c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,10 +37,13 @@ "@angular-devkit/build-angular": "^19.2.1", "@angular/cli": "^19.2.1", "@angular/compiler-cli": "^19.2.1", + "@snaplet/copycat": "^6.0.0", + "@snaplet/seed": "^0.98.0", "@types/compression": "^1.7.5", "@types/express": "^4.17.17", "@types/jasmine": "~5.1.0", "@types/node": "^18.18.0", + "@types/pg": "^8.15.2", "autoprefixer": "^10.4.19", "daisyui": "^4.12.10", "jasmine-core": "~5.1.0", @@ -49,9 +52,9 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "pg": "^8.16.0", "postcss": "^8.4.38", "prettier": "3.4.2", - "supabase": "^2.22.6", "tailwindcss": "^3.4.4", "tsx": "^4.19.4", "typescript": "~5.7.3", @@ -2463,6 +2466,30 @@ "node": ">=18" } }, + "node_modules/@faker-js/faker": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, + "node_modules/@glideapps/ts-necessities": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@glideapps/ts-necessities/-/ts-necessities-2.2.3.tgz", + "integrity": "sha512-gXi0awOZLHk3TbW55GZLCPP6O+y/b5X1pBXKBVckFONSwF1z1E5ND2BGJsghQFah+pW7pkkyFb2VhUQI2qhL5w==", + "dev": true, + "license": "MIT" + }, "node_modules/@inquirer/checkbox": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.3.tgz", @@ -2989,6 +3016,220 @@ "tslib": "2" } }, + "node_modules/@langchain/core": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.36.tgz", + "integrity": "sha512-qHLvScqERDeH7y2cLuJaSAlMwg3f/3Oc9nayRSXRU2UuaK/SOhI42cxiPLj1FnuHJSmN0rBQFkrLx02gI4mcVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.1.56-rc.1", + "mustache": "^4.2.0", + "p-queue": "^6.6.2", + "p-retry": "4", + "uuid": "^10.0.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/core/node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@langchain/core/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@langchain/core/node_modules/langsmith": { + "version": "0.1.68", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.68.tgz", + "integrity": "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/uuid": "^10.0.0", + "commander": "^10.0.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "openai": "*" + }, + "peerDependenciesMeta": { + "openai": { + "optional": true + } + } + }, + "node_modules/@langchain/core/node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/groq": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@langchain/groq/-/groq-0.0.15.tgz", + "integrity": "sha512-JN6dlPNHZEFKc8JjQzO+H7IqCpcE3s/1nYnb+sHxpc22Amr3yxXgp6CvanLhmD+R8Wj1AE1vG7AkzjM8MLtv+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@langchain/core": ">=0.2.16 <0.3.0", + "@langchain/openai": "~0.2.4", + "groq-sdk": "^0.3.2", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/openai": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.2.11.tgz", + "integrity": "sha512-Pu8+WfJojCgSf0bAsXb4AjqvcDyAWyoEB1AoCRNACgEnBWZuitz3hLwCo9I+6hAbeg3QJ37g82yKcmvKAg1feg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@langchain/core": ">=0.2.26 <0.3.0", + "js-tiktoken": "^1.0.12", + "openai": "^4.57.3", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/openai/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@langchain/openai/node_modules/openai": { + "version": "4.103.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.103.0.tgz", + "integrity": "sha512-eWcz9kdurkGOFDtd5ySS5y251H2uBgq9+1a2lTBnjMMzlexJ40Am5t6Mu76SSE87VvitPa0dkIAp75F+dZVC0g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@langchain/openai/node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -3568,6 +3809,139 @@ "dev": true, "license": "MIT" }, + "node_modules/@prisma/debug": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.14.0-dev.34.tgz", + "integrity": "sha512-mc4Ue07QjYcb4yV0ZXap2AJBLlBAk0owO3fHKWovQA9Ig2XXlxlAUesk9RxPYKj9zIpDZXYMPUC3iKIdUi5SUA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.14.0-dev.34.tgz", + "integrity": "sha512-RWkQHOPxSfy0ANoE0hhrDTf7SuNACILx/LTM1LINlWSYG+Ev/do+5RFbrCv6liCxi1fRZuuhtTux9sH56o01cQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.14.0-dev.34", + "@prisma/engines-version": "5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361", + "@prisma/fetch-engine": "5.14.0-dev.34", + "@prisma/get-platform": "5.14.0-dev.34" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361.tgz", + "integrity": "sha512-XkTJYtdOIrJkJv/tzXzsaUsfyvp82IWSPx4DlR52G0cyKoqT6lC55daIdsnuEoKPM2jPcL6P7dJENYBMGHQLEg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.14.0-dev.34.tgz", + "integrity": "sha512-Ieqp/Zfq7KaZWndJAq2K0Z5r77DBPyvXlKXbztXnyvoQhce+9QTkjwJ8U3dOHUwSwNqIb6TY7j1dal3epSUZkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.14.0-dev.34", + "@prisma/engines-version": "5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361", + "@prisma/get-platform": "5.14.0-dev.34" + } + }, + "node_modules/@prisma/generator-helper": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/generator-helper/-/generator-helper-5.14.0-dev.34.tgz", + "integrity": "sha512-AsY7piYVHtaGf/TjSoK2j7pZmG+xX/Mqv/VQMNJmfJDEGAnt1fXg6e6veSGLm/SqxA3JJhVCaX3XUHYDeXnsOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.14.0-dev.34" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.14.0-dev.34.tgz", + "integrity": "sha512-JlzzUMQKsj1cFMXiGMkqrdP7dl3OZtZQapEeCAoH42J6GCrEuV+qNhTOlkywyNuFDj+j1VjfE7p9HRFO1+kiiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.14.0-dev.34" + } + }, + "node_modules/@prisma/internals": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/internals/-/internals-5.14.0-dev.34.tgz", + "integrity": "sha512-FKToi0h7DFkSZ+eAo737RisLAlRrHq2VPRnm53aVe7LH1J4qwVhl7U+Gy9CsifUgi5VDX311M2W5hyaRcBs46A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.14.0-dev.34", + "@prisma/engines": "5.14.0-dev.34", + "@prisma/fetch-engine": "5.14.0-dev.34", + "@prisma/generator-helper": "5.14.0-dev.34", + "@prisma/get-platform": "5.14.0-dev.34", + "@prisma/prisma-schema-wasm": "5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361", + "@prisma/schema-files-loader": "5.14.0-dev.34", + "arg": "5.0.2", + "prompts": "2.4.2" + } + }, + "node_modules/@prisma/prisma-schema-wasm": { + "version": "5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361", + "resolved": "https://registry.npmjs.org/@prisma/prisma-schema-wasm/-/prisma-schema-wasm-5.14.0-6.264f24ce0b2f544ff968ff76bfaa999de1161361.tgz", + "integrity": "sha512-lMNW0WEI+eP5gPn+blBj2yK2znvQlWQbbcOdbqR6PmOOMZRPXbfoC1LgxFn0QrZalJ1csJSFPjmQiYcrv9/39w==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/schema-files-loader": { + "version": "5.14.0-dev.34", + "resolved": "https://registry.npmjs.org/@prisma/schema-files-loader/-/schema-files-loader-5.14.0-dev.34.tgz", + "integrity": "sha512-oO0dMzBJbNN3OwcNpRpKO6iq/rqWg02OKBeUI+Qy3Cwrqo5SlKO+DeolkUnx2PPWiHitDX/8UkGRBkMRG0HI9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "fs-extra": "11.1.1" + } + }, + "node_modules/@prisma/schema-files-loader/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@prisma/schema-files-loader/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@prisma/schema-files-loader/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.34.8", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", @@ -3582,6 +3956,31 @@ "darwin" ] }, + "node_modules/@sagold/json-pointer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@sagold/json-pointer/-/json-pointer-5.1.2.tgz", + "integrity": "sha512-+wAhJZBXa6MNxRScg6tkqEbChEHMgVZAhTHVJ60Y7sbtXtu9XA49KfUkdWlS2x78D6H9nryiKePiYozumauPfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sagold/json-query": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@sagold/json-query/-/json-query-6.2.0.tgz", + "integrity": "sha512-7bOIdUE6eHeoWtFm8TvHQHfTVSZuCs+3RpOKmZCDBIOrxpvF/rNFTeuvIyjHva/RR0yVS3kQtr+9TW72LQEZjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sagold/json-pointer": "^5.1.2", + "ebnf": "^1.9.1" + } + }, + "node_modules/@scaleleap/pg-format": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@scaleleap/pg-format/-/pg-format-1.0.0.tgz", + "integrity": "sha512-gFkcYMnpeylF2OJ30FsDBjwICB9JTiZ5i3guPwdiBDrJFwIKr+Zk6jwI8Mg22a4FwXn5ezd5cHEFMKqBqBz4RQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@schematics/angular": { "version": "19.2.1", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.2.1.tgz", @@ -3692,41 +4091,646 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "node_modules/@snaplet/copycat": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@snaplet/copycat/-/copycat-6.0.0.tgz", + "integrity": "sha512-ZBxlsWfhd+fxubKHrZglUQmSxHe70sGxHqTPiIBqoqX/41jABG8VCbgB8KrMm8to6V6Y/tBMqQUGzuWFFNHimg==", "dev": true, - "license": "MIT" - }, - "node_modules/@supabase/auth-js": { - "version": "2.69.1", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", - "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", - "license": "MIT", - "dependencies": { - "@supabase/node-fetch": "^2.6.14" - } - }, - "node_modules/@supabase/functions-js": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", - "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", "license": "MIT", "dependencies": { - "@supabase/node-fetch": "^2.6.14" + "@faker-js/faker": "^8.4.1", + "fictional": "^3.0.1", + "string-argv": "^0.3.2", + "uuid": "^9.0.1" } }, - "node_modules/@supabase/node-fetch": { - "version": "2.6.15", - "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", - "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" + "node_modules/@snaplet/seed": { + "version": "0.98.0", + "resolved": "https://registry.npmjs.org/@snaplet/seed/-/seed-0.98.0.tgz", + "integrity": "sha512-qLh7e596mKbY3DZW1CryhYyZdFJ/irAU760t00ac1VHsx/1Brfu3jpc6Bq69XBRZ6I3/nqPDlRckVP//bpFUMQ==", + "dev": true, + "hasInstallScript": true, + "license": "FSL-1.1-MIT", + "dependencies": { + "@inquirer/prompts": "^5.0.2", + "@langchain/core": "^0.2.18", + "@langchain/groq": "^0.0.15", + "@langchain/openai": "^0.2.4", + "@prisma/generator-helper": "5.14.0-dev.34", + "@prisma/internals": "5.14.0-dev.34", + "@scaleleap/pg-format": "^1.0.0", + "@total-typescript/ts-reset": "^0.5.1", + "ansi-escapes": "^6.2.1", + "c12": "^1.10.0", + "change-case": "^5.4.4", + "debug": "^4.3.4", + "dedent": "^1.5.3", + "execa": "^8.0.1", + "exit-hook": "^4.0.0", + "find-up": "^7.0.0", + "fs-extra": "^11.2.0", + "inflection": "^3.0.0", + "javascript-stringify": "^2.1.0", + "json-schema-library": "^9.3.4", + "kleur": "^4.1.5", + "multimatch": "^7.0.0", + "ora": "^8.0.1", + "quicktype-core": "23.0.149", + "remeda": "^1.61.0", + "sqlstring": "^2.3.3", + "terminal-link": "^3.0.0", + "uuid": "^9.0.1", + "yargs": "^17.7.2", + "zod": "^3.23.5" + }, + "bin": { + "snaplet-seed": "bin/cli.js" }, "engines": { - "node": "4.x || >=6.0.0" + "node": ">=18.5.0" + }, + "peerDependencies": { + "@prisma/client": ">=5", + "@snaplet/copycat": ">=2", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "better-sqlite3": ">=9", + "mysql2": ">=3", + "pg": ">=8", + "postgres": ">=3" + }, + "peerDependenciesMeta": { + "@prisma/client": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + } + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/checkbox/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/confirm": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz", + "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/core/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/password/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/prompts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.5.0.tgz", + "integrity": "sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^2.5.0", + "@inquirer/confirm": "^3.2.0", + "@inquirer/editor": "^2.2.0", + "@inquirer/expand": "^2.3.0", + "@inquirer/input": "^2.3.0", + "@inquirer/number": "^1.1.0", + "@inquirer/password": "^2.2.0", + "@inquirer/rawlist": "^2.3.0", + "@inquirer/search": "^1.1.0", + "@inquirer/select": "^2.5.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/select/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@snaplet/seed/node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@snaplet/seed/node_modules/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@snaplet/seed/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@snaplet/seed/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@snaplet/seed/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@snaplet/seed/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@snaplet/seed/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@snaplet/seed/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@snaplet/seed/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.69.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", + "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" } }, "node_modules/@supabase/postgrest-js": { @@ -3803,6 +4807,13 @@ "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" } }, + "node_modules/@total-typescript/ts-reset": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.5.1.tgz", + "integrity": "sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", @@ -4008,6 +5019,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.19.80", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz", @@ -4017,6 +5038,17 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/node-forge": { "version": "1.3.11", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", @@ -4027,6 +5059,18 @@ "@types/node": "*" } }, + "node_modules/@types/pg": { + "version": "8.15.2", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.2.tgz", + "integrity": "sha512-+BKxo5mM6+/A1soSHBI7ufUglqYXntChLDyTbvcAn1Lawi9J7J9Ok3jt6w7I0+T/UDJ4CyhHk66+GZbwmkYxSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, "node_modules/@types/phoenix": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", @@ -4097,6 +5141,27 @@ "@types/node": "*" } }, + "node_modules/@types/urijs": { + "version": "1.19.25", + "resolved": "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz", + "integrity": "sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", @@ -4358,6 +5423,19 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4445,6 +5523,19 @@ "node": ">= 14" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -4603,12 +5694,45 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-differ": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-4.0.0.tgz", + "integrity": "sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -4723,6 +5847,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==", + "dev": true + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -4791,23 +5921,6 @@ "node": "*" } }, - "node_modules/bin-links": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz", - "integrity": "sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA==", - "dev": true, - "license": "ISC", - "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" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -4912,6 +6025,13 @@ "node": ">=8" } }, + "node_modules/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "dev": true, + "license": "MIT" + }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -4982,24 +6102,117 @@ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, - "license": "MIT", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/c12/-/c12-1.11.2.tgz", + "integrity": "sha512-oBs8a4uvSDO9dm8b7OCFW7+dgtVrwmwnrVXYzLm43ta7ep2jCn/0MhoUFygIWtxhyy6+/MG7/agvpY0U1Iemew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.6.0", + "confbox": "^0.1.7", + "defu": "^6.1.4", + "dotenv": "^16.4.5", + "giget": "^1.2.3", + "jiti": "^1.21.6", + "mlly": "^1.7.1", + "ohash": "^1.1.3", + "pathe": "^1.1.2", + "perfect-debounce": "^1.0.0", + "pkg-types": "^1.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.4" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/c12/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", "dependencies": { - "run-applescript": "^7.0.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=18" + "node": ">= 6" + } + }, + "node_modules/c12/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/c12/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">= 0.8" + "node": ">=8.10.0" } }, "node_modules/cacache": { @@ -5173,6 +6386,19 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -5220,6 +6446,13 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -5227,6 +6460,16 @@ "dev": true, "license": "MIT" }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -5263,6 +6506,16 @@ "node": ">=6.0" } }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -5445,15 +6698,12 @@ "node": ">=0.10.0" } }, - "node_modules/cmd-shim": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz", - "integrity": "sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw==", + "node_modules/collection-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collection-utils/-/collection-utils-1.0.1.tgz", + "integrity": "sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==", "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } + "license": "Apache-2.0" }, "node_modules/color-convert": { "version": "2.0.1", @@ -5480,6 +6730,19 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -5562,6 +6825,13 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -5657,6 +6927,16 @@ "node": ">= 0.6" } }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -5800,6 +7080,37 @@ } } }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5829,6 +7140,16 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/css-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", @@ -5999,6 +7320,48 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/default-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", @@ -6055,6 +7418,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6064,6 +7444,13 @@ "node": ">= 0.8" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -6105,6 +7492,24 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "license": "Apache-2.0" }, + "node_modules/digest-fetch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-1.3.0.tgz", + "integrity": "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==", + "dev": true, + "license": "ISC", + "dependencies": { + "base-64": "^0.1.0", + "md5": "^2.3.0" + } + }, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", + "dev": true, + "license": "MIT" + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -6196,6 +7601,19 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -6223,6 +7641,16 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, + "node_modules/ebnf": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ebnf/-/ebnf-1.9.1.tgz", + "integrity": "sha512-uW2UKSsuty9ANJ3YByIQE4ANkD8nqUPO7r6Fwcc1ADKPe9FRdcPpMl3VEput4JSvKBJ4J86npIC2MLP0pYkCuw==", + "dev": true, + "license": "MIT", + "bin": { + "ebnf": "dist/bin.js" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6493,6 +7921,22 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", @@ -6642,6 +8086,16 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -6659,6 +8113,72 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exit-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-4.0.0.tgz", + "integrity": "sha512-Fqs7ChZm72y40wKjOFXBKg7nJZvQJmewP5/7LtePDdnah/+FH9Hp5sgMujSCMPXlxOAW2//1jrW9pnsY7o20vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/exponential-backoff": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", @@ -6749,6 +8269,13 @@ "node": ">=4" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -6790,6 +8317,13 @@ "node": ">= 6" } }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-uri": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", @@ -6869,6 +8403,19 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fictional": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fictional/-/fictional-3.0.1.tgz", + "integrity": "sha512-0JUaxdMWoyj/Udv4ourUzXgfu7fVdanGGYLFz5aWecPhhKpxoBBBxsfNowOKH4cuYgEFia65OY3spsYKkHe2ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "decimal.js": "^10.4.3", + "fast-json-stable-stringify": "^2.1.0", + "fnv-plus": "^1.3.1", + "siphash": "^1.1.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6965,6 +8512,13 @@ "dev": true, "license": "ISC" }, + "node_modules/fnv-plus": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz", + "integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw==", + "dev": true, + "license": "MIT" + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -7002,6 +8556,53 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -7174,6 +8775,19 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-tsconfig": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", @@ -7187,6 +8801,32 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz", + "integrity": "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.5.4", + "pathe": "^2.0.3", + "tar": "^6.2.1" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/giget/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -7278,6 +8918,45 @@ "dev": true, "license": "ISC" }, + "node_modules/groq-sdk": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/groq-sdk/-/groq-sdk-0.3.3.tgz", + "integrity": "sha512-wdOeZ2QymPjjP3tmFpUAnfMisoLbt7xF2MfpROeFAngcqWbfTyB9j9pMWSEAMF/E4gZx8f2Y+5zswO0q92CSxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "digest-fetch": "^1.3.0", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/groq-sdk/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -7551,6 +9230,26 @@ "node": ">= 14" } }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/hyperdyperid": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", @@ -7811,6 +9510,16 @@ "node": ">=0.8.19" } }, + "node_modules/inflection": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.2.tgz", + "integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -7891,6 +9600,13 @@ "node": ">=8" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -8049,6 +9765,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -8062,6 +9791,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, "node_modules/is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", @@ -8224,6 +9960,13 @@ "dev": true, "license": "MIT" }, + "node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -8264,6 +10007,23 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/js-tiktoken": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz", + "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.5.1" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8314,6 +10074,22 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/json-schema-library": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/json-schema-library/-/json-schema-library-9.3.5.tgz", + "integrity": "sha512-5eBDx7cbfs+RjylsVO+N36b0GOPtv78rfqgf2uON+uaHUIC62h63Y8pkV2ovKbaL4ZpQcHp21968x5nx/dFwqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sagold/json-pointer": "^5.1.2", + "@sagold/json-query": "^6.1.3", + "deepmerge": "^4.3.1", + "fast-copy": "^3.0.2", + "fast-deep-equal": "^3.1.3", + "smtp-address-parser": "1.0.10", + "valid-url": "^1.0.9" + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -8721,6 +10497,16 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/launch-editor": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", @@ -9238,6 +11024,18 @@ "node": ">= 0.4" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -9660,6 +11458,33 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/moo": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", + "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -9724,6 +11549,60 @@ "multicast-dns": "cli.js" } }, + "node_modules/multimatch": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-7.0.0.tgz", + "integrity": "sha512-SYU3HBAdF4psHEL/+jXDKHO95/m5P2RvboHT2Y0WtTttvJLP4H/2WS9WlQPFvF6C8d6SpLw8vjCnQOnVIVOSJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-differ": "^4.0.0", + "array-union": "^3.0.1", + "minimatch": "^9.0.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/multimatch/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", @@ -9763,6 +11642,36 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nearley": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz", + "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^2.19.0", + "moo": "^0.5.0", + "railroad-diagrams": "^1.0.0", + "randexp": "0.4.6" + }, + "bin": { + "nearley-railroad": "bin/nearley-railroad.js", + "nearley-test": "bin/nearley-test.js", + "nearley-unparse": "bin/nearley-unparse.js", + "nearleyc": "bin/nearleyc.js" + }, + "funding": { + "type": "individual", + "url": "https://nearley.js.org/#give-to-nearley" + } + }, + "node_modules/nearley/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/needle": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", @@ -9888,6 +11797,13 @@ "url": "https://opencollective.com/node-fetch" } }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "dev": true, + "license": "MIT" + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -10369,6 +12285,35 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", "inBundle": true, @@ -12767,6 +14712,34 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nypm": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz", + "integrity": "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "tinyexec": "^0.3.2", + "ufo": "^1.5.4" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/nypm/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12804,6 +14777,13 @@ "dev": true, "license": "MIT" }, + "node_modules/ohash": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.6.tgz", + "integrity": "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==", + "dev": true, + "license": "MIT" + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -12993,7 +14973,17 @@ "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.10.0" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, "node_modules/p-locate": { @@ -13054,6 +15044,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-retry": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", @@ -13072,6 +15079,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -13120,6 +15140,13 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parchment": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", @@ -13301,6 +15328,189 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.0.tgz", + "integrity": "sha512-7SKfdvP8CTNXjMUzfcVTaI+TDzBEeaUnVwiVGZQD1Hh33Kpev7liQba9uLd4CfN8r9mCVsD0JIpq03+Unpz+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.0", + "pg-pool": "^3.10.0", + "pg-protocol": "^1.10.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.5" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.5.tgz", + "integrity": "sha512-OOX22Vt0vOSRrdoUPKJ8Wi2OpE/o/h9T8X1s4qSkCedbNah9ei2W2765be8iMVxQUsvgT7zIAT2eIa9fs5+vtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", + "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-pool": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.0.tgz", + "integrity": "sha512-DzZ26On4sQ0KmqnO34muPcmKbhrjmyiO4lCCR0VwEd7MjmiKf5NTg/6+apUEu0NF7ESa37CGzFxH513CoUmWnA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.0.tgz", + "integrity": "sha512-IpdytjudNuLv8nhlHs/UrVBhU0e78J0oIS/0AVdTbWxSOkFUVdsHC/NrorO6nXsQNDTT1kzDSOMJubBQviX18Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pg/node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -13364,6 +15574,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -13623,6 +15862,56 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "dev": true, + "license": "MIT" + }, "node_modules/prettier": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", @@ -13649,6 +15938,16 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -13680,6 +15979,30 @@ "node": ">= 4" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -13753,6 +16076,72 @@ ], "license": "MIT" }, + "node_modules/quicktype-core": { + "version": "23.0.149", + "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.0.149.tgz", + "integrity": "sha512-P6orZe46XwDcl17MdJc1SLgAornP3XzEHYE25vhS2DWG5t0mszS9oSS5BiFir/XnBv2Ak0P70Zz5m7C2WhLjWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@glideapps/ts-necessities": "2.2.3", + "@types/urijs": "^1.19.25", + "browser-or-node": "^2.1.1", + "collection-utils": "^1.0.1", + "cross-fetch": "^4.0.0", + "is-url": "^1.2.4", + "js-base64": "^3.7.7", + "lodash": "^4.17.21", + "pako": "^1.0.6", + "pluralize": "^8.0.0", + "readable-stream": "4.5.2", + "unicode-properties": "^1.4.1", + "urijs": "^1.19.1", + "wordwrap": "^1.0.0", + "yaml": "^2.4.1" + } + }, + "node_modules/quicktype-core/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/quicktype-core/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/quill": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz", @@ -13782,12 +16171,33 @@ "node": ">= 12.0.0" } }, - "node_modules/quill/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, + "node_modules/quill/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/railroad-diagrams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", + "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/randexp": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", + "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "discontinuous-range": "1.0.0", + "ret": "~0.1.10" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13822,6 +16232,17 @@ "node": ">= 0.8" } }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -13831,16 +16252,6 @@ "pify": "^2.3.0" } }, - "node_modules/read-cmd-shim": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz", - "integrity": "sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -13985,6 +16396,13 @@ "node": ">=6" } }, + "node_modules/remeda": { + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-1.61.0.tgz", + "integrity": "sha512-caKfSz9rDeSKBQQnlJnVW3mbVdFgxgGWQKq1XlFokqjf+hQD5gxutLGTTY2A/x24UxVyJe9gH5fAkFI63ULw4A==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -14134,6 +16552,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -14774,6 +17202,13 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/siphash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/siphash/-/siphash-1.2.0.tgz", + "integrity": "sha512-zGo/O5A0Nr4oSteEAMlhemqQpCBbVTRaTjUQdO+QFUqe1iofq/NNPe2W1RxJreh89fIk6NhQcNi41UeTGCvr+g==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -14789,6 +17224,13 @@ "node": ">= 10" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -14843,6 +17285,19 @@ "npm": ">= 3.0.0" } }, + "node_modules/smtp-address-parser": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/smtp-address-parser/-/smtp-address-parser-1.0.10.tgz", + "integrity": "sha512-Osg9LmvGeAG/hyao4mldbflLOkkr3a+h4m1lwKCK5U8M6ZAr7tdXEz/+/vr752TSGE4MNUlUl9cIK2cB8cgzXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "nearley": "^2.20.1" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/socket.io": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", @@ -15135,6 +17590,16 @@ "wbuf": "^1.7.3" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -15142,6 +17607,16 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ssri": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", @@ -15164,6 +17639,19 @@ "node": ">= 0.8" } }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -15189,6 +17677,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -15295,6 +17793,19 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -15361,88 +17872,28 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/supabase": { - "version": "2.22.6", - "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.22.6.tgz", - "integrity": "sha512-W/A5JlKqrp0pxSaFRYwlWpk+aCql9xUp1ZeLVarRVtqsT6QPLqLsLPIwES0005lQKS3QBbdWDgYThEStPM1kxQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bin-links": "^5.0.0", - "https-proxy-agent": "^7.0.2", - "node-fetch": "^3.3.2", - "tar": "7.4.3" - }, - "bin": { - "supabase": "bin/supabase" - }, - "engines": { - "npm": ">=8" - } - }, - "node_modules/supabase/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/supabase/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supabase/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" - } - }, - "node_modules/supabase/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" }, "engines": { "node": ">=8" @@ -15691,6 +18142,52 @@ "dev": true, "license": "ISC" }, + "node_modules/terminal-link": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-3.0.0.tgz", + "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^5.0.0", + "supports-hyperlinks": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -15793,6 +18290,20 @@ "dev": true, "license": "MIT" }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -15991,6 +18502,13 @@ "node": "*" } }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -16031,6 +18549,17 @@ "node": ">=4" } }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", @@ -16041,6 +18570,24 @@ "node": ">=4" } }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" + }, "node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", @@ -16130,6 +18677,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16145,6 +18699,26 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==", + "dev": true + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -16829,6 +19403,13 @@ "dev": true, "license": "MIT" }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -16974,20 +19555,6 @@ "dev": true, "license": "ISC" }, - "node_modules/write-file-atomic": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", - "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/ws": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", @@ -17019,6 +19586,16 @@ "node": ">= 6" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -17145,6 +19722,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.25.28", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.28.tgz", + "integrity": "sha512-/nt/67WYKnr5by3YS7LroZJbtcCBurDKKPBPWWzaxvVCGuG/NOsiKkrjoOhI8mJ+SQUXEbUzeB3S+6XDUEEj7Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zone.js": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", diff --git a/package.json b/package.json index 7e18238..2207252 100755 --- a/package.json +++ b/package.json @@ -10,11 +10,11 @@ "serve:ssr:AngularBlogApp": "node dist/angular-blog-app/server/server.mjs", "build:stats": "ng build --stats-json", "analyze": "webpack-bundle-analyzer dist/angular-blog-app/stats.json", - "setup:local-supabase": "bash scripts/setup-local-supabase.sh", - "setup:local-supabase:win": "powershell -ExecutionPolicy Bypass -File scripts/setup-local-supabase.ps1", - "sync:cloud-supabase": "bash scripts/sync-from-cloud-supabase.sh", - "sync:cloud-supabase:win": "powershell -ExecutionPolicy Bypass -File scripts/sync-from-cloud-supabase.ps1", - "start:local": "ng serve --configuration=local" + "start:local": "ng serve --configuration=local", + "schema:pull": "find supabase/migrations -name '*_remote_schema.sql' -delete && supabase db pull --db-url $PG_EXPORT_URL", + "db:createSeed": "scripts/create-seed.sh", + "db:seed": "npx @snaplet/seed init", + "users:passwords": "scripts/set-passwords.sh" }, "private": true, "dependencies": { @@ -47,10 +47,13 @@ "@angular-devkit/build-angular": "^19.2.1", "@angular/cli": "^19.2.1", "@angular/compiler-cli": "^19.2.1", + "@snaplet/copycat": "^6.0.0", + "@snaplet/seed": "^0.98.0", "@types/compression": "^1.7.5", "@types/express": "^4.17.17", "@types/jasmine": "~5.1.0", "@types/node": "^18.18.0", + "@types/pg": "^8.15.2", "autoprefixer": "^10.4.19", "daisyui": "^4.12.10", "jasmine-core": "~5.1.0", @@ -59,9 +62,9 @@ "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "pg": "^8.16.0", "postcss": "^8.4.38", "prettier": "3.4.2", - "supabase": "^2.22.6", "tailwindcss": "^3.4.4", "tsx": "^4.19.4", "typescript": "~5.7.3", diff --git a/reset-password.js b/scripts/reset-password.js similarity index 100% rename from reset-password.js rename to scripts/reset-password.js diff --git a/reset-password.ts b/scripts/reset-password.ts similarity index 100% rename from reset-password.ts rename to scripts/reset-password.ts diff --git a/scripts/set-passwords.sh b/scripts/set-passwords.sh new file mode 100755 index 0000000..5893043 --- /dev/null +++ b/scripts/set-passwords.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail +# ───────────────────────────────────────── +# Sets passwords + confirms two EXISTING +# Auth users in the local Supabase stack. +# • user@example.com → user123 +# • admin@example.com → admin123 + role Admin +# Requires: curl, jq +# ───────────────────────────────────────── +SERVICE_ROLE="super-secret-jwt-token-with-at-least-32-characters-long" +BASE="http://localhost:54321/auth/v1/admin" +HDR=(-H "apikey: $SERVICE_ROLE" -H "Authorization: Bearer $SERVICE_ROLE" -H "Content-Type: application/json") + +get_id () { # $1=email + curl -sS "${HDR[@]:0:2}" "$BASE/users?email=$1" \ + | jq -r 'if type=="array" then (.[0].id // empty) else (.id // empty) end' +} + +create_user () { # $1=email $2=password $3=role $4=phone + local body + body=$(jq -n --arg em "$1" --arg pw "$2" --arg role "$3" --arg ph "$4" ' + { + email: $em, + password: $pw, + email_confirm: true + } + + ( ($role != "") | if . then {app_metadata:{role:$role}} else {} end ) + + ( ($ph != "") | if . then {phone:$ph, phone_confirm:true} else {} end ) + ') + curl -sS -X POST "${HDR[@]}" "$BASE/users" -d "$body" | jq -r '.id // empty' +} + +ensure_user () { # $1=email $2=password $3=role $4=phone + echo " → searching $1" + local id + id=$(get_id "$1") + if [ -z "$id" ]; then + echo " → not found; will create" + echo "▶ creating $1 …" + id=$(create_user "$1" "$2" "$3" "$4") + echo " → created with ID $id" + else + echo "▶ updating $1 password …" + echo " → current ID $id" + local patch + patch=$(jq -n --arg pw "$2" --arg role "$3" --arg ph "$4" ' + { + password: $pw, + email_confirm: true + } + + ( ($role != "") | if . then {app_metadata:{role:$role}} else {} end ) + + ( ($ph != "") | if . then {phone:$ph, phone_confirm:true} else {} end ) + ') + curl -sS -X PATCH "${HDR[@]}" "$BASE/users/$id" -d "$patch" >/dev/null + fi + echo " → finished for $1" +} + +echo "▶ processing user@example.com and admin@example.com ..." + +ensure_user "user@example.com" "user123" "" "" +ensure_user "admin@example.com" "admin123" "Admin" "999666999" + +echo "✅ passwords set & accounts confirmed (user123 / admin123)" diff --git a/scripts/setup-local-supabase.ps1 b/scripts/setup-local-supabase.ps1 deleted file mode 100644 index cfd5d60..0000000 --- a/scripts/setup-local-supabase.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -# PowerShell script for setting up local Supabase instance for Angular Blog App -# Windows-compatible version of setup-local-supabase.sh -# -# Note: If running this script directly (not through npm), you may need to change the execution policy: -# Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force - -# Stop on error -$ErrorActionPreference = "Stop" - -Write-Host "Setting up local Supabase instance for Angular Blog App..." - -# Check if Supabase CLI is installed -try { - $null = Get-Command supabase -ErrorAction Stop - Write-Host "Supabase CLI is already installed." -} catch { - Write-Host "Supabase CLI is not installed. Installing..." - npm install -g supabase -} - -# Check if Docker is running -try { - $null = docker info -} catch { - Write-Host "Docker is not running. Please start Docker and try again." - exit 1 -} - -# Initialize Supabase if not already initialized -if (-not (Test-Path "supabase\.branches")) { - Write-Host "Initializing Supabase..." - supabase init -} - -# Start Supabase -Write-Host "Starting Supabase..." -supabase start - -# Wait for Supabase to be ready -Write-Host "Waiting for Supabase to be ready..." -Start-Sleep -Seconds 5 - -# Apply schema -Write-Host "Applying database schema..." -supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres - -# Create a local environment file for development -if (-not (Test-Path "src\environments\environment.local.ts")) { - Write-Host "Creating local environment file..." - $envContent = @" -export const environment = { - production: false, - supabaseUrl: 'http://localhost:54321', - supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', -}; -"@ - Set-Content -Path "src\environments\environment.local.ts" -Value $envContent -} - -# Print success message -Write-Host "Local Supabase instance is now running!" -Write-Host "Admin user credentials:" -Write-Host " Email: admin@example.com" -Write-Host " Password: admin123" -Write-Host "" -Write-Host "Supabase Studio: http://localhost:54323" -Write-Host "API URL: http://localhost:54321" -Write-Host "" -Write-Host "To use the local Supabase instance in your Angular app, run:" -Write-Host " ng serve --configuration=local" -Write-Host "" -Write-Host "To stop Supabase, run:" -Write-Host " supabase stop" diff --git a/scripts/setup-local-supabase.sh b/scripts/setup-local-supabase.sh deleted file mode 100755 index f6e2260..0000000 --- a/scripts/setup-local-supabase.sh +++ /dev/null @@ -1,67 +0,0 @@ - -#!/bin/bash - -# Exit on error -set -e - -echo "Setting up local Supabase instance for Angular Blog App..." - -# Check if Supabase CLI is installed -if ! command -v supabase &> /dev/null; then - echo "Supabase CLI is not installed. Installing..." - npm install -g supabase -fi - -# Check if Docker is running -if ! docker info &> /dev/null; then - echo "Docker is not running. Please start Docker and try again." - exit 1 -fi - -# Initialize Supabase if not already initialized -if [ ! -f "supabase/.branches" ]; then - echo "Initializing Supabase..." - supabase init -fi - -# Start Supabase -echo "Starting Supabase..." -supabase start - -# Wait for Supabase to be ready -echo "Waiting for Supabase to be ready..." -sleep 5 - -# Apply schema -echo "Applying database schema..." -supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres - -# Create a local environment file for development -if [ ! -f "src/environments/environment.local.ts" ]; then - echo "Creating local environment file..." - cat > src/environments/environment.local.ts << EOL -export const environment = { - production: false, - supabaseUrl: 'http://localhost:54321', - supabaseKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0', -}; -EOL -fi - -# Print success message -echo "Local Supabase instance is now running!" -echo "Admin user credentials:" -echo " Email: admin@example.com" -echo " Password: admin123" -echo "" -echo "Supabase Studio: http://localhost:54323" -echo "API URL: http://localhost:54321" -echo "" -echo "To use the local Supabase instance in your Angular app, run:" -echo " ng serve --configuration=local" -echo "" -echo "To stop Supabase, run:" -echo " supabase stop" - -# Make the script executable -chmod +x scripts/setup-local-supabase.sh diff --git a/scripts/sync-from-cloud-supabase.ps1 b/scripts/sync-from-cloud-supabase.ps1 deleted file mode 100644 index 04fe331..0000000 --- a/scripts/sync-from-cloud-supabase.ps1 +++ /dev/null @@ -1,93 +0,0 @@ -# PowerShell script for syncing from cloud Supabase instance to local Supabase -# Windows-compatible version of sync-from-cloud-supabase.sh -# -# Note: If running this script directly (not through npm), you may need to change the execution policy: -# Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force - -# Stop on error -$ErrorActionPreference = "Stop" - -Write-Host "Syncing from cloud Supabase instance to local Supabase..." - -# Check if Supabase CLI is installed -try { - $null = Get-Command supabase -ErrorAction Stop - Write-Host "Supabase CLI is already installed." -} catch { - Write-Host "Supabase CLI is not installed. Installing..." - npm install -g supabase -} - -# Check if Docker is running -try { - $null = docker info -} catch { - Write-Host "Docker is not running. Please start Docker and try again." - exit 1 -} - -# Initialize Supabase if not already initialized -if (-not (Test-Path "supabase\.branches")) { - Write-Host "Initializing Supabase..." - supabase init -} - -# Extract Supabase URL and key from environment.ts -$envContent = Get-Content "src\environments\environment.ts" -Raw -$supabaseUrlMatch = [regex]::Match($envContent, "supabaseUrl: '([^']*)'") -$supabaseKeyMatch = [regex]::Match($envContent, "supabaseKey:\s*'([^']*)'") - -if (-not $supabaseUrlMatch.Success -or -not $supabaseKeyMatch.Success) { - Write-Host "Could not extract Supabase URL or key from environment.ts" - exit 1 -} - -$SUPABASE_URL = $supabaseUrlMatch.Groups[1].Value -$SUPABASE_KEY = $supabaseKeyMatch.Groups[1].Value - -# Extract project reference from URL -$PROJECT_REF = $SUPABASE_URL.Split('/')[2].Split('.')[0] - -Write-Host "Found Supabase project reference: $PROJECT_REF" - -# Start Supabase if not already running -try { - $null = supabase status -} catch { - Write-Host "Starting Supabase..." - supabase start -} - -# Wait for Supabase to be ready -Write-Host "Waiting for Supabase to be ready..." -Start-Sleep -Seconds 5 - -# Link to the remote project -Write-Host "Linking to remote Supabase project..." -supabase link --project-ref $PROJECT_REF --password $SUPABASE_KEY - -# Pull database schema and policies -Write-Host "Pulling database schema and policies from cloud..." -supabase db pull - -# Dump data from remote database (optional, can be commented out if not needed) -Write-Host "Dumping data from remote database..." -if (-not (Test-Path "supabase\seed")) { - New-Item -Path "supabase\seed" -ItemType Directory -Force | Out-Null -} -supabase db dump -f supabase/seed/remote_data.sql - -# Apply schema and data to local instance -Write-Host "Applying schema and data to local instance..." -supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres - -# Print success message -Write-Host "Local Supabase instance is now synced with cloud!" -Write-Host "Supabase Studio: http://localhost:54323" -Write-Host "API URL: http://localhost:54321" -Write-Host "" -Write-Host "To use the local Supabase instance in your Angular app, run:" -Write-Host " ng serve --configuration=local" -Write-Host "" -Write-Host "To stop Supabase, run:" -Write-Host " supabase stop" diff --git a/scripts/sync-from-cloud-supabase.sh b/scripts/sync-from-cloud-supabase.sh deleted file mode 100755 index 1210c9e..0000000 --- a/scripts/sync-from-cloud-supabase.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# Exit on error -set -e - -echo "Syncing from cloud Supabase instance to local Supabase..." - -# Check if Supabase CLI is installed -if ! command -v supabase &> /dev/null; then - echo "Supabase CLI is not installed. Installing..." - npm install -g supabase -fi - -# Check if Docker is running -if ! docker info &> /dev/null; then - echo "Docker is not running. Please start Docker and try again." - exit 1 -fi - -# Initialize Supabase if not already initialized -if [ ! -f "supabase/.branches" ]; then - echo "Initializing Supabase..." - supabase init -fi - -# Extract Supabase URL and key from environment.ts -SUPABASE_URL=$(grep -o "supabaseUrl: '[^']*'" src/environments/environment.ts | cut -d "'" -f 2) -SUPABASE_KEY=$(grep -o "supabaseKey: '[^']*'" src/environments/environment.ts | sed "s/supabaseKey: '//" | sed "s/',//") - -if [ -z "$SUPABASE_URL" ] || [ -z "$SUPABASE_KEY" ]; then - echo "Could not extract Supabase URL or key from environment.ts" - exit 1 -fi - -# Extract project reference from URL -PROJECT_REF=$(echo $SUPABASE_URL | cut -d '/' -f 3 | cut -d '.' -f 1) - -echo "Found Supabase project reference: $PROJECT_REF" - -# Start Supabase if not already running -if ! supabase status &> /dev/null; then - echo "Starting Supabase..." - supabase start -fi - -# Wait for Supabase to be ready -echo "Waiting for Supabase to be ready..." -sleep 5 - -# Link to the remote project -echo "Linking to remote Supabase project..." -supabase link --project-ref $PROJECT_REF --password $SUPABASE_KEY - -# Pull database schema and policies -echo "Pulling database schema and policies from cloud..." -supabase db pull - -# Dump data from remote database (optional, can be commented out if not needed) -echo "Dumping data from remote database..." -mkdir -p supabase/seed -supabase db dump -f supabase/seed/remote_data.sql - -# Apply schema and data to local instance -echo "Applying schema and data to local instance..." -supabase db reset --db-url postgresql://postgres:postgres@localhost:54322/postgres - -# Print success message -echo "Local Supabase instance is now synced with cloud!" -echo "Supabase Studio: http://localhost:54323" -echo "API URL: http://localhost:54321" -echo "" -echo "To use the local Supabase instance in your Angular app, run:" -echo " ng serve --configuration=local" -echo "" -echo "To stop Supabase, run:" -echo " supabase stop" - -# Make the script executable -chmod +x scripts/sync-from-cloud-supabase.sh diff --git a/src/app/admin/_components/add-post/add-post.component.ts b/src/app/admin/_components/add-post/add-post.component.ts index 8e16517..915875c 100755 --- a/src/app/admin/_components/add-post/add-post.component.ts +++ b/src/app/admin/_components/add-post/add-post.component.ts @@ -92,6 +92,13 @@ export class AddPostComponent implements OnInit { onSubmit(isDraft = false): void { this.highlightContent(); + //test for description + if (!this.blogForm?.controls?.description?.value) { + this.blogForm.controls?.description?.setValue( + this.blogForm.controls.content.value.toString().substring(0, 150), + ); + } + if (this.blogForm.valid) { const rawContent = this.blogForm.controls.content.value as string; const cleanedContent = rawContent.replace(/( |\u00A0)/g, ' '); diff --git a/src/app/admin/_models/post-from.inteface.ts b/src/app/admin/_models/post-from.inteface.ts index 518f012..790037a 100644 --- a/src/app/admin/_models/post-from.inteface.ts +++ b/src/app/admin/_models/post-from.inteface.ts @@ -7,4 +7,5 @@ export interface PostForm { is_draft: FormControl; created_at: FormControl; description?: FormControl; + tags: FormControl; } diff --git a/src/app/admin/_services/admin-api.service.ts b/src/app/admin/_services/admin-api.service.ts index 3a3c5af..a3972c5 100755 --- a/src/app/admin/_services/admin-api.service.ts +++ b/src/app/admin/_services/admin-api.service.ts @@ -1,18 +1,16 @@ import { inject, Injectable } from '@angular/core'; - import { map, Observable } from 'rxjs'; import { Post } from '../../supabase-types'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { SupabaseService } from '../../services/supabase.service'; +import { environment } from '../../../environments/environment.local'; @Injectable() export class AdminApiService { http = inject(HttpClient); supabaseService = inject(SupabaseService); - private readonly baseUrl = - 'https://aqdbdmepncxxuanlymwr.supabase.co/rest/v1/'; - private readonly apiKey = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFxZGJkbWVwbmN4eHVhbmx5bXdyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDUwNTA0MjYsImV4cCI6MjA2MDYyNjQyNn0.RNtZZ4Of4LIP3XuS9lumHYdjRLVUGXARtAxaTJmF7lc'; + private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`; + private readonly apiKey = environment.supabaseKey; async addPost(post: Post): Promise { const { error } = await this.supabaseService.getClient diff --git a/src/app/reader/_components/main-page/post/post.component.ts b/src/app/reader/_components/main-page/post/post.component.ts index cfbd241..69da7ee 100755 --- a/src/app/reader/_components/main-page/post/post.component.ts +++ b/src/app/reader/_components/main-page/post/post.component.ts @@ -35,7 +35,7 @@ export class PostComponent implements OnInit { postStore = inject(PostStore); post: Signal = this.postStore.post; - date: Signal = this.postStore.date; + date: Signal = this.postStore.formattedDate; private dialogService = inject(DynamicDialogService); private viewContainerRef = inject(ViewContainerRef); diff --git a/src/app/reader/_components/main-page/post/post.store.ts b/src/app/reader/_components/main-page/post/post.store.ts index e1c9a3c..52cf665 100644 --- a/src/app/reader/_components/main-page/post/post.store.ts +++ b/src/app/reader/_components/main-page/post/post.store.ts @@ -33,7 +33,9 @@ export const PostStore = signalStore( { providedIn: 'root' }, withState(initialState), withComputed((store) => ({ - date: computed(() => formatDateToDDMMYYYY(store.post()?.created_at)), + formattedDate: computed(() => + formatDateToDDMMYYYY(store.post()?.created_at), + ), })), withMethods((store, postService = inject(ReaderApiService)) => ({ getPost: rxMethod( diff --git a/src/app/reader/_services/reader-api.service.ts b/src/app/reader/_services/reader-api.service.ts index 393faf5..78adc4a 100755 --- a/src/app/reader/_services/reader-api.service.ts +++ b/src/app/reader/_services/reader-api.service.ts @@ -2,16 +2,15 @@ import { inject, Injectable } from '@angular/core'; import { SupabaseService } from '../../services/supabase.service'; import { Comment, Post, Profile, Tag, PostTag } from '../../types/supabase'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { map, Observable, tap } from 'rxjs'; +import { map, Observable } from 'rxjs'; +import { environment } from '../../../environments/environment'; @Injectable({ providedIn: 'root' }) export class ReaderApiService { supabaseService = inject(SupabaseService); http = inject(HttpClient); - private readonly baseUrl = - 'https://aqdbdmepncxxuanlymwr.supabase.co/rest/v1/'; - private readonly apiKey = - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFxZGJkbWVwbmN4eHVhbmx5bXdyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDUwNTA0MjYsImV4cCI6MjA2MDYyNjQyNn0.RNtZZ4Of4LIP3XuS9lumHYdjRLVUGXARtAxaTJmF7lc'; + private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`; + private readonly apiKey = environment.supabaseKey; getPost(id: string): Observable { const selectQuery = ` diff --git a/supabase/config.tmp b/supabase/config.tmp new file mode 100644 index 0000000..e69de29 diff --git a/supabase/config.toml b/supabase/config.toml index 155c40d..0de1cee 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -1,53 +1,151 @@ -# A string used to distinguish different Supabase projects on the same host. Defaults to the working -# directory name when running `supabase init`. -project_id = "angular-blog-app" +# 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 = "AngularBlogApp" [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 and storage are always included. -schemas = ["public", "storage", "auth"] -# Extra schemas to add to the search_path of every request. public is always included. +# endpoints. `public` and `graphql_public` schemas are included by default. +schemas = ["public", "graphql_public"] +# Extra schemas to add to the search_path of every request. 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] +# Enable HTTPS endpoints locally using a self-signed certificate. +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.vault] +# secret_key = "env(SECRET_VALUE)" + +[db.migrations] +# Specifies an ordered list of schema files that describe your database. +# Supports glob patterns relative to supabase directory: "./schemas/*.sql" +schema_paths = [] + +[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: "./seeds/*.sql" +sql_paths = ["./seed/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 -smtp_port = 54325 -pop3_port = 54326 +# 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" +# Image transformation API is available to Supabase Pro plan. +# [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://localhost:4200" +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://localhost:4200"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (1 -# week). +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.rate_limit] +# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled. +email_sent = 2 +# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled. +sms_sent = 30 +# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true. +anonymous_users = 30 +# Number of sessions that can be refreshed in a 5 minute interval per IP address. +token_refresh = 150 +# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users). +sign_in_sign_ups = 30 +# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address. +token_verifications = 30 + +# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`. +# [auth.captcha] +# enabled = true +# provider = "hcaptcha" +# secret = "" [auth.email] # Allow/disallow new user signups via email to your project. @@ -57,16 +155,153 @@ enable_signup = true 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 an external OAuth provider. The full list of providers are: "apple", "azure", "bitbucket", -# "discord", "facebook", "github", "gitlab", "google", "keycloak", "linkedin", "notion", "twitch", -# "twitter", "slack", "spotify", "workos", "zoom". -[auth.external.google] -enabled = true -client_id = "env(SUPABASE_AUTH_GOOGLE_CLIENT_ID)" -secret = "env(SUPABASE_AUTH_GOOGLE_SECRET)" +# Use a production-ready SMTP server +# [auth.email.smtp] +# enabled = true +# 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)" + +# Multi-factor-authentication is available to Supabase Pro plan. +[auth.mfa] +# Control how many MFA factors can be enrolled at once per user. +max_enrolled_factors = 10 + +# Control MFA via App Authenticator (TOTP) +[auth.mfa.totp] +enroll_enabled = false +verify_enabled = false + +# Configure MFA via Phone Messaging +[auth.mfa.phone] +enroll_enabled = false +verify_enabled = false +otp_length = 6 +template = "Your code is {{ .Code }}" +max_frequency = "5s" + +# Configure MFA 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" + +# Use Clerk as a third-party provider alongside Supabase Auth. +[auth.third_party.clerk] +enabled = false +# Obtain from https://clerk.com/setup/supabase +# domain = "example.clerk.accounts.dev" + +[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 +# The Deno major version to use. + +# [edge_runtime.secrets] +# secret_key = "env(SECRET_VALUE)" + +[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/supabase/migrations/schema.sql b/supabase/migrations/schema.sql deleted file mode 100644 index 46f36e1..0000000 --- a/supabase/migrations/schema.sql +++ /dev/null @@ -1,144 +0,0 @@ --- Create tables for the blog application - --- Enable RLS (Row Level Security) -ALTER DATABASE postgres SET "app.settings.jwt_secret" TO 'super-secret-jwt-token-with-at-least-32-characters-long'; - --- Create profiles table -CREATE TABLE IF NOT EXISTS public.profiles ( - id UUID PRIMARY KEY REFERENCES auth.users ON DELETE CASCADE, - username TEXT NOT NULL, - avatar_url TEXT, - created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -); - --- Create posts table -CREATE TABLE IF NOT EXISTS public.posts ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - title TEXT NOT NULL, - description TEXT NOT NULL, - content TEXT NOT NULL, - is_draft BOOLEAN DEFAULT true, - user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), - category TEXT, - updated TIMESTAMP WITH TIME ZONE, - reading_time INTEGER -); - --- Create tags table -CREATE TABLE IF NOT EXISTS public.tags ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name TEXT NOT NULL UNIQUE -); - --- Create post_tags table (junction table for many-to-many relationship) -CREATE TABLE IF NOT EXISTS public.post_tags ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - post_id UUID NOT NULL REFERENCES public.posts(id) ON DELETE CASCADE, - tag_id UUID NOT NULL REFERENCES public.tags(id) ON DELETE CASCADE, - UNIQUE(post_id, tag_id) -); - --- Create comments table -CREATE TABLE IF NOT EXISTS public.comments ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - content TEXT NOT NULL, - user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE, - post_id UUID NOT NULL REFERENCES public.posts(id) ON DELETE CASCADE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT now() -); - --- Set up Row Level Security (RLS) policies --- Profiles table policies -ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Public profiles are viewable by everyone" - ON public.profiles FOR SELECT - USING (true); - -CREATE POLICY "Users can insert their own profile" - ON public.profiles FOR INSERT - WITH CHECK (auth.uid() = id); - -CREATE POLICY "Users can update their own profile" - ON public.profiles FOR UPDATE - USING (auth.uid() = id); - --- Posts table policies -ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Public posts are viewable by everyone" - ON public.posts FOR SELECT - USING (NOT is_draft OR auth.uid() = user_id); - -CREATE POLICY "Users can insert their own posts" - ON public.posts FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own posts" - ON public.posts FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own posts" - ON public.posts FOR DELETE - USING (auth.uid() = user_id); - --- Comments table policies -ALTER TABLE public.comments ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Public comments are viewable by everyone" - ON public.comments FOR SELECT - USING (true); - -CREATE POLICY "Users can insert their own comments" - ON public.comments FOR INSERT - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update their own comments" - ON public.comments FOR UPDATE - USING (auth.uid() = user_id); - -CREATE POLICY "Users can delete their own comments" - ON public.comments FOR DELETE - USING (auth.uid() = user_id); - --- Tags table policies -ALTER TABLE public.tags ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Tags are viewable by everyone" - ON public.tags FOR SELECT - USING (true); - -CREATE POLICY "Only authenticated users can insert tags" - ON public.tags FOR INSERT - WITH CHECK (auth.role() = 'authenticated'); - --- Post_tags table policies -ALTER TABLE public.post_tags ENABLE ROW LEVEL SECURITY; - -CREATE POLICY "Post tags are viewable by everyone" - ON public.post_tags FOR SELECT - USING (true); - -CREATE POLICY "Only post owners can add tags to posts" - ON public.post_tags FOR INSERT - WITH CHECK ( - EXISTS ( - SELECT 1 FROM public.posts - WHERE id = post_id AND user_id = auth.uid() - ) - ); - --- Create functions and triggers -CREATE OR REPLACE FUNCTION public.handle_new_user() -RETURNS TRIGGER AS $$ -BEGIN - INSERT INTO public.profiles (id, username, avatar_url) - VALUES (new.id, new.email, 'https://www.gravatar.com/avatar/' || md5(lower(trim(new.email))) || '?d=mp'); - RETURN new; -END; -$$ LANGUAGE plpgsql SECURITY DEFINER; - -CREATE OR REPLACE TRIGGER on_auth_user_created - AFTER INSERT ON auth.users - FOR EACH ROW EXECUTE FUNCTION public.handle_new_user(); diff --git a/supabase/migrations/seed.sql b/supabase/migrations/seed.sql deleted file mode 100644 index 6be25a9..0000000 --- a/supabase/migrations/seed.sql +++ /dev/null @@ -1,26 +0,0 @@ --- Seed data for the blog application - --- Create a test admin user --- Note: In a real application, you would use a secure password -INSERT INTO auth.users (id, email, encrypted_password, email_confirmed_at, raw_app_meta_data, raw_user_meta_data) -VALUES ( - gen_random_uuid(), - 'admin@example.com', - crypt('admin123', gen_salt('bf')), - now(), - '{"provider":"email","providers":["email"],"role":"Admin"}', - '{"name":"Admin User"}' -) ON CONFLICT DO NOTHING; - --- Create some sample tags -INSERT INTO public.tags (name) VALUES - ('Angular'), - ('TypeScript'), - ('Supabase'), - ('Web Development'), - ('Frontend') -ON CONFLICT (name) DO NOTHING; - --- Note: The profiles table will be populated automatically by the trigger --- when users are created. The posts, comments, and post_tags tables --- can be populated through the application interface by the admin user. From 7a02a05fe542ec2e5992e9e33852e81f1e248a16 Mon Sep 17 00:00:00 2001 From: Luk Date: Sun, 8 Jun 2025 18:21:58 +0200 Subject: [PATCH 3/6] local supabase vol 3 --- .snaplet/config.json | 3 + .snaplet/dataModel.json | 5652 +++++++++++++++++ .vscode/settings.json | 24 + scripts/create-seed.sh | 48 + seed.config.ts | 13 + seed.ts | 25 + supabase-password-script-fix.md | 40 + supabase/.gitignore | 8 + .../20250525091356_remote_schema.sql | 587 ++ supabase/seed/seed.sql | 97 + 10 files changed, 6497 insertions(+) create mode 100644 .snaplet/config.json create mode 100644 .snaplet/dataModel.json create mode 100644 .vscode/settings.json create mode 100755 scripts/create-seed.sh create mode 100644 seed.config.ts create mode 100644 seed.ts create mode 100644 supabase-password-script-fix.md create mode 100644 supabase/.gitignore create mode 100644 supabase/migrations/20250525091356_remote_schema.sql create mode 100644 supabase/seed/seed.sql diff --git a/.snaplet/config.json b/.snaplet/config.json new file mode 100644 index 0000000..a25c325 --- /dev/null +++ b/.snaplet/config.json @@ -0,0 +1,3 @@ +{ + "adapter": "pg" +} \ No newline at end of file diff --git a/.snaplet/dataModel.json b/.snaplet/dataModel.json new file mode 100644 index 0000000..8e820f4 --- /dev/null +++ b/.snaplet/dataModel.json @@ -0,0 +1,5652 @@ +{ + "models": { + "_http_response": { + "id": "net._http_response", + "schemaName": "net", + "tableName": "_http_response", + "fields": [ + { + "id": "net._http_response.id", + "name": "id", + "columnName": "id", + "type": "int8", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.status_code", + "name": "status_code", + "columnName": "status_code", + "type": "int4", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.content_type", + "name": "content_type", + "columnName": "content_type", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.headers", + "name": "headers", + "columnName": "headers", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.content", + "name": "content", + "columnName": "content", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.timed_out", + "name": "timed_out", + "columnName": "timed_out", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.error_msg", + "name": "error_msg", + "columnName": "error_msg", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net._http_response.created", + "name": "created", + "columnName": "created", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [] + }, + "audit_log_entries": { + "id": "auth.audit_log_entries", + "schemaName": "auth", + "tableName": "audit_log_entries", + "fields": [ + { + "id": "auth.audit_log_entries.instance_id", + "name": "instance_id", + "columnName": "instance_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.audit_log_entries.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.audit_log_entries.payload", + "name": "payload", + "columnName": "payload", + "type": "json", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.audit_log_entries.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.audit_log_entries.ip_address", + "name": "ip_address", + "columnName": "ip_address", + "type": "varchar", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": 64 + } + ], + "uniqueConstraints": [ + { + "name": "audit_log_entries_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "buckets": { + "id": "storage.buckets", + "schemaName": "storage", + "tableName": "buckets", + "fields": [ + { + "id": "storage.buckets.id", + "name": "id", + "columnName": "id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "storage.buckets.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.owner", + "name": "owner", + "columnName": "owner", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.public", + "name": "public", + "columnName": "public", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.avif_autodetection", + "name": "avif_autodetection", + "columnName": "avif_autodetection", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.file_size_limit", + "name": "file_size_limit", + "columnName": "file_size_limit", + "type": "int8", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.allowed_mime_types", + "name": "allowed_mime_types", + "columnName": "allowed_mime_types", + "type": "text[]", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.buckets.owner_id", + "name": "owner_id", + "columnName": "owner_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "objects", + "type": "objects", + "isRequired": false, + "kind": "object", + "relationName": "objectsTobuckets", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "s3_multipart_uploads", + "type": "s3_multipart_uploads", + "isRequired": false, + "kind": "object", + "relationName": "s3_multipart_uploadsTobuckets", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "s3_multipart_uploads_parts", + "type": "s3_multipart_uploads_parts", + "isRequired": false, + "kind": "object", + "relationName": "s3_multipart_uploads_partsTobuckets", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "buckets_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "bname", + "fields": [ + "name" + ], + "nullNotDistinct": false + } + ] + }, + "comments": { + "id": "public.comments", + "schemaName": "public", + "tableName": "comments", + "fields": [ + { + "id": "public.comments.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "public.comments.post_id", + "name": "post_id", + "columnName": "post_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.comments.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.comments.content", + "name": "content", + "columnName": "content", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.comments.is_deleted", + "name": "is_deleted", + "columnName": "is_deleted", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "public.comments.is_reported", + "name": "is_reported", + "columnName": "is_reported", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "public.comments.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "posts", + "type": "posts", + "isRequired": true, + "kind": "object", + "relationName": "commentsToposts", + "relationFromFields": [ + "post_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "profiles", + "type": "profiles", + "isRequired": false, + "kind": "object", + "relationName": "commentsToprofiles", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "comments_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "extensions": { + "id": "_realtime.extensions", + "schemaName": "_realtime", + "tableName": "extensions", + "fields": [ + { + "id": "_realtime.extensions.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "_realtime.extensions.type", + "name": "type", + "columnName": "type", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.extensions.settings", + "name": "settings", + "columnName": "settings", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.extensions.tenant_external_id", + "name": "tenant_external_id", + "columnName": "tenant_external_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.extensions.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.extensions.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "tenants", + "type": "tenants", + "isRequired": false, + "kind": "object", + "relationName": "extensionsTotenants", + "relationFromFields": [ + "tenant_external_id" + ], + "relationToFields": [ + "external_id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "extensions_tenant_external_id_type_index", + "fields": [ + "tenant_external_id", + "type" + ], + "nullNotDistinct": false + } + ] + }, + "flow_state": { + "id": "auth.flow_state", + "schemaName": "auth", + "tableName": "flow_state", + "fields": [ + { + "id": "auth.flow_state.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.flow_state.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.auth_code", + "name": "auth_code", + "columnName": "auth_code", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.code_challenge_method", + "name": "code_challenge_method", + "columnName": "code_challenge_method", + "type": "code_challenge_method", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.code_challenge", + "name": "code_challenge", + "columnName": "code_challenge", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.provider_type", + "name": "provider_type", + "columnName": "provider_type", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.provider_access_token", + "name": "provider_access_token", + "columnName": "provider_access_token", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.provider_refresh_token", + "name": "provider_refresh_token", + "columnName": "provider_refresh_token", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.authentication_method", + "name": "authentication_method", + "columnName": "authentication_method", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.flow_state.auth_code_issued_at", + "name": "auth_code_issued_at", + "columnName": "auth_code_issued_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "saml_relay_states", + "type": "saml_relay_states", + "isRequired": false, + "kind": "object", + "relationName": "saml_relay_statesToflow_state", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "flow_state_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "hooks": { + "id": "supabase_functions.hooks", + "schemaName": "supabase_functions", + "tableName": "hooks", + "fields": [ + { + "id": "supabase_functions.hooks.id", + "name": "id", + "columnName": "id", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": { + "identifier": "\"supabase_functions\".\"hooks_id_seq\"", + "increment": 1, + "start": 1 + }, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "supabase_functions.hooks.hook_table_id", + "name": "hook_table_id", + "columnName": "hook_table_id", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "supabase_functions.hooks.hook_name", + "name": "hook_name", + "columnName": "hook_name", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "supabase_functions.hooks.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "supabase_functions.hooks.request_id", + "name": "request_id", + "columnName": "request_id", + "type": "int8", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "hooks_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "http_request_queue": { + "id": "net.http_request_queue", + "schemaName": "net", + "tableName": "http_request_queue", + "fields": [ + { + "id": "net.http_request_queue.id", + "name": "id", + "columnName": "id", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": { + "identifier": "\"net\".\"http_request_queue_id_seq\"", + "increment": 1, + "start": 1 + }, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "net.http_request_queue.method", + "name": "method", + "columnName": "method", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net.http_request_queue.url", + "name": "url", + "columnName": "url", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net.http_request_queue.headers", + "name": "headers", + "columnName": "headers", + "type": "jsonb", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net.http_request_queue.body", + "name": "body", + "columnName": "body", + "type": "bytea", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "net.http_request_queue.timeout_milliseconds", + "name": "timeout_milliseconds", + "columnName": "timeout_milliseconds", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [] + }, + "identities": { + "id": "auth.identities", + "schemaName": "auth", + "tableName": "identities", + "fields": [ + { + "id": "auth.identities.provider_id", + "name": "provider_id", + "columnName": "provider_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.identity_data", + "name": "identity_data", + "columnName": "identity_data", + "type": "jsonb", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.provider", + "name": "provider", + "columnName": "provider", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.last_sign_in_at", + "name": "last_sign_in_at", + "columnName": "last_sign_in_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.email", + "name": "email", + "columnName": "email", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": true, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.identities.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "name": "users", + "type": "users", + "isRequired": true, + "kind": "object", + "relationName": "identitiesTousers", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "identities_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "identities_provider_id_provider_unique", + "fields": [ + "provider", + "provider_id" + ], + "nullNotDistinct": false + } + ] + }, + "instances": { + "id": "auth.instances", + "schemaName": "auth", + "tableName": "instances", + "fields": [ + { + "id": "auth.instances.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.instances.uuid", + "name": "uuid", + "columnName": "uuid", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.instances.raw_base_config", + "name": "raw_base_config", + "columnName": "raw_base_config", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.instances.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.instances.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "instances_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "messages": { + "id": "realtime.messages", + "schemaName": "realtime", + "tableName": "messages", + "fields": [ + { + "id": "realtime.messages.topic", + "name": "topic", + "columnName": "topic", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.extension", + "name": "extension", + "columnName": "extension", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.payload", + "name": "payload", + "columnName": "payload", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.event", + "name": "event", + "columnName": "event", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.private", + "name": "private", + "columnName": "private", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.messages.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "realtime.messages.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "messages_pkey", + "fields": [ + "id", + "inserted_at" + ], + "nullNotDistinct": false + } + ] + }, + "mfa_amr_claims": { + "id": "auth.mfa_amr_claims", + "schemaName": "auth", + "tableName": "mfa_amr_claims", + "fields": [ + { + "id": "auth.mfa_amr_claims.session_id", + "name": "session_id", + "columnName": "session_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_amr_claims.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_amr_claims.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_amr_claims.authentication_method", + "name": "authentication_method", + "columnName": "authentication_method", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_amr_claims.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "name": "sessions", + "type": "sessions", + "isRequired": true, + "kind": "object", + "relationName": "mfa_amr_claimsTosessions", + "relationFromFields": [ + "session_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "amr_id_pk", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "mfa_amr_claims_session_id_authentication_method_pkey", + "fields": [ + "authentication_method", + "session_id" + ], + "nullNotDistinct": false + } + ] + }, + "mfa_challenges": { + "id": "auth.mfa_challenges", + "schemaName": "auth", + "tableName": "mfa_challenges", + "fields": [ + { + "id": "auth.mfa_challenges.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.factor_id", + "name": "factor_id", + "columnName": "factor_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.verified_at", + "name": "verified_at", + "columnName": "verified_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.ip_address", + "name": "ip_address", + "columnName": "ip_address", + "type": "inet", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.otp_code", + "name": "otp_code", + "columnName": "otp_code", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_challenges.web_authn_session_data", + "name": "web_authn_session_data", + "columnName": "web_authn_session_data", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "mfa_factors", + "type": "mfa_factors", + "isRequired": true, + "kind": "object", + "relationName": "mfa_challengesTomfa_factors", + "relationFromFields": [ + "factor_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "mfa_challenges_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "mfa_factors": { + "id": "auth.mfa_factors", + "schemaName": "auth", + "tableName": "mfa_factors", + "fields": [ + { + "id": "auth.mfa_factors.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.mfa_factors.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.friendly_name", + "name": "friendly_name", + "columnName": "friendly_name", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.factor_type", + "name": "factor_type", + "columnName": "factor_type", + "type": "factor_type", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.status", + "name": "status", + "columnName": "status", + "type": "factor_status", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.secret", + "name": "secret", + "columnName": "secret", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.phone", + "name": "phone", + "columnName": "phone", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.last_challenged_at", + "name": "last_challenged_at", + "columnName": "last_challenged_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.web_authn_credential", + "name": "web_authn_credential", + "columnName": "web_authn_credential", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.mfa_factors.web_authn_aaguid", + "name": "web_authn_aaguid", + "columnName": "web_authn_aaguid", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "users", + "type": "users", + "isRequired": true, + "kind": "object", + "relationName": "mfa_factorsTousers", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "mfa_challenges", + "type": "mfa_challenges", + "isRequired": false, + "kind": "object", + "relationName": "mfa_challengesTomfa_factors", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "mfa_factors_last_challenged_at_key", + "fields": [ + "last_challenged_at" + ], + "nullNotDistinct": false + }, + { + "name": "mfa_factors_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "mfa_factors_user_friendly_name_unique", + "fields": [ + "friendly_name", + "user_id" + ], + "nullNotDistinct": false + }, + { + "name": "unique_phone_factor_per_user", + "fields": [ + "phone", + "user_id" + ], + "nullNotDistinct": false + } + ] + }, + "storage_migrations": { + "id": "storage.migrations", + "schemaName": "storage", + "tableName": "migrations", + "fields": [ + { + "id": "storage.migrations.id", + "name": "id", + "columnName": "id", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "storage.migrations.name", + "name": "name", + "columnName": "name", + "type": "varchar", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 100 + }, + { + "id": "storage.migrations.hash", + "name": "hash", + "columnName": "hash", + "type": "varchar", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 40 + }, + { + "id": "storage.migrations.executed_at", + "name": "executed_at", + "columnName": "executed_at", + "type": "timestamp", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "migrations_name_key", + "fields": [ + "name" + ], + "nullNotDistinct": false + }, + { + "name": "migrations_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "supabase_functions_migrations": { + "id": "supabase_functions.migrations", + "schemaName": "supabase_functions", + "tableName": "migrations", + "fields": [ + { + "id": "supabase_functions.migrations.version", + "name": "version", + "columnName": "version", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "supabase_functions.migrations.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "migrations_pkey", + "fields": [ + "version" + ], + "nullNotDistinct": false + } + ] + }, + "objects": { + "id": "storage.objects", + "schemaName": "storage", + "tableName": "objects", + "fields": [ + { + "id": "storage.objects.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "storage.objects.bucket_id", + "name": "bucket_id", + "columnName": "bucket_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.owner", + "name": "owner", + "columnName": "owner", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.last_accessed_at", + "name": "last_accessed_at", + "columnName": "last_accessed_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.metadata", + "name": "metadata", + "columnName": "metadata", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.path_tokens", + "name": "path_tokens", + "columnName": "path_tokens", + "type": "text[]", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": true, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.version", + "name": "version", + "columnName": "version", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.owner_id", + "name": "owner_id", + "columnName": "owner_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.objects.user_metadata", + "name": "user_metadata", + "columnName": "user_metadata", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "buckets", + "type": "buckets", + "isRequired": false, + "kind": "object", + "relationName": "objectsTobuckets", + "relationFromFields": [ + "bucket_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "objects_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "bucketid_objname", + "fields": [ + "bucket_id", + "name" + ], + "nullNotDistinct": false + } + ] + }, + "one_time_tokens": { + "id": "auth.one_time_tokens", + "schemaName": "auth", + "tableName": "one_time_tokens", + "fields": [ + { + "id": "auth.one_time_tokens.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.token_type", + "name": "token_type", + "columnName": "token_type", + "type": "one_time_token_type", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.token_hash", + "name": "token_hash", + "columnName": "token_hash", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.relates_to", + "name": "relates_to", + "columnName": "relates_to", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "auth.one_time_tokens.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "users", + "type": "users", + "isRequired": true, + "kind": "object", + "relationName": "one_time_tokensTousers", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "one_time_tokens_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "one_time_tokens_user_id_token_type_key", + "fields": [ + "token_type", + "user_id" + ], + "nullNotDistinct": false + } + ] + }, + "post_tags": { + "id": "public.post_tags", + "schemaName": "public", + "tableName": "post_tags", + "fields": [ + { + "id": "public.post_tags.post_id", + "name": "post_id", + "columnName": "post_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "public.post_tags.tag_id", + "name": "tag_id", + "columnName": "tag_id", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "name": "posts", + "type": "posts", + "isRequired": true, + "kind": "object", + "relationName": "post_tagsToposts", + "relationFromFields": [ + "post_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "tags", + "type": "tags", + "isRequired": true, + "kind": "object", + "relationName": "post_tagsTotags", + "relationFromFields": [ + "tag_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "post_tags_pkey", + "fields": [ + "post_id", + "tag_id" + ], + "nullNotDistinct": false + } + ] + }, + "posts": { + "id": "public.posts", + "schemaName": "public", + "tableName": "posts", + "fields": [ + { + "id": "public.posts.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "public.posts.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.posts.title", + "name": "title", + "columnName": "title", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.posts.description", + "name": "description", + "columnName": "description", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.posts.content", + "name": "content", + "columnName": "content", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.posts.is_draft", + "name": "is_draft", + "columnName": "is_draft", + "type": "bool", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "public.posts.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "profiles", + "type": "profiles", + "isRequired": true, + "kind": "object", + "relationName": "postsToprofiles", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "comments", + "type": "comments", + "isRequired": false, + "kind": "object", + "relationName": "commentsToposts", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "post_tags", + "type": "post_tags", + "isRequired": false, + "kind": "object", + "relationName": "post_tagsToposts", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "posts_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "profiles": { + "id": "public.profiles", + "schemaName": "public", + "tableName": "profiles", + "fields": [ + { + "id": "public.profiles.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "public.profiles.username", + "name": "username", + "columnName": "username", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.profiles.avatar_url", + "name": "avatar_url", + "columnName": "avatar_url", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.profiles.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "users", + "type": "users", + "isRequired": true, + "kind": "object", + "relationName": "profilesTousers", + "relationFromFields": [ + "id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "comments", + "type": "comments", + "isRequired": false, + "kind": "object", + "relationName": "commentsToprofiles", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "posts", + "type": "posts", + "isRequired": false, + "kind": "object", + "relationName": "postsToprofiles", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "profiles_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "profiles_username_key", + "fields": [ + "username" + ], + "nullNotDistinct": false + } + ] + }, + "refresh_tokens": { + "id": "auth.refresh_tokens", + "schemaName": "auth", + "tableName": "refresh_tokens", + "fields": [ + { + "id": "auth.refresh_tokens.instance_id", + "name": "instance_id", + "columnName": "instance_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.refresh_tokens.id", + "name": "id", + "columnName": "id", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": { + "identifier": "\"auth\".\"refresh_tokens_id_seq\"", + "increment": 1, + "start": 1 + }, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "auth.refresh_tokens.token", + "name": "token", + "columnName": "token", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.refresh_tokens.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.refresh_tokens.revoked", + "name": "revoked", + "columnName": "revoked", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.refresh_tokens.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.refresh_tokens.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.refresh_tokens.parent", + "name": "parent", + "columnName": "parent", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.refresh_tokens.session_id", + "name": "session_id", + "columnName": "session_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "sessions", + "type": "sessions", + "isRequired": false, + "kind": "object", + "relationName": "refresh_tokensTosessions", + "relationFromFields": [ + "session_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "refresh_tokens_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "refresh_tokens_token_unique", + "fields": [ + "token" + ], + "nullNotDistinct": false + } + ] + }, + "s3_multipart_uploads": { + "id": "storage.s3_multipart_uploads", + "schemaName": "storage", + "tableName": "s3_multipart_uploads", + "fields": [ + { + "id": "storage.s3_multipart_uploads.id", + "name": "id", + "columnName": "id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.in_progress_size", + "name": "in_progress_size", + "columnName": "in_progress_size", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.upload_signature", + "name": "upload_signature", + "columnName": "upload_signature", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.bucket_id", + "name": "bucket_id", + "columnName": "bucket_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.key", + "name": "key", + "columnName": "key", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.version", + "name": "version", + "columnName": "version", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.owner_id", + "name": "owner_id", + "columnName": "owner_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads.user_metadata", + "name": "user_metadata", + "columnName": "user_metadata", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "buckets", + "type": "buckets", + "isRequired": true, + "kind": "object", + "relationName": "s3_multipart_uploadsTobuckets", + "relationFromFields": [ + "bucket_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "s3_multipart_uploads_parts", + "type": "s3_multipart_uploads_parts", + "isRequired": false, + "kind": "object", + "relationName": "s3_multipart_uploads_partsTos3_multipart_uploads", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "s3_multipart_uploads_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "s3_multipart_uploads_parts": { + "id": "storage.s3_multipart_uploads_parts", + "schemaName": "storage", + "tableName": "s3_multipart_uploads_parts", + "fields": [ + { + "id": "storage.s3_multipart_uploads_parts.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.upload_id", + "name": "upload_id", + "columnName": "upload_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.size", + "name": "size", + "columnName": "size", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.part_number", + "name": "part_number", + "columnName": "part_number", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.bucket_id", + "name": "bucket_id", + "columnName": "bucket_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.key", + "name": "key", + "columnName": "key", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.etag", + "name": "etag", + "columnName": "etag", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.owner_id", + "name": "owner_id", + "columnName": "owner_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.version", + "name": "version", + "columnName": "version", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "storage.s3_multipart_uploads_parts.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "buckets", + "type": "buckets", + "isRequired": true, + "kind": "object", + "relationName": "s3_multipart_uploads_partsTobuckets", + "relationFromFields": [ + "bucket_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "s3_multipart_uploads", + "type": "s3_multipart_uploads", + "isRequired": true, + "kind": "object", + "relationName": "s3_multipart_uploads_partsTos3_multipart_uploads", + "relationFromFields": [ + "upload_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "s3_multipart_uploads_parts_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "saml_providers": { + "id": "auth.saml_providers", + "schemaName": "auth", + "tableName": "saml_providers", + "fields": [ + { + "id": "auth.saml_providers.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.saml_providers.sso_provider_id", + "name": "sso_provider_id", + "columnName": "sso_provider_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.entity_id", + "name": "entity_id", + "columnName": "entity_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.metadata_xml", + "name": "metadata_xml", + "columnName": "metadata_xml", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.metadata_url", + "name": "metadata_url", + "columnName": "metadata_url", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.attribute_mapping", + "name": "attribute_mapping", + "columnName": "attribute_mapping", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_providers.name_id_format", + "name": "name_id_format", + "columnName": "name_id_format", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "sso_providers", + "type": "sso_providers", + "isRequired": true, + "kind": "object", + "relationName": "saml_providersTosso_providers", + "relationFromFields": [ + "sso_provider_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "saml_providers_entity_id_key", + "fields": [ + "entity_id" + ], + "nullNotDistinct": false + }, + { + "name": "saml_providers_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "saml_relay_states": { + "id": "auth.saml_relay_states", + "schemaName": "auth", + "tableName": "saml_relay_states", + "fields": [ + { + "id": "auth.saml_relay_states.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.sso_provider_id", + "name": "sso_provider_id", + "columnName": "sso_provider_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.request_id", + "name": "request_id", + "columnName": "request_id", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.for_email", + "name": "for_email", + "columnName": "for_email", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.redirect_to", + "name": "redirect_to", + "columnName": "redirect_to", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.saml_relay_states.flow_state_id", + "name": "flow_state_id", + "columnName": "flow_state_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "flow_state", + "type": "flow_state", + "isRequired": false, + "kind": "object", + "relationName": "saml_relay_statesToflow_state", + "relationFromFields": [ + "flow_state_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "sso_providers", + "type": "sso_providers", + "isRequired": true, + "kind": "object", + "relationName": "saml_relay_statesTosso_providers", + "relationFromFields": [ + "sso_provider_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "saml_relay_states_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "_realtime_schema_migrations": { + "id": "_realtime.schema_migrations", + "schemaName": "_realtime", + "tableName": "schema_migrations", + "fields": [ + { + "id": "_realtime.schema_migrations.version", + "name": "version", + "columnName": "version", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "_realtime.schema_migrations.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamp", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [] + }, + "auth_schema_migrations": { + "id": "auth.schema_migrations", + "schemaName": "auth", + "tableName": "schema_migrations", + "fields": [ + { + "id": "auth.schema_migrations.version", + "name": "version", + "columnName": "version", + "type": "varchar", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": 255 + } + ], + "uniqueConstraints": [ + { + "name": "schema_migrations_pkey", + "fields": [ + "version" + ], + "nullNotDistinct": false + } + ] + }, + "realtime_schema_migrations": { + "id": "realtime.schema_migrations", + "schemaName": "realtime", + "tableName": "schema_migrations", + "fields": [ + { + "id": "realtime.schema_migrations.version", + "name": "version", + "columnName": "version", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "realtime.schema_migrations.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamp", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [] + }, + "supabase_migrations_schema_migrations": { + "id": "supabase_migrations.schema_migrations", + "schemaName": "supabase_migrations", + "tableName": "schema_migrations", + "fields": [ + { + "id": "supabase_migrations.schema_migrations.version", + "name": "version", + "columnName": "version", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "supabase_migrations.schema_migrations.statements", + "name": "statements", + "columnName": "statements", + "type": "text[]", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "supabase_migrations.schema_migrations.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "schema_migrations_pkey", + "fields": [ + "version" + ], + "nullNotDistinct": false + } + ] + }, + "secrets": { + "id": "vault.secrets", + "schemaName": "vault", + "tableName": "secrets", + "fields": [ + { + "id": "vault.secrets.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "vault.secrets.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.description", + "name": "description", + "columnName": "description", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.secret", + "name": "secret", + "columnName": "secret", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.key_id", + "name": "key_id", + "columnName": "key_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.nonce", + "name": "nonce", + "columnName": "nonce", + "type": "bytea", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "vault.secrets.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "secrets_name_idx", + "fields": [ + "name" + ], + "nullNotDistinct": false + } + ] + }, + "seed_files": { + "id": "supabase_migrations.seed_files", + "schemaName": "supabase_migrations", + "tableName": "seed_files", + "fields": [ + { + "id": "supabase_migrations.seed_files.path", + "name": "path", + "columnName": "path", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "supabase_migrations.seed_files.hash", + "name": "hash", + "columnName": "hash", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "seed_files_pkey", + "fields": [ + "path" + ], + "nullNotDistinct": false + } + ] + }, + "sessions": { + "id": "auth.sessions", + "schemaName": "auth", + "tableName": "sessions", + "fields": [ + { + "id": "auth.sessions.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.sessions.user_id", + "name": "user_id", + "columnName": "user_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.factor_id", + "name": "factor_id", + "columnName": "factor_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.aal", + "name": "aal", + "columnName": "aal", + "type": "aal_level", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.not_after", + "name": "not_after", + "columnName": "not_after", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.refreshed_at", + "name": "refreshed_at", + "columnName": "refreshed_at", + "type": "timestamp", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.user_agent", + "name": "user_agent", + "columnName": "user_agent", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.ip", + "name": "ip", + "columnName": "ip", + "type": "inet", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sessions.tag", + "name": "tag", + "columnName": "tag", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "users", + "type": "users", + "isRequired": true, + "kind": "object", + "relationName": "sessionsTousers", + "relationFromFields": [ + "user_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "mfa_amr_claims", + "type": "mfa_amr_claims", + "isRequired": false, + "kind": "object", + "relationName": "mfa_amr_claimsTosessions", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "refresh_tokens", + "type": "refresh_tokens", + "isRequired": false, + "kind": "object", + "relationName": "refresh_tokensTosessions", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "sessions_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "sso_domains": { + "id": "auth.sso_domains", + "schemaName": "auth", + "tableName": "sso_domains", + "fields": [ + { + "id": "auth.sso_domains.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.sso_domains.sso_provider_id", + "name": "sso_provider_id", + "columnName": "sso_provider_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sso_domains.domain", + "name": "domain", + "columnName": "domain", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sso_domains.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sso_domains.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "sso_providers", + "type": "sso_providers", + "isRequired": true, + "kind": "object", + "relationName": "sso_domainsTosso_providers", + "relationFromFields": [ + "sso_provider_id" + ], + "relationToFields": [ + "id" + ], + "isList": false, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "sso_domains_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "sso_providers": { + "id": "auth.sso_providers", + "schemaName": "auth", + "tableName": "sso_providers", + "fields": [ + { + "id": "auth.sso_providers.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.sso_providers.resource_id", + "name": "resource_id", + "columnName": "resource_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sso_providers.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.sso_providers.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "saml_providers", + "type": "saml_providers", + "isRequired": false, + "kind": "object", + "relationName": "saml_providersTosso_providers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "saml_relay_states", + "type": "saml_relay_states", + "isRequired": false, + "kind": "object", + "relationName": "saml_relay_statesTosso_providers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "sso_domains", + "type": "sso_domains", + "isRequired": false, + "kind": "object", + "relationName": "sso_domainsTosso_providers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "sso_providers_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "subscription": { + "id": "realtime.subscription", + "schemaName": "realtime", + "tableName": "subscription", + "fields": [ + { + "id": "realtime.subscription.id", + "name": "id", + "columnName": "id", + "type": "int8", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": true, + "sequence": { + "identifier": "\"realtime\".\"realtime.subscription_id_seq\"", + "increment": 1, + "start": 1 + }, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "realtime.subscription.subscription_id", + "name": "subscription_id", + "columnName": "subscription_id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.subscription.entity", + "name": "entity", + "columnName": "entity", + "type": "regclass", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.subscription.filters", + "name": "filters", + "columnName": "filters", + "type": "user_defined_filter[]", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.subscription.claims", + "name": "claims", + "columnName": "claims", + "type": "jsonb", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.subscription.claims_role", + "name": "claims_role", + "columnName": "claims_role", + "type": "regrole", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": true, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "realtime.subscription.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + } + ], + "uniqueConstraints": [ + { + "name": "subscription_subscription_id_entity_filters_key", + "fields": [ + "entity", + "filters", + "subscription_id" + ], + "nullNotDistinct": false + } + ] + }, + "tags": { + "id": "public.tags", + "schemaName": "public", + "tableName": "tags", + "fields": [ + { + "id": "public.tags.id", + "name": "id", + "columnName": "id", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": { + "identifier": "\"public\".\"tags_id_seq\"", + "increment": 1, + "start": 1 + }, + "hasDefaultValue": true, + "isId": true, + "maxLength": null + }, + { + "id": "public.tags.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.tags.color", + "name": "color", + "columnName": "color", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "public.tags.icon", + "name": "icon", + "columnName": "icon", + "type": "text", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "name": "post_tags", + "type": "post_tags", + "isRequired": false, + "kind": "object", + "relationName": "post_tagsTotags", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "tags_name_key", + "fields": [ + "name" + ], + "nullNotDistinct": false + }, + { + "name": "tags_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + } + ] + }, + "tenants": { + "id": "_realtime.tenants", + "schemaName": "_realtime", + "tableName": "tenants", + "fields": [ + { + "id": "_realtime.tenants.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "_realtime.tenants.name", + "name": "name", + "columnName": "name", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.external_id", + "name": "external_id", + "columnName": "external_id", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.jwt_secret", + "name": "jwt_secret", + "columnName": "jwt_secret", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.max_concurrent_users", + "name": "max_concurrent_users", + "columnName": "max_concurrent_users", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.inserted_at", + "name": "inserted_at", + "columnName": "inserted_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamp", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.max_events_per_second", + "name": "max_events_per_second", + "columnName": "max_events_per_second", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.postgres_cdc_default", + "name": "postgres_cdc_default", + "columnName": "postgres_cdc_default", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.max_bytes_per_second", + "name": "max_bytes_per_second", + "columnName": "max_bytes_per_second", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.max_channels_per_client", + "name": "max_channels_per_client", + "columnName": "max_channels_per_client", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.max_joins_per_second", + "name": "max_joins_per_second", + "columnName": "max_joins_per_second", + "type": "int4", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.suspend", + "name": "suspend", + "columnName": "suspend", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.jwt_jwks", + "name": "jwt_jwks", + "columnName": "jwt_jwks", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.notify_private_alpha", + "name": "notify_private_alpha", + "columnName": "notify_private_alpha", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.private_only", + "name": "private_only", + "columnName": "private_only", + "type": "bool", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "_realtime.tenants.migrations_ran", + "name": "migrations_ran", + "columnName": "migrations_ran", + "type": "int4", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "extensions", + "type": "extensions", + "isRequired": false, + "kind": "object", + "relationName": "extensionsTotenants", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "tenants_external_id_index", + "fields": [ + "external_id" + ], + "nullNotDistinct": false + } + ] + }, + "users": { + "id": "auth.users", + "schemaName": "auth", + "tableName": "users", + "fields": [ + { + "id": "auth.users.instance_id", + "name": "instance_id", + "columnName": "instance_id", + "type": "uuid", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.id", + "name": "id", + "columnName": "id", + "type": "uuid", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": true, + "maxLength": null + }, + { + "id": "auth.users.aud", + "name": "aud", + "columnName": "aud", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.role", + "name": "role", + "columnName": "role", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.email", + "name": "email", + "columnName": "email", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.encrypted_password", + "name": "encrypted_password", + "columnName": "encrypted_password", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.email_confirmed_at", + "name": "email_confirmed_at", + "columnName": "email_confirmed_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.invited_at", + "name": "invited_at", + "columnName": "invited_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.confirmation_token", + "name": "confirmation_token", + "columnName": "confirmation_token", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.confirmation_sent_at", + "name": "confirmation_sent_at", + "columnName": "confirmation_sent_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.recovery_token", + "name": "recovery_token", + "columnName": "recovery_token", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.recovery_sent_at", + "name": "recovery_sent_at", + "columnName": "recovery_sent_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.email_change_token_new", + "name": "email_change_token_new", + "columnName": "email_change_token_new", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.email_change", + "name": "email_change", + "columnName": "email_change", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.email_change_sent_at", + "name": "email_change_sent_at", + "columnName": "email_change_sent_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.last_sign_in_at", + "name": "last_sign_in_at", + "columnName": "last_sign_in_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.raw_app_meta_data", + "name": "raw_app_meta_data", + "columnName": "raw_app_meta_data", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.raw_user_meta_data", + "name": "raw_user_meta_data", + "columnName": "raw_user_meta_data", + "type": "jsonb", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.is_super_admin", + "name": "is_super_admin", + "columnName": "is_super_admin", + "type": "bool", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.created_at", + "name": "created_at", + "columnName": "created_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.updated_at", + "name": "updated_at", + "columnName": "updated_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.phone", + "name": "phone", + "columnName": "phone", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.phone_confirmed_at", + "name": "phone_confirmed_at", + "columnName": "phone_confirmed_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.phone_change", + "name": "phone_change", + "columnName": "phone_change", + "type": "text", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.phone_change_token", + "name": "phone_change_token", + "columnName": "phone_change_token", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.phone_change_sent_at", + "name": "phone_change_sent_at", + "columnName": "phone_change_sent_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.confirmed_at", + "name": "confirmed_at", + "columnName": "confirmed_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": true, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.email_change_token_current", + "name": "email_change_token_current", + "columnName": "email_change_token_current", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.email_change_confirm_status", + "name": "email_change_confirm_status", + "columnName": "email_change_confirm_status", + "type": "int2", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.banned_until", + "name": "banned_until", + "columnName": "banned_until", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.reauthentication_token", + "name": "reauthentication_token", + "columnName": "reauthentication_token", + "type": "varchar", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": 255 + }, + { + "id": "auth.users.reauthentication_sent_at", + "name": "reauthentication_sent_at", + "columnName": "reauthentication_sent_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.is_sso_user", + "name": "is_sso_user", + "columnName": "is_sso_user", + "type": "bool", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.deleted_at", + "name": "deleted_at", + "columnName": "deleted_at", + "type": "timestamptz", + "isRequired": false, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false, + "isId": false, + "maxLength": null + }, + { + "id": "auth.users.is_anonymous", + "name": "is_anonymous", + "columnName": "is_anonymous", + "type": "bool", + "isRequired": true, + "kind": "scalar", + "isList": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": true, + "isId": false, + "maxLength": null + }, + { + "name": "identities", + "type": "identities", + "isRequired": false, + "kind": "object", + "relationName": "identitiesTousers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "mfa_factors", + "type": "mfa_factors", + "isRequired": false, + "kind": "object", + "relationName": "mfa_factorsTousers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "one_time_tokens", + "type": "one_time_tokens", + "isRequired": false, + "kind": "object", + "relationName": "one_time_tokensTousers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "sessions", + "type": "sessions", + "isRequired": false, + "kind": "object", + "relationName": "sessionsTousers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + }, + { + "name": "profiles", + "type": "profiles", + "isRequired": false, + "kind": "object", + "relationName": "profilesTousers", + "relationFromFields": [], + "relationToFields": [], + "isList": true, + "isId": false, + "isGenerated": false, + "sequence": false, + "hasDefaultValue": false + } + ], + "uniqueConstraints": [ + { + "name": "users_phone_key", + "fields": [ + "phone" + ], + "nullNotDistinct": false + }, + { + "name": "users_pkey", + "fields": [ + "id" + ], + "nullNotDistinct": false + }, + { + "name": "confirmation_token_idx", + "fields": [ + "confirmation_token" + ], + "nullNotDistinct": false + }, + { + "name": "email_change_token_current_idx", + "fields": [ + "email_change_token_current" + ], + "nullNotDistinct": false + }, + { + "name": "email_change_token_new_idx", + "fields": [ + "email_change_token_new" + ], + "nullNotDistinct": false + }, + { + "name": "reauthentication_token_idx", + "fields": [ + "reauthentication_token" + ], + "nullNotDistinct": false + }, + { + "name": "recovery_token_idx", + "fields": [ + "recovery_token" + ], + "nullNotDistinct": false + }, + { + "name": "users_email_partial_key", + "fields": [ + "email" + ], + "nullNotDistinct": false + } + ] + } + }, + "enums": { + "aal_level": { + "schemaName": "auth", + "values": [ + { + "name": "aal1" + }, + { + "name": "aal2" + }, + { + "name": "aal3" + } + ] + }, + "code_challenge_method": { + "schemaName": "auth", + "values": [ + { + "name": "plain" + }, + { + "name": "s256" + } + ] + }, + "factor_status": { + "schemaName": "auth", + "values": [ + { + "name": "unverified" + }, + { + "name": "verified" + } + ] + }, + "factor_type": { + "schemaName": "auth", + "values": [ + { + "name": "phone" + }, + { + "name": "totp" + }, + { + "name": "webauthn" + } + ] + }, + "one_time_token_type": { + "schemaName": "auth", + "values": [ + { + "name": "confirmation_token" + }, + { + "name": "email_change_token_current" + }, + { + "name": "email_change_token_new" + }, + { + "name": "phone_change_token" + }, + { + "name": "reauthentication_token" + }, + { + "name": "recovery_token" + } + ] + }, + "request_status": { + "schemaName": "net", + "values": [ + { + "name": "ERROR" + }, + { + "name": "PENDING" + }, + { + "name": "SUCCESS" + } + ] + }, + "action": { + "schemaName": "realtime", + "values": [ + { + "name": "DELETE" + }, + { + "name": "ERROR" + }, + { + "name": "INSERT" + }, + { + "name": "TRUNCATE" + }, + { + "name": "UPDATE" + } + ] + }, + "equality_op": { + "schemaName": "realtime", + "values": [ + { + "name": "eq" + }, + { + "name": "gt" + }, + { + "name": "gte" + }, + { + "name": "in" + }, + { + "name": "lt" + }, + { + "name": "lte" + }, + { + "name": "neq" + } + ] + } + } +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..af62c23 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "deno.enablePaths": [ + "supabase/functions" + ], + "deno.lint": true, + "deno.unstable": [ + "bare-node-builtins", + "byonm", + "sloppy-imports", + "unsafe-proto", + "webgpu", + "broadcast-channel", + "worker-options", + "cron", + "kv", + "ffi", + "fs", + "http", + "net" + ], + "[typescript]": { + "editor.defaultFormatter": "denoland.vscode-deno" + } +} diff --git a/scripts/create-seed.sh b/scripts/create-seed.sh new file mode 100755 index 0000000..68214b2 --- /dev/null +++ b/scripts/create-seed.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ─────────────────────────────────────────────── +# Creates a single SQL file (supabase/seed/seed.sql) +# • pulls current tags from the public REST endpoint +# • writes a COPY block for tags +# • appends two demo users (normal + Admin) to profiles +# Requirements: curl, jq +# ─────────────────────────────────────────────── + +command -v jq >/dev/null || { echo "❌ jq missing"; exit 1; } +command -v curl >/dev/null || { echo "❌ curl missing"; exit 1; } + +mkdir -p supabase/seed + +API_URL="https://aqdbdmepncxxuanlymwr.supabase.co/rest/v1/tags?select=*" +API_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFxZGJkbWVwbmN4eHVhbmx5bXdyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDUwNTA0MjYsImV4cCI6MjA2MDYyNjQyNn0.RNtZZ4Of4LIP3XuS9lumHYdjRLVUGXARtAxaTJmF7lc" + +tmp_json=$(mktemp) +seed_file="supabase/seed/seed.sql" + +echo "▶ downloading tags…" +curl -sSL "$API_URL" \ + -H "apikey: $API_KEY" \ + -H "Authorization: Bearer $API_KEY" \ + -H "Accept: application/json" > "$tmp_json" + +echo "▶ building seed.sql…" +{ + echo "-- Auto‑generated seed file" + echo "SET client_encoding = 'UTF8';" + echo "" + echo "COPY tags (id,name,color,icon) FROM STDIN CSV;" + jq -r '.[] | [.id, .name, .color, .icon] | @csv' "$tmp_json" + echo "\." + echo "" + echo "-- Demo auth.users rows" + echo "INSERT INTO auth.users (id, email, raw_app_meta_data)" + echo "VALUES" + echo " ('00000000-0000-4000-8000-000000000001', 'user@example.com', '{\"provider\":\"email\",\"providers\":[\"email\"]}')," + echo " ('00000000-0000-4000-8000-000000000002', 'admin@example.com', '{\"role\":\"Admin\",\"provider\":\"email\",\"providers\":[\"email\"]}');" +} > "$seed_file" + +rm -f "$tmp_json" + +echo "✅ Wrote $seed_file" + diff --git a/seed.config.ts b/seed.config.ts new file mode 100644 index 0000000..d0bfd72 --- /dev/null +++ b/seed.config.ts @@ -0,0 +1,13 @@ +import { SeedPg } from '@snaplet/seed/adapter-pg'; +import { defineConfig } from '@snaplet/seed/config'; +import { Client } from 'pg'; + +export default defineConfig({ + adapter: async () => { + const client = new Client( + 'postgresql://postgres:postgres@localhost:54322/postgres', + ); + await client.connect(); + return new SeedPg(client); + }, +}); diff --git a/seed.ts b/seed.ts new file mode 100644 index 0000000..c5e0028 --- /dev/null +++ b/seed.ts @@ -0,0 +1,25 @@ +/** + * ! Executing this script will delete all data in your database and seed it with 10 users. + * ! Make sure to adjust the script to your needs. + * Use any TypeScript runner to run this script, for example: `npx tsx seed.ts` + * Learn more about the Seed Client by following our guide: https://docs.snaplet.dev/seed/getting-started + */ +import { createSeedClient } from "@snaplet/seed"; + +const main = async () => { + const seed = await createSeedClient(); + + // Truncate all tables in the database + await seed.$resetDatabase(); + + // Seed the database with 10 users + await seed.users((x) => x(10)); + + // Type completion not working? You might want to reload your TypeScript Server to pick up the changes + + console.log("Database seeded successfully!"); + + process.exit(); +}; + +main(); \ No newline at end of file diff --git a/supabase-password-script-fix.md b/supabase-password-script-fix.md new file mode 100644 index 0000000..aedbae7 --- /dev/null +++ b/supabase-password-script-fix.md @@ -0,0 +1,40 @@ +# Supabase Password Script Fix + +## Issue Description +When running the `npm run users:passwords` command, the following error occurred: +``` +▶ fetching user IDs… +jq: error (at :0): Cannot index object with number +``` + +## Cause of the Issue +The error occurred in the `get_id` function of the `scripts/set-passwords.sh` script. The function was using jq to extract the user ID from the JSON response with the command: +```bash +jq -r '.[0].id // empty' +``` + +This command assumes that the response from the Supabase Auth API is an array, and it tries to access the first element (`.[0]`). However, the error message "Cannot index object with number" indicates that the response is actually an object, not an array, so the `.[0]` indexing fails. + +## Solution +The solution is to modify the jq command to handle both cases: if the response is an array, it will use the original approach; if it's an object, it will try to access the `id` property directly. + +### Changes to set-passwords.sh +```bash +# Before: +get_id () { # $1=email + curl -sS "${HDR[@]:0:2}" "$BASE/users?email=$1" | jq -r '.[0].id // empty' +} + +# After: +get_id () { # $1=email + curl -sS "${HDR[@]:0:2}" "$BASE/users?email=$1" | jq -r 'if type == "array" then .[0].id // empty else .id // empty end' +} +``` + +The new jq command uses the `type` function to check if the response is an array. If it is, it accesses the first element's id property as before. If it's an object, it tries to access the id property directly. This makes the script more robust and able to handle different response formats from the Supabase Auth API. + +## How to Test +To test this fix: +1. Make sure your local Supabase instance is running +2. Run the script: `npm run users:passwords` +3. Verify that it successfully sets the passwords for the two users without any errors diff --git a/supabase/.gitignore b/supabase/.gitignore new file mode 100644 index 0000000..ad9264f --- /dev/null +++ b/supabase/.gitignore @@ -0,0 +1,8 @@ +# Supabase +.branches +.temp + +# dotenvx +.env.keys +.env.local +.env.*.local diff --git a/supabase/migrations/20250525091356_remote_schema.sql b/supabase/migrations/20250525091356_remote_schema.sql new file mode 100644 index 0000000..100c97f --- /dev/null +++ b/supabase/migrations/20250525091356_remote_schema.sql @@ -0,0 +1,587 @@ + + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + + +COMMENT ON SCHEMA "public" IS 'standard public schema'; + + + +CREATE EXTENSION IF NOT EXISTS "pg_graphql" WITH SCHEMA "graphql"; + + + + + + +CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" WITH SCHEMA "extensions"; + + + + + + +CREATE EXTENSION IF NOT EXISTS "pgcrypto" WITH SCHEMA "extensions"; + + + + + + +CREATE EXTENSION IF NOT EXISTS "pgjwt" WITH SCHEMA "extensions"; + + + + + + +CREATE EXTENSION IF NOT EXISTS "supabase_vault" WITH SCHEMA "vault"; + + + + + + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA "extensions"; + + + + + +SET default_tablespace = ''; + +SET default_table_access_method = "heap"; + + +CREATE TABLE IF NOT EXISTS "public"."comments" ( + "id" "uuid" DEFAULT "extensions"."uuid_generate_v4"() NOT NULL, + "post_id" "uuid" NOT NULL, + "user_id" "uuid", + "content" "text" NOT NULL, + "is_deleted" boolean DEFAULT false, + "is_reported" boolean DEFAULT false, + "created_at" timestamp with time zone DEFAULT "now"() +); + + +ALTER TABLE "public"."comments" OWNER TO "postgres"; + + +CREATE TABLE IF NOT EXISTS "public"."post_tags" ( + "post_id" "uuid" NOT NULL, + "tag_id" integer NOT NULL +); + + +ALTER TABLE "public"."post_tags" OWNER TO "postgres"; + + +CREATE TABLE IF NOT EXISTS "public"."posts" ( + "id" "uuid" DEFAULT "extensions"."uuid_generate_v4"() NOT NULL, + "user_id" "uuid" NOT NULL DEFAULT auth.uid(), + "title" "text" NOT NULL, + "description" "text" NOT NULL, + "content" "text" NOT NULL, + "is_draft" boolean DEFAULT false NOT NULL, + "created_at" timestamp with time zone DEFAULT "now"() +); + + +ALTER TABLE "public"."posts" OWNER TO "postgres"; + + +CREATE TABLE IF NOT EXISTS "public"."profiles" ( + "id" "uuid" NOT NULL, + "username" "text" NOT NULL, + "avatar_url" "text", + "created_at" timestamp with time zone DEFAULT "now"() +); + + +ALTER TABLE "public"."profiles" OWNER TO "postgres"; + + +CREATE TABLE IF NOT EXISTS "public"."tags" ( + "id" integer NOT NULL, + "name" "text" NOT NULL, + "color" "text" NOT NULL, + "icon" "text" NOT NULL +); + + +ALTER TABLE "public"."tags" OWNER TO "postgres"; + + +CREATE SEQUENCE IF NOT EXISTS "public"."tags_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE "public"."tags_id_seq" OWNER TO "postgres"; + + +ALTER SEQUENCE "public"."tags_id_seq" OWNED BY "public"."tags"."id"; + + + +ALTER TABLE ONLY "public"."tags" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."tags_id_seq"'::"regclass"); + + + +ALTER TABLE ONLY "public"."comments" + ADD CONSTRAINT "comments_pkey" PRIMARY KEY ("id"); + + + +ALTER TABLE ONLY "public"."post_tags" + ADD CONSTRAINT "post_tags_pkey" PRIMARY KEY ("post_id", "tag_id"); + + + +ALTER TABLE ONLY "public"."posts" + ADD CONSTRAINT "posts_pkey" PRIMARY KEY ("id"); + + + +ALTER TABLE ONLY "public"."profiles" + ADD CONSTRAINT "profiles_pkey" PRIMARY KEY ("id"); + + + +ALTER TABLE ONLY "public"."profiles" + ADD CONSTRAINT "profiles_username_key" UNIQUE ("username"); + + + +ALTER TABLE ONLY "public"."tags" + ADD CONSTRAINT "tags_name_key" UNIQUE ("name"); + + + +ALTER TABLE ONLY "public"."tags" + ADD CONSTRAINT "tags_pkey" PRIMARY KEY ("id"); + + + +ALTER TABLE ONLY "public"."comments" + ADD CONSTRAINT "comments_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts"("id") ON DELETE CASCADE; + + + +ALTER TABLE ONLY "public"."comments" + ADD CONSTRAINT "comments_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."profiles"("id") ON DELETE CASCADE; + + + +ALTER TABLE ONLY "public"."post_tags" + ADD CONSTRAINT "post_tags_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts"("id") ON DELETE CASCADE; + + + +ALTER TABLE ONLY "public"."post_tags" + ADD CONSTRAINT "post_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON DELETE CASCADE; + + + +ALTER TABLE ONLY "public"."posts" + ADD CONSTRAINT "posts_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."profiles"("id") ON DELETE CASCADE; + + + +ALTER TABLE ONLY "public"."profiles" + ADD CONSTRAINT "profiles_id_fkey" FOREIGN KEY ("id") REFERENCES "auth"."users"("id") ON DELETE CASCADE; + + + +CREATE POLICY "Authenticated users can create profiles" ON "public"."profiles" FOR INSERT WITH CHECK (("auth"."role"() = 'authenticated'::"text")); + + + +CREATE POLICY "Only Admin can create post_tags" ON "public"."post_tags" FOR INSERT WITH CHECK (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can create posts" ON "public"."posts" FOR INSERT WITH CHECK (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can create tags" ON "public"."tags" FOR INSERT WITH CHECK (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can delete comments" ON "public"."comments" FOR DELETE USING ((("auth"."jwt"() ->> 'role'::"text") = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can delete post_tags" ON "public"."post_tags" FOR DELETE USING (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can delete posts" ON "public"."posts" FOR DELETE USING (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can delete tags" ON "public"."tags" FOR DELETE USING (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can edit posts" ON "public"."posts" FOR UPDATE USING (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can update post_tags" ON "public"."post_tags" FOR UPDATE USING (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Only Admin can update tags" ON "public"."tags" FOR UPDATE USING (("auth"."role"() = 'Admin'::"text")); + + + +CREATE POLICY "Public can read basic profile info" ON "public"."profiles" FOR SELECT USING (true); + + + +CREATE POLICY "Public can read comments" ON "public"."comments" FOR SELECT USING (true); + + + +CREATE POLICY "Public can read post_tags" ON "public"."post_tags" FOR SELECT USING (true); + + + +CREATE POLICY "Public can read posts" ON "public"."posts" FOR SELECT USING (true); + + + +CREATE POLICY "Public can read tags" ON "public"."tags" FOR SELECT USING (true); + + + +CREATE POLICY "Users can delete their own profile or Admin" ON "public"."profiles" FOR DELETE USING ((("auth"."uid"() = "id") OR (("auth"."jwt"() ->> 'role'::"text") = 'Admin'::"text"))); + + + +CREATE POLICY "Users can edit their own profile or Admin" ON "public"."profiles" FOR UPDATE USING ((("auth"."uid"() = "id") OR (("auth"."jwt"() ->> 'role'::"text") = 'Admin'::"text"))); + + + +ALTER TABLE "public"."comments" ENABLE ROW LEVEL SECURITY; + + +ALTER TABLE "public"."post_tags" ENABLE ROW LEVEL SECURITY; + + +ALTER TABLE "public"."posts" ENABLE ROW LEVEL SECURITY; + + +ALTER TABLE "public"."profiles" ENABLE ROW LEVEL SECURITY; + + +ALTER TABLE "public"."tags" ENABLE ROW LEVEL SECURITY; + + + + +ALTER PUBLICATION "supabase_realtime" OWNER TO "postgres"; + + +GRANT USAGE ON SCHEMA "public" TO "postgres"; +GRANT USAGE ON SCHEMA "public" TO "anon"; +GRANT USAGE ON SCHEMA "public" TO "authenticated"; +GRANT USAGE ON SCHEMA "public" TO "service_role"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +GRANT ALL ON TABLE "public"."comments" TO "anon"; +GRANT ALL ON TABLE "public"."comments" TO "authenticated"; +GRANT ALL ON TABLE "public"."comments" TO "service_role"; + + + +GRANT ALL ON TABLE "public"."post_tags" TO "anon"; +GRANT ALL ON TABLE "public"."post_tags" TO "authenticated"; +GRANT ALL ON TABLE "public"."post_tags" TO "service_role"; + + + +GRANT ALL ON TABLE "public"."posts" TO "anon"; +GRANT ALL ON TABLE "public"."posts" TO "authenticated"; +GRANT ALL ON TABLE "public"."posts" TO "service_role"; + + + +GRANT ALL ON TABLE "public"."profiles" TO "anon"; +GRANT ALL ON TABLE "public"."profiles" TO "authenticated"; +GRANT ALL ON TABLE "public"."profiles" TO "service_role"; + + + +GRANT ALL ON TABLE "public"."tags" TO "anon"; +GRANT ALL ON TABLE "public"."tags" TO "authenticated"; +GRANT ALL ON TABLE "public"."tags" TO "service_role"; + + + +GRANT ALL ON SEQUENCE "public"."tags_id_seq" TO "anon"; +GRANT ALL ON SEQUENCE "public"."tags_id_seq" TO "authenticated"; +GRANT ALL ON SEQUENCE "public"."tags_id_seq" TO "service_role"; + + + + + + + + + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON SEQUENCES TO "service_role"; + + + + + + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON FUNCTIONS TO "service_role"; + + + + + + +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "postgres"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "anon"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "authenticated"; +ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT ALL ON TABLES TO "service_role"; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +RESET ALL; diff --git a/supabase/seed/seed.sql b/supabase/seed/seed.sql new file mode 100644 index 0000000..200625c --- /dev/null +++ b/supabase/seed/seed.sql @@ -0,0 +1,97 @@ +-- -------------------------------------------- +-- UTF-8 seed file for a fresh Supabase project +-- -------------------------------------------- +SET client_encoding = 'UTF8'; +SET check_function_bodies = OFF; +SET client_min_messages = WARNING; + +------------------------------------------------ +-- 1. Tags (unchanged) +------------------------------------------------ +INSERT INTO tags (id, name, color, icon) VALUES + (1,'Angular','#DD0031','angular.svg'), + (2,'TypeScript','#007ACC','typescript.svg'), + (3,'JavaScript','#F7DF1E','javascript.svg'), + (4,'Firebase','#FFCA28','firebase.svg'), + (5,'Firestore','#FFA000','firestore.svg'), + (6,'Node.js','#339933','nodejs.svg'), + (7,'Cloud Computing','#4285F4','cloud-computing.svg'), + (8,'SSG/SSR','#9E9E9E','ssg.svg'), + (9,'Web Development','#E65100','web-development.svg'), + (10,'Performance','#43A047','performance.svg'), + (11,'Security','#D32F2F','security.svg'), + (12,'Deployment','#1976D2','deployment.svg'), + (13,'Testing','#8E24AA','testing.svg'), + (14,'Best Practices','#FFB300','best-practices.svg'), + (15,'Tutorials','#5E35B1','tutorials.svg'), + (16,'HTML','#E44D26','html.svg'), + (17,'CSS','#1572B6','css.svg') + ON CONFLICT (id) DO NOTHING; + +------------------------------------------------ +-- 2. Users (bcrypt via pgcrypto.crypt) +------------------------------------------------ +WITH users AS ( + SELECT * FROM ( VALUES + ('00000000-0000-4000-8000-000000000001'::uuid, 'user@example.com', 'Password123!', + '{"provider":"email","providers":["email"]}'::jsonb), + ('00000000-0000-4000-8000-000000000002'::uuid, 'admin@example.com', 'Admin123!', + '{"provider":"email","providers":["email"],"role":"Admin"}'::jsonb) + ) AS t(id,email,plain_pw,app_meta) +) +INSERT INTO auth.users ( + id, instance_id, + aud, role, + email, encrypted_password, + email_confirmed_at, + raw_app_meta_data, raw_user_meta_data, + created_at, updated_at, + -- tokens & change fields (must not be NULL in Gotrue) + confirmation_token, email_change, email_change_token_new, recovery_token +) +SELECT + id, + '00000000-0000-0000-0000-000000000000', + 'authenticated','authenticated', + email, + crypt(plain_pw, gen_salt('bf')), -- bcrypt hash:contentReference[oaicite:5]{index=5} + now(), + app_meta, '{}'::jsonb, + now(), now(), + '', '', '', '' +FROM users + ON CONFLICT (id) DO NOTHING; + +------------------------------------------------ +-- 3. Identities (provider_id is REQUIRED) 👈 +------------------------------------------------ +INSERT INTO auth.identities ( + id, + user_id, + provider_id, -- NEW in Gotrue v2.173+:contentReference[oaicite:6]{index=6} + provider, + identity_data, + last_sign_in_at, + created_at, + updated_at +) +SELECT + gen_random_uuid(), -- identity row id + u.id, + u.id, -- provider_id == user id for 'email' provider:contentReference[oaicite:7]{index=7} + 'email', + json_build_object('sub', u.id, 'email', u.email), + now(), now(), now() +FROM auth.users u +WHERE u.email IN ('user@example.com','admin@example.com') + ON CONFLICT (provider_id, provider) DO NOTHING; +------------------------------------------------ +-- 4. Profiles (one row per user) +------------------------------------------------ +INSERT INTO public.profiles (id, username, created_at) +SELECT + id, + split_part(email, '@', 1) AS username, -- “user” → user@example.com + now() +FROM auth.users + ON CONFLICT (id) DO NOTHING; From 65fc430d56dcb58aaf4c7448a80bccaf91e531af Mon Sep 17 00:00:00 2001 From: Luk Date: Tue, 10 Jun 2025 09:22:20 +0200 Subject: [PATCH 4/6] Add tag selection feature to post creation and update policies for admin role --- .../add-post/add-post.component.html | 9 + .../add-post/add-post.component.ts | 12 +- src/app/admin/_models/post-from.inteface.ts | 7 +- src/app/admin/_services/admin-api.service.ts | 6 +- .../tag-multi-select.component.html | 88 ++++++++ .../tag-multi-select.component.ts | 199 ++++++++++++++++++ .../20250525091356_remote_schema.sql | 2 +- 7 files changed, 313 insertions(+), 10 deletions(-) create mode 100644 src/app/shared/tag-multi-select/tag-multi-select.component.html create mode 100644 src/app/shared/tag-multi-select/tag-multi-select.component.ts diff --git a/src/app/admin/_components/add-post/add-post.component.html b/src/app/admin/_components/add-post/add-post.component.html index 36a5138..3771711 100755 --- a/src/app/admin/_components/add-post/add-post.component.html +++ b/src/app/admin/_components/add-post/add-post.component.html @@ -8,6 +8,15 @@
Title is required.
} } + + +
+ + +
+
diff --git a/src/app/admin/_components/add-post/add-post.component.ts b/src/app/admin/_components/add-post/add-post.component.ts index 915875c..f39a842 100755 --- a/src/app/admin/_components/add-post/add-post.component.ts +++ b/src/app/admin/_components/add-post/add-post.component.ts @@ -28,7 +28,8 @@ import { DynamicDialogService } from '../../../shared/dynamic-dialog/dynamic-dia import { ModalConfig } from '../../../shared/_models/modal-config.intreface'; import { AddImageComponent } from './add-image/add-image.component'; import { AddImageForm } from './add-image/add-image-controls.interface'; -import { Post } from '../../../types/supabase'; +import { PostInsert, PostUpdate, Tag } from '../../../types/supabase'; +import { TagMultiSelectComponent } from '../../../shared/tag-multi-select/tag-multi-select.component'; @Component({ selector: 'blog-add-post', @@ -39,6 +40,7 @@ import { Post } from '../../../types/supabase'; QuillEditorComponent, HighlightModule, RouterModule, + TagMultiSelectComponent, ], providers: [AdminApiService, NgModel], templateUrl: './add-post.component.html', @@ -65,6 +67,7 @@ export class AddPostComponent implements OnInit { created_at: new FormControl(null), description: new FormControl(null), is_draft: new FormControl(false, { nonNullable: true }), + tags: new FormControl([], { nonNullable: true }), }); range: Range | null = null; @@ -108,9 +111,12 @@ export class AddPostComponent implements OnInit { this.blogForm.controls.created_at.setValue(null); } if (this.postId) { - this.apiService.updatePost(this.postId, this.blogForm.value as Post); + this.apiService.updatePost( + this.postId, + this.blogForm.value as PostUpdate, + ); } else { - this.apiService.addPost(this.blogForm.value as Post); + this.apiService.addPost(this.blogForm.value as PostInsert); } } } diff --git a/src/app/admin/_models/post-from.inteface.ts b/src/app/admin/_models/post-from.inteface.ts index 790037a..f0382f6 100644 --- a/src/app/admin/_models/post-from.inteface.ts +++ b/src/app/admin/_models/post-from.inteface.ts @@ -1,11 +1,12 @@ import { FormControl } from '@angular/forms'; import { SafeHtml } from '@angular/platform-browser'; +import { Tag } from '../../types/supabase'; export interface PostForm { title: FormControl; - content: FormControl; + content: FormControl; is_draft: FormControl; created_at: FormControl; - description?: FormControl; - tags: FormControl; + description: FormControl; + tags: FormControl; } diff --git a/src/app/admin/_services/admin-api.service.ts b/src/app/admin/_services/admin-api.service.ts index a3972c5..7851327 100755 --- a/src/app/admin/_services/admin-api.service.ts +++ b/src/app/admin/_services/admin-api.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { map, Observable } from 'rxjs'; -import { Post } from '../../supabase-types'; +import { Post, PostInsert, PostUpdate } from '../../supabase-types'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { SupabaseService } from '../../services/supabase.service'; import { environment } from '../../../environments/environment.local'; @@ -12,7 +12,7 @@ export class AdminApiService { private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`; private readonly apiKey = environment.supabaseKey; - async addPost(post: Post): Promise { + async addPost(post: PostInsert): Promise { const { error } = await this.supabaseService.getClient .from('posts') .insert({ ...post }); @@ -43,7 +43,7 @@ export class AdminApiService { .pipe(map((results) => results[0] ?? null)); } - async updatePost(id: string, post: Post): Promise { + async updatePost(id: string, post: PostUpdate): Promise { await this.supabaseService.getClient .from('posts') .update({ ...post }) diff --git a/src/app/shared/tag-multi-select/tag-multi-select.component.html b/src/app/shared/tag-multi-select/tag-multi-select.component.html new file mode 100644 index 0000000..4d45f4a --- /dev/null +++ b/src/app/shared/tag-multi-select/tag-multi-select.component.html @@ -0,0 +1,88 @@ +
+ + @if (selectedTags().length > 0) { +
+ @for (tag of selectedTags(); track tag.id) { +
+ {{ tag.name }} + + + +
+ } +
+ } + + +
+ + + + @if (isOpen() && !disabled()) { +
+ @for (tag of filteredTags(); track tag.id) { +
+ {{ tag.name }} + @if (tag.color) { +
+ } +
+ } + @empty { +
+ No tags found +
+ } +
+ } +
+
diff --git a/src/app/shared/tag-multi-select/tag-multi-select.component.ts b/src/app/shared/tag-multi-select/tag-multi-select.component.ts new file mode 100644 index 0000000..efb1071 --- /dev/null +++ b/src/app/shared/tag-multi-select/tag-multi-select.component.ts @@ -0,0 +1,199 @@ +import { + ChangeDetectionStrategy, + Component, + forwardRef, + inject, + OnInit, + signal, + computed, + HostListener, + ElementRef, + viewChild, +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { ReaderApiService } from '../../reader/_services/reader-api.service'; +import { Tag } from '../../types/supabase'; + +@Component({ + selector: 'blog-tag-multi-select', + standalone: true, + imports: [], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TagMultiSelectComponent), + multi: true, + }, + ], + templateUrl: './tag-multi-select.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TagMultiSelectComponent implements ControlValueAccessor, OnInit { + private elementRef = inject(ElementRef); + private readerApi = inject(ReaderApiService); + + searchInput = viewChild.required>('searchInput'); + + // Signals + allTags = signal([]); + selectedTags = signal([]); + searchTerm = signal(''); + isOpen = signal(false); + disabled = signal(false); + focusedTagId = signal(null); + isTouched = signal(false); + + // Search subject for debouncing + private searchSubject = new Subject(); + + // Computed values + filteredTags = computed(() => { + const search = this.searchTerm().toLowerCase(); + const selected = this.selectedTags(); + return this.allTags().filter( + (tag) => + tag.name.toLowerCase().includes(search) && + !selected.some((s) => s.id === tag.id), + ); + }); + + // ControlValueAccessor + private onChange = (value: Tag[]) => {}; + private onTouched = () => {}; + + ngOnInit() { + this.loadTags(); + } + + private loadTags() { + this.readerApi.getTags().subscribe({ + next: (tags) => this.allTags.set(tags!), + error: (error) => console.error('Failed to load tags:', error), + }); + } + + onSearchChange(event: Event) { + const target = event.target as HTMLInputElement; + const value = target.value; + this.searchTerm.set(value); + this.searchSubject.next(value); + } + + onInputFocus() { + this.isOpen.set(true); + this.focusedTagId.set(null); + } + + onInputBlur() { + setTimeout(() => { + this.isOpen.set(false); + this.markAsTouched(); + }, 150); + } + + selectTag(tag: Tag): void { + if (!this.selectedTags().find((t) => t.id === tag.id)) { + this.selectedTags.update((tags) => [...tags, tag]); + this.onChange(this.selectedTags()); + this.onTouched(); + + this.searchTerm.set(''); + this.searchInput().nativeElement.value = ''; + + this.isOpen.set(true); + + setTimeout(() => { + this.searchInput().nativeElement.focus(); + }, 0); + } + } + + removeTag(tag: Tag) { + const current = this.selectedTags(); + const updated = current.filter((t) => t.id !== tag.id); + this.selectedTags.set(updated); + this.onChange(updated); + this.markAsTouched(); + } + + isTagSelected(tag: Tag): boolean { + return this.selectedTags().some((t) => t.id === tag.id); + } + + trackByTagId(index: number, tag: Tag): number { + return tag.id; + } + + private markAsTouched() { + if (!this.isTouched()) { + this.isTouched.set(true); + this.onTouched(); + } + } + + @HostListener('document:keydown', ['$event']) + onKeyDown(event: KeyboardEvent) { + if (!this.isOpen() || this.disabled()) return; + + const filtered = this.filteredTags(); + const currentIndex = filtered.findIndex( + (tag) => tag.id === this.focusedTagId(), + ); + + switch (event.key) { + case 'ArrowDown': + event.preventDefault(); + const nextIndex = + currentIndex < filtered.length - 1 ? currentIndex + 1 : 0; + this.focusedTagId.set(filtered[nextIndex]?.id || null); + break; + + case 'ArrowUp': + event.preventDefault(); + const prevIndex = + currentIndex > 0 ? currentIndex - 1 : filtered.length - 1; + this.focusedTagId.set(filtered[prevIndex]?.id || null); + break; + + case 'Enter': + event.preventDefault(); + const focusedTag = filtered.find( + (tag) => tag.id === this.focusedTagId(), + ); + if (focusedTag) { + this.selectTag(focusedTag); + } + break; + + case 'Escape': + event.preventDefault(); + this.isOpen.set(false); + this.searchInput().nativeElement.blur(); + break; + } + } + + @HostListener('document:click', ['$event']) + onDocumentClick(event: Event) { + if (!this.elementRef.nativeElement.contains(event.target as Node)) { + this.isOpen.set(false); + } + } + + writeValue(value: Tag[]): void { + this.selectedTags.set(value || []); + } + + registerOnChange(fn: (value: Tag[]) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled.set(isDisabled); + } +} diff --git a/supabase/migrations/20250525091356_remote_schema.sql b/supabase/migrations/20250525091356_remote_schema.sql index 100c97f..64bd4b3 100644 --- a/supabase/migrations/20250525091356_remote_schema.sql +++ b/supabase/migrations/20250525091356_remote_schema.sql @@ -210,7 +210,7 @@ CREATE POLICY "Authenticated users can create profiles" ON "public"."profiles" F -CREATE POLICY "Only Admin can create post_tags" ON "public"."post_tags" FOR INSERT WITH CHECK (("auth"."role"() = 'Admin'::"text")); +CREATE POLICY "Only Admin can create post_tags" ON "public"."post_tags" FOR INSERT WITH CHECK (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text")); From 1185ddcdfcf32ca4866ed80c4f74b84655c53896 Mon Sep 17 00:00:00 2001 From: Luk Date: Tue, 10 Jun 2025 11:27:42 +0200 Subject: [PATCH 5/6] Enhance post creation and update functionality to include tag management --- llms.txt | 129 ++++++++++++++++++ .../add-post/add-post.component.ts | 17 ++- src/app/admin/_services/admin-api.service.ts | 124 +++++++++++++++-- .../20250525091356_remote_schema.sql | 2 +- 4 files changed, 252 insertions(+), 20 deletions(-) create mode 100644 llms.txt diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..47c724d --- /dev/null +++ b/llms.txt @@ -0,0 +1,129 @@ +# Angular + +Angular — Deliver web apps with confidence 🚀 + +## Table of Contents + +- [What is Angular](https://angular.dev/overview) +- [Installation guide](https://angular.dev/installation) +- [Style Guide](https://next.angular.dev/style-guide) + +## Components + +- [What is a component](https://angular.dev/guide/components) +- [Component selectors](https://angular.dev/guide/components/selectors) +- [Styling components](https://angular.dev/guide/components/styling) +- [Accepting data with input properties](https://angular.dev/guide/components/inputs) +- [Custom events with output](https://angular.dev/guide/components/outputs) +- [Content projection](https://angular.dev/guide/components/content-projection) +- [Component lifecycle](https://angular.dev/guide/components/lifecycle) + +## Templates guides + +- [Template Overview](https://angular.dev/guide/templates) +- [Adding event listeners](https://angular.dev/guide/templates/event-listeners) +- [Binding text, properties and attributes](https://angular.dev/guide/templates/binding) +- [Control Flow](https://angular.dev/guide/templates/control-flow) +- [Template variable declaration](https://angular.dev/guide/templates/variables) +- [Deferred loading of components](https://angular.dev/guide/templates/defer) +- [Expression syntax](https://angular.dev/guide/templates/expression-syntax) + +## Directives + +- [Directives overview](https://angular.dev/guide/directives) +- [Attribute directives](https://angular.dev/guide/directives/attribute-directives) +- [Structural directives](https://angular.dev/guide/directives/structural-directives) +- [Directive composition](https://angular.dev/guide/directives/directive-composition-api) +- [Optimizing images](https://angular.dev/guide/image-optimization) + +## Signals + +- [Signals overview](https://angular.dev/guide/signals) +- [Dependent state with linkedSignal](https://angular.dev/guide/signals/linked-signal) +- [Async reactivity with resources](https://angular.dev/guide/signals/resource) + +## Dependency injection (DI) + +- [Dependency Injection overview](https://angular.dev/guide/di) +- [Understanding Dependency injection](https://angular.dev/guide/di/dependency-injection) +- [Creating an injectable service](https://angular.dev/guide/di/creating-injectable-service) +- [Configuring dependency providers](https://angular.dev/guide/di/dependency-injection-providers) +- [Injection context](https://angular.dev/guide/di/dependency-injection-context) +- [Hierarchical injectors](https://angular.dev/guide/di/hierarchical-dependency-injection) +- [Optimizing Injection tokens](https://angular.dev/guide/di/lightweight-injection-tokens) + +## RxJS + +- [RxJS interop with Angular signals](https://angular.dev/ecosystem/rxjs-interop) +- [Component output interop](https://angular.dev/ecosystem/rxjs-interop/output-interop) + +## Loading Data + +- [HttpClient overview](https://angular.dev/guide/http) +- [Setting up the HttpClient](https://angular.dev/guide/http/setup) +- [Making requests](https://angular.dev/guide/http/making-requests) +- [Intercepting requests](https://angular.dev/guide/http/interceptors) +- [Testing](https://angular.dev/guide/http/testing) + +## Forms +- [Forms overview](https://angular.dev/guide/forms) +- [Reactive Forms](https://angular.dev/guide/forms/reactive-forms) +- [Strictly types forms](https://angular.dev/guide/forms/typed-forms) +- [Template driven forms](https://angular.dev/guide/forms/template-driven-forms) +- [Validate forms input](https://angular.dev/guide/forms/form-validation) +- [Building dynamic forms](https://angular.dev/guide/forms/dynamic-forms) + +## Routing +- [Routing overview](https://angular.dev/guide/routing) +- [Common routing tasks](https://angular.dev/guide/routing/common-router-tasks) +- [Routing in an SPA](https://angular.dev/guide/routing/router-tutorial) +- [Creating custom route matches](https://angular.dev/guide/routing/routing-with-urlmatcher) + +## Server Side Rendering (SSR) + +- [SSR Overview](https://angular.dev/guide/performance) +- [SSR with Angular](https://angular.dev/guide/ssr) +- [Build-time prerendering (SSG)](https://angular.dev/guide/prerendering) +- [Hybrid rendering with server routing](https://angular.dev/guide/hybrid-rendering) +- [Hydration](https://angular.dev/guide/hydration) +- [Incremental Hydration](https://angular.dev/guide/incremental-hydration) + +# CLI +[Angular CLI Overview](https://angular.dev/tools/cli) + +## Testing + +- [Testing overview](https://angular.dev/guide/testing) +- [Testing coverage](https://angular.dev/guide/testing/code-coverage) +- [Testing services](https://angular.dev/guide/testing/services) +- [Basics of component testing](https://angular.dev/guide/testing/components-basics) +- [Component testing scenarios](https://angular.dev/guide/testing/components-scenarios) +- [Testing attribute directives](https://angular.dev/guide/testing/attribute-directives +- [Testing pipes](https://angular.dev/guide/testing/pipes +- [Debugging tests](https://angular.dev/guide/testing/debugging) +- [Testing utility apis](https://angular.dev/guide/testing/utility-apis) +- [Component harness overview](https://angular.dev/guide/testing/component-harnesses-overview) +- [Using component harness in tests](https://angular.dev/guide/testing/using-component-harnesses) +- [Creating a component harness for your components](https://angular.dev/guide/testing/creating-component-harnesses) + +## Animations +- [Animations your content](https://angular.dev/guide/animations/css) +- [Route transition animation](https://angular.dev/guide/animations/route-animations) +- [Migrating to native CSS animations](https://next.angular.dev/guide/animations/migration) + +## APIs +- [API reference](https://angular.dev/api) +- [CLI command reference](https://angular.dev/cli) + + +## Others + +- [Zoneless](https://angular.dev/guide/experimental/zoneless) +- [Error encyclopedia](https://angular.dev/errors) +- [Extended diagnostics](https://angular.dev/extended-diagnostics) +- [Update guide](https://angular.dev/update-guide) +- [Contribute to Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md) +- [Angular's Roadmap](https://angular.dev/roadmap) +- [Keeping your projects up-to-date](https://angular.dev/update) +- [Security](https://angular.dev/best-practices/security) +- [Internationalization (i18n)](https://angular.dev/guide/i18n) diff --git a/src/app/admin/_components/add-post/add-post.component.ts b/src/app/admin/_components/add-post/add-post.component.ts index f39a842..c341cf3 100755 --- a/src/app/admin/_components/add-post/add-post.component.ts +++ b/src/app/admin/_components/add-post/add-post.component.ts @@ -95,7 +95,7 @@ export class AddPostComponent implements OnInit { onSubmit(isDraft = false): void { this.highlightContent(); - //test for description + // Test for description if (!this.blogForm?.controls?.description?.value) { this.blogForm.controls?.description?.setValue( this.blogForm.controls.content.value.toString().substring(0, 150), @@ -107,16 +107,21 @@ export class AddPostComponent implements OnInit { const cleanedContent = rawContent.replace(/( |\u00A0)/g, ' '); this.blogForm.controls.content.setValue(cleanedContent); this.blogForm.controls.is_draft.setValue(isDraft); + if (!this.blogForm.controls.created_at.value) { this.blogForm.controls.created_at.setValue(null); } + + // Extract form data including tags + const formData = { + ...this.blogForm.value, + tags: this.blogForm.controls.tags.value, + }; + if (this.postId) { - this.apiService.updatePost( - this.postId, - this.blogForm.value as PostUpdate, - ); + this.apiService.updatePost(this.postId, formData as PostUpdate & { tags: Tag[] }); } else { - this.apiService.addPost(this.blogForm.value as PostInsert); + this.apiService.addPost(formData as PostInsert & { tags: Tag[] }); } } } diff --git a/src/app/admin/_services/admin-api.service.ts b/src/app/admin/_services/admin-api.service.ts index 7851327..9d1866f 100755 --- a/src/app/admin/_services/admin-api.service.ts +++ b/src/app/admin/_services/admin-api.service.ts @@ -1,6 +1,6 @@ import { inject, Injectable } from '@angular/core'; import { map, Observable } from 'rxjs'; -import { Post, PostInsert, PostUpdate } from '../../supabase-types'; +import { Post, PostInsert, PostUpdate, Tag } from '../../supabase-types'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { SupabaseService } from '../../services/supabase.service'; import { environment } from '../../../environments/environment.local'; @@ -12,10 +12,42 @@ export class AdminApiService { private readonly baseUrl = `${environment.supabaseUrl}/rest/v1/`; private readonly apiKey = environment.supabaseKey; - async addPost(post: PostInsert): Promise { - const { error } = await this.supabaseService.getClient - .from('posts') - .insert({ ...post }); + async addPost(post: PostInsert & { tags?: Tag[] }): Promise { + const { tags, ...postData } = post; + + try { + const { data: insertedPost, error: postError } = await this.supabaseService.getClient + .from('posts') + .insert({ ...postData }) + .select('id') + .single(); + + if (postError) { + console.error('Error inserting post:', postError); + throw postError; + } + + if (tags && tags.length > 0 && insertedPost) { + const postTagInserts = tags.map(tag => ({ + post_id: insertedPost.id, + tag_id: tag.id + })); + + const { error: tagError } = await this.supabaseService.getClient + .from('post_tags') + .insert(postTagInserts); + + if (tagError) { + console.error('Error inserting post tags:', tagError); + throw tagError; + } + } + + console.log('Post created successfully with tags'); + } catch (error) { + console.error('Failed to create post:', error); + throw error; + } } getPostById(id: string): Observable { @@ -43,13 +75,79 @@ export class AdminApiService { .pipe(map((results) => results[0] ?? null)); } - async updatePost(id: string, post: PostUpdate): Promise { - await this.supabaseService.getClient - .from('posts') - .update({ ...post }) - .eq('id', id) - .then((x) => { - console.log(x); - }); + async updatePost(id: string, post: PostUpdate & { tags?: Tag[] }): Promise { + const { tags, ...postData } = post; + + try { + // Update the post + const { error: postError } = await this.supabaseService.getClient + .from('posts') + .update({ ...postData }) + .eq('id', id); + + if (postError) { + console.error('Error updating post:', postError); + throw postError; + } + + // Handle tags if provided + if (tags !== undefined) { + // Get existing tags for comparison + const { data: existingPostTags, error: fetchError } = await this.supabaseService.getClient + .from('post_tags') + .select('tag_id') + .eq('post_id', id); + + if (fetchError) { + console.error('Error fetching existing post tags:', fetchError); + throw fetchError; + } + + const existingTagIds = (existingPostTags || []).map(pt => pt.tag_id).sort(); + const newTagIds = tags.map(tag => tag.id).sort(); + + // Check if tags have actually changed using JSON comparison for better accuracy + const tagsChanged = JSON.stringify(existingTagIds) !== JSON.stringify(newTagIds); + + if (tagsChanged) { + console.log('Tags changed, updating...'); + + // Delete existing post-tag relationships + const { error: deleteError } = await this.supabaseService.getClient + .from('post_tags') + .delete() + .eq('post_id', id); + + if (deleteError) { + console.error('Error deleting existing post tags:', deleteError); + throw deleteError; + } + + // Insert new post-tag relationships if tags exist + if (tags.length > 0) { + const postTagInserts = tags.map(tag => ({ + post_id: id, + tag_id: tag.id + })); + + const { error: insertError } = await this.supabaseService.getClient + .from('post_tags') + .insert(postTagInserts); + + if (insertError) { + console.error('Error inserting new post tags:', insertError); + throw insertError; + } + } + } else { + console.log('Tags unchanged, skipping tag update'); + } + } + + console.log('Post updated successfully'); + } catch (error) { + console.error('Failed to update post:', error); + throw error; + } } } diff --git a/supabase/migrations/20250525091356_remote_schema.sql b/supabase/migrations/20250525091356_remote_schema.sql index 64bd4b3..be2ff46 100644 --- a/supabase/migrations/20250525091356_remote_schema.sql +++ b/supabase/migrations/20250525091356_remote_schema.sql @@ -226,7 +226,7 @@ CREATE POLICY "Only Admin can delete comments" ON "public"."comments" FOR DELETE -CREATE POLICY "Only Admin can delete post_tags" ON "public"."post_tags" FOR DELETE USING (("auth"."role"() = 'Admin'::"text")); +CREATE POLICY "Only Admin can delete post_tags" ON "public"."post_tags" FOR DELETE USING (((("auth"."jwt"() -> 'app_metadata'::"text") ->> 'role'::"text") = 'Admin'::"text")); From bf076a55017dc1b652e494dc82a85816cd9cac40 Mon Sep 17 00:00:00 2001 From: Luk Date: Tue, 10 Jun 2025 11:32:47 +0200 Subject: [PATCH 6/6] Remove unused import of SafeHtml from post-form.interface.ts --- src/app/admin/_models/post-from.inteface.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/admin/_models/post-from.inteface.ts b/src/app/admin/_models/post-from.inteface.ts index f0382f6..bf96ec1 100644 --- a/src/app/admin/_models/post-from.inteface.ts +++ b/src/app/admin/_models/post-from.inteface.ts @@ -1,5 +1,4 @@ import { FormControl } from '@angular/forms'; -import { SafeHtml } from '@angular/platform-browser'; import { Tag } from '../../types/supabase'; export interface PostForm {